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_api.h"
17 
18 #include "app_recovery.h"
19 #include "hilog_tag_wrapper.h"
20 #include "js_runtime.h"
21 #include "js_runtime_utils.h"
22 
23 #include "napi/native_api.h"
24 #include "napi/native_common.h"
25 #include "napi/native_node_api.h"
26 
27 #include "napi_common_want.h"
28 #include "recovery_param.h"
29 #include "want.h"
30 
31 namespace OHOS {
32 namespace AbilityRuntime {
33 using namespace OHOS::AppExecFwk;
34 namespace {
35 enum RestartErrno {
36     NO_ERROR,
37     SAVED_STATE_NOT_EXIST,
38     LAST_RESTART_ENCOUNTER_ERROR,
39 };
40 class AppRecoveryApiRegistry {
41 public:
42     AppRecoveryApiRegistry() = default;
43     ~AppRecoveryApiRegistry() = default;
44 
Finalizer(napi_env env,void * data,void * hint)45     static void Finalizer(napi_env env, void *data, void *hint)
46     {
47         std::unique_ptr<AppRecoveryApiRegistry>(static_cast<AppRecoveryApiRegistry*>(data));
48     }
49 
EnableAppRecovery(napi_env env,napi_callback_info info)50     static napi_value EnableAppRecovery(napi_env env, napi_callback_info info)
51     {
52         GET_CB_INFO_AND_CALL(env, info, AppRecoveryApiRegistry, OnEnableAppRecovery);
53     }
54 
RestartApp(napi_env env,napi_callback_info info)55     static napi_value RestartApp(napi_env env, napi_callback_info info)
56     {
57         GET_CB_INFO_AND_CALL(env, info, AppRecoveryApiRegistry, OnRestartApp);
58     }
59 
SaveAppState(napi_env env,napi_callback_info info)60     static napi_value SaveAppState(napi_env env, napi_callback_info info)
61     {
62         GET_CB_INFO_AND_CALL(env, info, AppRecoveryApiRegistry, OnSaveAppState);
63     }
64 
SetRestartWant(napi_env env,napi_callback_info info)65     static napi_value SetRestartWant(napi_env env, napi_callback_info info)
66     {
67         GET_CB_INFO_AND_CALL(env, info, AppRecoveryApiRegistry, OnSetRestartWant);
68     }
69 
70 private:
OnEnableAppRecovery(napi_env env,const size_t argc,napi_value * argv)71     napi_value OnEnableAppRecovery(napi_env env, const size_t argc, napi_value* argv)
72     {
73         size_t parameterCount = argc;
74         napi_value result = CreateJsUndefined(env);
75         constexpr int maxCount = 3;
76         if (parameterCount > maxCount) {
77             return result;
78         }
79 
80         uint16_t flags[] = {
81             RestartFlag::ALWAYS_RESTART,
82             SaveOccasionFlag::SAVE_WHEN_ERROR,
83             SaveModeFlag::SAVE_WITH_FILE
84         };
85 
86         for (size_t i = 0; i < parameterCount; ++i) {
87             napi_valuetype paramType;
88             napi_typeof(env, argv[i], &paramType);
89             if (paramType != napi_number) {
90                 TAG_LOGE(
91                     AAFwkTag::RECOVERY, "argv[%{public}s] not number", std::to_string(i).c_str());
92                 return result;
93             }
94             int32_t tmp = 0;
95             napi_get_value_int32(env, argv[i], &tmp);
96             flags[i] = static_cast<uint16_t>(tmp);
97         }
98 
99         if (!CheckParamsValid(flags)) {
100             return result;
101         }
102 
103         AppRecovery::GetInstance().EnableAppRecovery(flags[0],  // 0:RestartFlag
104                                                      flags[1],  // 1:SaveOccasionFlag
105                                                      flags[2]); // 2:SaveModeFlag
106         return result;
107     }
108 
CheckParamsValid(const uint16_t params[])109     bool CheckParamsValid(const uint16_t params[])
110     {
111         uint16_t restartFlag = params[0];
112         constexpr uint16_t restartMaxVal = 0x0003;
113         if ((restartFlag < 0 || restartFlag > restartMaxVal) && (restartFlag != RestartFlag::NO_RESTART)) {
114             TAG_LOGE(AAFwkTag::RECOVERY, "invalid restartFlag: %{public}d", restartFlag);
115             return false;
116         }
117         uint16_t saveFlag = params[1];
118         constexpr uint16_t saveMaxVal = 0x0003;
119         if (saveFlag < SaveOccasionFlag::SAVE_WHEN_ERROR || saveFlag > saveMaxVal) {
120             TAG_LOGE(AAFwkTag::RECOVERY, "invalid saveOccasionFlag: %{public}d", saveFlag);
121             return false;
122         }
123         uint16_t saveModeFlag = params[2];
124         if (saveModeFlag < SaveModeFlag::SAVE_WITH_FILE || saveModeFlag > SaveModeFlag::SAVE_WITH_SHARED_MEMORY) {
125             TAG_LOGE(AAFwkTag::RECOVERY, "invalid saveModeFlag: %{public}d", saveModeFlag);
126             return false;
127         }
128         return true;
129     }
130 
OnSaveAppState(napi_env env,const size_t argc,napi_value * argv)131     napi_value OnSaveAppState(napi_env env, const size_t argc, napi_value* argv)
132     {
133         if (argc > 1) {
134             TAG_LOGE(AAFwkTag::RECOVERY, "invalid argc");
135             return CreateJsValue(env, false);
136         }
137         uintptr_t ability = 0;
138         if (argc == 1) {
139             napi_value value = argv[0];
140             if (value == nullptr) {
141                 TAG_LOGE(AAFwkTag::RECOVERY, "null abilityContext");
142                 return CreateJsValue(env, false);
143             }
144             void* result = nullptr;
145             napi_unwrap(env, value, &result);
146             ability = reinterpret_cast<uintptr_t>(result);
147         }
148         if (AppRecovery::GetInstance().ScheduleSaveAppState(StateReason::DEVELOPER_REQUEST, ability)) {
149             return CreateJsValue(env, true);
150         }
151         return CreateJsValue(env, false);
152     }
153 
OnRestartApp(napi_env env,const size_t argc,napi_value * argv)154     napi_value OnRestartApp(napi_env env, const size_t argc, napi_value* argv)
155     {
156         if (argc != 0) {
157             TAG_LOGE(AAFwkTag::RECOVERY, "invalid argc");
158             return CreateJsUndefined(env);
159         }
160 
161         AppRecovery::GetInstance().ScheduleRecoverApp(StateReason::DEVELOPER_REQUEST);
162         return CreateJsUndefined(env);
163     }
164 
OnSetRestartWant(napi_env env,const size_t argc,napi_value * argv)165     napi_value OnSetRestartWant(napi_env env, const size_t argc, napi_value* argv)
166     {
167         if (argc != 1) {
168             TAG_LOGE(AAFwkTag::RECOVERY, "invalid argc");
169             return CreateJsUndefined(env);
170         }
171         std::shared_ptr<AAFwk::Want> want = std::make_shared<AAFwk::Want>();
172         OHOS::AppExecFwk::UnwrapWant(env, argv[0], *(want.get()));
173         AppRecovery::GetInstance().SetRestartWant(want);
174         return CreateJsUndefined(env);
175     }
176 };
177 } // namespace
178 
AppRecoveryRestartFlagInit(napi_env env)179 napi_value AppRecoveryRestartFlagInit(napi_env env)
180 {
181     if (env == nullptr) {
182         TAG_LOGE(AAFwkTag::RECOVERY, "null env");
183         return nullptr;
184     }
185 
186     napi_value objValue = nullptr;
187     napi_create_object(env, &objValue);
188 
189     if (objValue == nullptr) {
190         TAG_LOGE(AAFwkTag::RECOVERY, "null obj");
191         return nullptr;
192     }
193 
194     napi_set_named_property(env, objValue, "ALWAYS_RESTART", CreateJsValue(env, RestartFlag::ALWAYS_RESTART));
195     napi_set_named_property(env, objValue, "RESTART_WHEN_JS_CRASH",
196         CreateJsValue(env, RestartFlag::RESTART_WHEN_JS_CRASH));
197     napi_set_named_property(env, objValue, "RESTART_WHEN_APP_FREEZE",
198         CreateJsValue(env, RestartFlag::RESTART_WHEN_APP_FREEZE));
199     napi_set_named_property(env, objValue, "NO_RESTART", CreateJsValue(env, RestartFlag::NO_RESTART));
200     return objValue;
201 }
202 
AppRecoveryStateSaveFlagInit(napi_env env)203 napi_value AppRecoveryStateSaveFlagInit(napi_env env)
204 {
205     if (env == nullptr) {
206         TAG_LOGE(AAFwkTag::RECOVERY, "null env");
207         return nullptr;
208     }
209 
210     napi_value objValue = nullptr;
211     napi_create_object(env, &objValue);
212 
213     if (objValue == nullptr) {
214         TAG_LOGE(AAFwkTag::RECOVERY, "null obj");
215         return nullptr;
216     }
217 
218     napi_set_named_property(env, objValue, "NONE", CreateJsValue(env, SaveOccasionFlag::NO_SAVE));
219     napi_set_named_property(env, objValue, "SAVE_WHEN_ERROR",
220         CreateJsValue(env, SaveOccasionFlag::SAVE_WHEN_ERROR));
221     napi_set_named_property(env, objValue, "SAVE_WHEN_BACKGROUND",
222         CreateJsValue(env, SaveOccasionFlag::SAVE_WHEN_BACKGROUND));
223     return objValue;
224 }
225 
AppRecoverySaveModeFlagInit(napi_env env)226 napi_value AppRecoverySaveModeFlagInit(napi_env env)
227 {
228     if (env == nullptr) {
229         TAG_LOGE(AAFwkTag::RECOVERY, "null env");
230         return nullptr;
231     }
232 
233     napi_value objValue = nullptr;
234     napi_create_object(env, &objValue);
235 
236     if (objValue == nullptr) {
237         TAG_LOGE(AAFwkTag::RECOVERY, "null obj");
238         return nullptr;
239     }
240     napi_set_named_property(env, objValue, "SAVE_WITH_FILE",
241         CreateJsValue(env, SaveModeFlag::SAVE_WITH_FILE));
242     napi_set_named_property(env, objValue, "SAVE_WITH_SHARED_MEMORY",
243         CreateJsValue(env, SaveModeFlag::SAVE_WITH_SHARED_MEMORY));
244     return objValue;
245 }
246 
InitAppRecoveryApiModule(napi_env env,napi_value exportObj)247 napi_value InitAppRecoveryApiModule(napi_env env, napi_value exportObj)
248 {
249     if (env == nullptr || exportObj == nullptr) {
250         TAG_LOGE(AAFwkTag::RECOVERY, "null env or exportObj");
251         return nullptr;
252     }
253 
254     std::unique_ptr<AppRecoveryApiRegistry> appRecoveryApi = std::make_unique<AppRecoveryApiRegistry>();
255     napi_wrap(env, exportObj, appRecoveryApi.release(), AppRecoveryApiRegistry::Finalizer, nullptr, nullptr);
256 
257     const char *moduleName = "AppRecovery";
258     BindNativeFunction(env, exportObj, "enableAppRecovery", moduleName, AppRecoveryApiRegistry::EnableAppRecovery);
259     BindNativeFunction(env, exportObj, "restartApp", moduleName, AppRecoveryApiRegistry::RestartApp);
260     BindNativeFunction(env, exportObj, "saveAppState", moduleName, AppRecoveryApiRegistry::SaveAppState);
261     BindNativeFunction(env, exportObj, "setRestartWant", moduleName, AppRecoveryApiRegistry::SetRestartWant);
262 
263     napi_set_named_property(env, exportObj, "RestartFlag", AppRecoveryRestartFlagInit(env));
264     napi_set_named_property(env, exportObj, "SaveOccasionFlag", AppRecoveryStateSaveFlagInit(env));
265     napi_set_named_property(env, exportObj, "SaveModeFlag", AppRecoverySaveModeFlagInit(env));
266 
267     return CreateJsUndefined(env);
268 }
269 }  // namespace AbilityRuntime
270 }  // namespace OHOS