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