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