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