1 /*
2 * Copyright (c) 2022-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "app_recovery.h"
17
18 #include <csignal>
19 #include <mutex>
20
21 #include <sys/mman.h>
22 #include <sys/stat.h>
23 #include <sys/types.h>
24
25 #include <syscall.h>
26 #include <unistd.h>
27
28 #include "ability_manager_client.h"
29 #include "context/application_context.h"
30 #include "directory_ex.h"
31 #include "file_ex.h"
32 #include "hilog_tag_wrapper.h"
33 #include "js_runtime.h"
34 #include "js_runtime_utils.h"
35 #include "js_ui_ability.h"
36 #include "mission_info.h"
37 #include "napi/native_api.h"
38 #include "napi/native_common.h"
39 #include "ohos_application.h"
40 #include "parcel.h"
41 #include "recovery_param.h"
42 #include "string_ex.h"
43 #include "string_wrapper.h"
44 #include "want_params.h"
45
46 namespace OHOS {
47 namespace AppExecFwk {
48 std::mutex g_mutex;
49 std::atomic<bool> g_blocked = false;
50 const int DELAY_TIME = 1000;
51
AppRecovery()52 AppRecovery::AppRecovery() : isEnable_(false), restartFlag_(RestartFlag::ALWAYS_RESTART),
53 saveOccasion_(SaveOccasionFlag::SAVE_WHEN_ERROR), saveMode_(SaveModeFlag::SAVE_WITH_FILE)
54 {
55 }
56
~AppRecovery()57 AppRecovery::~AppRecovery()
58 {
59 }
60
SigQuitHandler(int signal)61 static void SigQuitHandler(int signal)
62 {
63 g_blocked = true;
64 std::lock_guard<std::mutex> lock(g_mutex);
65 g_blocked = false;
66 }
67
BlockMainThreadLocked()68 static bool BlockMainThreadLocked()
69 {
70 struct sigaction action;
71 (void)memset_s(&action, sizeof(action), 0, sizeof(action));
72 sigfillset(&action.sa_mask);
73 action.sa_handler = SigQuitHandler;
74 action.sa_flags = 0;
75 if (sigaction(SIGQUIT, &action, nullptr) != 0) {
76 TAG_LOGE(AAFwkTag::RECOVERY, "Failed to register signal");
77 return false;
78 }
79
80 if (syscall(SYS_tgkill, getpid(), getpid(), SIGQUIT) != 0) {
81 TAG_LOGE(AAFwkTag::RECOVERY, "Failed to send SIGQUIT to main thread, errno(%d)", errno);
82 return false;
83 }
84 int left = 1000000; // 1s
85 constexpr int pollTime = 100; // 100us
86 while (left > 0) {
87 int ret = usleep(pollTime);
88 if (ret == 0) {
89 left -= pollTime;
90 } else {
91 left -= ret;
92 }
93
94 if (g_blocked) {
95 return true;
96 }
97 }
98 return false;
99 }
100
GetInstance()101 AppRecovery& AppRecovery::GetInstance()
102 {
103 static AppRecovery instance;
104 return instance;
105 }
106
InitApplicationInfo(const std::shared_ptr<EventHandler> & mainHandler,const std::shared_ptr<ApplicationInfo> & applicationInfo)107 bool AppRecovery::InitApplicationInfo(const std::shared_ptr<EventHandler>& mainHandler,
108 const std::shared_ptr<ApplicationInfo>& applicationInfo)
109 {
110 mainHandler_ = mainHandler;
111 applicationInfo_ = applicationInfo;
112 return true;
113 }
114
AddAbility(std::shared_ptr<AbilityRuntime::UIAbility> ability,const std::shared_ptr<AbilityInfo> & abilityInfo,const sptr<IRemoteObject> & token)115 bool AppRecovery::AddAbility(std::shared_ptr<AbilityRuntime::UIAbility> ability,
116 const std::shared_ptr<AbilityInfo>& abilityInfo, const sptr<IRemoteObject>& token)
117 {
118 if (isEnable_ && !abilityRecoverys_.empty() && !abilityInfo->recoverable) {
119 TAG_LOGE(AAFwkTag::RECOVERY, "recoverable is false");
120 return false;
121 }
122 ability_ = ability;
123 std::shared_ptr<AbilityRecovery> abilityRecovery = std::make_shared<AbilityRecovery>();
124 abilityRecovery->InitAbilityInfo(ability, abilityInfo, token);
125 abilityRecovery->EnableAbilityRecovery(useAppSettedValue_.load(), restartFlag_, saveOccasion_, saveMode_);
126 ability->EnableAbilityRecovery(abilityRecovery, useAppSettedValue_.load());
127 abilityRecoverys_.push_back(abilityRecovery);
128 auto handler = mainHandler_.lock();
129 if (handler != nullptr) {
130 auto task = []() {
131 AppRecovery::GetInstance().DeleteInValidMissionFiles();
132 };
133 if (!handler->PostTask(task, "AppRecovery:AddAbility", DELAY_TIME)) {
134 TAG_LOGE(AAFwkTag::RECOVERY, "DeleteInValidMissionFiles failed");
135 }
136 }
137 return true;
138 }
139
RemoveAbility(const sptr<IRemoteObject> & tokenId)140 bool AppRecovery::RemoveAbility(const sptr<IRemoteObject>& tokenId)
141 {
142 if (!isEnable_) {
143 TAG_LOGE(AAFwkTag::RECOVERY, "not enabled");
144 return false;
145 }
146
147 if (!tokenId) {
148 TAG_LOGE(AAFwkTag::RECOVERY, "tokenId is null");
149 return false;
150 }
151 TAG_LOGD(AAFwkTag::RECOVERY, "start");
152 auto itr = std::find_if(abilityRecoverys_.begin(), abilityRecoverys_.end(),
153 [&tokenId](std::shared_ptr<AbilityRecovery> &abilityRecovery) {
154 return (abilityRecovery && abilityRecovery->GetToken() == tokenId);
155 });
156 if (itr != abilityRecoverys_.end()) {
157 abilityRecoverys_.erase(itr);
158 }
159 return true;
160 }
161
ScheduleSaveAppState(StateReason reason,uintptr_t ability)162 bool AppRecovery::ScheduleSaveAppState(StateReason reason, uintptr_t ability)
163 {
164 TAG_LOGD(AAFwkTag::RECOVERY, "begin");
165 if (!isEnable_) {
166 TAG_LOGE(AAFwkTag::RECOVERY, "is not enabled");
167 return false;
168 }
169
170 if (!ShouldSaveAppState(reason)) {
171 TAG_LOGE(AAFwkTag::RECOVERY, "not save ability state");
172 return false;
173 }
174
175 if (reason == StateReason::APP_FREEZE) {
176 auto abilityPtr = ability_.lock();
177 if (!abilityPtr || !abilityPtr->GetAbilityContext()) {
178 TAG_LOGE(AAFwkTag::RECOVERY, "ability or context is nullptr");
179 return false;
180 }
181 std::lock_guard<std::mutex> lock(g_mutex);
182 if (!BlockMainThreadLocked()) {
183 TAG_LOGE(AAFwkTag::RECOVERY, "Failed to block main thread");
184 return false;
185 }
186 OHOS::AbilityRuntime::JsUIAbility& jsAbility = static_cast<AbilityRuntime::JsUIAbility&>(*abilityPtr);
187 AbilityRuntime::JsRuntime& runtime = const_cast<AbilityRuntime::JsRuntime&>(jsAbility.GetJsRuntime());
188 auto& nativeEngine = runtime.GetNativeEngine();
189 nativeEngine.AllowCrossThreadExecution();
190 AppRecovery::GetInstance().DoSaveAppState(reason, ability);
191 return true;
192 }
193
194 auto handler = mainHandler_.lock();
195 if (handler == nullptr) {
196 TAG_LOGE(AAFwkTag::RECOVERY, "handler is not exist");
197 return false;
198 }
199
200 auto task = [reason, ability]() {
201 AppRecovery::GetInstance().DoSaveAppState(reason, ability);
202 };
203 if (!handler->PostTask(task, "AppRecovery:SaveAppState")) {
204 TAG_LOGE(AAFwkTag::RECOVERY, "Failed to schedule save app state");
205 return false;
206 }
207
208 return true;
209 }
210
SetRestartWant(std::shared_ptr<AAFwk::Want> want)211 void AppRecovery::SetRestartWant(std::shared_ptr<AAFwk::Want> want)
212 {
213 TAG_LOGD(AAFwkTag::RECOVERY, "begin");
214 if (!isEnable_) {
215 TAG_LOGE(AAFwkTag::RECOVERY, "not enabled");
216 return;
217 }
218 want_ = want;
219 }
220
ScheduleRecoverApp(StateReason reason)221 bool AppRecovery::ScheduleRecoverApp(StateReason reason)
222 {
223 if (!isEnable_) {
224 TAG_LOGE(AAFwkTag::RECOVERY, "not enabled");
225 return false;
226 }
227
228 if (!ShouldRecoverApp(reason)) {
229 TAG_LOGE(AAFwkTag::RECOVERY, "not recover app");
230 return false;
231 }
232
233 if (abilityRecoverys_.empty()) {
234 TAG_LOGE(AAFwkTag::RECOVERY, "ability is nullptr");
235 return false;
236 }
237
238 if (reason == StateReason::APP_FREEZE) {
239 DoRecoverApp(reason);
240 return true;
241 }
242
243 auto handler = mainHandler_.lock();
244 if (handler == nullptr) {
245 TAG_LOGE(AAFwkTag::RECOVERY, "handler is not exist");
246 return false;
247 }
248
249 // may we save state in other thread or just restart.
250 // 1. check whether main handler is still avaliable
251 // 2. do state saving in main thread or just restart app with no state?
252 // 3. create an recovery thread for saving state, just block jsvm mult-thread checking mechaism
253
254 auto task = [reason]() {
255 AppRecovery::GetInstance().DoRecoverApp(reason);
256 };
257 if (!handler->PostTask(task, "AppRecovery:RecoverApp")) {
258 TAG_LOGE(AAFwkTag::RECOVERY, "Failed to schedule save app state");
259 }
260
261 return true;
262 }
263
TryRecoverApp(StateReason reason)264 bool AppRecovery::TryRecoverApp(StateReason reason)
265 {
266 if (!isEnable_) {
267 return false;
268 }
269
270 ScheduleSaveAppState(reason);
271 PersistAppState();
272 return ScheduleRecoverApp(reason);
273 }
274
DoRecoverApp(StateReason reason)275 void AppRecovery::DoRecoverApp(StateReason reason)
276 {
277 TAG_LOGD(AAFwkTag::RECOVERY, "begin");
278 if (abilityRecoverys_.empty()) {
279 TAG_LOGE(AAFwkTag::RECOVERY, "no ability exist");
280 return;
281 }
282 AAFwk::Want *want = nullptr;
283 if (want_ != nullptr) {
284 want = want_.get();
285 }
286
287 if (abilityRecoverys_.size() == 1) {
288 if (abilityRecoverys_.front()->IsOnForeground()) {
289 abilityRecoverys_.front()->ScheduleRecoverAbility(reason, want);
290 return;
291 }
292 }
293
294 for (auto itr = abilityRecoverys_.rbegin(); itr != abilityRecoverys_.rend(); itr++) {
295 if ((*itr)->IsOnForeground()) {
296 (*itr)->ScheduleRecoverAbility(reason, want);
297 break;
298 }
299 }
300 }
301
DoSaveAppState(StateReason reason,uintptr_t ability)302 void AppRecovery::DoSaveAppState(StateReason reason, uintptr_t ability)
303 {
304 TAG_LOGD(AAFwkTag::RECOVERY, "begin");
305 auto appInfo = applicationInfo_.lock();
306 if (appInfo == nullptr || abilityRecoverys_.empty()) {
307 TAG_LOGE(AAFwkTag::RECOVERY, "Application or ability info is not exist.");
308 return;
309 }
310
311 bool onlySaveTargetAbility = (ability != 0);
312 for (auto& abilityRecoveryRecord : abilityRecoverys_) {
313 if (!onlySaveTargetAbility) {
314 abilityRecoveryRecord->ScheduleSaveAbilityState(reason);
315 TAG_LOGD(AAFwkTag::RECOVERY, "not onlySaveTargetAbility");
316 continue;
317 }
318 if (abilityRecoveryRecord->IsSameAbility(ability)) {
319 abilityRecoveryRecord->ScheduleSaveAbilityState(reason);
320 TAG_LOGD(AAFwkTag::RECOVERY, "IsSameAbility");
321 break;
322 }
323 }
324 }
325
EnableAppRecovery(uint16_t restartFlag,uint16_t saveFlag,uint16_t saveMode)326 void AppRecovery::EnableAppRecovery(uint16_t restartFlag, uint16_t saveFlag, uint16_t saveMode)
327 {
328 isEnable_ = true;
329 restartFlag_ = restartFlag;
330 saveOccasion_ = saveFlag;
331 saveMode_ = saveMode;
332 useAppSettedValue_.store(true);
333 }
334
ShouldSaveAppState(StateReason reason)335 bool AppRecovery::ShouldSaveAppState(StateReason reason)
336 {
337 bool ret = false;
338 switch (reason) {
339 case StateReason::DEVELOPER_REQUEST:
340 ret = true;
341 break;
342
343 case StateReason::LIFECYCLE:
344 if ((saveOccasion_ & SaveOccasionFlag::SAVE_WHEN_BACKGROUND) != 0) {
345 ret = true;
346 }
347 break;
348
349 case StateReason::CPP_CRASH:
350 case StateReason::JS_ERROR:
351 case StateReason::CJ_ERROR:
352 case StateReason::APP_FREEZE: // appfreeze could not callback to js function safely.
353 if ((saveOccasion_ & SaveOccasionFlag::SAVE_WHEN_ERROR) != 0) {
354 ret = true;
355 }
356 break;
357 }
358 return ret;
359 }
360
ShouldRecoverApp(StateReason reason)361 bool AppRecovery::ShouldRecoverApp(StateReason reason)
362 {
363 if (restartFlag_ == RestartFlag::NO_RESTART) {
364 return false;
365 }
366
367 bool ret = false;
368 bool isAlwaysStart = false;
369 if (restartFlag_ == RestartFlag::ALWAYS_RESTART) {
370 isAlwaysStart = true;
371 }
372 switch (reason) {
373 case StateReason::DEVELOPER_REQUEST:
374 ret = true;
375 break;
376
377 case StateReason::LIFECYCLE:
378 ret = false;
379 break;
380
381 case StateReason::CPP_CRASH:
382 ret = false;
383 break;
384
385 case StateReason::JS_ERROR:
386 if (isAlwaysStart || (restartFlag_ & RestartFlag::RESTART_WHEN_JS_CRASH) != 0) {
387 ret = true;
388 }
389 break;
390
391 case StateReason::CJ_ERROR:
392 if (isAlwaysStart || (restartFlag_ & RestartFlag::RESTART_WHEN_CJ_CRASH) != 0) {
393 ret = true;
394 }
395 break;
396
397 case StateReason::APP_FREEZE:
398 if (isAlwaysStart || (restartFlag_ & RestartFlag::RESTART_WHEN_APP_FREEZE) != 0) {
399 ret = true;
400 }
401 break;
402 }
403 return ret;
404 }
405
DeleteInValidMissionFiles()406 void AppRecovery::DeleteInValidMissionFiles()
407 {
408 auto context = AbilityRuntime::Context::GetApplicationContext();
409 if (context == nullptr) {
410 return;
411 }
412
413 std::string fileDir = context->GetFilesDir();
414 TAG_LOGD(AAFwkTag::RECOVERY, "fileDir: %{public}s", fileDir.c_str());
415 if (fileDir.empty() || !OHOS::FileExists(fileDir)) {
416 TAG_LOGD(AAFwkTag::RECOVERY, "empty fileDir or fileDir not exist");
417 return;
418 }
419 std::vector<int32_t> missionIds;
420 std::vector<MissionValidResult> results;
421
422 if (!GetMissionIds(fileDir, missionIds)) {
423 TAG_LOGE(AAFwkTag::RECOVERY, "get mission id failed");
424 return;
425 }
426 if (missionIds.empty()) {
427 TAG_LOGD(AAFwkTag::RECOVERY, "missionIds empty");
428 return;
429 }
430 std::shared_ptr<AAFwk::AbilityManagerClient> abilityMgr = AAFwk::AbilityManagerClient::GetInstance();
431 if (abilityMgr == nullptr) {
432 TAG_LOGE(AAFwkTag::RECOVERY, "abilityMgr client is not exist");
433 return;
434 }
435 abilityMgr->IsValidMissionIds(missionIds, results);
436 if (results.empty()) {
437 TAG_LOGE(AAFwkTag::RECOVERY, "results is empty");
438 return;
439 }
440 for (auto& item : results) {
441 TAG_LOGI(AAFwkTag::RECOVERY, "missionId: %{public}d, isValid: %{public}d",
442 item.missionId, item.isValid);
443 if (!item.isValid) {
444 DeleteInValidMissionFileById(fileDir, item.missionId);
445 }
446 }
447 }
448
DeleteInValidMissionFileById(std::string fileDir,int32_t missionId)449 void AppRecovery::DeleteInValidMissionFileById(std::string fileDir, int32_t missionId)
450 {
451 std::string fileName = std::to_string(missionId) + ".state";
452 std::string file = fileDir + "/" + fileName;
453 bool ret = OHOS::RemoveFile(file);
454 if (!ret) {
455 TAG_LOGE(AAFwkTag::RECOVERY, "file: %{public}s failed", file.c_str());
456 }
457 }
458
ClearPageStack(std::string bundleName)459 void AppRecovery::ClearPageStack(std::string bundleName)
460 {
461 DeleteInValidMissionFiles();
462 std::shared_ptr<AAFwk::AbilityManagerClient> abilityMgr = AAFwk::AbilityManagerClient::GetInstance();
463 if (abilityMgr == nullptr) {
464 TAG_LOGE(AAFwkTag::RECOVERY, "abilityMgr not exist.");
465 return;
466 }
467 abilityMgr->ScheduleClearRecoveryPageStack();
468 }
469
GetMissionIds(std::string path,std::vector<int32_t> & missionIds)470 bool AppRecovery::GetMissionIds(std::string path, std::vector<int32_t> &missionIds)
471 {
472 DIR *dir = opendir(path.c_str());
473 if (dir == nullptr) {
474 TAG_LOGE(AAFwkTag::RECOVERY, "open dir error.");
475 return false;
476 }
477 struct dirent *ptr;
478 while ((ptr = readdir(dir)) != nullptr) {
479 if (ptr == nullptr) {
480 TAG_LOGE(AAFwkTag::RECOVERY, "read dir error.");
481 return false;
482 }
483 if (strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0) {
484 continue;
485 } else if (ptr->d_type == DT_REG) {
486 std::string fileName = ptr->d_name;
487 auto pos = fileName.find_first_of(".");
488 if (pos != std::string::npos) {
489 std::string missionIdStr = fileName.substr(0, pos);
490 missionIds.push_back(atoi(missionIdStr.c_str()));
491 }
492 } else {
493 continue;
494 }
495 }
496 closedir(dir);
497 return true;
498 }
499
PersistAppState()500 bool AppRecovery::PersistAppState()
501 {
502 if (saveMode_ == SaveModeFlag::SAVE_WITH_FILE) {
503 return true;
504 }
505
506 bool ret = true;
507 for (auto& abilityRecovery : abilityRecoverys_) {
508 ret = ret && abilityRecovery->PersistState();
509 }
510 return ret;
511 }
512
IsEnabled() const513 bool AppRecovery::IsEnabled() const
514 {
515 return isEnable_;
516 }
517
GetRestartFlag() const518 uint16_t AppRecovery::GetRestartFlag() const
519 {
520 return restartFlag_;
521 }
522
GetSaveOccasionFlag() const523 uint16_t AppRecovery::GetSaveOccasionFlag() const
524 {
525 return saveOccasion_;
526 }
527
GetSaveModeFlag() const528 uint16_t AppRecovery::GetSaveModeFlag() const
529 {
530 return saveMode_;
531 }
532 } // namespace AbilityRuntime
533 } // namespace OHOS
534