1 /*
2 * Copyright (c) 2023-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 "cloud_sync_napi.h"
17
18 #include <sys/types.h>
19
20 #include "async_work.h"
21 #include "cloud_sync_manager.h"
22 #include "dfs_error.h"
23 #include "dfsu_access_token_helper.h"
24 #include "securec.h"
25 #include "uri.h"
26 #include "utils_log.h"
27
28 namespace OHOS::FileManagement::CloudSync {
29 using namespace FileManagement::LibN;
30 using namespace std;
31 const int32_t PARAM0 = 0;
32 static const unsigned int READ_SIZE = 1024;
33 const string FILE_SCHEME = "file";
34 thread_local unique_ptr<ChangeListenerNapi> g_listObj = nullptr;
35 mutex CloudSyncNapi::sOnOffMutex_;
36 static mutex obsMutex_;
37
38 class ObserverImpl : public AAFwk::DataAbilityObserverStub {
39 public:
ObserverImpl(const shared_ptr<CloudNotifyObserver> cloudNotifyObserver)40 explicit ObserverImpl(const shared_ptr<CloudNotifyObserver> cloudNotifyObserver)
41 : cloudNotifyObserver_(cloudNotifyObserver){};
42 void OnChange();
43 void OnChangeExt(const AAFwk::ChangeInfo &info);
44 static sptr<ObserverImpl> GetObserver(const Uri &uri, const shared_ptr<CloudNotifyObserver> &observer);
45 static bool FindObserver(const Uri &uri, const shared_ptr<CloudNotifyObserver> &observer);
46 static bool DeleteObserver(const Uri &uri, const shared_ptr<CloudNotifyObserver> &observer);
47
48 private:
49 struct ObserverParam {
50 sptr<ObserverImpl> obs_;
51 list<Uri> uris_;
52 };
53 shared_ptr<CloudNotifyObserver> cloudNotifyObserver_;
54 static ConcurrentMap<CloudNotifyObserver *, ObserverParam> observers_;
55 };
56
57 ConcurrentMap<CloudNotifyObserver *, ObserverImpl::ObserverParam> ObserverImpl::observers_;
58
OnChange()59 void ObserverImpl::OnChange() {}
60
OnChangeExt(const AAFwk::ChangeInfo & info)61 void ObserverImpl::OnChangeExt(const AAFwk::ChangeInfo &info)
62 {
63 if (cloudNotifyObserver_ == nullptr) {
64 LOGE("cloudNotifyObserver_ is null!");
65 return;
66 }
67 cloudNotifyObserver_->OnchangeExt(info);
68 }
69
GetObserver(const Uri & uri,const shared_ptr<CloudNotifyObserver> & observer)70 sptr<ObserverImpl> ObserverImpl::GetObserver(const Uri &uri, const shared_ptr<CloudNotifyObserver> &observer)
71 {
72 lock_guard<mutex> lock(obsMutex_);
73 sptr<ObserverImpl> result = nullptr;
74 observers_.Compute(observer.get(), [&result, &uri, &observer](const auto &key, auto &value) {
75 if (value.obs_ == nullptr) {
76 value.obs_ = new (nothrow) ObserverImpl(observer);
77 value.uris_.push_back(uri);
78 } else {
79 auto it = find(value.uris_.begin(), value.uris_.end(), uri);
80 if (it == value.uris_.end()) {
81 value.uris_.push_back(uri);
82 }
83 }
84
85 result = value.obs_;
86 return result != nullptr;
87 });
88
89 return result;
90 }
91
FindObserver(const Uri & uri,const shared_ptr<CloudNotifyObserver> & observer)92 bool ObserverImpl::FindObserver(const Uri &uri, const shared_ptr<CloudNotifyObserver> &observer)
93 {
94 lock_guard<mutex> lock(obsMutex_);
95 auto result = observers_.Find(observer.get());
96 if (result.first) {
97 auto it = std::find(result.second.uris_.begin(), result.second.uris_.end(), uri);
98 if (it == result.second.uris_.end()) {
99 return false;
100 }
101 }
102 return result.first;
103 }
104
DeleteObserver(const Uri & uri,const shared_ptr<CloudNotifyObserver> & observer)105 bool ObserverImpl::DeleteObserver(const Uri &uri, const shared_ptr<CloudNotifyObserver> &observer)
106 {
107 lock_guard<mutex> lock(obsMutex_);
108 return observers_.ComputeIfPresent(observer.get(), [&uri](auto &key, auto &value) {
109 value.uris_.remove_if([&uri](const auto &value) { return uri == value; });
110 return !value.uris_.empty();
111 });
112 }
113
CloudSyncCallbackImpl(napi_env env,napi_value fun)114 CloudSyncCallbackImpl::CloudSyncCallbackImpl(napi_env env, napi_value fun) : env_(env)
115 {
116 if (fun != nullptr) {
117 napi_create_reference(env_, fun, 1, &cbOnRef_);
118 }
119 }
120
OnComplete(UvChangeMsg * msg)121 void CloudSyncCallbackImpl::OnComplete(UvChangeMsg *msg)
122 {
123 auto cloudSyncCallback = msg->cloudSyncCallback_.lock();
124 if (cloudSyncCallback == nullptr || cloudSyncCallback->cbOnRef_ == nullptr) {
125 LOGE("cloudSyncCallback->cbOnRef_ is nullptr");
126 return;
127 }
128 auto env = cloudSyncCallback->env_;
129 auto ref = cloudSyncCallback->cbOnRef_;
130 napi_handle_scope scope = nullptr;
131 napi_open_handle_scope(env, &scope);
132 napi_value jsCallback = nullptr;
133 napi_status status = napi_get_reference_value(env, ref, &jsCallback);
134 if (status != napi_ok) {
135 LOGE("Create reference failed, status: %{public}d", status);
136 napi_close_handle_scope(env, scope);
137 return;
138 }
139 NVal obj = NVal::CreateObject(env);
140 obj.AddProp("state", NVal::CreateInt32(env, (int32_t)msg->state_).val_);
141 obj.AddProp("error", NVal::CreateInt32(env, (int32_t)msg->error_).val_);
142 napi_value retVal = nullptr;
143 napi_value global = nullptr;
144 napi_get_global(env, &global);
145 status = napi_call_function(env, global, jsCallback, ARGS_ONE, &(obj.val_), &retVal);
146 if (status != napi_ok) {
147 LOGE("napi call function failed, status: %{public}d", status);
148 }
149 napi_close_handle_scope(env, scope);
150 }
151
OnSyncStateChanged(CloudSyncState state,ErrorType error)152 void CloudSyncCallbackImpl::OnSyncStateChanged(CloudSyncState state, ErrorType error)
153 {
154 uv_loop_s *loop = nullptr;
155 napi_get_uv_event_loop(env_, &loop);
156 if (loop == nullptr) {
157 return;
158 }
159
160 uv_work_t *work = new (nothrow) uv_work_t;
161 if (work == nullptr) {
162 LOGE("Failed to create uv work");
163 return;
164 }
165
166 UvChangeMsg *msg = new (std::nothrow) UvChangeMsg(shared_from_this(), state, error);
167 if (msg == nullptr) {
168 delete work;
169 return;
170 }
171
172 work->data = reinterpret_cast<void *>(msg);
173 int ret = uv_queue_work(
174 loop, work, [](uv_work_t *work) {},
175 [](uv_work_t *work, int status) {
176 auto msg = reinterpret_cast<UvChangeMsg *>(work->data);
177 OnComplete(msg);
178 delete msg;
179 delete work;
180 });
181 if (ret != 0) {
182 LOGE("Failed to execute libuv work queue, ret: %{public}d", ret);
183 delete msg;
184 delete work;
185 }
186 }
187
DeleteReference()188 void CloudSyncCallbackImpl::DeleteReference()
189 {
190 if (cbOnRef_ != nullptr) {
191 napi_delete_reference(env_, cbOnRef_);
192 cbOnRef_ = nullptr;
193 }
194 }
195
OnSyncStateChanged(SyncType type,SyncPromptState state)196 void CloudSyncCallbackImpl::OnSyncStateChanged(SyncType type, SyncPromptState state)
197 {
198 return;
199 }
200
GetBundleName(const napi_env & env,const NFuncArg & funcArg)201 string CloudSyncNapi::GetBundleName(const napi_env &env, const NFuncArg &funcArg)
202 {
203 string bundleName;
204 auto bundleEntity = NClass::GetEntityOf<BundleEntity>(env, funcArg.GetThisVar());
205 if (bundleEntity) {
206 bundleName = bundleEntity->bundleName_;
207 }
208 return bundleName;
209 }
210
Constructor(napi_env env,napi_callback_info info)211 napi_value CloudSyncNapi::Constructor(napi_env env, napi_callback_info info)
212 {
213 NFuncArg funcArg(env, info);
214 if (!funcArg.InitArgs(NARG_CNT::ZERO, NARG_CNT::ONE)) {
215 NError(E_PARAMS).ThrowErr(env, "Number of arguments unmatched");
216 return nullptr;
217 }
218 if (funcArg.GetArgc() == NARG_CNT::ZERO) {
219 LOGD("init without bundleName");
220 return funcArg.GetThisVar();
221 }
222 if (funcArg.GetArgc() == NARG_CNT::ONE) {
223 auto [succ, bundleName, ignore] = NVal(env, funcArg[(int)NARG_POS::FIRST]).ToUTF8String();
224 if (!succ || bundleName.get() == string("")) {
225 LOGE("Failed to get bundle name");
226 NError(E_PARAMS).ThrowErr(env);
227 return nullptr;
228 }
229 auto bundleEntity = make_unique<BundleEntity>(bundleName.get());
230 if (!NClass::SetEntityFor<BundleEntity>(env, funcArg.GetThisVar(), move(bundleEntity))) {
231 LOGE("Failed to set file entity");
232 NError(EIO).ThrowErr(env);
233 return nullptr;
234 }
235 LOGI("init with bundleName");
236 return funcArg.GetThisVar();
237 }
238 return nullptr;
239 }
240
OnCallback(napi_env env,napi_callback_info info)241 napi_value CloudSyncNapi::OnCallback(napi_env env, napi_callback_info info)
242 {
243 NFuncArg funcArg(env, info);
244 if (!funcArg.InitArgs(NARG_CNT::TWO)) {
245 NError(E_PARAMS).ThrowErr(env, "Number of arguments unmatched");
246 LOGE("OnCallback Number of arguments unmatched");
247 return nullptr;
248 }
249
250 auto [succ, type, ignore] = NVal(env, funcArg[(int)NARG_POS::FIRST]).ToUTF8String();
251 if (!(succ && (type.get() == std::string("progress")))) {
252 NError(E_PARAMS).ThrowErr(env);
253 return nullptr;
254 }
255
256 if (!NVal(env, funcArg[(int)NARG_POS::SECOND]).TypeIs(napi_function)) {
257 LOGE("Argument type mismatch");
258 NError(E_PARAMS).ThrowErr(env);
259 return nullptr;
260 }
261
262 if (callback_ != nullptr) {
263 LOGI("callback already exist");
264 return NVal::CreateUndefined(env).val_;
265 }
266
267 string bundleName = GetBundleName(env, funcArg);
268 callback_ = make_shared<CloudSyncCallbackImpl>(env, NVal(env, funcArg[(int)NARG_POS::SECOND]).val_);
269 int32_t ret = CloudSyncManager::GetInstance().RegisterCallback(callback_, bundleName);
270 if (ret != E_OK) {
271 LOGE("OnCallback Register error, result: %{public}d", ret);
272 NError(Convert2JsErrNum(ret)).ThrowErr(env);
273 return nullptr;
274 }
275
276 return NVal::CreateUndefined(env).val_;
277 }
278
OffCallback(napi_env env,napi_callback_info info)279 napi_value CloudSyncNapi::OffCallback(napi_env env, napi_callback_info info)
280 {
281 NFuncArg funcArg(env, info);
282 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) {
283 NError(E_PARAMS).ThrowErr(env, "Number of arguments unmatched");
284 LOGE("OffCallback Number of arguments unmatched");
285 return nullptr;
286 }
287
288 auto [succ, type, ignore] = NVal(env, funcArg[(int)NARG_POS::FIRST]).ToUTF8String();
289 if (!(succ && (type.get() == std::string("progress")))) {
290 NError(E_PARAMS).ThrowErr(env);
291 return nullptr;
292 }
293
294 if (funcArg.GetArgc() == (uint)NARG_CNT::TWO && !NVal(env, funcArg[(int)NARG_POS::SECOND]).TypeIs(napi_function)) {
295 LOGE("Argument type mismatch");
296 NError(E_PARAMS).ThrowErr(env);
297 return nullptr;
298 }
299
300 string bundleName = GetBundleName(env, funcArg);
301 int32_t ret = CloudSyncManager::GetInstance().UnRegisterCallback(bundleName);
302 if (ret != E_OK) {
303 LOGE("OffCallback UnRegister error, result: %{public}d", ret);
304 NError(Convert2JsErrNum(ret)).ThrowErr(env);
305 return nullptr;
306 }
307 if (callback_ != nullptr) {
308 /* napi delete reference */
309 callback_->DeleteReference();
310 callback_ = nullptr;
311 }
312 return NVal::CreateUndefined(env).val_;
313 }
314
Start(napi_env env,napi_callback_info info)315 napi_value CloudSyncNapi::Start(napi_env env, napi_callback_info info)
316 {
317 NFuncArg funcArg(env, info);
318 if (!funcArg.InitArgs(NARG_CNT::ZERO, NARG_CNT::ONE)) {
319 NError(E_PARAMS).ThrowErr(env);
320 }
321
322 string bundleName = GetBundleName(env, funcArg);
323 auto cbExec = [bundleName]() -> NError {
324 int32_t ret = CloudSyncManager::GetInstance().StartSync(bundleName);
325 if (ret != E_OK) {
326 LOGE("Start Sync error, result: %{public}d", ret);
327 return NError(Convert2JsErrNum(ret));
328 }
329 return NError(ERRNO_NOERR);
330 };
331
332 auto cbComplete = [](napi_env env, NError err) -> NVal {
333 if (err) {
334 return {env, err.GetNapiErr(env)};
335 }
336 return NVal::CreateUndefined(env);
337 };
338
339 std::string procedureName = "Start";
340 auto asyncWork = GetPromiseOrCallBackWork(env, funcArg, static_cast<size_t>(NARG_CNT::TWO));
341 return asyncWork == nullptr ? nullptr : asyncWork->Schedule(procedureName, cbExec, cbComplete).val_;
342 }
343
Stop(napi_env env,napi_callback_info info)344 napi_value CloudSyncNapi::Stop(napi_env env, napi_callback_info info)
345 {
346 NFuncArg funcArg(env, info);
347 if (!funcArg.InitArgs(NARG_CNT::ZERO, NARG_CNT::ONE)) {
348 NError(E_PARAMS).ThrowErr(env);
349 return nullptr;
350 }
351
352 string bundleName = GetBundleName(env, funcArg);
353 auto cbExec = [bundleName]() -> NError {
354 int32_t ret = CloudSyncManager::GetInstance().StopSync(bundleName);
355 if (ret != E_OK) {
356 LOGE("Stop Sync error, result: %{public}d", ret);
357 return NError(Convert2JsErrNum(ret));
358 }
359 return NError(ERRNO_NOERR);
360 };
361
362 auto cbComplete = [](napi_env env, NError err) -> NVal {
363 if (err) {
364 return {env, err.GetNapiErr(env)};
365 }
366 return NVal::CreateUndefined(env);
367 };
368
369 std::string procedureName = "Stop";
370 auto asyncWork = GetPromiseOrCallBackWork(env, funcArg, static_cast<size_t>(NARG_CNT::TWO));
371 return asyncWork == nullptr ? nullptr : asyncWork->Schedule(procedureName, cbExec, cbComplete).val_;
372 }
373
CheckRef(napi_env env,napi_ref ref,ChangeListenerNapi & listObj,const string & uri)374 bool CloudSyncNapi::CheckRef(napi_env env, napi_ref ref, ChangeListenerNapi &listObj, const string &uri)
375 {
376 napi_value offCallback = nullptr;
377 napi_status status = napi_get_reference_value(env, ref, &offCallback);
378 if (status != napi_ok) {
379 LOGE("Create reference fail, status: %{public}d", status);
380 return false;
381 }
382 bool isSame = false;
383 shared_ptr<CloudNotifyObserver> obs;
384 string obsUri;
385 {
386 lock_guard<mutex> lock(sOnOffMutex_);
387 for (auto it = listObj.observers_.begin(); it < listObj.observers_.end(); it++) {
388 napi_value onCallback = nullptr;
389 status = napi_get_reference_value(env, (*it)->ref_, &onCallback);
390 if (status != napi_ok) {
391 LOGE("Create reference fail, status: %{public}d", status);
392 return false;
393 }
394 napi_strict_equals(env, offCallback, onCallback, &isSame);
395 if (isSame) {
396 obsUri = (*it)->uri_;
397 if (uri.compare(obsUri) != 0) {
398 return true;
399 }
400 return false;
401 }
402 }
403 }
404 return true;
405 }
406
CheckIsValidUri(Uri uri)407 static bool CheckIsValidUri(Uri uri)
408 {
409 string scheme = uri.GetScheme();
410 if (scheme != FILE_SCHEME) {
411 return false;
412 }
413 string sandboxPath = uri.GetPath();
414 char realPath[PATH_MAX + 1]{'\0'};
415 if (realpath(sandboxPath.c_str(), realPath) == nullptr) {
416 LOGE("realpath failed with %{public}d", errno);
417 return false;
418 }
419 if (strncmp(realPath, sandboxPath.c_str(), sandboxPath.size()) != 0) {
420 LOGE("sandboxPath is not equal to realPath");
421 return false;
422 }
423 if (sandboxPath.find("/data/storage/el2/cloud") != 0) {
424 LOGE("not surported uri");
425 return false;
426 }
427 return true;
428 }
429
GetRegisterParams(napi_env env,napi_callback_info info,RegisterParams & registerParams)430 static int32_t GetRegisterParams(napi_env env, napi_callback_info info, RegisterParams ®isterParams)
431 {
432 NFuncArg funcArg(env, info);
433 if (!funcArg.InitArgs(NARG_CNT::THREE)) {
434 LOGE("Arguments number mismatch");
435 return E_PARAMS;
436 }
437
438 auto [succUri, uri, ignore] = NVal(env, funcArg[(int)NARG_POS::FIRST]).ToUTF8String();
439 if (!succUri) {
440 LOGE("get arg uri fail");
441 return E_PARAMS;
442 }
443 registerParams.uri = string(uri.get());
444 if (!CheckIsValidUri(Uri(registerParams.uri))) {
445 LOGE("RegisterChange uri parameter format error!");
446 return E_PARAMS;
447 }
448
449 auto [succRecursion, recursion] = NVal(env, funcArg[(int)NARG_POS::SECOND]).ToBool();
450 if (!succRecursion) {
451 LOGE("get arg recursion fail");
452 return E_PARAMS;
453 }
454 registerParams.recursion = recursion;
455
456 if (!NVal(env, funcArg[(int)NARG_POS::THIRD]).TypeIs(napi_function)) {
457 LOGE("Argument type mismatch");
458 return E_PARAMS;
459 }
460 napi_status status =
461 napi_create_reference(env, NVal(env, funcArg[(int)NARG_POS::THIRD]).val_, 1, ®isterParams.cbOnRef);
462 if (status != napi_ok) {
463 LOGE("Create reference fail, status: %{public}d", status);
464 return E_PARAMS;
465 }
466
467 return ERR_OK;
468 }
469
RegisterToObs(napi_env env,const RegisterParams & registerParams)470 int32_t CloudSyncNapi::RegisterToObs(napi_env env, const RegisterParams ®isterParams)
471 {
472 auto observer = make_shared<CloudNotifyObserver>(*g_listObj, registerParams.uri, registerParams.cbOnRef);
473 Uri uri(registerParams.uri);
474 auto obsMgrClient = AAFwk::DataObsMgrClient::GetInstance();
475 if (obsMgrClient == nullptr) {
476 LOGE("get DataObsMgrClient failed");
477 return E_SA_LOAD_FAILED;
478 }
479 sptr<ObserverImpl> obs = ObserverImpl::GetObserver(uri, observer);
480 if (obs == nullptr) {
481 LOGE("new ObserverImpl failed");
482 return E_INVAL_ARG;
483 }
484 ErrCode ret = obsMgrClient->RegisterObserverExt(uri, obs, registerParams.recursion);
485 if (ret != E_OK) {
486 LOGE("ObsMgr register fail");
487 ObserverImpl::DeleteObserver(uri, observer);
488 return E_INVAL_ARG;
489 }
490 lock_guard<mutex> lock(CloudSyncNapi::sOnOffMutex_);
491 g_listObj->observers_.push_back(observer);
492 return E_OK;
493 }
494
RegisterChange(napi_env env,napi_callback_info info)495 napi_value CloudSyncNapi::RegisterChange(napi_env env, napi_callback_info info)
496 {
497 if (!DfsuAccessTokenHelper::CheckCallerPermission(PERM_CLOUD_SYNC)) {
498 LOGE("permission denied");
499 NError(E_PERMISSION_DENIED).ThrowErr(env);
500 return nullptr;
501 }
502 if (!DfsuAccessTokenHelper::IsSystemApp()) {
503 LOGE("caller hap is not system hap");
504 NError(E_PERMISSION_SYSTEM).ThrowErr(env);
505 return nullptr;
506 }
507
508 if (g_listObj == nullptr) {
509 g_listObj = make_unique<ChangeListenerNapi>(env);
510 }
511
512 RegisterParams registerParams;
513 int32_t ret = GetRegisterParams(env, info, registerParams);
514 if (ret != ERR_OK) {
515 LOGE("Get Params fail");
516 NError(ret).ThrowErr(env);
517 return nullptr;
518 }
519
520 if (CheckRef(env, registerParams.cbOnRef, *g_listObj, registerParams.uri)) {
521 ret = RegisterToObs(env, registerParams);
522 if (ret != E_OK) {
523 LOGE("Get Params fail");
524 NError(ret).ThrowErr(env);
525 return nullptr;
526 }
527 } else {
528 LOGE("Check Ref fail");
529 NError(E_PARAMS).ThrowErr(env);
530 napi_delete_reference(env, registerParams.cbOnRef);
531 return nullptr;
532 }
533 return NVal::CreateUndefined(env).val_;
534 }
535
UnregisterFromObs(napi_env env,const string & uri)536 napi_value CloudSyncNapi::UnregisterFromObs(napi_env env, const string &uri)
537 {
538 auto obsMgrClient = AAFwk::DataObsMgrClient::GetInstance();
539 if (obsMgrClient == nullptr) {
540 LOGE("get DataObsMgrClient failed");
541 NError(E_SA_LOAD_FAILED).ThrowErr(env);
542 return nullptr;
543 }
544 std::vector<std::shared_ptr<CloudNotifyObserver>> offObservers;
545 {
546 lock_guard<mutex> lock(sOnOffMutex_);
547 for (auto iter = g_listObj->observers_.begin(); iter != g_listObj->observers_.end();) {
548 if (uri == (*iter)->uri_) {
549 offObservers.push_back(*iter);
550 vector<shared_ptr<CloudNotifyObserver>>::iterator tmp = iter;
551 iter = g_listObj->observers_.erase(tmp);
552 } else {
553 iter++;
554 }
555 }
556 }
557 for (auto observer : offObservers) {
558 if (!ObserverImpl::FindObserver(Uri(uri), observer)) {
559 LOGE("observer not exist");
560 NError(E_PARAMS).ThrowErr(env);
561 return nullptr;
562 }
563 sptr<ObserverImpl> obs = ObserverImpl::GetObserver(Uri(uri), observer);
564 if (obs == nullptr) {
565 LOGE("new observerimpl failed");
566 NError(E_PARAMS).ThrowErr(env);
567 return nullptr;
568 }
569 ErrCode ret = obsMgrClient->UnregisterObserverExt(Uri(uri), obs);
570 if (ret != ERR_OK) {
571 LOGE("call obs unregister fail");
572 NError(E_PARAMS).ThrowErr(env);
573 return nullptr;
574 }
575 ObserverImpl::DeleteObserver(Uri(uri), observer);
576 }
577 return NVal::CreateUndefined(env).val_;
578 }
579
UnregisterChange(napi_env env,napi_callback_info info)580 napi_value CloudSyncNapi::UnregisterChange(napi_env env, napi_callback_info info)
581 {
582 if (!DfsuAccessTokenHelper::CheckCallerPermission(PERM_CLOUD_SYNC)) {
583 LOGE("permission denied");
584 NError(E_PERMISSION_DENIED).ThrowErr(env);
585 return nullptr;
586 }
587 if (!DfsuAccessTokenHelper::IsSystemApp()) {
588 LOGE("caller hap is not system hap");
589 NError(E_PERMISSION_SYSTEM).ThrowErr(env);
590 return nullptr;
591 }
592
593 if (g_listObj == nullptr || g_listObj->observers_.empty()) {
594 LOGI("no obs to unregister");
595 return nullptr;
596 }
597
598 NFuncArg funcArg(env, info);
599 if (!funcArg.InitArgs(NARG_CNT::ONE)) {
600 LOGE("params number mismatch");
601 NError(E_PARAMS).ThrowErr(env);
602 return nullptr;
603 }
604 auto [succUri, uri, ignore] = NVal(env, funcArg[(int)NARG_POS::FIRST]).ToUTF8String();
605 if (!succUri || !CheckIsValidUri(Uri(uri.get()))) {
606 LOGE("get uri fail");
607 NError(E_PARAMS).ThrowErr(env);
608 return nullptr;
609 }
610
611 return UnregisterFromObs(env, uri.get());
612 }
613
SetClassName(const std::string classname)614 void CloudSyncNapi::SetClassName(const std::string classname)
615 {
616 className_ = classname;
617 }
618
GetClassName()619 std::string CloudSyncNapi::GetClassName()
620 {
621 return className_;
622 }
623
ToExport(std::vector<napi_property_descriptor> props)624 bool CloudSyncNapi::ToExport(std::vector<napi_property_descriptor> props)
625 {
626 std::string className = GetClassName();
627 auto [succ, classValue] =
628 NClass::DefineClass(exports_.env_, className, Constructor, std::move(props));
629 if (!succ) {
630 NError(E_GETRESULT).ThrowErr(exports_.env_);
631 LOGE("Failed to define CloudSyncNapi class");
632 return false;
633 }
634
635 succ = NClass::SaveClass(exports_.env_, className, classValue);
636 if (!succ) {
637 NError(E_GETRESULT).ThrowErr(exports_.env_);
638 LOGE("Failed to save CloudSyncNapi class");
639 return false;
640 }
641
642 return exports_.AddProp(className, classValue);
643 }
644
Export()645 bool CloudSyncNapi::Export()
646 {
647 std::vector<napi_property_descriptor> props = {
648 NVal::DeclareNapiFunction("on", OnCallback),
649 NVal::DeclareNapiFunction("off", OffCallback),
650 NVal::DeclareNapiFunction("start", Start),
651 NVal::DeclareNapiFunction("stop", Stop),
652 };
653 std::string className = GetClassName();
654 auto [succ, classValue] =
655 NClass::DefineClass(exports_.env_, className, CloudSyncNapi::Constructor, std::move(props));
656 if (!succ) {
657 NError(E_GETRESULT).ThrowErr(exports_.env_);
658 LOGE("Failed to define GallerySync class");
659 return false;
660 }
661
662 succ = NClass::SaveClass(exports_.env_, className, classValue);
663 if (!succ) {
664 NError(E_GETRESULT).ThrowErr(exports_.env_);
665 LOGE("Failed to save GallerySync class");
666 return false;
667 }
668
669 return exports_.AddProp(className, classValue);
670 }
671
OnChange(CloudChangeListener & listener,const napi_ref cbRef)672 void ChangeListenerNapi::OnChange(CloudChangeListener &listener, const napi_ref cbRef)
673 {
674 uv_loop_s *loop = nullptr;
675 napi_get_uv_event_loop(env_, &loop);
676 if (loop == nullptr) {
677 return;
678 }
679
680 uv_work_t *work = new (nothrow) uv_work_t;
681 if (work == nullptr) {
682 return;
683 }
684
685 UvChangeMsg *msg = new (std::nothrow) UvChangeMsg(env_, cbRef, listener.changeInfo, listener.strUri);
686 if (msg == nullptr) {
687 delete work;
688 return;
689 }
690 if (!listener.changeInfo.uris_.empty()) {
691 if (static_cast<NotifyType>(listener.changeInfo.changeType_) == NotifyType::NOTIFY_NONE) {
692 LOGE("changeInfo.changeType_ is other");
693 delete msg;
694 delete work;
695 return;
696 }
697 if (msg->changeInfo_.size_ > 0) {
698 msg->data_ = (uint8_t *)malloc(msg->changeInfo_.size_);
699 if (msg->data_ == nullptr) {
700 LOGE("new msg->data failed");
701 delete msg;
702 delete work;
703 return;
704 }
705 int copyRet = memcpy_s(msg->data_, msg->changeInfo_.size_, msg->changeInfo_.data_, msg->changeInfo_.size_);
706 if (copyRet != 0) {
707 LOGE("Parcel data copy failed, err = %{public}d", copyRet);
708 }
709 }
710 }
711 work->data = reinterpret_cast<void *>(msg);
712
713 int ret = UvQueueWork(loop, work);
714 if (ret != 0) {
715 LOGE("Failed to execute libuv work queue, ret: %{public}d", ret);
716 delete msg;
717 delete work;
718 }
719 }
720
UvQueueWork(uv_loop_s * loop,uv_work_t * work)721 int32_t ChangeListenerNapi::UvQueueWork(uv_loop_s *loop, uv_work_t *work)
722 {
723 return uv_queue_work(
724 loop, work, [](uv_work_t *w) {},
725 [](uv_work_t *w, int s) {
726 // js thread
727 if (w == nullptr) {
728 return;
729 }
730
731 UvChangeMsg *msg = reinterpret_cast<UvChangeMsg *>(w->data);
732 do {
733 if (msg == nullptr) {
734 LOGE("UvChangeMsg is null");
735 break;
736 }
737 napi_env env = msg->env_;
738 napi_handle_scope scope = nullptr;
739 napi_open_handle_scope(env, &scope);
740
741 napi_value jsCallback = nullptr;
742 napi_status status = napi_get_reference_value(env, msg->ref_, &jsCallback);
743 if (status != napi_ok) {
744 napi_close_handle_scope(env, scope);
745 LOGE("Create reference fail, status: %{public}d", status);
746 break;
747 }
748 napi_value retVal = nullptr;
749 napi_value result[ARGS_ONE];
750 result[PARAM0] = ChangeListenerNapi::SolveOnChange(env, msg);
751 if (result[PARAM0] == nullptr) {
752 napi_close_handle_scope(env, scope);
753 break;
754 }
755 napi_call_function(env, nullptr, jsCallback, ARGS_ONE, result, &retVal);
756 napi_close_handle_scope(env, scope);
757 if (status != napi_ok) {
758 LOGE("CallJs napi_call_function fail, status: %{public}d", status);
759 break;
760 }
761 } while (0);
762 delete msg;
763 delete w;
764 });
765 }
766
SetValueArray(const napi_env & env,const char * fieldStr,const std::list<Uri> listValue,napi_value & result)767 static napi_status SetValueArray(const napi_env &env, const char *fieldStr, const std::list<Uri> listValue,
768 napi_value &result)
769 {
770 napi_value value = nullptr;
771 napi_status status = napi_create_array_with_length(env, listValue.size(), &value);
772 if (status != napi_ok) {
773 LOGE("Create array error! field: %{public}s", fieldStr);
774 return status;
775 }
776 int elementIndex = 0;
777 for (auto uri : listValue) {
778 napi_value uriRet = nullptr;
779 napi_create_string_utf8(env, uri.ToString().c_str(), NAPI_AUTO_LENGTH, &uriRet);
780 status = napi_set_element(env, value, elementIndex++, uriRet);
781 if (status != napi_ok) {
782 LOGE("Set lite item failed, error: %d", status);
783 }
784 }
785 status = napi_set_named_property(env, result, fieldStr, value);
786 if (status != napi_ok) {
787 LOGE("Set array named property error! field: %{public}s", fieldStr);
788 }
789
790 return status;
791 }
792
SetValueInt32(const napi_env & env,const char * fieldStr,const int intValue,napi_value & result)793 static napi_status SetValueInt32(const napi_env &env, const char *fieldStr, const int intValue, napi_value &result)
794 {
795 napi_value value;
796 napi_status status = napi_create_int32(env, intValue, &value);
797 if (status != napi_ok) {
798 LOGE("Set value create int32 error! field: %{public}s", fieldStr);
799 return status;
800 }
801 status = napi_set_named_property(env, result, fieldStr, value);
802 if (status != napi_ok) {
803 LOGE("Set int32 named property error! field: %{public}s", fieldStr);
804 }
805 return status;
806 }
807
SetIsDir(const napi_env & env,const shared_ptr<MessageParcel> parcel,napi_value & result)808 static napi_status SetIsDir(const napi_env &env, const shared_ptr<MessageParcel> parcel, napi_value &result)
809 {
810 uint32_t len = 0;
811 napi_status status = napi_invalid_arg;
812 if (!parcel->ReadUint32(len)) {
813 LOGE("Failed to read sub uri list length");
814 return status;
815 }
816 napi_value isDirArray = nullptr;
817 napi_create_array_with_length(env, len, &isDirArray);
818 int elementIndex = 0;
819 if (len > READ_SIZE) {
820 return napi_invalid_arg;
821 }
822 for (uint32_t i = 0; i < len; i++) {
823 bool isDir = parcel->ReadBool();
824 napi_value isDirRet = nullptr;
825 napi_get_boolean(env, isDir, &isDirRet);
826 napi_set_element(env, isDirArray, elementIndex++, isDirRet);
827 }
828 status = napi_set_named_property(env, result, "isDirectory", isDirArray);
829 if (status != napi_ok) {
830 LOGE("Set subUri named property error!");
831 }
832 return status;
833 }
834
SolveOnChange(napi_env env,UvChangeMsg * msg)835 napi_value ChangeListenerNapi::SolveOnChange(napi_env env, UvChangeMsg *msg)
836 {
837 static napi_value result;
838 if (msg->changeInfo_.uris_.empty()) {
839 napi_get_undefined(env, &result);
840 return result;
841 }
842 napi_create_object(env, &result);
843 SetValueArray(env, "uris", msg->changeInfo_.uris_, result);
844 if (msg->data_ != nullptr && msg->changeInfo_.size_ > 0) {
845 shared_ptr<MessageParcel> parcel = make_shared<MessageParcel>();
846 if (parcel->ParseFrom(reinterpret_cast<uintptr_t>(msg->data_), msg->changeInfo_.size_)) {
847 napi_status status = SetIsDir(env, parcel, result);
848 if (status != napi_ok) {
849 LOGE("Set subArray named property error! field: subUris");
850 return nullptr;
851 }
852 }
853 }
854 SetValueInt32(env, "type", (int)msg->changeInfo_.changeType_, result);
855 return result;
856 }
857
OnChange()858 void CloudNotifyObserver::OnChange() {}
859
OnchangeExt(const AAFwk::ChangeInfo & changeInfo)860 void CloudNotifyObserver::OnchangeExt(const AAFwk::ChangeInfo &changeInfo)
861 {
862 CloudChangeListener listener;
863 listener.changeInfo = changeInfo;
864 listener.strUri = uri_;
865 listObj_.OnChange(listener, ref_);
866 }
867
868 } // namespace OHOS::FileManagement::CloudSync
869