1 /*
2 * Copyright (c) 2022 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_runtime/js_ability.h"
29 #include "js_runtime.h"
30 #include "js_runtime_utils.h"
31 #include "mission_info.h"
32 #include "napi/native_api.h"
33 #include "napi/native_common.h"
34
35 #include "ability_manager_client.h"
36
37 #include "directory_ex.h"
38 #include "file_ex.h"
39 #include "hilog_wrapper.h"
40 #include "parcel.h"
41 #include "want_params.h"
42 #include "recovery_param.h"
43 #include "string_ex.h"
44 #include "string_wrapper.h"
45 #include "ohos_application.h"
46
47 namespace OHOS {
48 namespace AppExecFwk {
49 std::mutex g_mutex;
50 std::atomic<bool> g_blocked = false;
51 const int DELAY_TIME = 1000;
52
AppRecovery()53 AppRecovery::AppRecovery() : isEnable_(false), restartFlag_(RestartFlag::ALWAYS_RESTART),
54 saveOccasion_(SaveOccasionFlag::SAVE_WHEN_ERROR), saveMode_(SaveModeFlag::SAVE_WITH_FILE)
55 {
56 }
57
~AppRecovery()58 AppRecovery::~AppRecovery()
59 {
60 }
61
SigQuitHandler(int signal)62 static void SigQuitHandler(int signal)
63 {
64 g_blocked = true;
65 std::lock_guard<std::mutex> lock(g_mutex);
66 g_blocked = false;
67 }
68
BlockMainThreadLocked()69 static bool BlockMainThreadLocked()
70 {
71 struct sigaction action;
72 (void)memset_s(&action, sizeof(action), 0, sizeof(action));
73 sigfillset(&action.sa_mask);
74 action.sa_handler = SigQuitHandler;
75 action.sa_flags = 0;
76 if (sigaction(SIGQUIT, &action, nullptr) != 0) {
77 HILOG_ERROR("AppRecovery Failed to register signal");
78 return false;
79 }
80
81 if (syscall(SYS_tgkill, getpid(), getpid(), SIGQUIT) != 0) {
82 HILOG_ERROR("Failed to send SIGQUIT to main thread, errno(%d).", errno);
83 return false;
84 }
85 int left = 1000000; // 1s
86 constexpr int pollTime = 100; // 100us
87 while (left > 0) {
88 int ret = usleep(pollTime);
89 if (ret == 0) {
90 left -= pollTime;
91 } else {
92 left -= ret;
93 }
94
95 if (g_blocked) {
96 return true;
97 }
98 }
99 return false;
100 }
101
GetInstance()102 AppRecovery& AppRecovery::GetInstance()
103 {
104 static AppRecovery instance;
105 return instance;
106 }
107
InitApplicationInfo(const std::shared_ptr<EventHandler> & mainHandler,const std::shared_ptr<ApplicationInfo> & applicationInfo)108 bool AppRecovery::InitApplicationInfo(const std::shared_ptr<EventHandler>& mainHandler,
109 const std::shared_ptr<ApplicationInfo>& applicationInfo)
110 {
111 mainHandler_ = mainHandler;
112 applicationInfo_ = applicationInfo;
113 return true;
114 }
115
AddAbility(std::shared_ptr<Ability> ability,const std::shared_ptr<AbilityInfo> & abilityInfo,const sptr<IRemoteObject> & token)116 bool AppRecovery::AddAbility(std::shared_ptr<Ability> ability,
117 const std::shared_ptr<AbilityInfo>& abilityInfo, const sptr<IRemoteObject>& token)
118 {
119 if (!isEnable_) {
120 HILOG_ERROR("AppRecovery not enabled.");
121 return false;
122 }
123
124 if (!abilityRecoverys_.empty() && !abilityInfo->recoverable) {
125 HILOG_ERROR("AppRecovery abilityRecoverys is not empty but ability recoverable is false.");
126 return false;
127 }
128 ability_ = ability;
129 std::shared_ptr<AbilityRecovery> abilityRecovery = std::make_shared<AbilityRecovery>();
130 abilityRecovery->InitAbilityInfo(ability, abilityInfo, token);
131 abilityRecovery->EnableAbilityRecovery(restartFlag_, saveOccasion_, saveMode_);
132 ability->EnableAbilityRecovery(abilityRecovery);
133 abilityRecoverys_.push_back(abilityRecovery);
134 auto handler = mainHandler_.lock();
135 if (handler != nullptr) {
136 auto task = []() {
137 AppRecovery::GetInstance().DeleteInValidMissionFiles();
138 };
139 if (!handler->PostTask(task, DELAY_TIME)) {
140 HILOG_ERROR("Failed to DeleteInValidMissionFiles.");
141 }
142 }
143 return true;
144 }
145
RemoveAbility(const sptr<IRemoteObject> & tokenId)146 bool AppRecovery::RemoveAbility(const sptr<IRemoteObject>& tokenId)
147 {
148 if (!isEnable_) {
149 HILOG_ERROR("AppRecovery not enabled. not removeAbility");
150 return false;
151 }
152
153 if (!tokenId) {
154 HILOG_ERROR("AppRecovery removeAbility tokenId is null.");
155 return false;
156 }
157 HILOG_DEBUG("AppRecovery removeAbility start");
158 auto itr = std::find_if(abilityRecoverys_.begin(), abilityRecoverys_.end(),
159 [&tokenId](std::shared_ptr<AbilityRecovery> &abilityRecovery) {
160 return (abilityRecovery && abilityRecovery->GetToken() == tokenId);
161 });
162 if (itr != abilityRecoverys_.end()) {
163 abilityRecoverys_.erase(itr);
164 HILOG_DEBUG("AppRecovery removeAbility done");
165 }
166 return true;
167 }
168
ScheduleSaveAppState(StateReason reason,uintptr_t ability)169 bool AppRecovery::ScheduleSaveAppState(StateReason reason, uintptr_t ability)
170 {
171 HILOG_DEBUG("AppRecovery ScheduleSaveAppState begin");
172 if (!isEnable_) {
173 HILOG_ERROR("AppRecovery ScheduleSaveAppState. is not enabled");
174 return false;
175 }
176
177 if (!ShouldSaveAppState(reason)) {
178 HILOG_ERROR("AppRecovery ts not save ability state");
179 return false;
180 }
181
182 if (reason == StateReason::APP_FREEZE) {
183 auto abilityPtr = ability_.lock();
184 if (!abilityPtr || !abilityPtr->GetAbilityContext()) {
185 HILOG_ERROR("AppRecovery ScheduleSaveAppState ability or context is nullptr");
186 return false;
187 }
188 std::lock_guard<std::mutex> lock(g_mutex);
189 if (!BlockMainThreadLocked()) {
190 HILOG_ERROR("AppRecovery Failed to block main thread, skip save state when appfreeze");
191 return false;
192 }
193 OHOS::AbilityRuntime::JsAbility& jsAbility = static_cast<AbilityRuntime::JsAbility&>(*abilityPtr);
194 AbilityRuntime::JsRuntime& runtime = const_cast<AbilityRuntime::JsRuntime&>(jsAbility.GetJsRuntime());
195 auto& nativeEngine = runtime.GetNativeEngine();
196 nativeEngine.AllowCrossThreadExecution();
197 AppRecovery::GetInstance().DoSaveAppState(reason, ability);
198 return true;
199 }
200
201 auto handler = mainHandler_.lock();
202 if (handler == nullptr) {
203 HILOG_ERROR("ScheduleSaveAppState. main handler is not exist");
204 return false;
205 }
206
207 auto task = [reason, ability]() {
208 AppRecovery::GetInstance().DoSaveAppState(reason, ability);
209 };
210 if (!handler->PostTask(task)) {
211 HILOG_ERROR("Failed to schedule save app state.");
212 return false;
213 }
214
215 return true;
216 }
217
SetRestartWant(std::shared_ptr<AAFwk::Want> want)218 void AppRecovery::SetRestartWant(std::shared_ptr<AAFwk::Want> want)
219 {
220 HILOG_DEBUG("AppRecovery SetRestartWant begin");
221 if (!isEnable_) {
222 HILOG_ERROR("AppRecovery SetRestartWant not enabled");
223 return;
224 }
225 want_ = want;
226 }
227
ScheduleRecoverApp(StateReason reason)228 bool AppRecovery::ScheduleRecoverApp(StateReason reason)
229 {
230 if (!isEnable_) {
231 HILOG_ERROR("AppRecovery ScheduleRecoverApp. is not enabled");
232 return false;
233 }
234
235 if (!ShouldRecoverApp(reason)) {
236 HILOG_ERROR("AppRecovery ScheduleRecoverApp. is not recover app");
237 return false;
238 }
239
240 if (abilityRecoverys_.empty()) {
241 HILOG_ERROR("AppRecovery ScheduleRecoverApp ability is nullptr");
242 return false;
243 }
244
245 if (reason == StateReason::APP_FREEZE) {
246 DoRecoverApp(reason);
247 return true;
248 }
249
250 auto handler = mainHandler_.lock();
251 if (handler == nullptr) {
252 HILOG_ERROR("AppRecovery ScheduleRecoverApp main handler is not exist");
253 return false;
254 }
255
256 // may we save state in other thread or just restart.
257 // 1. check whether main handler is still avaliable
258 // 2. do state saving in main thread or just restart app with no state?
259 // 3. create an recovery thread for saving state, just block jsvm mult-thread checking mechaism
260
261 auto task = [reason]() {
262 AppRecovery::GetInstance().DoRecoverApp(reason);
263 };
264 if (!handler->PostTask(task)) {
265 HILOG_ERROR("Failed to schedule save app state.");
266 }
267
268 return true;
269 }
270
TryRecoverApp(StateReason reason)271 bool AppRecovery::TryRecoverApp(StateReason reason)
272 {
273 if (!isEnable_) {
274 return false;
275 }
276
277 ScheduleSaveAppState(reason);
278 PersistAppState();
279 return ScheduleRecoverApp(reason);
280 }
281
DoRecoverApp(StateReason reason)282 void AppRecovery::DoRecoverApp(StateReason reason)
283 {
284 HILOG_DEBUG("AppRecovery DoRecoverApp begin");
285 if (abilityRecoverys_.empty()) {
286 HILOG_ERROR("AppRecovery no ability exist! ");
287 return;
288 }
289 AAFwk::Want *want = nullptr;
290 if (want_ != nullptr) {
291 want = want_.get();
292 }
293
294 if (abilityRecoverys_.size() == 1) {
295 abilityRecoverys_.front()->ScheduleRecoverAbility(reason, want);
296 return;
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 HILOG_WARN("AppRecovery no foreground ability, not DoRecoverApp!");
306 }
307
DoSaveAppState(StateReason reason,uintptr_t ability)308 void AppRecovery::DoSaveAppState(StateReason reason, uintptr_t ability)
309 {
310 HILOG_DEBUG("AppRecovery DoSaveAppState begin");
311 auto appInfo = applicationInfo_.lock();
312 if (appInfo == nullptr || abilityRecoverys_.empty()) {
313 HILOG_ERROR("AppRecovery Application or ability info is not exist.");
314 return;
315 }
316
317 bool onlySaveTargetAbility = (ability != 0);
318 for (auto& abilityRecoveryRecord : abilityRecoverys_) {
319 if (!onlySaveTargetAbility) {
320 abilityRecoveryRecord->ScheduleSaveAbilityState(reason);
321 HILOG_DEBUG("AppRecovery not onlySaveTargetAbility ScheduleSaveAbilityState");
322 continue;
323 }
324 if (abilityRecoveryRecord->IsSameAbility(ability)) {
325 abilityRecoveryRecord->ScheduleSaveAbilityState(reason);
326 HILOG_DEBUG("AppRecovery IsSameAbility ScheduleSaveAbilityState");
327 break;
328 }
329 }
330 }
331
EnableAppRecovery(uint16_t restartFlag,uint16_t saveFlag,uint16_t saveMode)332 void AppRecovery::EnableAppRecovery(uint16_t restartFlag, uint16_t saveFlag, uint16_t saveMode)
333 {
334 isEnable_ = true;
335 restartFlag_ = restartFlag;
336 saveOccasion_ = saveFlag;
337 saveMode_ = saveMode;
338 }
339
ShouldSaveAppState(StateReason reason)340 bool AppRecovery::ShouldSaveAppState(StateReason reason)
341 {
342 bool ret = false;
343 switch (reason) {
344 case StateReason::DEVELOPER_REQUEST:
345 ret = true;
346 break;
347
348 case StateReason::LIFECYCLE:
349 if ((saveOccasion_ & SaveOccasionFlag::SAVE_WHEN_BACKGROUND) != 0) {
350 ret = true;
351 }
352 break;
353
354 case StateReason::CPP_CRASH:
355 case StateReason::JS_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::APP_FREEZE:
396 if (isAlwaysStart || (restartFlag_ & RestartFlag::RESTART_WHEN_APP_FREEZE) != 0) {
397 ret = true;
398 }
399 break;
400 }
401 return ret;
402 }
403
DeleteInValidMissionFiles()404 void AppRecovery::DeleteInValidMissionFiles()
405 {
406 auto context = AbilityRuntime::Context::GetApplicationContext();
407 if (context == nullptr) {
408 return;
409 }
410
411 std::string fileDir = context->GetFilesDir();
412 HILOG_DEBUG("AppRecovery DeleteInValidMissionFiles fileDir: %{public}s", fileDir.c_str());
413 if (fileDir.empty() || !OHOS::FileExists(fileDir)) {
414 HILOG_DEBUG("AppRecovery DeleteInValidMissionFiles fileDir is empty or fileDir is not exists.");
415 return;
416 }
417 std::vector<int32_t> missionIds;
418 std::vector<MissionVaildResult> results;
419
420 if (!GetMissionIds(fileDir, missionIds)) {
421 HILOG_ERROR("AppRecovery get mssion file id error.");
422 return;
423 }
424 if (missionIds.empty()) {
425 HILOG_DEBUG("AppRecovery no mission file, no need delete it.");
426 return;
427 }
428 std::shared_ptr<AAFwk::AbilityManagerClient> abilityMgr = AAFwk::AbilityManagerClient::GetInstance();
429 if (abilityMgr == nullptr) {
430 HILOG_ERROR("AppRecovery DeleteInValidMissionFiles. abilityMgr client is not exist.");
431 return;
432 }
433 abilityMgr->IsValidMissionIds(missionIds, results);
434 if (results.empty()) {
435 HILOG_ERROR("AppRecovery DeleteInValidMissionFiles. results is empty.");
436 return;
437 }
438 for (auto& item : results) {
439 HILOG_INFO("AppRecovery missionResult: missionId: %{public}d, isValid: %{public}d", item.missionId,
440 item.isVaild);
441 if (!item.isVaild) {
442 DeleteInValidMissionFileById(fileDir, item.missionId);
443 }
444 }
445 }
446
DeleteInValidMissionFileById(std::string fileDir,int32_t missionId)447 void AppRecovery::DeleteInValidMissionFileById(std::string fileDir, int32_t missionId)
448 {
449 std::string fileName = std::to_string(missionId) + ".state";
450 std::string file = fileDir + "/" + fileName;
451 bool ret = OHOS::RemoveFile(file);
452 if (!ret) {
453 HILOG_ERROR("AppRecovery delete the file: %{public}s failed", file.c_str());
454 }
455 HILOG_DEBUG("AppRecovery delete the file: %{public}s done", file.c_str());
456 }
457
GetMissionIds(std::string path,std::vector<int32_t> & missionIds)458 bool AppRecovery::GetMissionIds(std::string path, std::vector<int32_t> &missionIds)
459 {
460 DIR *dir = opendir(path.c_str());
461 if (dir == nullptr) {
462 HILOG_ERROR("AppRecovery GetMissionIds open dir error.");
463 return false;
464 }
465 struct dirent *ptr;
466 while ((ptr = readdir(dir)) != nullptr) {
467 if (ptr == nullptr) {
468 HILOG_ERROR("AppRecovery GetMissionIds read dir error.");
469 return false;
470 }
471 if (strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0) {
472 continue;
473 } else if (ptr->d_type == DT_REG) {
474 std::string fileName = ptr->d_name;
475 auto pos = fileName.find_first_of(".");
476 if (pos != std::string::npos) {
477 std::string missionIdStr = fileName.substr(0, pos);
478 missionIds.push_back(atoi(missionIdStr.c_str()));
479 }
480 } else {
481 continue;
482 }
483 }
484 closedir(dir);
485 return true;
486 }
487
PersistAppState()488 bool AppRecovery::PersistAppState()
489 {
490 if (saveMode_ == SaveModeFlag::SAVE_WITH_FILE) {
491 return true;
492 }
493
494 bool ret = true;
495 for (auto& abilityRecovery : abilityRecoverys_) {
496 ret = ret && abilityRecovery->PersistState();
497 }
498 return ret;
499 }
500
IsEnabled() const501 bool AppRecovery::IsEnabled() const
502 {
503 return isEnable_;
504 }
505
GetRestartFlag() const506 uint16_t AppRecovery::GetRestartFlag() const
507 {
508 return restartFlag_;
509 }
510
GetSaveOccasionFlag() const511 uint16_t AppRecovery::GetSaveOccasionFlag() const
512 {
513 return saveOccasion_;
514 }
515
GetSaveModeFlag() const516 uint16_t AppRecovery::GetSaveModeFlag() const
517 {
518 return saveMode_;
519 }
520 } // namespace AbilityRuntime
521 } // namespace OHOS
522