• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2023 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 "ext_backup_js.h"
17 
18 #include <cstdio>
19 #include <memory>
20 #include <sstream>
21 #include <sys/stat.h>
22 #include <sys/types.h>
23 
24 #include "bundle_mgr_client.h"
25 #include "js_runtime.h"
26 #include "js_runtime_utils.h"
27 #include "napi/native_api.h"
28 #include "napi/native_node_api.h"
29 #include "napi_common_util.h"
30 #include "napi_common_want.h"
31 #include "napi_remote_object.h"
32 #include "unique_fd.h"
33 
34 #include "b_error/b_error.h"
35 #include "b_error/b_excep_utils.h"
36 #include "b_json/b_json_cached_entity.h"
37 #include "b_json/b_json_entity_extension_config.h"
38 #include "b_resources/b_constants.h"
39 #include "ext_extension.h"
40 #include "filemgmt_libhilog.h"
41 
42 namespace OHOS::FileManagement::Backup {
43 using namespace std;
44 
GetSrcPath(const AppExecFwk::AbilityInfo & info)45 static string GetSrcPath(const AppExecFwk::AbilityInfo &info)
46 {
47     using AbilityRuntime::Extension;
48     stringstream ss;
49 
50     // API9(stage model) 中通过 $(module)$(name)/$(srcEntrance/(.*$)/(.abc)) 获取自定义插件路径
51     if (!info.srcEntrance.empty()) {
52         ss << info.moduleName << '/' << string(info.srcEntrance, 0, info.srcEntrance.rfind(".")) << ".abc";
53         return ss.str();
54     }
55     return "";
56 }
57 
Init(const shared_ptr<AppExecFwk::AbilityLocalRecord> & record,const shared_ptr<AppExecFwk::OHOSApplication> & application,shared_ptr<AppExecFwk::AbilityHandler> & handler,const sptr<IRemoteObject> & token)58 void ExtBackupJs::Init(const shared_ptr<AppExecFwk::AbilityLocalRecord> &record,
59                        const shared_ptr<AppExecFwk::OHOSApplication> &application,
60                        shared_ptr<AppExecFwk::AbilityHandler> &handler,
61                        const sptr<IRemoteObject> &token)
62 {
63     HILOGI("Init the BackupExtensionAbility(JS)");
64     try {
65         ExtBackup::Init(record, application, handler, token);
66         BExcepUltils::BAssert(abilityInfo_, BError::Codes::EXT_BROKEN_FRAMEWORK, "Invalid abilityInfo_");
67         // 获取应用扩展的 BackupExtensionAbility 的路径
68         const AppExecFwk::AbilityInfo &info = *abilityInfo_;
69         string bundleName = info.bundleName;
70         string moduleName(info.moduleName + "::" + info.name);
71         string modulePath = GetSrcPath(info);
72         int moduleType = static_cast<int>(info.type);
73         HILOGI("Try to load %{public}s's %{public}s(type %{public}d) from %{public}s", bundleName.c_str(),
74                moduleName.c_str(), moduleType, modulePath.c_str());
75 
76         // 加载用户扩展 BackupExtensionAbility 到 JS 引擎,并将之暂存在 jsObj_ 中。注意,允许加载失败,往后执行默认逻辑
77         AbilityRuntime::HandleScope handleScope(jsRuntime_);
78         jsObj_ = jsRuntime_.LoadModule(moduleName, modulePath, info.hapPath,
79                                        abilityInfo_->compileMode == AbilityRuntime::CompileMode::ES_MODULE);
80         if (jsObj_) {
81             HILOGI("Wow! Here's a custsom BackupExtensionAbility");
82         } else {
83             HILOGW("Oops! There's no custom BackupExtensionAbility");
84         }
85     } catch (const BError &e) {
86         HILOGE("%{public}s", e.what());
87     } catch (const exception &e) {
88         HILOGE("%{public}s", e.what());
89     }
90 }
91 
CallObjectMethod(string_view name,const vector<NativeValue * > & argv)92 [[maybe_unused]] tuple<ErrCode, NativeValue *> ExtBackupJs::CallObjectMethod(string_view name,
93                                                                              const vector<NativeValue *> &argv)
94 {
95     HILOGI("Call %{public}s", name.data());
96 
97     if (!jsObj_) {
98         return {BError(BError::Codes::EXT_BROKEN_FRAMEWORK, "Invalid jsObj_").GetCode(), nullptr};
99     }
100 
101     AbilityRuntime::HandleScope handleScope(jsRuntime_);
102 
103     NativeValue *value = jsObj_->Get();
104     NativeObject *obj = AbilityRuntime::ConvertNativeValueTo<NativeObject>(value);
105     if (!obj) {
106         return {BError(BError::Codes::EXT_INVAL_ARG, "The custom BackupAbilityExtension is required to be an object")
107                     .GetCode(),
108                 nullptr};
109     }
110 
111     NativeValue *method = obj->GetProperty(name.data());
112     if (!method || method->TypeOf() != NATIVE_FUNCTION) {
113         return {BError(BError::Codes::EXT_INVAL_ARG, string(name).append(" is required to be a function")).GetCode(),
114                 nullptr};
115     }
116 
117     auto ret = jsRuntime_.GetNativeEngine().CallFunction(value, method, argv.data(), argv.size());
118     if (!ret) {
119         return {BError(BError::Codes::EXT_INVAL_ARG, string(name).append(" raised an exception")).GetCode(), nullptr};
120     }
121     return {BError(BError::Codes::OK).GetCode(), ret};
122 }
123 
Create(const unique_ptr<AbilityRuntime::Runtime> & runtime)124 ExtBackupJs *ExtBackupJs::Create(const unique_ptr<AbilityRuntime::Runtime> &runtime)
125 {
126     HILOGI("Create as an BackupExtensionAbility(JS)");
127     return new ExtBackupJs(static_cast<AbilityRuntime::JsRuntime &>(*runtime));
128 }
129 
OnBackup(void)130 ErrCode ExtBackupJs::OnBackup(void)
131 {
132     HILOGI("BackupExtensionAbility(JS) OnBackup.");
133     BExcepUltils::BAssert(jsObj_, BError::Codes::EXT_BROKEN_FRAMEWORK,
134                           "The app does not provide the onRestore interface.");
135 
136     auto retParser = [](NativeEngine &engine, NativeValue *result) -> bool {
137         return true;
138     };
139 
140     auto errCode = CallJsMethod("onBackup", jsRuntime_, jsObj_.get(), {}, retParser);
141     if (errCode != ERR_OK) {
142         HILOGE("CallJsMethod error, code:%{public}d.", errCode);
143         return errCode;
144     }
145     return ERR_OK;
146 }
147 
OnRestore(void)148 ErrCode ExtBackupJs::OnRestore(void)
149 {
150     HILOGI("BackupExtensionAbility(JS) OnRestore.");
151     BExcepUltils::BAssert(jsObj_, BError::Codes::EXT_BROKEN_FRAMEWORK,
152                           "The app does not provide the onRestore interface.");
153 
154     auto argParser = [appVersionCode(appVersionCode_),
155                       appVersionStr(appVersionStr_)](NativeEngine &engine, vector<NativeValue *> &argv) -> bool {
156         NativeValue *verCode = engine.CreateNumber(appVersionCode);
157         NativeValue *verStr = engine.CreateString(appVersionStr.c_str(), appVersionStr.length());
158         NativeValue *param = engine.CreateObject();
159         auto paramObj = reinterpret_cast<NativeObject *>(param->GetInterface(NativeObject::INTERFACE_ID));
160         paramObj->SetProperty("code", verCode);
161         paramObj->SetProperty("name", verStr);
162         argv.push_back(param);
163         return true;
164     };
165 
166     auto retParser = [](NativeEngine &engine, NativeValue *result) -> bool {
167         return true;
168     };
169 
170     auto errCode = CallJsMethod("onRestore", jsRuntime_, jsObj_.get(), argParser, retParser);
171     if (errCode != ERR_OK) {
172         HILOGE("CallJsMethod error, code:%{public}d.", errCode);
173         return errCode;
174     }
175     return ERR_OK;
176 }
177 
DoCallJsMethod(CallJsParam * param)178 static int DoCallJsMethod(CallJsParam *param)
179 {
180     AbilityRuntime::JsRuntime *jsRuntime = param->jsRuntime;
181     if (jsRuntime == nullptr) {
182         HILOGE("failed to get jsRuntime.");
183         return EINVAL;
184     }
185     AbilityRuntime::HandleEscape handleEscape(*jsRuntime);
186     auto &nativeEngine = jsRuntime->GetNativeEngine();
187     vector<NativeValue *> argv = {};
188     if (param->argParser != nullptr) {
189         if (!param->argParser(nativeEngine, argv)) {
190             HILOGE("failed to get params.");
191             return EINVAL;
192         }
193     }
194     NativeValue *value = param->jsObj->Get();
195     if (value == nullptr) {
196         HILOGE("failed to get native value object.");
197         return EINVAL;
198     }
199     NativeObject *obj = AbilityRuntime::ConvertNativeValueTo<NativeObject>(value);
200     if (obj == nullptr) {
201         HILOGE("failed to get BackupExtAbility object.");
202         return EINVAL;
203     }
204     NativeValue *method = obj->GetProperty(param->funcName.c_str());
205     if (method == nullptr) {
206         HILOGE("failed to get %{public}s from BackupExtAbility object.", param->funcName.c_str());
207         return EINVAL;
208     }
209     if (param->retParser == nullptr) {
210         HILOGE("ResultValueParser must not null.");
211         return EINVAL;
212     }
213     if (!param->retParser(nativeEngine,
214                           handleEscape.Escape(nativeEngine.CallFunction(value, method, argv.data(), argv.size())))) {
215         HILOGI("Parser js result fail.");
216         return EINVAL;
217     }
218     return ERR_OK;
219 }
220 
CallJsMethod(const std::string & funcName,AbilityRuntime::JsRuntime & jsRuntime,NativeReference * jsObj,InputArgsParser argParser,ResultValueParser retParser)221 int ExtBackupJs::CallJsMethod(const std::string &funcName,
222                               AbilityRuntime::JsRuntime &jsRuntime,
223                               NativeReference *jsObj,
224                               InputArgsParser argParser,
225                               ResultValueParser retParser)
226 {
227     uv_loop_s *loop = nullptr;
228     napi_status status = napi_get_uv_event_loop(reinterpret_cast<napi_env>(&jsRuntime.GetNativeEngine()), &loop);
229     if (status != napi_ok) {
230         HILOGE("failed to get uv event loop.");
231         return EINVAL;
232     }
233     auto param = std::make_shared<CallJsParam>(funcName, &jsRuntime, jsObj, argParser, retParser);
234     BExcepUltils::BAssert(param, BError::Codes::EXT_BROKEN_FRAMEWORK, "failed to new param.");
235 
236     auto work = std::make_shared<uv_work_t>();
237     BExcepUltils::BAssert(work, BError::Codes::EXT_BROKEN_FRAMEWORK, "failed to new uv_work_t.");
238 
239     work->data = reinterpret_cast<void *>(param.get());
240     int ret = uv_queue_work(
241         loop, work.get(), [](uv_work_t *work) {},
242         [](uv_work_t *work, int status) {
243             CallJsParam *param = reinterpret_cast<CallJsParam *>(work->data);
244             do {
245                 if (param == nullptr) {
246                     HILOGE("failed to get CallJsParam.");
247                     break;
248                 }
249                 if (DoCallJsMethod(param) != ERR_OK) {
250                     HILOGE("failed to call DoCallJsMethod.");
251                 }
252             } while (false);
253             std::unique_lock<std::mutex> lock(param->backupOperateMutex);
254             param->isReady = true;
255             param->backupOperateCondition.notify_one();
256         });
257     if (ret != 0) {
258         HILOGE("failed to exec uv_queue_work.");
259         return EINVAL;
260     }
261     std::unique_lock<std::mutex> lock(param->backupOperateMutex);
262     param->backupOperateCondition.wait(lock, [param]() { return param->isReady; });
263 
264     return ERR_OK;
265 }
266 } // namespace OHOS::FileManagement::Backup
267