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