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