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