• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_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     return contextObj;
448 }
449 
ExtBackupJs(AbilityRuntime::JsRuntime & jsRuntime)450 ExtBackupJs::ExtBackupJs(AbilityRuntime::JsRuntime &jsRuntime) : jsRuntime_(jsRuntime)
451 {
452     std::lock_guard<std::mutex> lock(g_extBackupValidLock);
453     g_extBackupCount += 1;
454     HILOGI("ExtBackupJs::ExtBackupJs, count=%{public}d.", g_extBackupCount);
455 }
456 
~ExtBackupJs()457 ExtBackupJs::~ExtBackupJs()
458 {
459     jsRuntime_.FreeNativeReference(std::move(jsObj_));
460     std::lock_guard<std::mutex> lock(g_extBackupValidLock);
461     g_extBackupCount -= 1;
462     HILOGI("ExtBackupJs::~ExtBackupJs, count=%{public}d.", g_extBackupCount);
463 }
464 
ExportJsContext(void)465 void ExtBackupJs::ExportJsContext(void)
466 {
467     auto env = jsRuntime_.GetNapiEnv();
468     if (jsObj_ == nullptr) {
469         HILOGE("Failed to get js object.");
470         return;
471     }
472     napi_value obj = jsObj_->GetNapiValue();
473     if (obj == nullptr) {
474         HILOGE("Failed to get BackupExtAbility object");
475         return;
476     }
477 
478     auto context = GetContext();
479     if (context == nullptr) {
480         HILOGE("Failed to get context");
481         return;
482     }
483 
484     HILOGI("CreateBackupExtAbilityContext");
485     napi_value contextObj = CreateExtBackupJsContext(env, context);
486     auto contextRef = jsRuntime_.LoadSystemModule("application.BackupExtensionContext", &contextObj, ARGC_ONE);
487     if (!contextRef) {
488         HILOGE("context is nullptr");
489         return;
490     }
491     contextObj = contextRef->GetNapiValue();
492     HILOGI("Bind context");
493     context->Bind(jsRuntime_, contextRef.release());
494     napi_set_named_property(env, obj, "context", contextObj);
495 
496     auto workContext = new (std::nothrow) std::weak_ptr<ExtBackupContext>(context);
497     if (workContext == nullptr) {
498         HILOGE("Failed to create ExtBackupContext.");
499         return;
500     }
501     napi_coerce_to_native_binding_object(env, contextObj, AbilityRuntime::DetachCallbackFunc,
502                                          AttachBackupExtensionContext, workContext, nullptr);
503     HILOGI("Set backup extension ability context pointer is nullptr: %{public}d", context.get() == nullptr);
504     napi_wrap(
505         env, contextObj, workContext,
506         [](napi_env, void *data, void *) {
507             HILOG_DEBUG("Finalizer for weak_ptr base context is called");
508             delete static_cast<std::weak_ptr<ExtBackupContext> *>(data);
509         },
510         nullptr, nullptr);
511 }
512 
CallObjectMethod(string_view name,const vector<napi_value> & argv)513 [[maybe_unused]] tuple<ErrCode, napi_value> ExtBackupJs::CallObjectMethod(string_view name,
514                                                                           const vector<napi_value> &argv)
515 {
516     HILOGI("Call %{public}s", name.data());
517     return {BError(BError::Codes::OK).GetCode(), nullptr};
518 }
519 
Create(const unique_ptr<AbilityRuntime::Runtime> & runtime)520 ExtBackupJs *ExtBackupJs::Create(const unique_ptr<AbilityRuntime::Runtime> &runtime)
521 {
522     HILOGI("Create as an BackupExtensionAbility(JS)");
523     return new ExtBackupJs(static_cast<AbilityRuntime::JsRuntime &>(*runtime));
524 }
525 
OnBackup(function<void (ErrCode,std::string)> callback,std::function<void (ErrCode,const std::string)> callbackEx)526 ErrCode ExtBackupJs::OnBackup(function<void(ErrCode, std::string)> callback,
527     std::function<void(ErrCode, const std::string)> callbackEx)
528 {
529     HILOGI("BackupExtensionAbility(JS) OnBackup ex");
530     BExcepUltils::BAssert(jsObj_, BError::Codes::EXT_BROKEN_FRAMEWORK,
531         "The app does not provide the onBackup interface.");
532     BExcepUltils::BAssert(callback, BError::Codes::EXT_BROKEN_FRAMEWORK, "OnBackup callback is nullptr.");
533     BExcepUltils::BAssert(callbackEx, BError::Codes::EXT_BROKEN_FRAMEWORK, "OnBackup callbackEx is nullptr.");
534     callExtDefaultFunc_.store(false);
535     callJsExMethodDone_.store(false);
536     callbackInfo_ = std::make_shared<CallbackInfo>(callback);
537     callbackInfoEx_ = std::make_shared<CallbackInfoEx>(callbackEx);
538     return CallJsOnBackupEx();
539 }
540 
CallJsOnBackupEx()541 ErrCode ExtBackupJs::CallJsOnBackupEx()
542 {
543     HILOGI("Start call app js method onBackupEx");
544     auto retParser = [jsRuntime{ &jsRuntime_ }, callbackInfoEx { callbackInfoEx_ }](napi_env envir,
545         napi_value result) -> bool {
546         if (!CheckPromise(envir, result)) {
547             string str;
548             bool isExceptionPending;
549             napi_is_exception_pending(envir, &isExceptionPending);
550             HILOGI("napi exception pending = %{public}d.", isExceptionPending);
551             if (isExceptionPending) {
552                 napi_value exception;
553                 DealNapiException(envir, exception, str);
554                 napi_fatal_exception(envir, exception);
555                 callbackInfoEx->callbackParam(BError(BError::Codes::EXT_THROW_EXCEPTION), str);
556             } else {
557                 DealNapiStrValue(envir, result, str);
558                 callbackInfoEx->callbackParam(BError(BError::Codes::OK), str);
559             }
560             return true;
561         }
562         HILOGI("CheckPromise onBackupEx ok");
563         return CallPromiseEx(*jsRuntime, result, callbackInfoEx.get());
564     };
565     auto errCode = CallJsMethod("onBackupEx", jsRuntime_, jsObj_.get(), ParseBackupExInfo(), retParser);
566     if (errCode != ERR_OK) {
567         HILOGE("Call onBackupEx error");
568         return errCode;
569     }
570     HILOGI("Check call onBackupEx load");
571     std::unique_lock<std::mutex> lock(callJsMutex_);
572     callJsCon_.wait(lock, [this] { return callJsExMethodDone_.load(); });
573 
574     if (!callExtDefaultFunc_.load()) {
575         HILOGI("Call Js method onBackupEx done");
576         return ERR_OK;
577     }
578     return CallJsOnBackup();
579 }
580 
CallJsOnBackup()581 ErrCode ExtBackupJs::CallJsOnBackup()
582 {
583     HILOGI("Start call app js method onBackup");
584     auto retParser = [jsRuntime {&jsRuntime_}, callbackInfo {callbackInfo_}](napi_env env,
585         napi_value result) -> bool {
586         if (!CheckPromise(env, result)) {
587             string str;
588             bool isExceptionPending;
589             napi_is_exception_pending(env, &isExceptionPending);
590             HILOGI("napi exception pending = %{public}d.", isExceptionPending);
591             if (isExceptionPending) {
592                 napi_value exception;
593                 DealNapiException(env, exception, str);
594                 napi_fatal_exception(env, exception);
595                 callbackInfo->callback(BError(BError::Codes::EXT_THROW_EXCEPTION), str);
596             } else {
597                 callbackInfo->callback(BError(BError::Codes::OK), str);
598             }
599             return true;
600         }
601         HILOGI("CheckPromise Js Method onBackup ok.");
602         return CallPromise(*jsRuntime, result, callbackInfo.get());
603     };
604     auto errCode = CallJsMethod("onBackup", jsRuntime_, jsObj_.get(), {}, retParser);
605     if (errCode != ERR_OK) {
606         HILOGE("CallJsMethod error, code:%{public}d.", errCode);
607     }
608     return errCode;
609 }
610 
OnRestore(function<void (ErrCode,std::string)> callback,std::function<void (ErrCode,const std::string)> callbackEx)611 ErrCode ExtBackupJs::OnRestore(function<void(ErrCode, std::string)> callback,
612     std::function<void(ErrCode, const std::string)> callbackEx)
613 {
614     HILOGI("BackupExtensionAbility(JS) OnRestore.");
615     BExcepUltils::BAssert(jsObj_, BError::Codes::EXT_BROKEN_FRAMEWORK,
616         "The app does not provide the onRestore interface.");
617     BExcepUltils::BAssert(callback, BError::Codes::EXT_BROKEN_FRAMEWORK, "OnRestore callback is nullptr.");
618     BExcepUltils::BAssert(callbackEx, BError::Codes::EXT_BROKEN_FRAMEWORK, "OnRestore callbackEx is nullptr.");
619     callExtDefaultFunc_.store(false);
620     callJsExMethodDone_.store(false);
621     callbackInfo_ = std::make_shared<CallbackInfo>(callback);
622     callbackInfoEx_ = std::make_shared<CallbackInfoEx>(callbackEx);
623     return CallJSRestoreEx();
624 }
625 
CallJSRestoreEx()626 ErrCode ExtBackupJs::CallJSRestoreEx()
627 {
628     HILOGI("Start call app js method onRestoreEx");
629     auto retParser = [jsRuntime {&jsRuntime_}, callbackInfoEx {callbackInfoEx_}](napi_env envir, napi_value result) ->
630         bool {
631         if (!CheckPromise(envir, result)) {
632             string str;
633             bool isExceptionPending;
634             napi_is_exception_pending(envir, &isExceptionPending);
635             HILOGI("napi exception pending = %{public}d.", isExceptionPending);
636             if (isExceptionPending) {
637                 napi_value exception;
638                 DealNapiException(envir, exception, str);
639                 napi_fatal_exception(envir, exception);
640                 callbackInfoEx->callbackParam(BError(BError::Codes::EXT_THROW_EXCEPTION), str);
641             } else {
642                 DealNapiStrValue(envir, result, str);
643                 callbackInfoEx->callbackParam(BError(BError::Codes::OK), str);
644             }
645             return true;
646         }
647         HILOGI("CheckPromise onRestoreEx ok");
648         return CallPromiseEx(*jsRuntime, result, callbackInfoEx.get());
649     };
650     auto errCode = CallJsMethod("onRestoreEx", jsRuntime_, jsObj_.get(), ParseRestoreExInfo(), retParser);
651     if (errCode != ERR_OK) {
652         HILOGE("Call onRestoreEx error");
653         return errCode;
654     }
655     HILOGI("Check callRestoreExDone load");
656     std::unique_lock<std::mutex> lock(callJsMutex_);
657     callJsCon_.wait(lock, [this] { return callJsExMethodDone_.load(); });
658     HILOGI("Check needCallOnRestore load");
659     if (!callExtDefaultFunc_.load()) {
660         HILOGI("Call Js method onRestoreEx done");
661         return ERR_OK;
662     }
663     return CallJSRestore();
664 }
665 
CallJSRestore()666 ErrCode ExtBackupJs::CallJSRestore()
667 {
668     HILOGI("Start call app js method onRestore");
669     auto retParser = [jsRuntime {&jsRuntime_}, callbackInfo {callbackInfo_}](napi_env env, napi_value result) -> bool {
670         if (!CheckPromise(env, result)) {
671             string str;
672             bool isExceptionPending;
673             napi_is_exception_pending(env, &isExceptionPending);
674             HILOGI("napi exception pending = %{public}d.", isExceptionPending);
675             if (isExceptionPending) {
676                 napi_value exception;
677                 DealNapiException(env, exception, str);
678                 napi_fatal_exception(env, exception);
679                 callbackInfo->callback(BError(BError::Codes::EXT_THROW_EXCEPTION), str);
680             } else {
681                 callbackInfo->callback(BError(BError::Codes::OK), str);
682             }
683             return true;
684         }
685         HILOGI("CheckPromise Js Method onRestore ok.");
686         return CallPromise(*jsRuntime, result, callbackInfo.get());
687     };
688     auto errCode = CallJsMethod("onRestore", jsRuntime_, jsObj_.get(), ParseRestoreInfo(), retParser);
689     if (errCode != ERR_OK) {
690         HILOGE("CallJsMethod error, code:%{public}d.", errCode);
691         return errCode;
692     }
693     return ERR_OK;
694 }
695 
GetBackupInfo(std::function<void (ErrCode,const std::string)> callback)696 ErrCode ExtBackupJs::GetBackupInfo(std::function<void(ErrCode, const std::string)> callback)
697 {
698     HILOGI("BackupExtensionAbility(JS) GetBackupInfo begin.");
699     BExcepUltils::BAssert(jsObj_, BError::Codes::EXT_BROKEN_FRAMEWORK,
700                           "The app does not provide the GetBackupInfo interface.");
701     callbackInfoBackup_ = std::make_shared<CallbackInfoBackup>(callback);
702     auto retParser = [jsRuntime {&jsRuntime_}, callBackInfo {callbackInfoBackup_}](napi_env env,
703         napi_value result) -> bool {
704         if (!CheckPromise(env, result)) {
705             bool isExceptionPending;
706             napi_is_exception_pending(env, &isExceptionPending);
707             HILOGI("napi exception pending = %{public}d.", isExceptionPending);
708             if (isExceptionPending) {
709                 string str;
710                 napi_value exception;
711                 DealNapiException(env, exception, str);
712                 callBackInfo->callbackParam(BError(BError::Codes::EXT_THROW_EXCEPTION), str);
713                 return false;
714             }
715             size_t strLen = 0;
716             napi_status status = napi_get_value_string_utf8(env, result, nullptr, -1, &strLen);
717             if (status != napi_ok) {
718                 return false;
719             }
720             size_t bufLen = strLen + 1;
721             unique_ptr<char[]> str = make_unique<char[]>(bufLen);
722             status = napi_get_value_string_utf8(env, result, str.get(), bufLen, &strLen);
723             callBackInfo->callbackParam(BError(BError::Codes::OK), str.get());
724             return true;
725         }
726         HILOGI("BackupExtensionAbulity(JS) GetBackupInfo ok.");
727         return CallPromiseEx(*jsRuntime, result, callBackInfo.get());
728     };
729 
730     auto errCode = CallJsMethod("getBackupInfo", jsRuntime_, jsObj_.get(), {}, retParser);
731     if (errCode != ERR_OK) {
732         HILOGE("CallJsMethod error, code:%{public}d.", errCode);
733     }
734     HILOGI("BackupExtensionAbulity(JS) GetBackupInfo end.");
735     return errCode;
736 }
737 
DoCallJsMethod(CallJsParam * param)738 static int DoCallJsMethod(CallJsParam *param)
739 {
740     AbilityRuntime::JsRuntime *jsRuntime = param->jsRuntime;
741     HILOGI("Start execute DoCallJsMethod");
742     if (jsRuntime == nullptr) {
743         HILOGE("failed to get jsRuntime.");
744         return EINVAL;
745     }
746     AbilityRuntime::HandleEscape handleEscape(*jsRuntime);
747     auto env = jsRuntime->GetNapiEnv();
748     napi_handle_scope scope = nullptr;
749     napi_open_handle_scope(env, &scope);
750     if (scope == nullptr) {
751         HILOGE("scope is nullptr");
752         return EINVAL;
753     }
754     vector<napi_value> argv = {};
755     if (param->argParser != nullptr) {
756         if (!param->argParser(env, argv)) {
757             HILOGE("failed to get params.");
758             napi_close_handle_scope(env, scope);
759             return EINVAL;
760         }
761     }
762     napi_value value = param->jsObj->GetNapiValue();
763     if (value == nullptr) {
764         HILOGE("failed to get napi value object.");
765         napi_close_handle_scope(env, scope);
766         return EINVAL;
767     }
768     napi_status status;
769     napi_value method;
770     status = napi_get_named_property(env, value, param->funcName.c_str(), &method);
771     if (status != napi_ok || param->retParser == nullptr) {
772         HILOGE("ResultValueParser must not null.");
773         napi_close_handle_scope(env, scope);
774         return EINVAL;
775     }
776     napi_value result;
777     HILOGI("Extension start do call current js method, methodName:%{public}s", param->funcName.c_str());
778     napi_call_function(env, value, method, argv.size(), argv.data(), &result);
779     if (!param->retParser(env, handleEscape.Escape(result))) {
780         HILOGE("Parser js result fail.");
781         napi_close_handle_scope(env, scope);
782         return EINVAL;
783     }
784     napi_close_handle_scope(env, scope);
785     HILOGI("End execute DoCallJsMethod");
786     return ERR_OK;
787 }
788 
CallJsMethod(const std::string & funcName,AbilityRuntime::JsRuntime & jsRuntime,NativeReference * jsObj,InputArgsParser argParser,ResultValueParser retParser)789 int ExtBackupJs::CallJsMethod(const std::string &funcName,
790                               AbilityRuntime::JsRuntime &jsRuntime,
791                               NativeReference *jsObj,
792                               InputArgsParser argParser,
793                               ResultValueParser retParser)
794 {
795     uv_loop_s *loop = nullptr;
796     napi_status status = napi_get_uv_event_loop(jsRuntime.GetNapiEnv(), &loop);
797     if (status != napi_ok) {
798         HILOGE("failed to get uv event loop.");
799         return EINVAL;
800     }
801     auto param = std::make_shared<CallJsParam>(funcName, &jsRuntime, jsObj, argParser, retParser);
802     BExcepUltils::BAssert(param, BError::Codes::EXT_BROKEN_FRAMEWORK, "failed to new param.");
803 
804     auto work = std::make_shared<uv_work_t>();
805     BExcepUltils::BAssert(work, BError::Codes::EXT_BROKEN_FRAMEWORK, "failed to new uv_work_t.");
806 
807     work->data = reinterpret_cast<void *>(param.get());
808     HILOGI("Will execute current js method");
809     int ret = uv_queue_work(
810         loop, work.get(), [](uv_work_t *work) {},
811         [](uv_work_t *work, int status) {
812             CallJsParam *param = reinterpret_cast<CallJsParam *>(work->data);
813             do {
814                 if (param == nullptr) {
815                     HILOGE("failed to get CallJsParam.");
816                     break;
817                 }
818                 HILOGI("Start call current js method");
819                 if (DoCallJsMethod(param) != ERR_OK) {
820                     HILOGE("failed to call DoCallJsMethod.");
821                 }
822             } while (false);
823             HILOGI("will notify current thread info");
824             std::unique_lock<std::mutex> lock(param->backupOperateMutex);
825             param->isReady.store(true);
826             param->backupOperateCondition.notify_all();
827         });
828     if (ret != 0) {
829         HILOGE("failed to exec uv_queue_work.");
830         return EINVAL;
831     }
832     HILOGI("Wait execute current js method");
833     std::unique_lock<std::mutex> lock(param->backupOperateMutex);
834     param->backupOperateCondition.wait(lock, [param]() { return param->isReady.load(); });
835     HILOGI("End do call current js method");
836     return ERR_OK;
837 }
838 
ParseBackupExInfo()839 std::function<bool(napi_env env, std::vector<napi_value> &argv)> ExtBackupJs::ParseBackupExInfo()
840 {
841     auto onBackupExFun = [backupExtInfo(backupExtInfo_)](napi_env env, vector<napi_value> &argv) -> bool {
842         napi_value backupExtInfoVal = nullptr;
843         napi_create_object(env, &backupExtInfoVal);
844         HILOGI("backupExtInfo is:%{public}s", GetAnonyString(backupExtInfo).c_str());
845         napi_create_string_utf8(env, backupExtInfo.c_str(), backupExtInfo.size(), &backupExtInfoVal);
846         argv.push_back(backupExtInfoVal);
847         return true;
848     };
849     return onBackupExFun;
850 }
851 
ParseRestoreExInfo()852 std::function<bool(napi_env env, std::vector<napi_value> &argv)> ExtBackupJs::ParseRestoreExInfo()
853 {
854     auto onRestoreExFun = [appVersionCode(appVersionCode_), appVersionStr(appVersionStr_),
855         restoreExtInfo(restoreExtInfo_)](napi_env env, vector<napi_value> &argv) -> bool {
856         HILOGI("restoreExtInfo is:%{public}s", GetAnonyString(restoreExtInfo).c_str());
857         napi_value objValue = nullptr;
858         napi_value restoreRetValue = nullptr;
859         napi_create_object(env, &objValue);
860         napi_create_object(env, &restoreRetValue);
861         napi_set_named_property(env, objValue, "code", AbilityRuntime::CreateJsValue(env, appVersionCode));
862         napi_set_named_property(env, objValue, "name", AbilityRuntime::CreateJsValue(env, appVersionStr.c_str()));
863         napi_create_string_utf8(env, restoreExtInfo.c_str(), restoreExtInfo.size(), &restoreRetValue);
864         argv.push_back(objValue);
865         argv.push_back(restoreRetValue);
866         return true;
867     };
868     return onRestoreExFun;
869 }
870 
ParseRestoreInfo()871 std::function<bool(napi_env env, std::vector<napi_value> &argv)> ExtBackupJs::ParseRestoreInfo()
872 {
873     auto onRestoreFun = [appVersionCode(appVersionCode_), appVersionStr(appVersionStr_)](napi_env env,
874         vector<napi_value> &argv) -> bool {
875         std::string appVersionStrFlag = appVersionStr;
876         int64_t appVersionCodeFlag = appVersionCode;
877         napi_value objValue = nullptr;
878         napi_create_object(env, &objValue);
879         auto pos = appVersionStrFlag.find_first_of(BConstants::VERSION_NAME_SEPARATOR_CHAR);
880         std::string appVersionFlag = "";
881         if (pos != string::npos) {
882             appVersionFlag = appVersionStrFlag.substr(0, pos);
883             if (appVersionFlag == BConstants::DEFAULT_VERSION_NAME) {
884                 appVersionStrFlag = appVersionFlag;
885                 appVersionCodeFlag = 0;
886             }
887         }
888         napi_set_named_property(env, objValue, "code", AbilityRuntime::CreateJsValue(env, appVersionCodeFlag));
889         napi_set_named_property(env, objValue, "name", AbilityRuntime::CreateJsValue(env, appVersionStrFlag.c_str()));
890         argv.push_back(objValue);
891         return true;
892     };
893     return onRestoreFun;
894 }
895 
InvokeAppExtMethod(ErrCode errCode,const std::string result)896 ErrCode ExtBackupJs::InvokeAppExtMethod(ErrCode errCode, const std::string result)
897 {
898     HILOGI("Start Get onBackupEx/onRestoreEx method result, errCode: %{public}d, result: %{public}s",
899         errCode, result.c_str());
900     if ((result.size() == 0) && (errCode == BError(BError::Codes::OK))) {
901         callExtDefaultFunc_.store(true);
902     } else {
903         callExtDefaultFunc_.store(false);
904     }
905     callJsExMethodDone_.store(true);
906     callJsCon_.notify_one();
907     HILOGI("End Get App onBackupEx/onRestoreEx method result");
908     return ERR_OK;
909 }
910 
OnProcess(std::function<void (ErrCode,const std::string)> callback)911 ErrCode ExtBackupJs::OnProcess(std::function<void(ErrCode, const std::string)> callback)
912 {
913     HILOGI("BackupExtensionAbility(JS) OnProcess begin.");
914     BExcepUltils::BAssert(jsObj_, BError::Codes::EXT_BROKEN_FRAMEWORK,
915                           "The app does not provide the OnProcess interface.");
916     onProcessCallback_ = std::make_shared<OnProcessCallBackInfo>(callback);
917     auto retParser = [jsRuntime {&jsRuntime_}, callBackInfo {onProcessCallback_}](napi_env env,
918         napi_value result) -> bool {
919             string processStr;
920             bool isExceptionPending;
921             napi_is_exception_pending(env, &isExceptionPending);
922             HILOGI("napi exception pending = %{public}d.", isExceptionPending);
923             if (isExceptionPending) {
924                 napi_value exception;
925                 napi_get_and_clear_last_exception(env, &exception);
926                 callBackInfo->onProcessCallback(BError(BError::Codes::EXT_THROW_EXCEPTION), processStr);
927             } else {
928                 DealNapiStrValue(env, result, processStr);
929                 callBackInfo->onProcessCallback(BError(BError::Codes::OK), processStr);
930             }
931             return true;
932     };
933     auto errCode = CallJsMethod("onProcess", jsRuntime_, jsObj_.get(), {}, retParser);
934     if (errCode != ERR_OK) {
935         HILOGE("CallJsMethod error, code:%{public}d.", errCode);
936     }
937     HILOGI("BackupExtensionAbulity(JS) OnProcess end.");
938     return errCode;
939 }
940 } // namespace OHOS::FileManagement::Backup
941