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 "ability_recovery.h"
17
18 #include <sys/mman.h>
19 #include <sys/stat.h>
20 #include <sys/types.h>
21 #include <unistd.h>
22
23 #include "ability_manager_client.h"
24 #include "app_recovery_parcel_allocator.h"
25 #include "context/application_context.h"
26 #include "file_ex.h"
27 #include "hilog_tag_wrapper.h"
28 #include "hitrace_meter.h"
29 #include "js_runtime.h"
30 #include "js_runtime_utils.h"
31 #include "napi/native_api.h"
32 #include "napi/native_common.h"
33 #include "parcel.h"
34 #include "recovery_param.h"
35 #include "string_ex.h"
36 #include "string_wrapper.h"
37 #include "want_params.h"
38
39 namespace OHOS {
40 namespace AppExecFwk {
41 namespace {
42 constexpr size_t DEFAULT_RECOVERY_MAX_RESTORE_SIZE = 400 * 1024;
43 constexpr int32_t CALL_BACK_ERROR = -1;
44
GetSaveAppCachePath(int32_t savedStateId)45 static std::string GetSaveAppCachePath(int32_t savedStateId)
46 {
47 auto context = AbilityRuntime::Context::GetApplicationContext();
48 if (context == nullptr) {
49 return "";
50 }
51
52 std::string fileDir = context->GetFilesDir();
53 TAG_LOGD(AAFwkTag::RECOVERY, "fileDir %{public}s", fileDir.c_str());
54 if (fileDir.empty() || !OHOS::FileExists(fileDir)) {
55 TAG_LOGE(AAFwkTag::RECOVERY, "empty fileDir or fileDir not exist");
56 return "";
57 }
58
59 std::string fileName = std::to_string(savedStateId) + ".state";
60 return fileDir + "/" + fileName;
61 }
62 }
63
AbilityRecovery()64 AbilityRecovery::AbilityRecovery() : isEnable_(false), restartFlag_(RestartFlag::ALWAYS_RESTART),
65 saveOccasion_(SaveOccasionFlag::SAVE_WHEN_ERROR), saveMode_(SaveModeFlag::SAVE_WITH_FILE)
66 {
67 }
68
~AbilityRecovery()69 AbilityRecovery::~AbilityRecovery()
70 {
71 }
72
InitAbilityInfo(const std::shared_ptr<AbilityRuntime::UIAbility> ability,const std::shared_ptr<AbilityInfo> & abilityInfo,const sptr<IRemoteObject> & token)73 bool AbilityRecovery::InitAbilityInfo(const std::shared_ptr<AbilityRuntime::UIAbility> ability,
74 const std::shared_ptr<AbilityInfo>& abilityInfo, const sptr<IRemoteObject>& token)
75 {
76 isEnable_ = true;
77 ability_ = ability;
78 abilityInfo_ = abilityInfo;
79 token_ = token;
80 auto abilityContext = ability->GetAbilityContext();
81 if (abilityContext != nullptr) {
82 abilityContext->GetMissionId(missionId_);
83 }
84 return true;
85 }
86
EnableAbilityRecovery(bool useAppSettedValue,uint16_t restartFlag,uint16_t saveFlag,uint16_t saveMode)87 void AbilityRecovery::EnableAbilityRecovery(bool useAppSettedValue, uint16_t restartFlag, uint16_t saveFlag,
88 uint16_t saveMode)
89 {
90 isEnable_ = true;
91 restartFlag_ = restartFlag;
92 useAppSettedValue_.store(useAppSettedValue);
93 saveOccasion_ = useAppSettedValue ? saveFlag : SaveOccasionFlag::SAVE_WHEN_BACKGROUND;
94 saveMode_ = saveMode;
95 }
96
IsSameAbility(uintptr_t ability)97 bool AbilityRecovery::IsSameAbility(uintptr_t ability)
98 {
99 return ability == jsAbilityPtr_;
100 }
101
SetJsAbility(uintptr_t ability)102 void AbilityRecovery::SetJsAbility(uintptr_t ability)
103 {
104 jsAbilityPtr_ = ability;
105 }
106
SaveStateCallback(AppExecFwk::OnSaveStateResult result)107 void AbilityRecovery::SaveStateCallback(AppExecFwk::OnSaveStateResult result)
108 {
109 if (result.status != AppExecFwk::OnSaveResult::ALL_AGREE &&
110 result.status != AppExecFwk::OnSaveResult::RECOVERY_AGREE) {
111 TAG_LOGE(AAFwkTag::RECOVERY, "save params failed");
112 return;
113 }
114 auto ability = ability_.lock();
115 if (ability == nullptr) {
116 return;
117 }
118 #ifdef SUPPORT_SCREEN
119 std::string pageStack = DefaultRecovery() ? ability->GetContentInfoForDefaultRecovery() :
120 ability->GetContentInfoForRecovery();
121 if (!pageStack.empty()) {
122 result.wantParams.SetParam("pageStack", AAFwk::String::Box(pageStack));
123 } else {
124 TAG_LOGE(AAFwkTag::RECOVERY, "get page stack failed");
125 }
126 TAG_LOGD(AAFwkTag::RECOVERY, "pageStack size: %{public}zu", pageStack.size());
127 #endif
128 if (saveMode_ == SaveModeFlag::SAVE_WITH_FILE) {
129 SerializeDataToFile(missionId_, result.wantParams);
130 } else if (saveMode_ == SaveModeFlag::SAVE_WITH_SHARED_MEMORY) {
131 params_ = result.wantParams;
132 }
133 auto token = token_.promote();
134 if (token == nullptr) {
135 TAG_LOGE(AAFwkTag::RECOVERY, "null token");
136 return;
137 }
138 std::shared_ptr<AAFwk::AbilityManagerClient> abilityMgr = AAFwk::AbilityManagerClient::GetInstance();
139 if (abilityMgr == nullptr) {
140 TAG_LOGE(AAFwkTag::RECOVERY, "null abilityMgr");
141 return;
142 }
143 abilityMgr->EnableRecoverAbility(token);
144 if (result.reason == StateReason::LIFECYCLE && DefaultRecovery()) {
145 TAG_LOGD(AAFwkTag::RECOVERY, "AppRecovery ScheduleSaveAbilityState SubmitSaveRecoveryInfo");
146 abilityMgr->SubmitSaveRecoveryInfo(token);
147 }
148 }
149
SaveAbilityState(StateReason reason)150 bool AbilityRecovery::SaveAbilityState(StateReason reason)
151 {
152 HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__);
153 auto ability = ability_.lock();
154 auto abilityInfo = abilityInfo_.lock();
155 if (ability == nullptr || abilityInfo == nullptr) {
156 TAG_LOGE(AAFwkTag::RECOVERY, "null ability");
157 return false;
158 }
159
160 auto *callbackInfo = AppExecFwk::AbilityTransactionCallbackInfo<AppExecFwk::OnSaveStateResult>::Create();
161 if (callbackInfo == nullptr) {
162 TAG_LOGE(AAFwkTag::APPKIT, "null callbackInfo");
163 return false;
164 }
165
166 auto weak = std::weak_ptr<AbilityRecovery>(shared_from_this());
167 auto callback = [weak](AppExecFwk::OnSaveStateResult result) {
168 auto recovery = weak.lock();
169 if (recovery != nullptr) {
170 recovery->SaveStateCallback(result);
171 }
172 };
173 callbackInfo->Push(callback);
174 AAFwk::WantParams wantParams;
175 bool isAsync = false;
176 int32_t status = ability->OnSaveState(AppExecFwk::StateType::APP_RECOVERY,
177 wantParams, callbackInfo, isAsync, reason);
178 if (status == CALL_BACK_ERROR) {
179 AppExecFwk::AbilityTransactionCallbackInfo<AppExecFwk::OnSaveStateResult>::Destroy(callbackInfo);
180 }
181 return true;
182 }
183
SerializeDataToFile(int32_t savedStateId,WantParams & params)184 bool AbilityRecovery::SerializeDataToFile(int32_t savedStateId, WantParams& params)
185 {
186 HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__);
187 std::string file = GetSaveAppCachePath(savedStateId);
188 if (file.empty()) {
189 TAG_LOGE(AAFwkTag::RECOVERY, "persisted file path failed");
190 return false;
191 }
192 Parcel parcel;
193 if (!params.Marshalling(parcel)) {
194 TAG_LOGE(AAFwkTag::RECOVERY, "Marshalling want param failed");
195 return false;
196 }
197
198 FILE *fileF = fopen(file.c_str(), "w+");
199 if (fileF == nullptr) {
200 TAG_LOGE(AAFwkTag::RECOVERY, "errno: %{public}d", errno);
201 return false;
202 }
203 size_t sz = parcel.GetDataSize();
204 uintptr_t buf = parcel.GetData();
205 if (sz == 0 || buf == 0) {
206 TAG_LOGE(AAFwkTag::RECOVERY, "get parcel data failed");
207 fclose(fileF);
208 return false;
209 }
210
211 if (DefaultRecovery() && (sz > DEFAULT_RECOVERY_MAX_RESTORE_SIZE)) {
212 TAG_LOGE(AAFwkTag::RECOVERY, "data is too large, size: %{public}zu", sz);
213 fclose(fileF);
214 return false;
215 }
216
217 size_t nwrite = fwrite(reinterpret_cast<uint8_t*>(buf), 1, sz, fileF);
218 if (nwrite != ERR_OK) {
219 TAG_LOGE(AAFwkTag::RECOVERY, "persist parcel data failed %{public}d", errno);
220 }
221 TAG_LOGD(AAFwkTag::RECOVERY, "file size: %{public}zu", sz);
222 fclose(fileF);
223 return true;
224 }
225
ReadSerializeDataFromFile(int32_t savedStateId,WantParams & params)226 bool AbilityRecovery::ReadSerializeDataFromFile(int32_t savedStateId, WantParams& params)
227 {
228 HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__);
229 std::string file = GetSaveAppCachePath(savedStateId);
230 if (file.empty()) {
231 TAG_LOGE(AAFwkTag::RECOVERY, "persisted file path failed");
232 return false;
233 }
234
235 TAG_LOGD(AAFwkTag::RECOVERY, "file path %{public}s", file.c_str());
236 char path[PATH_MAX] = {0};
237 if (realpath(file.c_str(), path) == nullptr) {
238 TAG_LOGE(AAFwkTag::RECOVERY, "errno is %{public}d", errno);
239 return false;
240 }
241
242 FILE *fileF = fopen(path, "r");
243 if (fileF == nullptr) {
244 TAG_LOGE(AAFwkTag::RECOVERY, "file open err: %{public}d", errno);
245 remove(path);
246 return false;
247 }
248 int fd = fileno(fileF);
249 struct stat statbuf;
250 if (fstat(fd, &statbuf) < 0) {
251 fclose(fileF);
252 remove(path);
253 return false;
254 }
255
256 auto mapFile = static_cast<uint8_t*>(mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0));
257 if (mapFile == MAP_FAILED) {
258 fclose(fileF);
259 remove(path);
260 return false;
261 }
262
263 Parcel parcel(new AppRecoveryParcelAllocator()); // do not dealloc mmap area
264 if (!parcel.ParseFrom(reinterpret_cast<uintptr_t>(mapFile), statbuf.st_size)) {
265 munmap(mapFile, statbuf.st_size);
266 fclose(fileF);
267 remove(path);
268 return false;
269 }
270
271 auto parsedParam = WantParams::Unmarshalling(parcel);
272 if (parsedParam != nullptr) {
273 params = *parsedParam;
274 delete parsedParam;
275 } else {
276 munmap(mapFile, statbuf.st_size);
277 fclose(fileF);
278 remove(path);
279 return false;
280 }
281
282 munmap(mapFile, statbuf.st_size);
283 fclose(fileF);
284 remove(path);
285 return true;
286 }
287
ScheduleSaveAbilityState(StateReason reason)288 bool AbilityRecovery::ScheduleSaveAbilityState(StateReason reason)
289 {
290 HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__);
291 if (!isEnable_) {
292 TAG_LOGE(AAFwkTag::RECOVERY, "not enable");
293 return false;
294 }
295
296 if (missionId_ <= 0) {
297 TAG_LOGE(AAFwkTag::RECOVERY, "invalid missionId_");
298 return false;
299 }
300
301 if (!IsSaveAbilityState(reason)) {
302 TAG_LOGE(AAFwkTag::RECOVERY, "not save ability state");
303 return false;
304 }
305
306 return SaveAbilityState(reason);
307 }
308
ScheduleRecoverAbility(StateReason reason,const Want * want)309 bool AbilityRecovery::ScheduleRecoverAbility(StateReason reason, const Want *want)
310 {
311 HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__);
312 if (!isEnable_) {
313 TAG_LOGE(AAFwkTag::RECOVERY, "not enable");
314 return false;
315 }
316
317 std::shared_ptr<AAFwk::AbilityManagerClient> abilityMgr = AAFwk::AbilityManagerClient::GetInstance();
318 if (abilityMgr == nullptr) {
319 TAG_LOGE(AAFwkTag::RECOVERY, "null abilityMgr");
320 return false;
321 }
322
323 auto token = token_.promote();
324 if (token == nullptr) {
325 return false;
326 }
327 abilityMgr->ScheduleRecoverAbility(token, reason, want);
328 return true;
329 }
330
PersistState()331 bool AbilityRecovery::PersistState()
332 {
333 auto abilityInfo = abilityInfo_.lock();
334 if (abilityInfo == nullptr) {
335 TAG_LOGE(AAFwkTag::RECOVERY, "null abilityInfo");
336 return false;
337 }
338 if (missionId_ <= 0) {
339 TAG_LOGE(AAFwkTag::RECOVERY, "invalid missionId");
340 return false;
341 }
342 if (!params_.IsEmpty()) {
343 SerializeDataToFile(missionId_, params_);
344 }
345 return true;
346 }
347
IsOnForeground()348 bool AbilityRecovery::IsOnForeground()
349 {
350 auto ability = ability_.lock();
351 if (ability == nullptr) {
352 return false;
353 }
354 AbilityLifecycleExecutor::LifecycleState state = ability->GetState();
355 TAG_LOGI(AAFwkTag::RECOVERY, "state: %{public}d", state);
356 if (state == AbilityLifecycleExecutor::LifecycleState::FOREGROUND_NEW) {
357 return true;
358 }
359 return false;
360 }
361
LoadSavedState(StateReason reason)362 bool AbilityRecovery::LoadSavedState(StateReason reason)
363 {
364 HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__);
365 auto abilityInfo = abilityInfo_.lock();
366 if (abilityInfo == nullptr) {
367 TAG_LOGE(AAFwkTag::RECOVERY, "null abilityInfo");
368 return false;
369 }
370
371 if (hasTryLoad_) {
372 return hasLoaded_;
373 }
374 if (missionId_ <= 0) {
375 TAG_LOGE(AAFwkTag::RECOVERY, "invalid missionId_");
376 return false;
377 }
378 hasTryLoad_ = true;
379
380 TAG_LOGD(AAFwkTag::RECOVERY, "missionId_:%{public}d", missionId_);
381 if (!ReadSerializeDataFromFile(missionId_, params_)) {
382 TAG_LOGE(AAFwkTag::RECOVERY, "find record for id:%{public}d failed", missionId_);
383 hasLoaded_ = false;
384 return hasLoaded_;
385 }
386
387 auto stringObj = AAFwk::IString::Query(params_.GetParam("pageStack"));
388 if (stringObj != nullptr) {
389 pageStack_ = AAFwk::String::Unbox(stringObj);
390 }
391 hasLoaded_ = true;
392 return hasLoaded_;
393 }
394
ScheduleRestoreAbilityState(StateReason reason,const Want & want)395 bool AbilityRecovery::ScheduleRestoreAbilityState(StateReason reason, const Want &want)
396 {
397 HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__);
398 if (!isEnable_) {
399 TAG_LOGE(AAFwkTag::RECOVERY, "not enable");
400 return false;
401 }
402
403 if (!IsSaveAbilityState(reason)) {
404 TAG_LOGE(AAFwkTag::RECOVERY, "not save ability state");
405 return false;
406 }
407
408 if (!LoadSavedState(reason)) {
409 TAG_LOGE(AAFwkTag::RECOVERY, "no saved state ");
410 return false;
411 }
412
413 const WantParams &wantParams = want.GetParams();
414 WantParams &wantCurrent = const_cast<WantParams&>(wantParams);
415 for (auto& i : params_.GetParams()) {
416 wantCurrent.SetParam(i.first, i.second.GetRefPtr());
417 }
418 return true;
419 }
420
GetSavedPageStack(StateReason reason)421 std::string AbilityRecovery::GetSavedPageStack(StateReason reason)
422 {
423 HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__);
424 if (!LoadSavedState(reason)) {
425 TAG_LOGE(AAFwkTag::RECOVERY, "no saved state");
426 return "";
427 }
428
429 if (pageStack_.empty()) {
430 TAG_LOGE(AAFwkTag::RECOVERY, "empty pageStack_");
431 }
432 return pageStack_;
433 }
434
IsSaveAbilityState(StateReason reason)435 bool AbilityRecovery::IsSaveAbilityState(StateReason reason)
436 {
437 TAG_LOGD(AAFwkTag::RECOVERY, "enter");
438 bool ret = false;
439 switch (reason) {
440 case StateReason::DEVELOPER_REQUEST:
441 ret = true;
442 break;
443
444 case StateReason::LIFECYCLE:
445 if ((saveOccasion_ & SaveOccasionFlag::SAVE_WHEN_BACKGROUND) != 0) {
446 ret = true;
447 }
448 break;
449
450 case StateReason::CPP_CRASH:
451 case StateReason::JS_ERROR:
452 case StateReason::CJ_ERROR:
453 case StateReason::APP_FREEZE:
454 if ((saveOccasion_ & SaveOccasionFlag::SAVE_WHEN_ERROR) != 0) {
455 ret = true;
456 }
457 break;
458
459 default:
460 ret = false;
461 break;
462 }
463 return ret;
464 }
465
GetRestartFlag() const466 uint16_t AbilityRecovery::GetRestartFlag() const
467 {
468 return restartFlag_;
469 }
470
GetSaveOccasionFlag() const471 uint16_t AbilityRecovery::GetSaveOccasionFlag() const
472 {
473 return saveOccasion_;
474 }
475
GetSaveModeFlag() const476 uint16_t AbilityRecovery::GetSaveModeFlag() const
477 {
478 return saveMode_;
479 }
480
DefaultRecovery() const481 bool AbilityRecovery::DefaultRecovery() const
482 {
483 return !(useAppSettedValue_.load());
484 }
485 } // namespace AbilityRuntime
486 } // namespace OHOS