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 "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 "ext_backup_context_js.h"
26 #include "js_extension_context.h"
27 #include "js_native_api.h"
28 #include "js_native_api_types.h"
29 #include "js_runtime.h"
30 #include "js_runtime_utils.h"
31 #include "napi/native_api.h"
32 #include "napi/native_node_api.h"
33 #include "napi_common_util.h"
34 #include "napi_common_want.h"
35 #include "napi_remote_object.h"
36 #include "unique_fd.h"
37
38 #include "b_anony/b_anony.h"
39 #include "b_error/b_error.h"
40 #include "b_error/b_excep_utils.h"
41 #include "b_json/b_json_cached_entity.h"
42 #include "b_json/b_json_entity_extension_config.h"
43 #include "b_resources/b_constants.h"
44 #include "ext_extension.h"
45 #include "filemgmt_libhilog.h"
46
47 namespace OHOS::FileManagement::Backup {
48 using namespace std;
49 constexpr size_t ARGC_ONE = 1;
50 static std::mutex g_extBackupValidLock;
51 static int32_t g_extBackupCount = 0;
52
GetSrcPath(const AppExecFwk::AbilityInfo & info)53 static string GetSrcPath(const AppExecFwk::AbilityInfo &info)
54 {
55 using AbilityRuntime::Extension;
56 stringstream ss;
57
58 // API9(stage model) 中通过 $(module)$(name)/$(srcEntrance/(.*$)/(.abc)) 获取自定义插件路径
59 if (!info.srcEntrance.empty()) {
60 ss << info.moduleName << '/' << string(info.srcEntrance, 0, info.srcEntrance.rfind(".")) << ".abc";
61 return ss.str();
62 }
63 return "";
64 }
65
DealNapiStrValue(napi_env env,const napi_value napi_StrValue,std::string & result)66 static napi_status DealNapiStrValue(napi_env env, const napi_value napi_StrValue, std::string &result)
67 {
68 HILOGI("Start DealNapiStrValue");
69 std::string buffer = "";
70 size_t bufferSize = 0;
71 napi_status status = napi_ok;
72 status = napi_get_value_string_utf8(env, napi_StrValue, nullptr, -1, &bufferSize);
73 if (status != napi_ok) {
74 HILOGE("Can not get buffer size");
75 return status;
76 }
77 buffer.reserve(bufferSize + 1);
78 buffer.resize(bufferSize);
79 if (bufferSize > 0) {
80 status = napi_get_value_string_utf8(env, napi_StrValue, buffer.data(), bufferSize + 1, &bufferSize);
81 if (status != napi_ok) {
82 HILOGE("Can not get buffer value");
83 return status;
84 }
85 }
86 result = buffer;
87 return status;
88 }
89
DealNapiException(napi_env env,napi_value & exception,std::string & exceptionInfo)90 static napi_status DealNapiException(napi_env env, napi_value &exception, std::string &exceptionInfo)
91 {
92 HILOGI("call DealNapiException start.");
93 napi_status status = napi_get_and_clear_last_exception(env, &exception);
94 if (status != napi_ok) {
95 HILOGE("call napi_get_and_clear_last_exception failed.");
96 return status;
97 }
98 status = DealNapiStrValue(env, exception, exceptionInfo);
99 if (status != napi_ok) {
100 HILOGE("call DealNapiStrValue failed.");
101 return status;
102 }
103 HILOGI("call DealNapiException end, exception info = %{public}s.", exceptionInfo.c_str());
104 return status;
105 }
106
PromiseCallback(napi_env env,napi_callback_info info)107 static napi_value PromiseCallback(napi_env env, napi_callback_info info)
108 {
109 std::lock_guard<std::mutex> lock(g_extBackupValidLock);
110 if (g_extBackupCount <= 0) {
111 HILOGE("ExtBackup is invalid, count=%{public}d", g_extBackupCount);
112 return nullptr;
113 }
114 HILOGI("Promise callback.");
115 void *data = nullptr;
116 if (napi_get_cb_info(env, info, nullptr, 0, nullptr, &data) != napi_ok) {
117 HILOGE("Failed to get callback info.");
118 return nullptr;
119 }
120 auto *callbackInfo = static_cast<CallbackInfo *>(data);
121 if (callbackInfo == nullptr) {
122 HILOGE("CallbackInfo is nullptr");
123 return nullptr;
124 }
125 string str;
126 callbackInfo->callback(BError(BError::Codes::OK), str);
127 data = nullptr;
128 return nullptr;
129 }
130
PromiseCatchCallback(napi_env env,napi_callback_info info)131 static napi_value PromiseCatchCallback(napi_env env, napi_callback_info info)
132 {
133 HILOGI("Promise catch callback begin.");
134 size_t argc = 1;
135 napi_value argv = {nullptr};
136 void *data = nullptr;
137 NAPI_CALL_NO_THROW(napi_get_cb_info(env, info, &argc, &argv, nullptr, &data), nullptr);
138 string exceptionInfo;
139 DealNapiStrValue(env, argv, exceptionInfo);
140 HILOGI("Catch exception info is %{public}s.", exceptionInfo.c_str());
141 auto *callbackInfo = static_cast<CallbackInfo *>(data);
142 if (callbackInfo == nullptr) {
143 HILOGE("CallbackInfo is nullptr");
144 return nullptr;
145 }
146 napi_status throwStatus = napi_fatal_exception(env, argv);
147 if (throwStatus != napi_ok) {
148 HILOGE("Failed to throw an exception, %{public}d", throwStatus);
149 return nullptr;
150 }
151 std::lock_guard<std::mutex> lock(g_extBackupValidLock);
152 if (g_extBackupCount <= 0) {
153 HILOGE("ExtBackup is invalid, count=%{public}d", g_extBackupCount);
154 data = nullptr;
155 return nullptr;
156 }
157 callbackInfo->callback(BError(BError::Codes::EXT_THROW_EXCEPTION), exceptionInfo);
158 data = nullptr;
159 HILOGI("Promise catch callback end.");
160 return nullptr;
161 }
162
PromiseCallbackEx(napi_env env,napi_callback_info info)163 static napi_value PromiseCallbackEx(napi_env env, napi_callback_info info)
164 {
165 std::lock_guard<std::mutex> lock(g_extBackupValidLock);
166 if (g_extBackupCount <= 0) {
167 HILOGE("ExtBackup is invalid, count=%{public}d", g_extBackupCount);
168 return nullptr;
169 }
170 HILOGI("PromiseEx callback.");
171 void *data = nullptr;
172 std::string str;
173 size_t argc = 1;
174 napi_value argv = {nullptr};
175 NAPI_CALL_NO_THROW(napi_get_cb_info(env, info, &argc, &argv, nullptr, &data), nullptr);
176 auto *callbackInfoEx = static_cast<CallbackInfoEx *>(data);
177 if (callbackInfoEx == nullptr) {
178 HILOGE("CallbackInfo is nullPtr");
179 return nullptr;
180 }
181 DealNapiStrValue(env, argv, str);
182 callbackInfoEx->callbackParam(BError(BError::Codes::OK), str);
183 data = nullptr;
184 return nullptr;
185 }
186
PromiseCatchCallbackEx(napi_env env,napi_callback_info info)187 static napi_value PromiseCatchCallbackEx(napi_env env, napi_callback_info info)
188 {
189 HILOGI("PromiseEx catch callback begin.");
190 void *data = nullptr;
191 size_t argc = 1;
192 napi_value argv = {nullptr};
193 NAPI_CALL_NO_THROW(napi_get_cb_info(env, info, &argc, &argv, nullptr, &data), nullptr);
194 string exceptionInfo;
195 DealNapiStrValue(env, argv, exceptionInfo);
196 HILOGI("Catch exception info is %{public}s.", exceptionInfo.c_str());
197 auto *callbackInfoEx = static_cast<CallbackInfoEx *>(data);
198 if (callbackInfoEx == nullptr) {
199 HILOGE("CallbackInfo is nullPtr");
200 return nullptr;
201 }
202 napi_status throwStatus = napi_fatal_exception(env, argv);
203 if (throwStatus != napi_ok) {
204 HILOGE("Failed to throw an exception, %{public}d", throwStatus);
205 return nullptr;
206 }
207 std::lock_guard<std::mutex> lock(g_extBackupValidLock);
208 if (g_extBackupCount <= 0) {
209 HILOGE("ExtBackup is invalid, count=%{public}d", g_extBackupCount);
210 data = nullptr;
211 return nullptr;
212 }
213 callbackInfoEx->callbackParam(BError(BError::Codes::EXT_THROW_EXCEPTION), exceptionInfo);
214 data = nullptr;
215 HILOGI("PromiseEx catch callback end.");
216 return nullptr;
217 }
218
CheckPromise(napi_env env,napi_value value)219 static bool CheckPromise(napi_env env, napi_value value)
220 {
221 if (value == nullptr) {
222 HILOGE("CheckPromise, result is null, no need to call promise.");
223 return false;
224 }
225 bool isPromise = false;
226 if (napi_is_promise(env, value, &isPromise) != napi_ok) {
227 HILOGE("CheckPromise, result is not promise, no need to call promise.");
228 return false;
229 }
230 return isPromise;
231 }
232
CallCatchPromise(AbilityRuntime::JsRuntime & jsRuntime,napi_value result,CallbackInfo * callbackInfo)233 static bool CallCatchPromise(AbilityRuntime::JsRuntime &jsRuntime, napi_value result, CallbackInfo *callbackInfo)
234 {
235 HILOGI("CallCatchPromise Begin.");
236 AbilityRuntime::HandleScope handleScope(jsRuntime);
237 auto env = jsRuntime.GetNapiEnv();
238 napi_value method = nullptr;
239 if (napi_get_named_property(env, result, "catch", &method) != napi_ok) {
240 HILOGE("CallCatchPromise, Failed to get method catch");
241 return false;
242 }
243 bool isCallable = false;
244 if (napi_is_callable(env, method, &isCallable) != napi_ok) {
245 HILOGE("CallCatchPromise, Failed to check method then is callable");
246 return false;
247 }
248 if (!isCallable) {
249 HILOGE("CallCatchPromise, property then is not callable.");
250 return false;
251 }
252 napi_value ret;
253 napi_create_function(env, "promiseCatchCallback", strlen("promiseCatchCallback"), PromiseCatchCallback,
254 callbackInfo, &ret);
255 napi_value argv[1] = {ret};
256 napi_call_function(env, result, method, 1, argv, nullptr);
257 return true;
258 }
259
CallPromise(AbilityRuntime::JsRuntime & jsRuntime,napi_value result,CallbackInfo * callbackInfo)260 static bool CallPromise(AbilityRuntime::JsRuntime &jsRuntime, napi_value result, CallbackInfo *callbackInfo)
261 {
262 AbilityRuntime::HandleScope handleScope(jsRuntime);
263 auto env = jsRuntime.GetNapiEnv();
264 napi_value method = nullptr;
265 if (napi_get_named_property(env, result, "then", &method) != napi_ok) {
266 HILOGE("CallPromise, Failed to get method then");
267 return false;
268 }
269 bool isCallable = false;
270 if (napi_is_callable(env, method, &isCallable) != napi_ok) {
271 HILOGE("CallPromise, Failed to check method then is callable");
272 return false;
273 }
274 if (!isCallable) {
275 HILOGE("CallPromise, property then is not callable.");
276 return false;
277 }
278 napi_value ret;
279 napi_create_function(env, "promiseCallback", strlen("promiseCallback"), PromiseCallback, callbackInfo, &ret);
280 napi_value argv[1] = {ret};
281 napi_call_function(env, result, method, 1, argv, nullptr);
282 if (!CallCatchPromise(jsRuntime, result, callbackInfo)) {
283 HILOGE("CallCatchPromise failed.");
284 return false;
285 }
286 return true;
287 }
288
CallCatchPromiseEx(AbilityRuntime::JsRuntime & jsRuntime,napi_value result,CallbackInfoEx * callbackInfoEx)289 static bool CallCatchPromiseEx(AbilityRuntime::JsRuntime &jsRuntime, napi_value result, CallbackInfoEx *callbackInfoEx)
290 {
291 HILOGI("CallCatchPromiseEx Begin.");
292 AbilityRuntime::HandleScope handleScope(jsRuntime);
293 auto env = jsRuntime.GetNapiEnv();
294 napi_value method = nullptr;
295 if (napi_get_named_property(env, result, "catch", &method) != napi_ok) {
296 HILOGE("CallCatchPromiseEx, Failed to get method catch");
297 return false;
298 }
299 bool isCallable = false;
300 if (napi_is_callable(env, method, &isCallable) != napi_ok) {
301 HILOGE("CallCatchPromiseEx, Failed to check method then is callable");
302 return false;
303 }
304 if (!isCallable) {
305 HILOGE("CallCatchPromiseEx, property then is not callable.");
306 return false;
307 }
308 napi_value ret;
309 napi_create_function(env, "promiseCatchCallbackEx", strlen("promiseCatchCallbackEx"), PromiseCatchCallbackEx,
310 callbackInfoEx, &ret);
311 napi_value argv[1] = {ret};
312 napi_call_function(env, result, method, 1, argv, nullptr);
313 return true;
314 }
315
CallPromiseEx(AbilityRuntime::JsRuntime & jsRuntime,napi_value result,CallbackInfoEx * callbackInfoEx)316 static bool CallPromiseEx(AbilityRuntime::JsRuntime &jsRuntime, napi_value result, CallbackInfoEx *callbackInfoEx)
317 {
318 AbilityRuntime::HandleScope handleScope(jsRuntime);
319 auto env = jsRuntime.GetNapiEnv();
320 napi_value method = nullptr;
321 if (napi_get_named_property(env, result, "then", &method) != napi_ok) {
322 HILOGE("CallPromise, Failed to get method then");
323 return false;
324 }
325 bool isCallable = false;
326 if (napi_is_callable(env, method, &isCallable) != napi_ok) {
327 HILOGE("CallPromise, Failed to check method then is callable");
328 return false;
329 }
330 if (!isCallable) {
331 HILOGE("CallPromise, property then is not callable.");
332 return false;
333 }
334 napi_value ret;
335 napi_create_function(env, "promiseCallbackEx", strlen("promiseCallbackEx"), PromiseCallbackEx, callbackInfoEx,
336 &ret);
337 napi_value argv[1] = {ret};
338 napi_call_function(env, result, method, 1, argv, nullptr);
339 if (!CallCatchPromiseEx(jsRuntime, result, callbackInfoEx)) {
340 HILOGE("CallCatchPromiseEx failed.");
341 return false;
342 }
343 return true;
344 }
345
CallPromiseEx(AbilityRuntime::JsRuntime & jsRuntime,napi_value result,CallbackInfoBackup * callbackInfoBackup)346 static bool CallPromiseEx(AbilityRuntime::JsRuntime &jsRuntime, napi_value result,
347 CallbackInfoBackup *callbackInfoBackup)
348 {
349 AbilityRuntime::HandleScope handleScope(jsRuntime);
350 auto env = jsRuntime.GetNapiEnv();
351 napi_value method = nullptr;
352 if (napi_get_named_property(env, result, "then", &method) != napi_ok) {
353 HILOGE("CallPromise, Failed to get method then");
354 return false;
355 }
356 bool isCallable = false;
357 if (napi_is_callable(env, method, &isCallable) != napi_ok) {
358 HILOGE("CallPromise, Failed to check method then is callable");
359 return false;
360 }
361 if (!isCallable) {
362 HILOGE("CallPromise, property then is not callable.");
363 return false;
364 }
365 napi_value ret;
366 napi_create_function(env, "promiseCallbackEx", strlen("promiseCallbackEx"), PromiseCallbackEx, callbackInfoBackup,
367 &ret);
368 napi_value argv[1] = {ret};
369 napi_call_function(env, result, method, 1, argv, nullptr);
370 return true;
371 }
372
Init(const shared_ptr<AppExecFwk::AbilityLocalRecord> & record,const shared_ptr<AppExecFwk::OHOSApplication> & application,shared_ptr<AppExecFwk::AbilityHandler> & handler,const sptr<IRemoteObject> & token)373 void ExtBackupJs::Init(const shared_ptr<AppExecFwk::AbilityLocalRecord> &record,
374 const shared_ptr<AppExecFwk::OHOSApplication> &application,
375 shared_ptr<AppExecFwk::AbilityHandler> &handler,
376 const sptr<IRemoteObject> &token)
377 {
378 HILOGI("Init the BackupExtensionAbility(JS)");
379 try {
380 ExtBackup::Init(record, application, handler, token);
381 BExcepUltils::BAssert(abilityInfo_, BError::Codes::EXT_BROKEN_FRAMEWORK, "Invalid abilityInfo_");
382 // 获取应用扩展的 BackupExtensionAbility 的路径
383 const AppExecFwk::AbilityInfo &info = *abilityInfo_;
384 string bundleName = info.bundleName;
385 string moduleName(info.moduleName + "::" + info.name);
386 string modulePath = GetSrcPath(info);
387 int moduleType = static_cast<int>(info.type);
388 HILOGI("Try to load %{public}s's %{public}s(type %{public}d) from %{public}s", bundleName.c_str(),
389 moduleName.c_str(), moduleType, modulePath.c_str());
390
391 // 加载用户扩展 BackupExtensionAbility 到 JS 引擎,并将之暂存在 jsObj_ 中。注意,允许加载失败,往后执行默认逻辑
392 AbilityRuntime::HandleScope handleScope(jsRuntime_);
393 jsObj_ = jsRuntime_.LoadModule(moduleName, modulePath, info.hapPath,
394 abilityInfo_->compileMode == AbilityRuntime::CompileMode::ES_MODULE);
395 if (jsObj_ == nullptr) {
396 HILOGW("Oops! There's no custom BackupExtensionAbility");
397 return;
398 }
399 HILOGI("Wow! Here's a custsom BackupExtensionAbility");
400 ExportJsContext();
401 } catch (const BError &e) {
402 HILOGE("%{public}s", e.what());
403 } catch (const exception &e) {
404 HILOGE("%{public}s", e.what());
405 }
406 }
407
AttachBackupExtensionContext(napi_env env,void * value,void *)408 napi_value AttachBackupExtensionContext(napi_env env, void *value, void *)
409 {
410 HILOGI("AttachBackupExtensionContext");
411 if (value == nullptr || env == nullptr) {
412 HILOG_WARN("invalid parameter.");
413 return nullptr;
414 }
415 auto ptr = reinterpret_cast<std::weak_ptr<ExtBackupContext> *>(value)->lock();
416 if (ptr == nullptr) {
417 HILOGE("invalid context.");
418 return nullptr;
419 }
420 auto object = CreateExtBackupJsContext(env, ptr);
421 if (object == nullptr) {
422 HILOGE("Failed to get js backup extension context");
423 return nullptr;
424 }
425 auto contextRef =
426 AbilityRuntime::JsRuntime::LoadSystemModuleByEngine(env, "application.BackupExtensionContext", &object, 1);
427 if (contextRef == nullptr) {
428 HILOGE("Failed to load BackupExtensionContext.");
429 return nullptr;
430 }
431 napi_value contextObj = contextRef->GetNapiValue();
432 napi_coerce_to_native_binding_object(env, contextObj, AbilityRuntime::DetachCallbackFunc,
433 AttachBackupExtensionContext, value, nullptr);
434
435 auto workContext = new (std::nothrow) std::weak_ptr<ExtBackupContext>(ptr);
436 if (workContext == nullptr) {
437 HILOGE("Failed to get backup extension context");
438 return nullptr;
439 }
440 napi_status status = napi_wrap(
441 env, contextObj, workContext,
442 [](napi_env, void *data, void *) {
443 HILOG_DEBUG("Finalizer for weak_ptr base context is called");
444 delete static_cast<std::weak_ptr<ExtBackupContext> *>(data);
445 },
446 nullptr, nullptr);
447 if (status != napi_ok) {
448 HILOG_DEBUG("Failed to wrap js instance");
449 delete workContext;
450 workContext = nullptr;
451 }
452 return contextObj;
453 }
454
ExtBackupJs(AbilityRuntime::JsRuntime & jsRuntime)455 ExtBackupJs::ExtBackupJs(AbilityRuntime::JsRuntime &jsRuntime) : jsRuntime_(jsRuntime)
456 {
457 std::lock_guard<std::mutex> lock(g_extBackupValidLock);
458 g_extBackupCount += 1;
459 HILOGI("ExtBackupJs::ExtBackupJs, count=%{public}d.", g_extBackupCount);
460 }
461
~ExtBackupJs()462 ExtBackupJs::~ExtBackupJs()
463 {
464 jsRuntime_.FreeNativeReference(std::move(jsObj_));
465 std::lock_guard<std::mutex> lock(g_extBackupValidLock);
466 g_extBackupCount -= 1;
467 HILOGI("ExtBackupJs::~ExtBackupJs, count=%{public}d.", g_extBackupCount);
468 }
469
ExportJsContext(void)470 void ExtBackupJs::ExportJsContext(void)
471 {
472 auto env = jsRuntime_.GetNapiEnv();
473 if (jsObj_ == nullptr) {
474 HILOGE("Failed to get js object.");
475 return;
476 }
477 napi_value obj = jsObj_->GetNapiValue();
478 if (obj == nullptr) {
479 HILOGE("Failed to get BackupExtAbility object");
480 return;
481 }
482
483 auto context = GetContext();
484 if (context == nullptr) {
485 HILOGE("Failed to get context");
486 return;
487 }
488
489 HILOGI("CreateBackupExtAbilityContext");
490 napi_value contextObj = CreateExtBackupJsContext(env, context);
491 auto contextRef = jsRuntime_.LoadSystemModule("application.BackupExtensionContext", &contextObj, ARGC_ONE);
492 if (!contextRef) {
493 HILOGE("context is nullptr");
494 return;
495 }
496 contextObj = contextRef->GetNapiValue();
497 HILOGI("Bind context");
498 context->Bind(jsRuntime_, contextRef.release());
499 napi_set_named_property(env, obj, "context", contextObj);
500
501 auto workContext = new (std::nothrow) std::weak_ptr<ExtBackupContext>(context);
502 if (workContext == nullptr) {
503 HILOGE("Failed to create ExtBackupContext.");
504 return;
505 }
506 napi_coerce_to_native_binding_object(env, contextObj, AbilityRuntime::DetachCallbackFunc,
507 AttachBackupExtensionContext, workContext, nullptr);
508 HILOGI("Set backup extension ability context pointer is nullptr: %{public}d", context.get() == nullptr);
509 napi_status status = napi_wrap(
510 env, contextObj, workContext,
511 [](napi_env, void *data, void *) {
512 HILOG_DEBUG("Finalizer for weak_ptr base context is called");
513 delete static_cast<std::weak_ptr<ExtBackupContext> *>(data);
514 },
515 nullptr, nullptr);
516 if (status != napi_ok) {
517 HILOG_DEBUG("Failed to wrap js instance");
518 delete workContext;
519 workContext = nullptr;
520 }
521 }
522
CallObjectMethod(string_view name,const vector<napi_value> & argv)523 [[maybe_unused]] tuple<ErrCode, napi_value> ExtBackupJs::CallObjectMethod(string_view name,
524 const vector<napi_value> &argv)
525 {
526 HILOGI("Call %{public}s", name.data());
527 return {BError(BError::Codes::OK).GetCode(), nullptr};
528 }
529
Create(const unique_ptr<AbilityRuntime::Runtime> & runtime)530 ExtBackupJs *ExtBackupJs::Create(const unique_ptr<AbilityRuntime::Runtime> &runtime)
531 {
532 HILOGI("Create as an BackupExtensionAbility(JS)");
533 return new ExtBackupJs(static_cast<AbilityRuntime::JsRuntime &>(*runtime));
534 }
535
OnBackup(function<void (ErrCode,std::string)> callback,std::function<void (ErrCode,const std::string)> callbackEx)536 ErrCode ExtBackupJs::OnBackup(function<void(ErrCode, std::string)> callback,
537 std::function<void(ErrCode, const std::string)> callbackEx)
538 {
539 HILOGI("BackupExtensionAbility(JS) OnBackup ex");
540 BExcepUltils::BAssert(jsObj_, BError::Codes::EXT_BROKEN_FRAMEWORK,
541 "The app does not provide the onBackup interface.");
542 BExcepUltils::BAssert(callback, BError::Codes::EXT_BROKEN_FRAMEWORK, "OnBackup callback is nullptr.");
543 BExcepUltils::BAssert(callbackEx, BError::Codes::EXT_BROKEN_FRAMEWORK, "OnBackup callbackEx is nullptr.");
544 callExtDefaultFunc_.store(false);
545 callJsExMethodDone_.store(false);
546 callbackInfo_ = std::make_shared<CallbackInfo>(callback);
547 callbackInfoEx_ = std::make_shared<CallbackInfoEx>(callbackEx);
548 return CallJsOnBackupEx();
549 }
550
CallJsOnBackupEx()551 ErrCode ExtBackupJs::CallJsOnBackupEx()
552 {
553 HILOGI("Start call app js method onBackupEx");
554 auto retParser = [jsRuntime{ &jsRuntime_ }, callbackInfoEx { callbackInfoEx_ }](napi_env envir,
555 napi_value result) -> bool {
556 if (!CheckPromise(envir, result)) {
557 string str;
558 bool isExceptionPending;
559 napi_is_exception_pending(envir, &isExceptionPending);
560 HILOGI("napi exception pending = %{public}d.", isExceptionPending);
561 if (isExceptionPending) {
562 napi_value exception;
563 DealNapiException(envir, exception, str);
564 napi_fatal_exception(envir, exception);
565 callbackInfoEx->callbackParam(BError(BError::Codes::EXT_THROW_EXCEPTION), str);
566 } else {
567 DealNapiStrValue(envir, result, str);
568 callbackInfoEx->callbackParam(BError(BError::Codes::OK), str);
569 }
570 return true;
571 }
572 HILOGI("CheckPromise onBackupEx ok");
573 return CallPromiseEx(*jsRuntime, result, callbackInfoEx.get());
574 };
575 auto errCode = CallJsMethod("onBackupEx", jsRuntime_, jsObj_.get(), ParseBackupExInfo(), retParser);
576 if (errCode != ERR_OK) {
577 HILOGE("Call onBackupEx error");
578 return errCode;
579 }
580 HILOGI("Check call onBackupEx load");
581 std::unique_lock<std::mutex> lock(callJsMutex_);
582 callJsCon_.wait(lock, [this] { return callJsExMethodDone_.load(); });
583
584 if (!callExtDefaultFunc_.load()) {
585 HILOGI("Call Js method onBackupEx done");
586 return ERR_OK;
587 }
588 return CallJsOnBackup();
589 }
590
CallJsOnBackup()591 ErrCode ExtBackupJs::CallJsOnBackup()
592 {
593 HILOGI("Start call app js method onBackup");
594 auto retParser = [jsRuntime {&jsRuntime_}, callbackInfo {callbackInfo_}](napi_env env,
595 napi_value result) -> bool {
596 if (!CheckPromise(env, result)) {
597 string str;
598 bool isExceptionPending;
599 napi_is_exception_pending(env, &isExceptionPending);
600 HILOGI("napi exception pending = %{public}d.", isExceptionPending);
601 if (isExceptionPending) {
602 napi_value exception;
603 DealNapiException(env, exception, str);
604 napi_fatal_exception(env, exception);
605 callbackInfo->callback(BError(BError::Codes::EXT_THROW_EXCEPTION), str);
606 } else {
607 callbackInfo->callback(BError(BError::Codes::OK), str);
608 }
609 return true;
610 }
611 HILOGI("CheckPromise Js Method onBackup ok.");
612 return CallPromise(*jsRuntime, result, callbackInfo.get());
613 };
614 auto errCode = CallJsMethod("onBackup", jsRuntime_, jsObj_.get(), {}, retParser);
615 if (errCode != ERR_OK) {
616 HILOGE("CallJsMethod error, code:%{public}d.", errCode);
617 }
618 return errCode;
619 }
620
OnRestore(function<void (ErrCode,std::string)> callback,std::function<void (ErrCode,const std::string)> callbackEx)621 ErrCode ExtBackupJs::OnRestore(function<void(ErrCode, std::string)> callback,
622 std::function<void(ErrCode, const std::string)> callbackEx)
623 {
624 HILOGI("BackupExtensionAbility(JS) OnRestore.");
625 BExcepUltils::BAssert(jsObj_, BError::Codes::EXT_BROKEN_FRAMEWORK,
626 "The app does not provide the onRestore interface.");
627 BExcepUltils::BAssert(callback, BError::Codes::EXT_BROKEN_FRAMEWORK, "OnRestore callback is nullptr.");
628 BExcepUltils::BAssert(callbackEx, BError::Codes::EXT_BROKEN_FRAMEWORK, "OnRestore callbackEx is nullptr.");
629 callExtDefaultFunc_.store(false);
630 callJsExMethodDone_.store(false);
631 callbackInfo_ = std::make_shared<CallbackInfo>(callback);
632 callbackInfoEx_ = std::make_shared<CallbackInfoEx>(callbackEx);
633 return CallJSRestoreEx();
634 }
635
CallJSRestoreEx()636 ErrCode ExtBackupJs::CallJSRestoreEx()
637 {
638 HILOGI("Start call app js method onRestoreEx");
639 auto retParser = [jsRuntime {&jsRuntime_}, callbackInfoEx {callbackInfoEx_}](napi_env envir, napi_value result) ->
640 bool {
641 if (!CheckPromise(envir, result)) {
642 string str;
643 bool isExceptionPending;
644 napi_is_exception_pending(envir, &isExceptionPending);
645 HILOGI("napi exception pending = %{public}d.", isExceptionPending);
646 if (isExceptionPending) {
647 napi_value exception;
648 DealNapiException(envir, exception, str);
649 napi_fatal_exception(envir, exception);
650 callbackInfoEx->callbackParam(BError(BError::Codes::EXT_THROW_EXCEPTION), str);
651 } else {
652 DealNapiStrValue(envir, result, str);
653 callbackInfoEx->callbackParam(BError(BError::Codes::OK), str);
654 }
655 return true;
656 }
657 HILOGI("CheckPromise onRestoreEx ok");
658 return CallPromiseEx(*jsRuntime, result, callbackInfoEx.get());
659 };
660 auto errCode = CallJsMethod("onRestoreEx", jsRuntime_, jsObj_.get(), ParseRestoreExInfo(), retParser);
661 if (errCode != ERR_OK) {
662 HILOGE("Call onRestoreEx error");
663 return errCode;
664 }
665 HILOGI("Check callRestoreExDone load");
666 std::unique_lock<std::mutex> lock(callJsMutex_);
667 callJsCon_.wait(lock, [this] { return callJsExMethodDone_.load(); });
668 HILOGI("Check needCallOnRestore load");
669 if (!callExtDefaultFunc_.load()) {
670 HILOGI("Call Js method onRestoreEx done");
671 return ERR_OK;
672 }
673 return CallJSRestore();
674 }
675
CallJSRestore()676 ErrCode ExtBackupJs::CallJSRestore()
677 {
678 HILOGI("Start call app js method onRestore");
679 auto retParser = [jsRuntime {&jsRuntime_}, callbackInfo {callbackInfo_}](napi_env env, napi_value result) -> bool {
680 if (!CheckPromise(env, result)) {
681 string str;
682 bool isExceptionPending;
683 napi_is_exception_pending(env, &isExceptionPending);
684 HILOGI("napi exception pending = %{public}d.", isExceptionPending);
685 if (isExceptionPending) {
686 napi_value exception;
687 DealNapiException(env, exception, str);
688 napi_fatal_exception(env, exception);
689 callbackInfo->callback(BError(BError::Codes::EXT_THROW_EXCEPTION), str);
690 } else {
691 callbackInfo->callback(BError(BError::Codes::OK), str);
692 }
693 return true;
694 }
695 HILOGI("CheckPromise Js Method onRestore ok.");
696 return CallPromise(*jsRuntime, result, callbackInfo.get());
697 };
698 auto errCode = CallJsMethod("onRestore", jsRuntime_, jsObj_.get(), ParseRestoreInfo(), retParser);
699 if (errCode != ERR_OK) {
700 HILOGE("CallJsMethod error, code:%{public}d.", errCode);
701 return errCode;
702 }
703 return ERR_OK;
704 }
705
GetBackupInfo(std::function<void (ErrCode,const std::string)> callback)706 ErrCode ExtBackupJs::GetBackupInfo(std::function<void(ErrCode, const std::string)> callback)
707 {
708 HILOGI("BackupExtensionAbility(JS) GetBackupInfo begin.");
709 BExcepUltils::BAssert(jsObj_, BError::Codes::EXT_BROKEN_FRAMEWORK,
710 "The app does not provide the GetBackupInfo interface.");
711 callbackInfoBackup_ = std::make_shared<CallbackInfoBackup>(callback);
712 auto retParser = [jsRuntime {&jsRuntime_}, callBackInfo {callbackInfoBackup_}](napi_env env,
713 napi_value result) -> bool {
714 if (!CheckPromise(env, result)) {
715 bool isExceptionPending;
716 napi_is_exception_pending(env, &isExceptionPending);
717 HILOGI("napi exception pending = %{public}d.", isExceptionPending);
718 if (isExceptionPending) {
719 string str;
720 napi_value exception;
721 DealNapiException(env, exception, str);
722 callBackInfo->callbackParam(BError(BError::Codes::EXT_THROW_EXCEPTION), str);
723 return false;
724 }
725 size_t strLen = 0;
726 napi_status status = napi_get_value_string_utf8(env, result, nullptr, -1, &strLen);
727 if (status != napi_ok) {
728 return false;
729 }
730 size_t bufLen = strLen + 1;
731 unique_ptr<char[]> str = make_unique<char[]>(bufLen);
732 status = napi_get_value_string_utf8(env, result, str.get(), bufLen, &strLen);
733 callBackInfo->callbackParam(BError(BError::Codes::OK), str.get());
734 return true;
735 }
736 HILOGI("BackupExtensionAbulity(JS) GetBackupInfo ok.");
737 return CallPromiseEx(*jsRuntime, result, callBackInfo.get());
738 };
739
740 auto errCode = CallJsMethod("getBackupInfo", jsRuntime_, jsObj_.get(), {}, retParser);
741 if (errCode != ERR_OK) {
742 HILOGE("CallJsMethod error, code:%{public}d.", errCode);
743 }
744 HILOGI("BackupExtensionAbulity(JS) GetBackupInfo end.");
745 return errCode;
746 }
747
DoCallJsMethod(CallJsParam * param)748 static int DoCallJsMethod(CallJsParam *param)
749 {
750 AbilityRuntime::JsRuntime *jsRuntime = param->jsRuntime;
751 HILOGI("Start execute DoCallJsMethod");
752 if (jsRuntime == nullptr) {
753 HILOGE("failed to get jsRuntime.");
754 return EINVAL;
755 }
756 AbilityRuntime::HandleEscape handleEscape(*jsRuntime);
757 auto env = jsRuntime->GetNapiEnv();
758 napi_handle_scope scope = nullptr;
759 napi_open_handle_scope(env, &scope);
760 if (scope == nullptr) {
761 HILOGE("scope is nullptr");
762 return EINVAL;
763 }
764 vector<napi_value> argv = {};
765 if (param->argParser != nullptr) {
766 if (!param->argParser(env, argv)) {
767 HILOGE("failed to get params.");
768 napi_close_handle_scope(env, scope);
769 return EINVAL;
770 }
771 }
772 napi_value value = param->jsObj->GetNapiValue();
773 if (value == nullptr) {
774 HILOGE("failed to get napi value object.");
775 napi_close_handle_scope(env, scope);
776 return EINVAL;
777 }
778 napi_status status;
779 napi_value method;
780 status = napi_get_named_property(env, value, param->funcName.c_str(), &method);
781 if (status != napi_ok || param->retParser == nullptr) {
782 HILOGE("ResultValueParser must not null.");
783 napi_close_handle_scope(env, scope);
784 return EINVAL;
785 }
786 napi_value result;
787 HILOGI("Extension start do call current js method, methodName:%{public}s", param->funcName.c_str());
788 napi_call_function(env, value, method, argv.size(), argv.data(), &result);
789 if (!param->retParser(env, handleEscape.Escape(result))) {
790 HILOGE("Parser js result fail.");
791 napi_close_handle_scope(env, scope);
792 return EINVAL;
793 }
794 napi_close_handle_scope(env, scope);
795 HILOGI("End execute DoCallJsMethod");
796 return ERR_OK;
797 }
798
CallJsMethod(const std::string & funcName,AbilityRuntime::JsRuntime & jsRuntime,NativeReference * jsObj,InputArgsParser argParser,ResultValueParser retParser)799 int ExtBackupJs::CallJsMethod(const std::string &funcName,
800 AbilityRuntime::JsRuntime &jsRuntime,
801 NativeReference *jsObj,
802 InputArgsParser argParser,
803 ResultValueParser retParser)
804 {
805 uv_loop_s *loop = nullptr;
806 napi_status status = napi_get_uv_event_loop(jsRuntime.GetNapiEnv(), &loop);
807 if (status != napi_ok) {
808 HILOGE("failed to get uv event loop.");
809 return EINVAL;
810 }
811 auto param = std::make_shared<CallJsParam>(funcName, &jsRuntime, jsObj, argParser, retParser);
812 BExcepUltils::BAssert(param, BError::Codes::EXT_BROKEN_FRAMEWORK, "failed to new param.");
813
814 auto work = std::make_shared<uv_work_t>();
815 BExcepUltils::BAssert(work, BError::Codes::EXT_BROKEN_FRAMEWORK, "failed to new uv_work_t.");
816
817 work->data = reinterpret_cast<void *>(param.get());
818 HILOGI("Will execute current js method");
819 int ret = uv_queue_work(
820 loop, work.get(), [](uv_work_t *work) {
821 HILOGI("Enter, %{public}zu", (size_t)work);
822 },
823 [](uv_work_t *work, int status) {
824 HILOGI("AsyncWork Enter, %{public}zu", (size_t)work);
825 CallJsParam *param = reinterpret_cast<CallJsParam *>(work->data);
826 do {
827 if (param == nullptr) {
828 HILOGE("failed to get CallJsParam.");
829 break;
830 }
831 HILOGI("Start call current js method");
832 if (DoCallJsMethod(param) != ERR_OK) {
833 HILOGE("failed to call DoCallJsMethod.");
834 }
835 } while (false);
836 HILOGI("will notify current thread info");
837 std::unique_lock<std::mutex> lock(param->backupOperateMutex);
838 param->isReady.store(true);
839 param->backupOperateCondition.notify_all();
840 });
841 if (ret != 0) {
842 HILOGE("failed to exec uv_queue_work.");
843 return EINVAL;
844 }
845 HILOGI("Wait execute current js method");
846 std::unique_lock<std::mutex> lock(param->backupOperateMutex);
847 param->backupOperateCondition.wait(lock, [param]() { return param->isReady.load(); });
848 HILOGI("End do call current js method");
849 return ERR_OK;
850 }
851
ParseBackupExInfo()852 std::function<bool(napi_env env, std::vector<napi_value> &argv)> ExtBackupJs::ParseBackupExInfo()
853 {
854 auto onBackupExFun = [backupExtInfo(backupExtInfo_)](napi_env env, vector<napi_value> &argv) -> bool {
855 napi_value backupExtInfoVal = nullptr;
856 napi_create_object(env, &backupExtInfoVal);
857 HILOGI("backupExtInfo is:%{public}s", GetAnonyString(backupExtInfo).c_str());
858 napi_create_string_utf8(env, backupExtInfo.c_str(), backupExtInfo.size(), &backupExtInfoVal);
859 argv.push_back(backupExtInfoVal);
860 return true;
861 };
862 return onBackupExFun;
863 }
864
ParseRestoreExInfo()865 std::function<bool(napi_env env, std::vector<napi_value> &argv)> ExtBackupJs::ParseRestoreExInfo()
866 {
867 auto onRestoreExFun = [appVersionCode(appVersionCode_), appVersionStr(appVersionStr_),
868 restoreExtInfo(restoreExtInfo_)](napi_env env, vector<napi_value> &argv) -> bool {
869 HILOGI("restoreExtInfo is:%{public}s", GetAnonyString(restoreExtInfo).c_str());
870 napi_value objValue = nullptr;
871 napi_value restoreRetValue = nullptr;
872 napi_create_object(env, &objValue);
873 napi_create_object(env, &restoreRetValue);
874 napi_set_named_property(env, objValue, "code", AbilityRuntime::CreateJsValue(env, appVersionCode));
875 napi_set_named_property(env, objValue, "name", AbilityRuntime::CreateJsValue(env, appVersionStr.c_str()));
876 napi_create_string_utf8(env, restoreExtInfo.c_str(), restoreExtInfo.size(), &restoreRetValue);
877 argv.push_back(objValue);
878 argv.push_back(restoreRetValue);
879 return true;
880 };
881 return onRestoreExFun;
882 }
883
ParseRestoreInfo()884 std::function<bool(napi_env env, std::vector<napi_value> &argv)> ExtBackupJs::ParseRestoreInfo()
885 {
886 auto onRestoreFun = [appVersionCode(appVersionCode_), appVersionStr(appVersionStr_)](napi_env env,
887 vector<napi_value> &argv) -> bool {
888 std::string appVersionStrFlag = appVersionStr;
889 int64_t appVersionCodeFlag = appVersionCode;
890 napi_value objValue = nullptr;
891 napi_create_object(env, &objValue);
892 auto pos = appVersionStrFlag.find_first_of(BConstants::VERSION_NAME_SEPARATOR_CHAR);
893 std::string appVersionFlag = "";
894 if (pos != string::npos) {
895 appVersionFlag = appVersionStrFlag.substr(0, pos);
896 if (appVersionFlag == BConstants::DEFAULT_VERSION_NAME) {
897 appVersionStrFlag = appVersionFlag;
898 appVersionCodeFlag = 0;
899 }
900 }
901 napi_set_named_property(env, objValue, "code", AbilityRuntime::CreateJsValue(env, appVersionCodeFlag));
902 napi_set_named_property(env, objValue, "name", AbilityRuntime::CreateJsValue(env, appVersionStrFlag.c_str()));
903 argv.push_back(objValue);
904 return true;
905 };
906 return onRestoreFun;
907 }
908
InvokeAppExtMethod(ErrCode errCode,const std::string result)909 ErrCode ExtBackupJs::InvokeAppExtMethod(ErrCode errCode, const std::string result)
910 {
911 HILOGI("Start Get onBackupEx/onRestoreEx method result, errCode: %{public}d, result: %{public}s",
912 errCode, result.c_str());
913 if ((result.size() == 0) && (errCode == BError(BError::Codes::OK))) {
914 callExtDefaultFunc_.store(true);
915 } else {
916 callExtDefaultFunc_.store(false);
917 }
918 callJsExMethodDone_.store(true);
919 callJsCon_.notify_one();
920 HILOGI("End Get App onBackupEx/onRestoreEx method result");
921 return ERR_OK;
922 }
923
OnProcess(std::function<void (ErrCode,const std::string)> callback)924 ErrCode ExtBackupJs::OnProcess(std::function<void(ErrCode, const std::string)> callback)
925 {
926 HILOGI("BackupExtensionAbility(JS) OnProcess begin.");
927 BExcepUltils::BAssert(jsObj_, BError::Codes::EXT_BROKEN_FRAMEWORK,
928 "The app does not provide the OnProcess interface.");
929 onProcessCallback_ = std::make_shared<OnProcessCallBackInfo>(callback);
930 auto retParser = [jsRuntime {&jsRuntime_}, callBackInfo {onProcessCallback_}](napi_env env,
931 napi_value result) -> bool {
932 string processStr;
933 bool isExceptionPending;
934 napi_is_exception_pending(env, &isExceptionPending);
935 HILOGI("napi exception pending = %{public}d.", isExceptionPending);
936 if (isExceptionPending) {
937 napi_value exception;
938 napi_get_and_clear_last_exception(env, &exception);
939 callBackInfo->onProcessCallback(BError(BError::Codes::EXT_THROW_EXCEPTION), processStr);
940 } else {
941 DealNapiStrValue(env, result, processStr);
942 callBackInfo->onProcessCallback(BError(BError::Codes::OK), processStr);
943 }
944 return true;
945 };
946 auto errCode = CallJsMethod("onProcess", jsRuntime_, jsObj_.get(), {}, retParser);
947 if (errCode != ERR_OK) {
948 HILOGE("CallJsMethod error, code:%{public}d.", errCode);
949 }
950 HILOGI("BackupExtensionAbulity(JS) OnProcess end.");
951 return errCode;
952 }
953 } // namespace OHOS::FileManagement::Backup
954