1 /*
2 * Copyright (c) 2025 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_callback_ani.h"
17
18 #include "ani_utils.h"
19 #include "utils_log.h"
20
21 namespace OHOS::FileManagement::CloudSync {
22
23 using namespace std;
24 using namespace arkts::ani_signature;
25
26 constexpr int32_t ANI_SCOPE_SIZE = 16;
27 static const unsigned int READ_SIZE = 1024;
28 static mutex obsMutex_;
29
30 ConcurrentMap<CloudNotifyObserver *, ObserverImpl::ObserverParam> ObserverImpl::observers_;
31
OnChange()32 void ObserverImpl::OnChange() {}
33
OnChangeExt(const AAFwk::ChangeInfo & info)34 void ObserverImpl::OnChangeExt(const AAFwk::ChangeInfo &info)
35 {
36 if (cloudNotifyObserver_ == nullptr) {
37 LOGE("cloudNotifyObserver_ is null!");
38 return;
39 }
40 cloudNotifyObserver_->OnChangeExt(info);
41 }
42
GetObserver(const Uri & uri,const shared_ptr<CloudNotifyObserver> & observer)43 sptr<ObserverImpl> ObserverImpl::GetObserver(const Uri &uri, const shared_ptr<CloudNotifyObserver> &observer)
44 {
45 lock_guard<mutex> lock(obsMutex_);
46 sptr<ObserverImpl> result = nullptr;
47 observers_.Compute(observer.get(), [&result, &uri, &observer](const auto &key, auto &value) {
48 if (value.obs_ == nullptr) {
49 value.obs_ = new (nothrow) ObserverImpl(observer);
50 value.uris_.push_back(uri);
51 } else {
52 auto it = find(value.uris_.begin(), value.uris_.end(), uri);
53 if (it == value.uris_.end()) {
54 value.uris_.push_back(uri);
55 }
56 }
57
58 result = value.obs_;
59 return result != nullptr;
60 });
61
62 return result;
63 }
64
FindObserver(const Uri & uri,const shared_ptr<CloudNotifyObserver> & observer)65 bool ObserverImpl::FindObserver(const Uri &uri, const shared_ptr<CloudNotifyObserver> &observer)
66 {
67 lock_guard<mutex> lock(obsMutex_);
68 auto result = observers_.Find(observer.get());
69 if (result.first) {
70 auto it = std::find(result.second.uris_.begin(), result.second.uris_.end(), uri);
71 if (it == result.second.uris_.end()) {
72 return false;
73 }
74 }
75 return result.first;
76 }
77
DeleteObserver(const Uri & uri,const shared_ptr<CloudNotifyObserver> & observer)78 bool ObserverImpl::DeleteObserver(const Uri &uri, const shared_ptr<CloudNotifyObserver> &observer)
79 {
80 lock_guard<mutex> lock(obsMutex_);
81 return observers_.ComputeIfPresent(observer.get(), [&uri](auto &key, auto &value) {
82 value.uris_.remove_if([&uri](const auto &value) { return uri == value; });
83 return !value.uris_.empty();
84 });
85 }
86
CloudSyncCallbackAniImpl(ani_env * env,ani_ref fun)87 CloudSyncCallbackAniImpl::CloudSyncCallbackAniImpl(ani_env *env, ani_ref fun)
88 {
89 if (env == nullptr || env->GetVM(&vm_)) {
90 LOGE("CloudSyncCallbackAniImpl get vm failed.");
91 return;
92 }
93 if (fun != nullptr) {
94 cbOnRef_ = fun;
95 }
96 }
97
GetSyncProgress(CloudSyncState state,ErrorType error,const ani_class & cls,ani_object & pg)98 void CloudSyncCallbackAniImpl::GetSyncProgress(
99 CloudSyncState state, ErrorType error, const ani_class &cls, ani_object &pg)
100 {
101 ani_env *env = nullptr;
102 if (vm_ == nullptr || vm_->GetEnv(ANI_VERSION_1, &env)) {
103 LOGE("CloudSyncCallbackAniImpl get env failed.");
104 return;
105 }
106 ani_method ctor;
107 std::string ct = Builder::BuildConstructorName();
108 std::string argSign = Builder::BuildSignatureDescriptor({
109 Builder::BuildEnum("@ohos.file.cloudSync.cloudSync.SyncState"),
110 Builder::BuildEnum("@ohos.file.cloudSync.cloudSync.ErrorType")
111 });
112 ani_status ret = env->Class_FindMethod(cls, ct.c_str(), argSign.c_str(), &ctor);
113 if (ret != ANI_OK) {
114 LOGE("find ctor method failed. ret = %{public}d", ret);
115 return;
116 }
117
118 ani_enum stateEnum;
119 Type stateSign = Builder::BuildEnum("@ohos.file.cloudSync.cloudSync.SyncState");
120 env->FindEnum(stateSign.Descriptor().c_str(), &stateEnum);
121 ani_enum errorEnum;
122 Type errorSign = Builder::BuildEnum("@ohos.file.cloudSync.cloudSync.ErrorType");
123 env->FindEnum(errorSign.Descriptor().c_str(), &errorEnum);
124
125 ani_enum_item stateEnumItem;
126 env->Enum_GetEnumItemByIndex(stateEnum, state, &stateEnumItem);
127 ani_enum_item errorEnumItem;
128 env->Enum_GetEnumItemByIndex(errorEnum, error, &errorEnumItem);
129
130 ret = env->Object_New(cls, ctor, &pg, stateEnumItem, errorEnumItem);
131 if (ret != ANI_OK) {
132 LOGE("create new object failed. ret = %{public}d", ret);
133 }
134 }
135
OnSyncStateChanged(CloudSyncState state,ErrorType error)136 void CloudSyncCallbackAniImpl::OnSyncStateChanged(CloudSyncState state, ErrorType error)
137 {
138 auto task = [this, state, error]() {
139 LOGI("OnSyncStateChanged state is %{public}d, error is %{public}d", state, error);
140 ani_env *tmpEnv = nullptr;
141 if (vm_ == nullptr || vm_->GetEnv(ANI_VERSION_1, &tmpEnv)) {
142 LOGE("CloudSyncCallbackAniImpl get env failed.");
143 return;
144 }
145 ani_size nr_refs = ANI_SCOPE_SIZE;
146 ani_status ret = tmpEnv->CreateLocalScope(nr_refs);
147 if (ret != ANI_OK) {
148 LOGE("crete local scope failed. ret = %{public}d", ret);
149 return;
150 }
151 ani_namespace ns {};
152 Namespace nsSign = Builder::BuildNamespace("@ohos.file.cloudSync.cloudSync");
153 ret = tmpEnv->FindNamespace(nsSign.Descriptor().c_str(), &ns);
154 if (ret != ANI_OK) {
155 LOGE("find namespace failed. ret = %{public}d", ret);
156 return;
157 }
158 Type clsName = Builder::BuildClass("SyncProgressInner");
159 ani_class cls;
160 ret = tmpEnv->Namespace_FindClass(ns, clsName.Descriptor().c_str(), &cls);
161 if (ret != ANI_OK) {
162 LOGE("find class failed. ret = %{public}d", ret);
163 return;
164 }
165 ani_object pg;
166 GetSyncProgress(state, error, cls, pg);
167 ani_ref ref_;
168 ani_fn_object etsCb = reinterpret_cast<ani_fn_object>(cbOnRef_);
169 std::vector<ani_ref> vec = { pg };
170 ret = tmpEnv->FunctionalObject_Call(etsCb, 1, vec.data(), &ref_);
171 if (ret != ANI_OK) {
172 LOGE("ani call function failed. ret = %{public}d", ret);
173 return;
174 }
175 ret = tmpEnv->DestroyLocalScope();
176 if (ret != ANI_OK) {
177 LOGE("failed to DestroyLocalScope. ret = %{public}d", ret);
178 }
179 };
180 if (!ANIUtils::SendEventToMainThread(task)) {
181 LOGE("failed to send event");
182 }
183 }
184
DeleteReference()185 void CloudSyncCallbackAniImpl::DeleteReference()
186 {
187 ani_env *env = nullptr;
188 if (vm_ == nullptr || vm_->GetEnv(ANI_VERSION_1, &env)) {
189 LOGE("CloudSyncCallbackAniImpl get env failed.");
190 return;
191 }
192 if (cbOnRef_ != nullptr) {
193 env->GlobalReference_Delete(cbOnRef_);
194 cbOnRef_ = nullptr;
195 }
196 }
197
OnSyncStateChanged(SyncType type,SyncPromptState state)198 void CloudSyncCallbackAniImpl::OnSyncStateChanged(SyncType type, SyncPromptState state)
199 {
200 return;
201 }
202
SetValueArray(ani_env * env,const std::list<Uri> listValue,ani_object & uris)203 ani_status ChangeListenerAni::SetValueArray(ani_env *env, const std::list<Uri> listValue, ani_object &uris)
204 {
205 ani_class arrayCls = nullptr;
206 Type arrSign = Builder::BuildClass("escompat.Array");
207 ani_status ret = env->FindClass(arrSign.Descriptor().c_str(), &arrayCls);
208 if (ret != ANI_OK) {
209 LOGE("find ani array failed. ret = %{public}d", static_cast<int32_t>(ret));
210 return ret;
211 }
212
213 ani_method arrayCtor;
214 std::string ct = Builder::BuildConstructorName();
215 std::string ctSign = Builder::BuildSignatureDescriptor({Builder::BuildInt()});
216 ret = env->Class_FindMethod(arrayCls, ct.c_str(), ctSign.c_str(), &arrayCtor);
217 if (ret != ANI_OK) {
218 LOGE("array find method failed. ret = %{public}d", static_cast<int32_t>(ret));
219 return ret;
220 }
221
222 std::vector<std::string> strings(listValue.size());
223 int elementIndex = 0;
224 for (auto uri : listValue) {
225 strings[elementIndex++] = uri.ToString();
226 }
227
228 ret = env->Object_New(arrayCls, arrayCtor, &uris, strings.size());
229 if (ret != ANI_OK) {
230 LOGE("set array uri new object failed. ret = %{public}d", static_cast<int32_t>(ret));
231 return ret;
232 }
233
234 ani_size index = 0;
235 for (auto str : strings) {
236 ani_string ani_str;
237 ret = env->String_NewUTF8(str.c_str(), str.size(), &ani_str);
238 if (ret != ANI_OK) {
239 LOGE("std string to ani string failed. ret = %{public}d", static_cast<int32_t>(ret));
240 return ret;
241 }
242 std::string setSign = Builder::BuildSignatureDescriptor({Builder::BuildInt(), Builder::BuildNull()});
243 ret = env->Object_CallMethodByName_Void(uris, "$_set", setSign.c_str(), index, ani_str);
244 if (ret != ANI_OK) {
245 LOGE("set array uri value failed. ret = %{public}d", static_cast<int32_t>(ret));
246 return ret;
247 }
248 index++;
249 }
250 return ANI_OK;
251 }
252
SetIsDir(ani_env * env,const shared_ptr<MessageParcel> parcel,ani_object & isDirectory)253 ani_status ChangeListenerAni::SetIsDir(ani_env *env, const shared_ptr<MessageParcel> parcel, ani_object &isDirectory)
254 {
255 uint32_t len = 0;
256 if (!parcel->ReadUint32(len)) {
257 LOGE("Failed to read sub uri list length");
258 return ANI_INVALID_ARGS;
259 }
260 if (len > READ_SIZE) {
261 return ANI_INVALID_ARGS;
262 }
263 ani_class arrayCls = nullptr;
264 Type arrSign = Builder::BuildClass("escompat.Array");
265 ani_status ret = env->FindClass(arrSign.Descriptor().c_str(), &arrayCls);
266 if (ret != ANI_OK) {
267 LOGE("find ani array failed. ret = %{public}d", static_cast<int32_t>(ret));
268 return ret;
269 }
270
271 ani_method arrayCtor;
272 std::string ct = Builder::BuildConstructorName();
273 std::string ctSign = Builder::BuildSignatureDescriptor({Builder::BuildInt()});
274 ret = env->Class_FindMethod(arrayCls, ct.c_str(), ctSign.c_str(), &arrayCtor);
275 if (ret != ANI_OK) {
276 LOGE("array find method failed. ret = %{public}d", static_cast<int32_t>(ret));
277 return ret;
278 }
279 std::vector<bool> isDirs(len);
280 for (uint32_t i = 0; i < len; i++) {
281 isDirs[i] = parcel->ReadBool();
282 }
283 ret = env->Object_New(arrayCls, arrayCtor, &isDirectory, isDirs.size());
284 if (ret != ANI_OK) {
285 LOGE("set array uri new object failed. ret = %{public}d", static_cast<int32_t>(ret));
286 return ret;
287 }
288 ani_size index = 0;
289 for (auto isDir : isDirs) {
290 std::string setSign = Builder::BuildSignatureDescriptor({Builder::BuildInt(), Builder::BuildNull()});
291 ret = env->Object_CallMethodByName_Void(
292 isDirectory, "$_set", setSign.c_str(), index, static_cast<ani_boolean>(isDir));
293 if (ret != ANI_OK) {
294 LOGE("set array uri value failed. ret = %{public}d", static_cast<int32_t>(ret));
295 return ret;
296 }
297 index++;
298 }
299 return ANI_OK;
300 }
301
GetChangeDataObject(ani_env * env,CloudChangeListener & listener,ani_class cls,ani_object & changeData)302 ani_status ChangeListenerAni::GetChangeDataObject(
303 ani_env *env, CloudChangeListener &listener, ani_class cls, ani_object &changeData)
304 {
305 ani_object uris;
306 ani_status ret = SetValueArray(env, listener.changeInfo.uris_, uris);
307 if (ret != ANI_OK) {
308 LOGE("set array uris failed. ret = %{public}d", ret);
309 return ret;
310 }
311 ani_object isDirectory;
312 if (listener.changeInfo.size_ > 0) {
313 shared_ptr<MessageParcel> parcel = make_shared<MessageParcel>();
314 if (parcel->ParseFrom(reinterpret_cast<uintptr_t>(listener.changeInfo.data_), listener.changeInfo.size_)) {
315 ret = SetIsDir(env, parcel, isDirectory);
316 if (ret != ANI_OK) {
317 LOGE("Set subArray named property error! field: subUris");
318 return ret;
319 }
320 }
321 }
322 ani_enum notifyTypeEnum;
323 Type notifyTypeSign = Builder::BuildEnum("@ohos.file.cloudSync.cloudSync.NotifyType");
324 env->FindEnum(notifyTypeSign.Descriptor().c_str(), ¬ifyTypeEnum);
325 ani_enum_item notifyTypeEnumItem;
326 env->Enum_GetEnumItemByIndex(notifyTypeEnum, listener.changeInfo.changeType_, ¬ifyTypeEnumItem);
327 ani_method ctor;
328 std::string ct = Builder::BuildConstructorName();
329 std::string ctSign = Builder::BuildSignatureDescriptor({notifyTypeSign,
330 Builder::BuildClass("escompat.Array"), Builder::BuildClass("escompat.Array")});
331 ret = env->Class_FindMethod(cls, ct.c_str(), ctSign.c_str(), &ctor);
332 if (ret != ANI_OK) {
333 LOGE("find ctor method failed. ret = %{public}d", ret);
334 return ret;
335 }
336 ret = env->Object_New(cls, ctor, &changeData, notifyTypeEnumItem, isDirectory, uris);
337 if (ret != ANI_OK) {
338 LOGE("create new object failed. ret = %{public}d", ret);
339 return ret;
340 }
341 return ANI_OK;
342 }
343
OnChange(CloudChangeListener & listener,const ani_ref cbRef)344 void ChangeListenerAni::OnChange(CloudChangeListener &listener, const ani_ref cbRef)
345 {
346 auto task = [this, listener, cbRef]() {
347 LOGI("ChangeListenerAni OnChange");
348 ani_env *tmpEnv = env_;
349 ani_size nr_refs = ANI_SCOPE_SIZE;
350 ani_status ret = tmpEnv->CreateLocalScope(nr_refs);
351 if (ret != ANI_OK) {
352 LOGE("crete local scope failed. ret = %{public}d", ret);
353 return;
354 }
355 CloudChangeListener tmpListener = listener;
356 if (tmpListener.changeInfo.uris_.empty()) {
357 return;
358 }
359 ani_namespace ns {};
360 Namespace nsSign = Builder::BuildNamespace("@ohos.file.cloudSync.cloudSync");
361 ret = tmpEnv->FindNamespace(nsSign.Descriptor().c_str(), &ns);
362 if (ret != ANI_OK) {
363 LOGE("find namespace failed. ret = %{public}d", ret);
364 return;
365 }
366 Type clsName = Builder::BuildClass("ChangeDataInner");
367 ani_class cls;
368 ret = tmpEnv->Namespace_FindClass(ns, clsName.Descriptor().c_str(), &cls);
369 if (ret != ANI_OK) {
370 LOGE("find class failed. ret = %{public}d", ret);
371 return;
372 }
373 ani_object changeData;
374 ret = GetChangeDataObject(tmpEnv, tmpListener, cls, changeData);
375 ani_ref ref_;
376 ani_fn_object etsCb = reinterpret_cast<ani_fn_object>(cbRef);
377 std::vector<ani_ref> vec = { changeData };
378 ret = tmpEnv->FunctionalObject_Call(etsCb, 1, vec.data(), &ref_);
379 if (ret != ANI_OK) {
380 LOGE("ani call function failed. ret = %{public}d", ret);
381 return;
382 }
383 ret = tmpEnv->DestroyLocalScope();
384 if (ret != ANI_OK) {
385 LOGE("failed to DestroyLocalScope. ret = %{public}d", ret);
386 }
387 };
388 if (!ANIUtils::SendEventToMainThread(task)) {
389 LOGE("failed to send event");
390 }
391 }
392
OnChange()393 void CloudNotifyObserver::OnChange() {}
394
OnChangeExt(const AAFwk::ChangeInfo & changeInfo)395 void CloudNotifyObserver::OnChangeExt(const AAFwk::ChangeInfo &changeInfo)
396 {
397 CloudChangeListener listener;
398 listener.changeInfo = changeInfo;
399 listener.strUri = uri_;
400 listObj_.OnChange(listener, ref_);
401 }
402
GetOptimProgress(ani_env * env,OptimizeState state,int32_t progress,ani_class cls,ani_object & data)403 ani_status CloudOptimizeCallbackAniImpl::GetOptimProgress(
404 ani_env *env, OptimizeState state, int32_t progress, ani_class cls, ani_object &data)
405 {
406 LOGI("CloudOptimizeCallbackAniImpl OnOptimizeProcess");
407 ani_enum optimizeStateEnum;
408 Type optimizeStateSign = Builder::BuildEnum("@ohos.file.cloudSync.cloudSync.OptimizeState");
409 ani_status ret = env->FindEnum(optimizeStateSign.Descriptor().c_str(), &optimizeStateEnum);
410 if (ret != ANI_OK) {
411 LOGE("find optim state failed. ret = %{public}d", ret);
412 return ret;
413 }
414 ani_enum_item optimizeStateTypeEnumItem;
415 ret = env->Enum_GetEnumItemByIndex(optimizeStateEnum, state, &optimizeStateTypeEnumItem);
416 if (ret != ANI_OK) {
417 LOGE("set optim state item failed. ret = %{public}d", ret);
418 return ret;
419 }
420
421 ani_method ctor;
422 std::string ct = Builder::BuildConstructorName();
423 std::string ctSign = Builder::BuildSignatureDescriptor({optimizeStateSign, Builder::BuildDouble()});
424 ret = env->Class_FindMethod(cls, ct.c_str(), ctSign.c_str(), &ctor);
425 if (ret != ANI_OK) {
426 LOGE("find ctor method failed. ret = %{public}d", ret);
427 return ret;
428 }
429
430 ret = env->Object_New(cls, ctor, &data, optimizeStateTypeEnumItem, static_cast<double>(progress));
431 if (ret != ANI_OK) {
432 LOGE("create new object failed. ret = %{public}d", ret);
433 return ret;
434 }
435 return ANI_OK;
436 }
437
OnOptimizeProcess(const OptimizeState state,const int32_t progress)438 void CloudOptimizeCallbackAniImpl::OnOptimizeProcess(const OptimizeState state, const int32_t progress)
439 {
440 auto task = [this, state, progress]() {
441 if (!cbOnRef_) {
442 LOGE("cbOnRef_ is nullptr");
443 return;
444 }
445 ani_env *tmpEnv = env_;
446 ani_size nr_refs = ANI_SCOPE_SIZE;
447 ani_status ret = tmpEnv->CreateLocalScope(nr_refs);
448 if (ret != ANI_OK) {
449 LOGE("crete local scope failed. ret = %{public}d", ret);
450 return;
451 }
452 ani_namespace ns {};
453 Namespace nsSign = Builder::BuildNamespace("@ohos.file.cloudSync.cloudSync");
454 ret = tmpEnv->FindNamespace(nsSign.Descriptor().c_str(), &ns);
455 if (ret != ANI_OK) {
456 LOGE("find namespace failed. ret = %{public}d", ret);
457 return;
458 }
459 Type clsName = Builder::BuildClass("OptimizeSpaceProgressInner");
460 ani_class cls;
461 ret = tmpEnv->Namespace_FindClass(ns, clsName.Descriptor().c_str(), &cls);
462 if (ret != ANI_OK) {
463 LOGE("find class failed. ret = %{public}d", ret);
464 return;
465 }
466 ani_object optimProgressData;
467 ret = GetOptimProgress(tmpEnv, state, progress, cls, optimProgressData);
468 if (ret != ANI_OK) {
469 LOGE("GetOptimProgress failed. ret = %{public}d", ret);
470 return;
471 }
472 ani_ref ref_;
473 ani_fn_object etsCb = reinterpret_cast<ani_fn_object>(cbOnRef_);
474 std::vector<ani_ref> vec = { optimProgressData };
475 ret = tmpEnv->FunctionalObject_Call(etsCb, 1, vec.data(), &ref_);
476 if (ret != ANI_OK) {
477 LOGE("ani call function failed. ret = %{public}d", ret);
478 return;
479 }
480 ret = tmpEnv->DestroyLocalScope();
481 if (ret != ANI_OK) {
482 LOGE("failed to DestroyLocalScope. ret = %{public}d", ret);
483 }
484 };
485 if (!ANIUtils::SendEventToMainThread(task)) {
486 LOGE("failed to send event");
487 }
488 }
489 } // namespace OHOS::FileManagement::CloudSync