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