• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_) {
119         TAG_LOGE(AAFwkTag::RECOVERY, "not enabled");
120         return false;
121     }
122 
123     if (!abilityRecoverys_.empty() && !abilityInfo->recoverable) {
124         TAG_LOGE(AAFwkTag::RECOVERY, "ability recoverable is false");
125         return false;
126     }
127     ability_ = ability;
128     std::shared_ptr<AbilityRecovery> abilityRecovery = std::make_shared<AbilityRecovery>();
129     abilityRecovery->InitAbilityInfo(ability, abilityInfo, token);
130     abilityRecovery->EnableAbilityRecovery(restartFlag_, saveOccasion_, saveMode_);
131     ability->EnableAbilityRecovery(abilityRecovery);
132     abilityRecoverys_.push_back(abilityRecovery);
133     auto handler = mainHandler_.lock();
134     if (handler != nullptr) {
135         auto task = []() {
136             AppRecovery::GetInstance().DeleteInValidMissionFiles();
137         };
138         if (!handler->PostTask(task, "AppRecovery:AddAbility", DELAY_TIME)) {
139             TAG_LOGE(AAFwkTag::RECOVERY, "DeleteInValidMissionFiles failed");
140         }
141     }
142     return true;
143 }
144 
RemoveAbility(const sptr<IRemoteObject> & tokenId)145 bool AppRecovery::RemoveAbility(const sptr<IRemoteObject>& tokenId)
146 {
147     if (!isEnable_) {
148         TAG_LOGE(AAFwkTag::RECOVERY, "not enabled");
149         return false;
150     }
151 
152     if (!tokenId) {
153         TAG_LOGE(AAFwkTag::RECOVERY, "tokenId is null");
154         return false;
155     }
156     TAG_LOGD(AAFwkTag::RECOVERY, "start");
157     auto itr = std::find_if(abilityRecoverys_.begin(), abilityRecoverys_.end(),
158         [&tokenId](std::shared_ptr<AbilityRecovery> &abilityRecovery) {
159         return (abilityRecovery && abilityRecovery->GetToken() == tokenId);
160     });
161     if (itr != abilityRecoverys_.end()) {
162         abilityRecoverys_.erase(itr);
163     }
164     return true;
165 }
166 
ScheduleSaveAppState(StateReason reason,uintptr_t ability)167 bool AppRecovery::ScheduleSaveAppState(StateReason reason, uintptr_t ability)
168 {
169     TAG_LOGD(AAFwkTag::RECOVERY, "begin");
170     if (!isEnable_) {
171         TAG_LOGE(AAFwkTag::RECOVERY, "is not enabled");
172         return false;
173     }
174 
175     if (!ShouldSaveAppState(reason)) {
176         TAG_LOGE(AAFwkTag::RECOVERY, "not save ability state");
177         return false;
178     }
179 
180     if (reason == StateReason::APP_FREEZE) {
181         auto abilityPtr = ability_.lock();
182         if (!abilityPtr || !abilityPtr->GetAbilityContext()) {
183             TAG_LOGE(AAFwkTag::RECOVERY, "ability or context is nullptr");
184             return false;
185         }
186         std::lock_guard<std::mutex> lock(g_mutex);
187         if (!BlockMainThreadLocked()) {
188             TAG_LOGE(AAFwkTag::RECOVERY, "Failed to block main thread");
189             return false;
190         }
191         OHOS::AbilityRuntime::JsUIAbility& jsAbility = static_cast<AbilityRuntime::JsUIAbility&>(*abilityPtr);
192         AbilityRuntime::JsRuntime& runtime = const_cast<AbilityRuntime::JsRuntime&>(jsAbility.GetJsRuntime());
193         auto& nativeEngine = runtime.GetNativeEngine();
194         nativeEngine.AllowCrossThreadExecution();
195         AppRecovery::GetInstance().DoSaveAppState(reason, ability);
196         return true;
197     }
198 
199     auto handler = mainHandler_.lock();
200     if (handler == nullptr) {
201         TAG_LOGE(AAFwkTag::RECOVERY, "handler is not exist");
202         return false;
203     }
204 
205     auto task = [reason, ability]() {
206         AppRecovery::GetInstance().DoSaveAppState(reason, ability);
207     };
208     if (!handler->PostTask(task, "AppRecovery:SaveAppState")) {
209         TAG_LOGE(AAFwkTag::RECOVERY, "Failed to schedule save app state");
210         return false;
211     }
212 
213     return true;
214 }
215 
SetRestartWant(std::shared_ptr<AAFwk::Want> want)216 void AppRecovery::SetRestartWant(std::shared_ptr<AAFwk::Want> want)
217 {
218     TAG_LOGD(AAFwkTag::RECOVERY, "begin");
219     if (!isEnable_) {
220         TAG_LOGE(AAFwkTag::RECOVERY, "not enabled");
221         return;
222     }
223     want_ = want;
224 }
225 
ScheduleRecoverApp(StateReason reason)226 bool AppRecovery::ScheduleRecoverApp(StateReason reason)
227 {
228     if (!isEnable_) {
229         TAG_LOGE(AAFwkTag::RECOVERY, "not enabled");
230         return false;
231     }
232 
233     if (!ShouldRecoverApp(reason)) {
234         TAG_LOGE(AAFwkTag::RECOVERY, "not recover app");
235         return false;
236     }
237 
238     if (abilityRecoverys_.empty()) {
239         TAG_LOGE(AAFwkTag::RECOVERY, "ability is nullptr");
240         return false;
241     }
242 
243     if (reason == StateReason::APP_FREEZE) {
244         DoRecoverApp(reason);
245         return true;
246     }
247 
248     auto handler = mainHandler_.lock();
249     if (handler == nullptr) {
250         TAG_LOGE(AAFwkTag::RECOVERY, "handler is not exist");
251         return false;
252     }
253 
254     // may we save state in other thread or just restart.
255     // 1. check whether main handler is still avaliable
256     // 2. do state saving in main thread or just restart app with no state?
257     // 3. create an recovery thread for saving state, just block jsvm mult-thread checking mechaism
258 
259     auto task = [reason]() {
260         AppRecovery::GetInstance().DoRecoverApp(reason);
261     };
262     if (!handler->PostTask(task, "AppRecovery:RecoverApp")) {
263         TAG_LOGE(AAFwkTag::RECOVERY, "Failed to schedule save app state");
264     }
265 
266     return true;
267 }
268 
TryRecoverApp(StateReason reason)269 bool AppRecovery::TryRecoverApp(StateReason reason)
270 {
271     if (!isEnable_) {
272         return false;
273     }
274 
275     ScheduleSaveAppState(reason);
276     PersistAppState();
277     return ScheduleRecoverApp(reason);
278 }
279 
DoRecoverApp(StateReason reason)280 void AppRecovery::DoRecoverApp(StateReason reason)
281 {
282     TAG_LOGD(AAFwkTag::RECOVERY, "begin");
283     if (abilityRecoverys_.empty()) {
284         TAG_LOGE(AAFwkTag::RECOVERY, "no ability exist");
285         return;
286     }
287     AAFwk::Want *want = nullptr;
288     if (want_ != nullptr) {
289         want = want_.get();
290     }
291 
292     if (abilityRecoverys_.size() == 1) {
293         if (abilityRecoverys_.front()->IsOnForeground()) {
294             abilityRecoverys_.front()->ScheduleRecoverAbility(reason, want);
295             return;
296         }
297     }
298 
299     for (auto itr = abilityRecoverys_.rbegin(); itr != abilityRecoverys_.rend(); itr++) {
300         if ((*itr)->IsOnForeground()) {
301             (*itr)->ScheduleRecoverAbility(reason, want);
302             break;
303         }
304     }
305 }
306 
DoSaveAppState(StateReason reason,uintptr_t ability)307 void AppRecovery::DoSaveAppState(StateReason reason, uintptr_t ability)
308 {
309     TAG_LOGD(AAFwkTag::RECOVERY, "begin");
310     auto appInfo = applicationInfo_.lock();
311     if (appInfo == nullptr || abilityRecoverys_.empty()) {
312         TAG_LOGE(AAFwkTag::RECOVERY, "Application or ability info is not exist.");
313         return;
314     }
315 
316     bool onlySaveTargetAbility = (ability != 0);
317     for (auto& abilityRecoveryRecord : abilityRecoverys_) {
318         if (!onlySaveTargetAbility) {
319             abilityRecoveryRecord->ScheduleSaveAbilityState(reason);
320             TAG_LOGD(AAFwkTag::RECOVERY, "not onlySaveTargetAbility");
321             continue;
322         }
323         if (abilityRecoveryRecord->IsSameAbility(ability)) {
324             abilityRecoveryRecord->ScheduleSaveAbilityState(reason);
325             TAG_LOGD(AAFwkTag::RECOVERY, "IsSameAbility");
326             break;
327         }
328     }
329 }
330 
EnableAppRecovery(uint16_t restartFlag,uint16_t saveFlag,uint16_t saveMode)331 void AppRecovery::EnableAppRecovery(uint16_t restartFlag, uint16_t saveFlag, uint16_t saveMode)
332 {
333     isEnable_ = true;
334     restartFlag_ = restartFlag;
335     saveOccasion_ = saveFlag;
336     saveMode_ = saveMode;
337 }
338 
ShouldSaveAppState(StateReason reason)339 bool AppRecovery::ShouldSaveAppState(StateReason reason)
340 {
341     bool ret = false;
342     switch (reason) {
343         case StateReason::DEVELOPER_REQUEST:
344             ret = true;
345             break;
346 
347         case StateReason::LIFECYCLE:
348             if ((saveOccasion_ & SaveOccasionFlag::SAVE_WHEN_BACKGROUND) != 0) {
349                 ret = true;
350             }
351             break;
352 
353         case StateReason::CPP_CRASH:
354         case StateReason::JS_ERROR:
355         case StateReason::CJ_ERROR:
356         case StateReason::APP_FREEZE: // appfreeze could not callback to js function safely.
357             if ((saveOccasion_ & SaveOccasionFlag::SAVE_WHEN_ERROR) != 0) {
358                 ret = true;
359             }
360             break;
361     }
362     return ret;
363 }
364 
ShouldRecoverApp(StateReason reason)365 bool AppRecovery::ShouldRecoverApp(StateReason reason)
366 {
367     if (restartFlag_ == RestartFlag::NO_RESTART) {
368         return false;
369     }
370 
371     bool ret = false;
372     bool isAlwaysStart = false;
373     if (restartFlag_ == RestartFlag::ALWAYS_RESTART) {
374         isAlwaysStart = true;
375     }
376     switch (reason) {
377         case StateReason::DEVELOPER_REQUEST:
378             ret = true;
379             break;
380 
381         case StateReason::LIFECYCLE:
382             ret = false;
383             break;
384 
385         case StateReason::CPP_CRASH:
386             ret = false;
387             break;
388 
389         case StateReason::JS_ERROR:
390             if (isAlwaysStart || (restartFlag_ & RestartFlag::RESTART_WHEN_JS_CRASH) != 0) {
391                 ret = true;
392             }
393             break;
394 
395         case StateReason::CJ_ERROR:
396             if (isAlwaysStart || (restartFlag_ & RestartFlag::RESTART_WHEN_CJ_CRASH) != 0) {
397                 ret = true;
398             }
399             break;
400 
401         case StateReason::APP_FREEZE:
402             if (isAlwaysStart || (restartFlag_ & RestartFlag::RESTART_WHEN_APP_FREEZE) != 0) {
403                 ret = true;
404             }
405             break;
406     }
407     return ret;
408 }
409 
DeleteInValidMissionFiles()410 void AppRecovery::DeleteInValidMissionFiles()
411 {
412     auto context = AbilityRuntime::Context::GetApplicationContext();
413     if (context == nullptr) {
414         return;
415     }
416 
417     std::string fileDir = context->GetFilesDir();
418     TAG_LOGD(AAFwkTag::RECOVERY, "fileDir: %{public}s", fileDir.c_str());
419     if (fileDir.empty() || !OHOS::FileExists(fileDir)) {
420         TAG_LOGD(AAFwkTag::RECOVERY, "empty fileDir or fileDir not exist");
421         return;
422     }
423     std::vector<int32_t> missionIds;
424     std::vector<MissionValidResult> results;
425 
426     if (!GetMissionIds(fileDir, missionIds)) {
427         TAG_LOGE(AAFwkTag::RECOVERY, "get mission id failed");
428         return;
429     }
430     if (missionIds.empty()) {
431         TAG_LOGD(AAFwkTag::RECOVERY, "missionIds empty");
432         return;
433     }
434     std::shared_ptr<AAFwk::AbilityManagerClient> abilityMgr = AAFwk::AbilityManagerClient::GetInstance();
435     if (abilityMgr == nullptr) {
436         TAG_LOGE(AAFwkTag::RECOVERY, "abilityMgr client is not exist");
437         return;
438     }
439     abilityMgr->IsValidMissionIds(missionIds, results);
440     if (results.empty()) {
441         TAG_LOGE(AAFwkTag::RECOVERY, "results is empty");
442         return;
443     }
444     for (auto& item : results) {
445         TAG_LOGI(AAFwkTag::RECOVERY, "missionId: %{public}d, isValid: %{public}d",
446             item.missionId, item.isValid);
447         if (!item.isValid) {
448             DeleteInValidMissionFileById(fileDir, item.missionId);
449         }
450     }
451 }
452 
DeleteInValidMissionFileById(std::string fileDir,int32_t missionId)453 void AppRecovery::DeleteInValidMissionFileById(std::string fileDir, int32_t missionId)
454 {
455     std::string fileName = std::to_string(missionId) + ".state";
456     std::string file = fileDir + "/" + fileName;
457     bool ret = OHOS::RemoveFile(file);
458     if (!ret) {
459         TAG_LOGE(AAFwkTag::RECOVERY, "file: %{public}s failed", file.c_str());
460     }
461 }
462 
GetMissionIds(std::string path,std::vector<int32_t> & missionIds)463 bool AppRecovery::GetMissionIds(std::string path, std::vector<int32_t> &missionIds)
464 {
465     DIR *dir = opendir(path.c_str());
466     if (dir == nullptr) {
467         TAG_LOGE(AAFwkTag::RECOVERY, "open dir error.");
468         return false;
469     }
470     struct dirent *ptr;
471     while ((ptr = readdir(dir)) != nullptr) {
472         if (ptr == nullptr) {
473             TAG_LOGE(AAFwkTag::RECOVERY, "read dir error.");
474             return false;
475         }
476         if (strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0) {
477             continue;
478         } else if (ptr->d_type == DT_REG) {
479             std::string fileName = ptr->d_name;
480             auto pos = fileName.find_first_of(".");
481             if (pos != std::string::npos) {
482                 std::string missionIdStr = fileName.substr(0, pos);
483                 missionIds.push_back(atoi(missionIdStr.c_str()));
484             }
485         } else {
486             continue;
487         }
488     }
489     closedir(dir);
490     return true;
491 }
492 
PersistAppState()493 bool AppRecovery::PersistAppState()
494 {
495     if (saveMode_ == SaveModeFlag::SAVE_WITH_FILE) {
496         return true;
497     }
498 
499     bool ret = true;
500     for (auto& abilityRecovery : abilityRecoverys_) {
501         ret = ret && abilityRecovery->PersistState();
502     }
503     return ret;
504 }
505 
IsEnabled() const506 bool AppRecovery::IsEnabled() const
507 {
508     return isEnable_;
509 }
510 
GetRestartFlag() const511 uint16_t AppRecovery::GetRestartFlag() const
512 {
513     return restartFlag_;
514 }
515 
GetSaveOccasionFlag() const516 uint16_t AppRecovery::GetSaveOccasionFlag() const
517 {
518     return saveOccasion_;
519 }
520 
GetSaveModeFlag() const521 uint16_t AppRecovery::GetSaveModeFlag() const
522 {
523     return saveMode_;
524 }
525 }  // namespace AbilityRuntime
526 }  // namespace OHOS
527