diff --git a/interfaces/kits/js/ani/file_cloud_sync/include/cloud_file_cache_ani.h b/interfaces/kits/js/ani/file_cloud_sync/include/cloud_file_cache_ani.h index 181c6d8fba74ca5d8028e12744410854152db98e..94da17f2cd59e9d8cd5c773f440663d5cff32272 100644 --- a/interfaces/kits/js/ani/file_cloud_sync/include/cloud_file_cache_ani.h +++ b/interfaces/kits/js/ani/file_cloud_sync/include/cloud_file_cache_ani.h @@ -19,7 +19,6 @@ #include "cloud_file_cache_core.h" namespace OHOS::FileManagement::CloudSync { - class CloudFileCacheAni final { public: static void CloudFileCacheConstructor(ani_env *env, ani_object object); @@ -27,6 +26,7 @@ public: static void CloudFileCacheOff0(ani_env *env, ani_object object, ani_string evt, ani_object fun); static void CloudFileCacheOff1(ani_env *env, ani_object object, ani_string evt); static void CloudFileCacheStart(ani_env *env, ani_object object, ani_string uri); + static ani_int CloudFileCacheStartBatch(ani_env *env, ani_object object, ani_object uriList); static void CloudFileCacheStop(ani_env *env, ani_object object, ani_string uri, ani_boolean needClean); static void CloudFileCacheCleanCache(ani_env *env, ani_object object, ani_string uri); }; diff --git a/interfaces/kits/js/ani/file_cloud_sync/include/cloud_download_callback_middle.h b/interfaces/kits/js/ani/file_cloud_sync/include/register_callback_manager_ani.h similarity index 39% rename from interfaces/kits/js/ani/file_cloud_sync/include/cloud_download_callback_middle.h rename to interfaces/kits/js/ani/file_cloud_sync/include/register_callback_manager_ani.h index 53d7b9a052d650829b5fb3a78afd35017eadf5d4..d6ec15bf91a19c6fa91c041d217cbe11f927d4d7 100644 --- a/interfaces/kits/js/ani/file_cloud_sync/include/cloud_download_callback_middle.h +++ b/interfaces/kits/js/ani/file_cloud_sync/include/register_callback_manager_ani.h @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -13,18 +13,31 @@ * limitations under the License. */ -#ifndef OHOS_FILEMGMT_CLOUD_DOWNLOAD_CALLBACK_MIDDLE_H -#define OHOS_FILEMGMT_CLOUD_DOWNLOAD_CALLBACK_MIDDLE_H +#ifndef OHOS_FILEMGMT_REGISTER_CALLBACK_MANAGER_ANI_H +#define OHOS_FILEMGMT_REGISTER_CALLBACK_MANAGER_ANI_H -#include "cloud_download_callback.h" +#include +#include -namespace OHOS::FileManagement::CloudSync { +#include "ani_utils.h" +#include "register_callback_manager.h" -class CloudDownloadCallbackMiddle : public CloudDownloadCallback { +namespace OHOS::FileManagement::CloudSync { +class RegisterCallbackManagerAni : public RegisterCallbackManager { public: - virtual ~CloudDownloadCallbackMiddle() = default; - virtual void OnDownloadProcess(const DownloadProgressObj &progress) = 0; - virtual void DeleteReference() {}; + RegisterCallbackManagerAni() = default; + bool IsExisted(ani_env env, ani_object callback); + ani_status RegisterCallback(ani_env env, ani_object callback); + ani_status UnregisterCallback(ani_env env, ani_object callback); + void OnJsCallback(ani_env env, ani_object *value, uint32_t argc); + void OnDownloadCallback(std::shared_ptr progress, + std::shared_ptr callbackImpl) override; + +protected: + ani_env env_; + std::recursive_mutex callbackMtx_; + std::atomic validRefNum_{0}; + std::list> callbackList_; }; } // namespace OHOS::FileManagement::CloudSync -#endif // OHOS_FILEMGMT_CLOUD_DOWNLOAD_CALLBACK_MIDDLE_H +#endif // OHOS_FILEMGMT_REGISTER_CALLBACK_MANAGER_ANI_H \ No newline at end of file diff --git a/interfaces/kits/js/ani/file_cloud_sync/src/cloud_download_callback_ani.cpp b/interfaces/kits/js/ani/file_cloud_sync/src/cloud_download_callback_ani.cpp deleted file mode 100644 index 3e727b17d61f9ffe76065ca8d1b2c67a689b8eb4..0000000000000000000000000000000000000000 --- a/interfaces/kits/js/ani/file_cloud_sync/src/cloud_download_callback_ani.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (c) 2025 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "cloud_download_callback_ani.h" - -#include "ani_utils.h" -#include "utils_log.h" - -namespace OHOS::FileManagement::CloudSync { - -using namespace arkts::ani_signature; -constexpr int32_t ANI_SCOPE_SIZE = 16; - -CloudDownloadCallbackAniImpl::CloudDownloadCallbackAniImpl(ani_env *env, ani_ref fun, bool isBatch) -{ - env_ = env; - if (fun != nullptr) { - cbOnRef_ = fun; - } - isBatch_ = isBatch; -} - -void CloudDownloadCallbackAniImpl::GetDownloadProgress( - const DownloadProgressObj &progress, const ani_class &cls, ani_object &pg) -{ - ani_method ctor; - std::string ct = Builder::BuildConstructorName(); - std::string argSign = Builder::BuildSignatureDescriptor({ - Builder::BuildDouble(), Builder::BuildEnum("@ohos.file.cloudSync.cloudSync.State"), - Builder::BuildDouble(), Builder::BuildDouble(), Builder::BuildClass("std.core.String"), - Builder::BuildEnum("@ohos.file.cloudSync.cloudSync.DownloadErrorType") - }); - ani_status ret = env_->Class_FindMethod(cls, ct.c_str(), argSign.c_str(), &ctor); - if (ret != ANI_OK) { - LOGE("find ctor method failed. ret = %{public}d", ret); - return; - } - ani_string uri = nullptr; - ret = env_->String_NewUTF8(progress.path.c_str(), progress.path.size(), &uri); - if (ret != ANI_OK) { - LOGE("get uri failed. ret = %{public}d", ret); - return; - } - - ani_enum stateEnum; - Type stateSign = Builder::BuildEnum("@ohos.file.cloudSync.cloudSync.State"); - env_->FindEnum(stateSign.Descriptor().c_str(), &stateEnum); - ani_enum downloadErrorEnum; - Type errorSign = Builder::BuildEnum("@ohos.file.cloudSync.cloudSync.DownloadErrorType"); - env_->FindEnum(errorSign.Descriptor().c_str(), &downloadErrorEnum); - - ani_enum_item stateEnumItem; - ani_enum_item downloadErrorEnumItem; - env_->Enum_GetEnumItemByIndex(downloadErrorEnum, progress.downloadErrorType, &downloadErrorEnumItem); - - if (!isBatch_) { - env_->Enum_GetEnumItemByIndex(stateEnum, progress.state, &stateEnumItem); - ret = env_->Object_New(cls, ctor, &pg, static_cast(progress.downloadId), stateEnumItem, - static_cast(progress.downloadedSize), static_cast(progress.totalSize), uri, - downloadErrorEnumItem); - } else { - env_->Enum_GetEnumItemByIndex(stateEnum, progress.batchState, &stateEnumItem); - ret = env_->Object_New(cls, ctor, &pg, static_cast(progress.downloadId), stateEnumItem, - static_cast(progress.batchDownloadSize), static_cast(progress.batchTotalSize), - static_cast(progress.batchSuccNum), static_cast(progress.batchFailNum), - static_cast(progress.batchTotalNum), downloadErrorEnumItem); - } - - if (ret != ANI_OK) { - LOGE("create new object failed. ret = %{public}d", ret); - } -} - -void CloudDownloadCallbackAniImpl::OnDownloadProcess(const DownloadProgressObj &progress) -{ - auto task = [this, progress]() { - LOGI("CloudDownloadCallbackAniImpl OnDownloadProcess"); - ani_env *tmpEnv = env_; - ani_size nr_refs = ANI_SCOPE_SIZE; - ani_status ret = tmpEnv->CreateLocalScope(nr_refs); - if (ret != ANI_OK) { - LOGE("crete local scope failed. ret = %{public}d", ret); - return; - } - ani_namespace ns {}; - Namespace nsSign = Builder::BuildNamespace("@ohos.file.cloudSync.cloudSync"); - ret = tmpEnv->FindNamespace(nsSign.Descriptor().c_str(), &ns); - if (ret != ANI_OK) { - LOGE("find namespace failed. ret = %{public}d", ret); - return; - } - Type clsName = Builder::BuildClass("DownloadProgressInner"); - ani_class cls; - ret = tmpEnv->Namespace_FindClass(ns, clsName.Descriptor().c_str(), &cls); - if (ret != ANI_OK) { - LOGE("find class failed. ret = %{public}d", ret); - return; - } - ani_object pg; - GetDownloadProgress(progress, cls, pg); - ani_ref ref_; - ani_fn_object etsCb = reinterpret_cast(cbOnRef_); - std::vector vec = { pg }; - ret = tmpEnv->FunctionalObject_Call(etsCb, 1, vec.data(), &ref_); - if (ret != ANI_OK) { - LOGE("ani call function failed. ret = %{public}d", ret); - return; - } - ret = tmpEnv->DestroyLocalScope(); - if (ret != ANI_OK) { - LOGE("failed to DestroyLocalScope. ret = %{public}d", ret); - } - }; - if (!ANIUtils::SendEventToMainThread(task)) { - LOGE("failed to send event"); - } -} - -void CloudDownloadCallbackAniImpl::DeleteReference() -{ - if (cbOnRef_ != nullptr) { - env_->GlobalReference_Delete(cbOnRef_); - cbOnRef_ = nullptr; - } -} -} // namespace OHOS::FileManagement::CloudSync \ No newline at end of file diff --git a/interfaces/kits/js/ani/file_cloud_sync/src/cloud_file_cache_ani.cpp b/interfaces/kits/js/ani/file_cloud_sync/src/cloud_file_cache_ani.cpp index a3f654561b6445fd5781896d06ef26f171d81dd5..71f6fd77379c3dc4f83cdef70b16b0b0e3b69104 100644 --- a/interfaces/kits/js/ani/file_cloud_sync/src/cloud_file_cache_ani.cpp +++ b/interfaces/kits/js/ani/file_cloud_sync/src/cloud_file_cache_ani.cpp @@ -40,7 +40,7 @@ static CloudFileCacheCore *CloudFileCacheUnwrap(ani_env *env, ani_object object) void CloudFileCacheAni::CloudFileCacheConstructor(ani_env *env, ani_object object) { - ani_namespace ns {}; + ani_namespace ns{}; Namespace nsSign = Builder::BuildNamespace("@ohos.file.cloudSync.cloudSync"); ani_status ret = env->FindNamespace(nsSign.Descriptor().c_str(), &ns); if (ret != ANI_OK) { @@ -92,18 +92,14 @@ void CloudFileCacheAni::CloudFileCacheOn(ani_env *env, ani_object object, ani_st return; } - ani_ref cbOnRef; - ret = env->GlobalReference_Create(reinterpret_cast(fun), &cbOnRef); - if (ret != ANI_OK) { + if (event == "multiProgress") { + event = MULTI_PROGRESS; + } + if (event != PROGRESS && event != MULTI_PROGRESS) { + LOGE("Invalid argument for event type."); ErrorHandler::Throw(env, E_IPCSS); return; } - std::shared_ptr callback = nullptr; - if (event == "progress") { - callback = std::make_shared(env, cbOnRef); - } else if (event == "multiProgress") { - callback = std::make_shared(env, cbOnRef, true); - } auto cloudFileCache = CloudFileCacheUnwrap(env, object); if (cloudFileCache == nullptr) { @@ -111,26 +107,58 @@ void CloudFileCacheAni::CloudFileCacheOn(ani_env *env, ani_object object, ani_st ErrorHandler::Throw(env, E_IPCSS); return; } - auto data = cloudFileCache->DoOn(event, callback); - if (!data.IsSuccess()) { - const auto &err = data.GetError(); - LOGE("cloudFileCache do on failed, ret = %{public}d", err.GetErrNo()); - ErrorHandler::Throw(env, err); + + std::shared_ptr callbackImpl = cloudFileCache->GetCallbackImpl(event, true); + auto status = callbackImpl->registerMgr_->RegisterCallback(env, fun); + if (status != ani_ok) { + LOGE("Failed to register callback, status: %{public}d.", status); + ErrorHandler::Throw(env, E_IPCSS); + return; } } void CloudFileCacheAni::CloudFileCacheOff0(ani_env *env, ani_object object, ani_string evt, ani_object fun) { - ani_ref cbOnRef; - ani_status ret = env->GlobalReference_Create(reinterpret_cast(fun), &cbOnRef); + std::string event; + ret = ANIUtils::AniString2String(env, evt, event); if (ret != ANI_OK) { ErrorHandler::Throw(env, E_IPCSS); return; } - auto callback = std::make_shared(env, cbOnRef); - std::string event; - ret = ANIUtils::AniString2String(env, evt, event); + if (event == "multiProgress") { + event = MULTI_PROGRESS; + } + if (event != PROGRESS && event == MULTI_PROGRESS) { + LOGE("Invalid argument for event type."); + ErrorHandler::Throw(env, E_IPCSS); + return; + } + + std::shared_ptr callbackImpl = cloudFileCache->InitCallbackImpl(event); + if (callbackImpl == nullptr || callbackImpl->registerMgr_ == nullptr) { + LOGE("Invalid argument for event type."); + ErrorHandler::Throw(env, E_IPCSS); + return; + } + + auto status = callbackImpl->registerMgr_->UnregisterCallback(env, fun); + if (status != ani_ok) { + LOGE("Failed to unregister callback, status: %{public}d.", status); + ErrorHandler::Throw(env, E_IPCSS); + return; + } +} + +void CloudFileCacheAni::CloudFileCacheOff1(ani_env *env, ani_object object, ani_string evt) +{ + CloudFileCacheOff0(env, object, evt, nullptr); +} + +void CloudFileCacheAni::CloudFileCacheStart(ani_env *env, ani_object object, ani_string uri) +{ + std::string uriInput; + ani_status ret = ANIUtils::AniString2String(env, uri, uriInput); if (ret != ANI_OK) { ErrorHandler::Throw(env, E_IPCSS); return; @@ -142,38 +170,51 @@ void CloudFileCacheAni::CloudFileCacheOff0(ani_env *env, ani_object object, ani_ ErrorHandler::Throw(env, E_IPCSS); return; } - auto data = cloudFileCache->DoOff(event, callback); + auto data = cloudFileCache->DoStart(uriInput); if (!data.IsSuccess()) { const auto &err = data.GetError(); - LOGE("cloudFileCache do off failed, ret = %{public}d", err.GetErrNo()); + LOGE("cloudFileCache do start failed, ret = %{public}d", err.GetErrNo()); ErrorHandler::Throw(env, err); } } -void CloudFileCacheAni::CloudFileCacheOff1(ani_env *env, ani_object object, ani_string evt) +ani_int CloudFileCacheAni::CloudFileCacheStartBatch(ani_env *env, + ani_object object, + ani_object uriList, + ani_int fileType) { - std::string event; - ani_status ret = ANIUtils::AniString2String(env, evt, event); + std::vector urisInput; + ani_status ret = ANIUtils::AniString2String(env, uri, urisInput); // Todo if (ret != ANI_OK) { ErrorHandler::Throw(env, E_IPCSS); - return; + return static_cast(ret); + } + + int32_t fieldKey = FieldKey::FIELDKEY_CONTENT; + ani_status ret = ANIUtils::AniString2String(env, fileType, fieldKey); // Todo + if (ret != ANI_OK) { + ErrorHandler::Throw(env, E_IPCSS); + return static_cast(ret); } auto cloudFileCache = CloudFileCacheUnwrap(env, object); if (cloudFileCache == nullptr) { LOGE("Cannot wrap cloudFileCache."); ErrorHandler::Throw(env, E_IPCSS); - return; + return static_cast(E_IPCSS); } - auto data = cloudFileCache->DoOff(event); + auto data = cloudFileCache->DoStart(urisInput, fieldKey); if (!data.IsSuccess()) { const auto &err = data.GetError(); - LOGE("cloudFileCache do off failed, ret = %{public}d", err.GetErrNo()); + LOGE("cloudFileCache do start failed, ret = %{public}d", err.GetErrNo()); ErrorHandler::Throw(env, err); + return err.GetErrNo(); } + + return static_cast(data.GetData().value()); } -void CloudFileCacheAni::CloudFileCacheStart(ani_env *env, ani_object object, ani_string uri) +void CloudFileCacheAni::CloudFileCacheStop(ani_env *env, ani_object object, ani_string uri, ani_boolean needClean) { std::string uriInput; ani_status ret = ANIUtils::AniString2String(env, uri, uriInput); @@ -188,18 +229,21 @@ void CloudFileCacheAni::CloudFileCacheStart(ani_env *env, ani_object object, ani ErrorHandler::Throw(env, E_IPCSS); return; } - auto data = cloudFileCache->DoStart(uriInput); + + bool needCleanInput = needClean; + + auto data = cloudFileCache->DoStop(uriInput, needCleanInput); if (!data.IsSuccess()) { const auto &err = data.GetError(); - LOGE("cloudFileCache do start failed, ret = %{public}d", err.GetErrNo()); + LOGE("cloudFileCache do stop failed, ret = %{public}d", err.GetErrNo()); ErrorHandler::Throw(env, err); } } -void CloudFileCacheAni::CloudFileCacheStop(ani_env *env, ani_object object, ani_string uri, ani_boolean needClean) +void CloudFileCacheAni::CloudFileCacheStopBatch(ani_env *env, ani_object object, ani_int taskId, ani_boolean needClean) { - std::string uriInput; - ani_status ret = ANIUtils::AniString2String(env, uri, uriInput); + std::string downloadId; + ani_status ret = ANIUtils::AniString2String(env, taskId, downloadId); // todo if (ret != ANI_OK) { ErrorHandler::Throw(env, E_IPCSS); return; @@ -214,7 +258,7 @@ void CloudFileCacheAni::CloudFileCacheStop(ani_env *env, ani_object object, ani_ bool needCleanInput = needClean; - auto data = cloudFileCache->DoStop(uriInput, needCleanInput); + auto data = cloudFileCache->DoStop(downloadId, needCleanInput); if (!data.IsSuccess()) { const auto &err = data.GetError(); LOGE("cloudFileCache do stop failed, ret = %{public}d", err.GetErrNo()); diff --git a/interfaces/kits/js/ani/file_cloud_sync/src/register_callback_manager_ani.cpp b/interfaces/kits/js/ani/file_cloud_sync/src/register_callback_manager_ani.cpp new file mode 100644 index 0000000000000000000000000000000000000000..61c365e96c56faf0f507749cc524b4ef8d3ebb62 --- /dev/null +++ b/interfaces/kits/js/ani/file_cloud_sync/src/register_callback_manager_ani.cpp @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "register_callback_manager_ani.h" +#include + +namespace OHOS::FileManagement::CloudSync { +using namespace std; +ani_status RegisterCallbackManagerAni::RegisterCallback(ani_env env, ani_object callback) +{ + if (callback == nullptr) { + return napi_invalid_arg; // todo + } + std::lock_guard lock(callbackMtx_); + if (IsExisted(callback)) { + return napi_ok; + } + ani_ref ref; + ani_status ret = env->GlobalReference_Create(reinterpret_cast(callback), &ref); + if (ret != ANI_OK) { + LOGE("Failed to create reference: %{public}d", status); + return xxx; // todo + } + + callbackList_.push_back(std::make_pair(true, ref)); + validRefNum_++; + return ANI_OK; +} + +ani_status RegisterCallbackManagerAni::UnregisterCallback(ani_env env, ani_object callback) +{ + if (validRefNum_ == 0) { + return ANI_OK; + } + ani_ref ref; + ani_status ret = env->GlobalReference_Create(reinterpret_cast(callback), &ref); + if (ret != ANI_OK) { + LOGE("Failed to create reference: %{public}d", status); + return xxx; // todo + } + std::lock_guard lock(callbackMtx_); + if (callback == nullptr) { + for (auto &iter : callbackList_) { + iter.first = false; + } + validRefNum_ = 0; + return ANI_OK; + } + bool isSame = false; + for (auto &iter : callbackList_) { + ani_status status = env->Reference_StrictEquals(iter.second, ref, &isSame); + if (status != ANI_OK) { + LOGE("Failed to strict compare: %{public}d", status); + continue; + } + if (isSame) { + if (iter.first) { + iter.first = false; + validRefNum_--; + } + break; + } + } + + return ANI_OK; +} + +// No need to lock +bool RegisterCallbackManagerAni::IsExisted(ani_env env, ani_ref ref) +{ + bool isExisted = false; + for (auto &iter : callbackList_) { + ani_status status = env->Reference_StrictEquals(iter.second, ref, &isSame); + if (status != ANI_OK) { + LOGE("Failed to strict compare: %{public}d", status); + continue; + } + if (isExisted) { + return iter.first; + } + } + + return false; +} + +// Running in JS thread +void RegisterCallbackManagerAni::OnJsCallback(ani_env env, ani_object value, uint32_t argc) +{ + std::lock_guard lock(callbackMtx_); + for (auto iter = callbackList_.begin(); iter != callbackList_.end();) { + if (!iter->first) { + env->GlobalReference_Delete(iter->second); + iter = callbackList_.erase(iter); + continue; + } + ani_ref ref; + ani_fn_object etsCb = reinterpret_cast(iter->second); + std::vector vec = {value}; // Todo: 可以直接隐式转换吗? + ret = env->FunctionalObject_Call(etsCb, argc, vec.data(), &ref); + if (ret != ANI_OK) { + LOGE("ani call function failed. ret = %{public}d", ret); + return; + } + iter++; + } +} + +using namespace arkts::ani_signature; +constexpr int32_t ANI_SCOPE_SIZE = 16; + +static ani_object GetSingleDownloadAniObject(ani_env env, std::shared_ptr &progress) +{ + ani_namespace ns{}; + Namespace nsSign = Builder::BuildNamespace("@ohos.file.cloudSync.cloudSync"); + ret = env->FindNamespace(nsSign.Descriptor().c_str(), &ns); + if (ret != ANI_OK) { + LOGE("find namespace failed. ret = %{public}d", ret); + return nullptr; + } + Type clsName = Builder::BuildClass("DownloadProgressInner"); + ani_class cls; + ret = env->Namespace_FindClass(ns, clsName.Descriptor().c_str(), &cls); + if (ret != ANI_OK) { + LOGE("find class failed. ret = %{public}d", ret); + return nullptr; + } + ani_method ctor; + std::string ct = Builder::BuildConstructorName(); + std::string argSign = Builder::BuildSignatureDescriptor( + {Builder::BuildDouble(), Builder::BuildEnum("@ohos.file.cloudSync.cloudSync.State"), Builder::BuildDouble(), + Builder::BuildDouble(), Builder::BuildClass("std.core.String"), + Builder::BuildEnum("@ohos.file.cloudSync.cloudSync.DownloadErrorType")}); + ani_status ret = env->Class_FindMethod(cls, ct.c_str(), argSign.c_str(), &ctor); + if (ret != ANI_OK) { + LOGE("find ctor method failed. ret = %{public}d", ret); + return nullptr; + } + ani_string uri = nullptr; + ret = env->String_NewUTF8(progress->GetUri().c_str(), progress->GetUri().size(), &uri); + if (ret != ANI_OK) { + LOGE("get uri failed. ret = %{public}d", ret); + return nullptr; + } + + ani_enum stateEnum; + Type stateSign = Builder::BuildEnum("@ohos.file.cloudSync.cloudSync.State"); + env->FindEnum(stateSign.Descriptor().c_str(), &stateEnum); + ani_enum downloadErrorEnum; + Type errorSign = Builder::BuildEnum("@ohos.file.cloudSync.cloudSync.DownloadErrorType"); + env->FindEnum(errorSign.Descriptor().c_str(), &downloadErrorEnum); + + ani_enum_item stateEnumItem; + ani_enum_item downloadErrorEnumItem; + env->Enum_GetEnumItemByIndex(downloadErrorEnum, progress->GetErrorType(), &downloadErrorEnumItem); + env->Enum_GetEnumItemByIndex(stateEnum, progress->GetState(), &stateEnumItem); + ani_object pg; + ret = env->Object_New(cls, ctor, &pg, static_cast(progress->GetTaskId()), stateEnumItem, + static_cast(progress->GetDownloadedSize()), + static_cast(progress->GetTotalSize()), uri, downloadErrorEnumItem); + if (ret != ANI_OK) { + LOGE("create new object failed. ret = %{public}d", ret); + } + return pg; +} + +static ani_object GetBatchDownloadAniObject(ani_env env, std::shared_ptr &progress) +{ + ani_object pg; + // todo: xxx + return pg; +} + +static ani_object ConverDlProgressToObject(ani_env env, std::shared_ptr &progress) +{ + if (progress == nullptr) { + LOGE("Progress is null"); + return nullptr; + } + if (auto singleProgress = std::dynamic_pointer_cast(progress)) { + return GetSingleDownloadAniObject(env, singleProgress); + } + if (auto batchProgress = std::dynamic_pointer_cast(progress)) { + return GetBatchDownloadAniObject(env, batchProgress); + } + LOGE("Failed to convert progress to nvalue"); + return nullptr; +} + +void RegisterCallbackManagerAni::OnDownloadCallback(std::shared_ptr progress, + std::shared_ptr callbackImpl) +{ + std::shared_ptr registerMgr = shared_from_this(); + auto task = [progress, registerMgr, callbackImpl]() mutable { + if (progress == nullptr || callbackImpl == nullptr || registerMgr == nullptr) { + LOGE("Failed to callback, is callbackImpl null: %{public}d", (callbackImpl == nullptr)); + return; + } + LOGI("CloudDownloadCallbackAniImpl OnDownloadProcess"); + ani_env *tmpEnv = registerMgr->env_; + ani_size nr_refs = ANI_SCOPE_SIZE; + ani_status ret = tmpEnv->CreateLocalScope(nr_refs); + if (ret != ANI_OK) { + LOGE("crete local scope failed. ret = %{public}d", ret); + return; + } + ani_namespace ns{}; + Namespace nsSign = Builder::BuildNamespace("@ohos.file.cloudSync.cloudSync"); + ret = tmpEnv->FindNamespace(nsSign.Descriptor().c_str(), &ns); + if (ret != ANI_OK) { + LOGE("find namespace failed. ret = %{public}d", ret); + return; + } + ani_object pg = ConverDlProgressToObject(tmpEnv, progress); + ret = registerMgr->OnJsCallback(tmpEnv, pg, 1); + if (ret != ANI_OK) { + LOGE("ani call function failed. ret = %{public}d", ret); + return; + } + ret = tmpEnv->DestroyLocalScope(); + if (ret != ANI_OK) { + LOGE("failed to DestroyLocalScope. ret = %{public}d", ret); + } + if (progress->IsNeedClean()) { + callbackImpl->RemoveDownloadInfo(progress->GetTaskId()); + } + }; + if (!ANIUtils::SendEventToMainThread(task)) { + LOGE("failed to send event"); + } +} +} // namespace OHOS::FileManagement::CloudSync \ No newline at end of file diff --git a/interfaces/kits/js/cloudfilesync/BUILD.gn b/interfaces/kits/js/cloudfilesync/BUILD.gn index fac14ae4105b208eec0fef97ae30136280b12f67..34ff8248b5c37fdb849e911799e91926bf883c08 100644 --- a/interfaces/kits/js/cloudfilesync/BUILD.gn +++ b/interfaces/kits/js/cloudfilesync/BUILD.gn @@ -23,6 +23,7 @@ config("optimize-size") { cflags_cc = [ "-fvisibility-inlines-hidden", "-Oz", + "-frtti" ] } @@ -37,16 +38,19 @@ ohos_shared_library("cloudsync") { cfi_cross_dso = true debug = false } + include_dirs = [ "${interfaces_js_path}/common" ] sources = [ "cloud_file_cache_napi.cpp", - "cloud_file_download_napi.cpp", "cloud_file_napi.cpp", - "cloud_file_version_napi.cpp", "cloud_sync_n_exporter.cpp", "cloud_sync_napi.cpp", "file_sync_napi.cpp", "gallery_sync_napi.cpp", - "register_callback_manager.cpp", + "${interfaces_js_path}/common/cloud_download_callback_impl.cpp", + "${interfaces_js_path}/common/cloud_download_callback_middle.cpp", + "${interfaces_js_path}/common/download_progress.cpp", + "multi_download_progress_napi.cpp", + "register_callback_manager_napi.cpp", ] deps = [ "${innerkits_native_path}/cloudsync_kit_inner:cloudsync_kit_inner", diff --git a/interfaces/kits/js/cloudfilesync/cloud_file_cache_core.cpp b/interfaces/kits/js/cloudfilesync/cloud_file_cache_core.cpp index e982b80fbefbf382244dce2b34b7db926899c442..cbdc3d304a9bea818690e45a797f057f995c20bf 100644 --- a/interfaces/kits/js/cloudfilesync/cloud_file_cache_core.cpp +++ b/interfaces/kits/js/cloudfilesync/cloud_file_cache_core.cpp @@ -20,145 +20,129 @@ namespace OHOS::FileManagement::CloudSync { const int32_t E_PARAMS = 401; - -bool RegisterManager::HasEvent(const string &eventType) +FsResult CloudFileCacheCore::Constructor() { - unique_lock registerMutex_; - bool hasEvent = false; - for (auto &iter : registerInfo_) { - if (iter->eventType == eventType) { - hasEvent = true; - break; - } + CloudFileCacheCore *cloudFileCachePtr = new CloudFileCacheCore(); + if (cloudFileCachePtr == nullptr) { + LOGE("Failed to create CloudFileCacheCore object on heap."); + return FsResult::Error(ENOMEM); } - return hasEvent; + + return FsResult::Success(move(cloudFileCachePtr)); } -bool RegisterManager::AddRegisterInfo(shared_ptr info) +FsResult CloudFileCacheCore::DoOn(const string &event, const shared_ptr callback) { - if (HasEvent(info->eventType)) { - return false; - } - { - unique_lock registerMutex_; - registerInfo_.insert(info); - } - return true; + return FsResult::Success(); +} + +FsResult CloudFileCacheCore::DoOff(const string &event, + const optional> &callback) +{ + return FsResult::Success(); } -bool RegisterManager::RemoveRegisterInfo(const string &eventType) +std::shared_ptr CloudFileCacheCore::InitCallbackImpl(const std::string &eventType) { - unique_lock registerMutex_; - bool isFound = false; - for (auto iter = registerInfo_.begin(); iter != registerInfo_.end();) { - if ((*iter)->eventType == eventType) { - isFound = true; - iter = registerInfo_.erase(iter); + std::shared_ptr callbackImpl = nullptr; + std::lock_guard lock(registerMutex); + auto iter = registerMap.find(eventType); + if (iter == registerMap.end() || iter->second == nullptr) { + std::unique_ptr normal = std::make_unique(); + if (eventType == PROGRESS) { + callbackImpl = std::make_shared(normal); } else { - iter++; + callbackImpl = std::make_shared(normal); } + callbackImpl->registerMgr_ = std::make_unique(); + registerMap.insert(make_pair(eventType, callbackImpl)); + } else { + callbackImpl = iter->second; } - return isFound; + return callbackImpl; } -FsResult CloudFileCacheCore::Constructor() +std::shared_ptr CloudFileCacheCore::GetSingleCallbackImpl() { - CloudFileCacheCore *cloudFileCachePtr = new CloudFileCacheCore(); - if (cloudFileCachePtr == nullptr) { - LOGE("Failed to create CloudFileCacheCore object on heap."); - return FsResult::Error(ENOMEM); + std::lock_guard lock(registerMutex); + if (auto it = registerMap.find(PROGRESS); it != registerMap.end()) { + return std::dynamic_pointer_cast(it->second); } - return FsResult::Success(move(cloudFileCachePtr)); -} - -CloudFileCacheCore::CloudFileCacheCore() -{ - LOGI("Create fileCacheEntity"); - fileCacheEntity = make_unique(); + return nullptr; } -FsResult CloudFileCacheCore::DoOn(const string &event, const shared_ptr callback) +std::shared_ptr CloudFileCacheCore::GetBatchCallbackImpl() { - LOGI("On begin"); - if (event != PROGRESS && event != MULTI_PROGRESS) { - LOGE("On get progress failed!"); - return FsResult::Error(E_PARAMS); + std::lock_guard lock(registerMutex); + if (auto it = registerMap.find(MULTI_PROGRESS); it != registerMap.end()) { + return std::dynamic_pointer_cast(it->second); } - if (!fileCacheEntity) { - LOGE("Failed to get file cache entity."); - return FsResult::Error(E_PARAMS); - } + return nullptr; +} - auto arg = make_shared(); - arg->eventType = event; - arg->callback = callback; - if (!fileCacheEntity->registerMgr.AddRegisterInfo(arg)) { - LOGE("Batch-On register callback fail, callback already exist"); - return FsResult::Error(E_PARAMS); +FsResult CloudFileCacheCore::DoStart(const string &uri) +{ + InitCallbackImpl(PROGRESS); + auto callbackImpl = GetSingleCallbackImpl(); + if (callbackImpl == nullptr) { + LOGE("Failed to stop download, callback is null!"); + return FsResult::Error(Convert2ErrNum(EINVAL)); } - - int32_t ret = CloudSyncManager::GetInstance().RegisterFileCacheCallback(arg->callback); + int32_t ret = callbackImpl->StartDownloadInner(uri); if (ret != E_OK) { - LOGE("RegisterDownloadFileCallback error, ret: %{public}d", ret); - (void)fileCacheEntity->registerMgr.RemoveRegisterInfo(event); + LOGE("Stop Download failed! ret = %{public}d", ret); return FsResult::Error(Convert2ErrNum(ret)); } return FsResult::Success(); } -FsResult CloudFileCacheCore::DoOff( - const string &event, const optional> &callback) +FsResult CloudFileCacheCore::DoStart(const std::vector < std::string & uriList, int32_t fieldKey) { - LOGI("Off begin"); - if (event != PROGRESS && event != MULTI_PROGRESS) { - LOGE("Off get progress failed!"); - return FsResult::Error(E_PARAMS); - } - - if (!fileCacheEntity) { - LOGE("Failed to get file cache entity."); - return FsResult::Error(E_PARAMS); - } - - if (!fileCacheEntity->registerMgr.HasEvent(event)) { - LOGE("Batch-Off no callback is registered for this event type: %{public}s.", event.c_str()); - return FsResult::Error(E_PARAMS); - } - - int32_t ret = CloudSyncManager::GetInstance().UnregisterFileCacheCallback(); + InitCallbackImpl(MULTI_PROGRESS); + auto callbackImpl = GetBatchCallbackImpl(); + if (callbackImpl == nullptr) { + LOGE("Failed to stop download, callback is null!"); + return FsResult::Error(Convert2ErrNum(EINVAL)); + } + int64_t downloadId = 0; + int32_t ret = callbackImpl->StartDownloadInner(uriList, fieldKey, downloadId); if (ret != E_OK) { - LOGE("Failed to unregister callback, error: %{public}d", ret); + LOGE("Stop Download failed! ret = %{public}d", ret); return FsResult::Error(Convert2ErrNum(ret)); } - if (!fileCacheEntity->registerMgr.RemoveRegisterInfo(event)) { - LOGE("Batch-Off remove callback is failed, event type: %{public}s.", event.c_str()); - return FsResult::Error(E_PARAMS); - } - - return FsResult::Success(); + return FsResult::Success(downloadId); } -FsResult CloudFileCacheCore::DoStart(const string &uri) +FsResult CloudFileCacheCore::DoStop(const string &uri, bool needClean) { - int32_t ret = CloudSyncManager::GetInstance().StartFileCache(uri); + auto callbackImpl = GetSingleCallbackImpl(); + if (callbackImpl == nullptr) { + LOGE("Failed to stop download, callback is null!"); + return FsResult::Error(Convert2ErrNum(EINVAL)); + } + int32_t ret = callbackImpl->StopDownloadInner(uri, needClean); if (ret != E_OK) { - LOGE("Start Download failed with errormessage: ret = %{public}d", ret); + LOGE("Stop Download failed! ret = %{public}d", ret); return FsResult::Error(Convert2ErrNum(ret)); } - LOGI("Start Download successfully!"); return FsResult::Success(); } -FsResult CloudFileCacheCore::DoStop(const string &uri, const bool needClean) +FsResult CloudFileCacheCore::DoStop(int64_t downloadId, bool needClean) { - int32_t ret = CloudSyncManager::GetInstance().StopDownloadFile(uri, needClean); + auto callbackImpl = GetBatchCallbackImpl(); + if (callbackImpl == nullptr) { + LOGE("Failed to stop batch download, callback is null!"); + return FsResult::Error(Convert2ErrNum(EINVAL)); + } + int32_t ret = callbackImpl->StopDownloadInner(downloadId, needClean); if (ret != E_OK) { - LOGE("Stop Download failed! ret = %{public}d", ret); + LOGE("Stop batch download failed! ret = %{public}d", ret); return FsResult::Error(Convert2ErrNum(ret)); } diff --git a/interfaces/kits/js/cloudfilesync/cloud_file_cache_core.h b/interfaces/kits/js/cloudfilesync/cloud_file_cache_core.h index 151f3b9620b17a98223223bcd7bf7a743260eba9..3e357535e247d710f4dad15bf0e5862a74291c46 100644 --- a/interfaces/kits/js/cloudfilesync/cloud_file_cache_core.h +++ b/interfaces/kits/js/cloudfilesync/cloud_file_cache_core.h @@ -17,62 +17,35 @@ #define OHOS_FILEMGMT_CLOUD_FILE_CACHE_CORE_H #include -#include -#include +#include #include "cloud_download_callback_ani.h" #include "filemgmt_libfs.h" namespace OHOS::FileManagement::CloudSync { using namespace ModuleFileIO; -using namespace std; const std::string PROGRESS = "progress"; -const std::string MULTI_PROGRESS = "multiProgress"; - -struct RegisterInfoArg { - std::string eventType; - std::shared_ptr callback; - ~RegisterInfoArg() - { - if (callback != nullptr) { - callback->DeleteReference(); - callback = nullptr; - } - } -}; - -class RegisterManager { -public: - RegisterManager() = default; - ~RegisterManager() = default; - bool AddRegisterInfo(std::shared_ptr info); - bool RemoveRegisterInfo(const std::string &eventType); - bool HasEvent(const std::string &eventType); - -private: - std::mutex registerMutex_; - std::unordered_set> registerInfo_; -}; - -struct FileCacheEntity { - RegisterManager registerMgr; -}; - +const std::string MULTI_PROGRESS = "batchDownload"; class CloudFileCacheCore { public: - CloudFileCacheCore(); + CloudFileCacheCore() = default; ~CloudFileCacheCore() = default; - static FsResult Constructor(); - FsResult DoOn(const std::string &event, const std::shared_ptr callback); - FsResult DoOff(const string &event, - const std::optional> &callback = std::nullopt); - FsResult DoStart(const string &uri); - FsResult DoStop(const string &uri, bool needClean = false); - FsResult CleanCache(const string &uri); + static ModuleFileIO::FsResult Constructor(); + ModuleFileIO::FsResult DoStart(const std::string &uri); + ModuleFileIO::FsResult + DoStart(const std::vector < std::string & uriList, int32_t fieldKey, int64_t &downloadId); + ModuleFileIO::FsResult DoStop(const std::string &uri, bool needClean = false); + ModuleFileIO::FsResult DoStop(int64_t downloadId, bool needClean = false); + ModuleFileIO::FsResult CleanCache(const std::string &uri); + std::shared_ptr InitCallbackImpl(const std::string &eventType); + + template + std::shared_ptr GetCallbackImpl(); private: - std::unique_ptr fileCacheEntity; + std::mutex registerMutex; + std::unordered_map> registerMap; }; } // namespace OHOS::FileManagement::CloudSync diff --git a/interfaces/kits/js/cloudfilesync/cloud_file_cache_napi.cpp b/interfaces/kits/js/cloudfilesync/cloud_file_cache_napi.cpp index b846ebd70642cd088db01695ee6f5f22bfa2ee49..3a3960870fe3079893f8b614c8ec1594603a8fa5 100644 --- a/interfaces/kits/js/cloudfilesync/cloud_file_cache_napi.cpp +++ b/interfaces/kits/js/cloudfilesync/cloud_file_cache_napi.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Huawei Device Co., Ltd. + * Copyright (c) 2023-2025 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -16,71 +16,36 @@ #include "cloud_file_cache_napi.h" #include +#include +#include #include #include "async_work.h" +#include "cloud_download_callback_impl.h" #include "cloud_sync_manager.h" #include "dfs_error.h" +#include "download_progress.h" +#include "multi_download_progress_napi.h" +#include "register_callback_manager_napi.h" #include "utils_log.h" #include "uv.h" namespace OHOS::FileManagement::CloudSync { using namespace FileManagement::LibN; using namespace std; - -bool RegisterManager::HasEvent(const string &eventType) -{ - unique_lock registerMutex_; - bool hasEvent = false; - for (auto &iter : registerInfo_) { - if (iter->eventType == eventType) { - hasEvent = true; - break; - } - } - return hasEvent; -} - -bool RegisterManager::AddRegisterInfo(shared_ptr info) -{ - if (HasEvent(info->eventType)) { - return false; - } - { - unique_lock registerMutex_; - registerInfo_.insert(info); - } - return true; -} - -bool RegisterManager::RemoveRegisterInfo(const string &eventType) -{ - unique_lock registerMutex_; - bool isFound = false; - for (auto iter = registerInfo_.begin(); iter != registerInfo_.end();) { - if ((*iter)->eventType == eventType) { - isFound = true; - iter = registerInfo_.erase(iter); - } else { - iter++; - } - } - return isFound; -} - napi_value CloudFileCacheNapi::Constructor(napi_env env, napi_callback_info info) { NFuncArg funcArg(env, info); if (!funcArg.InitArgs(NARG_CNT::ZERO)) { LOGE("Start Number of arguments unmatched"); - NError(E_PARAMS).ThrowErr(env); + NError(Convert2JsErrNum(E_SERVICE_INNER_ERROR)).ThrowErr(env); return nullptr; } auto fileCacheEntity = make_unique(); if (!NClass::SetEntityFor(env, funcArg.GetThisVar(), move(fileCacheEntity))) { LOGE("Failed to set file cache entity."); - NError(E_UNKNOWN_ERR).ThrowErr(env); + NError(Convert2JsErrNum(E_SERVICE_INNER_ERROR)).ThrowErr(env); return nullptr; } return funcArg.GetThisVar(); @@ -91,14 +56,14 @@ bool CloudFileCacheNapi::ToExport(std::vector props) std::string className = GetClassName(); auto [succ, classValue] = NClass::DefineClass(exports_.env_, className, Constructor, std::move(props)); if (!succ) { - NError(E_UNKNOWN_ERR).ThrowErr(exports_.env_); + NError(Convert2JsErrNum(E_SERVICE_INNER_ERROR)).ThrowErr(exports_.env_); LOGE("Failed to define GallerySync class"); return false; } succ = NClass::SaveClass(exports_.env_, className, classValue); if (!succ) { - NError(E_UNKNOWN_ERR).ThrowErr(exports_.env_); + NError(Convert2JsErrNum(E_SERVICE_INNER_ERROR)).ThrowErr(exports_.env_); LOGE("Failed to save GallerySync class"); return false; } @@ -146,31 +111,62 @@ bool CloudFileCacheNapi::Export() return ToExport(props); } +static std::tuple> + FillParamForStart(napi_env env, NFuncArg &funcArg) +{ + auto [succUri, uri, size] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String(); + if (!succUri || size == 0) { + LOGE("Start get uri parameter failed!"); + return {EINVAL, "", nullptr}; + } + + auto fileCacheEntity = NClass::GetEntityOf(env, funcArg.GetThisVar()); + if (fileCacheEntity == nullptr) { + LOGE("Failed to get file cache entity"); + return {EINVAL, "", nullptr}; + } + + std::shared_ptr callbackImpl = nullptr; + { + std::unique_lock lock(fileCacheEntity->registerMutex); + auto iter = fileCacheEntity->registerMap.find(PROGRESS); + if (iter == fileCacheEntity->registerMap.end()) { + std::shared_ptr normal = std::make_shared(); + callbackImpl = std::make_shared(normal); + callbackImpl->registerMgr_ = std::make_shared(env); + fileCacheEntity->registerMap.insert(make_pair(PROGRESS, callbackImpl)); + } else { + callbackImpl = std::dynamic_pointer_cast(iter->second); + } + } + return {E_OK, string(uri.get()), callbackImpl}; +} + napi_value CloudFileCacheNapi::StartFileCache(napi_env env, napi_callback_info info) { NFuncArg funcArg(env, info); if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) { LOGE("Start Number of arguments unmatched"); - NError(E_PARAMS).ThrowErr(env); + NError(EINVAL).ThrowErr(env); return nullptr; } - auto [succUri, uri, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String(); - if (!succUri) { - LOGE("Start get uri parameter failed!"); - NError(E_PARAMS).ThrowErr(env); + + auto [ret, uri, callbackImpl] = FillParamForStart(env, funcArg); + if (ret != E_OK) { + NError(ret).ThrowErr(env); return nullptr; } - auto cbExec = [uri = string(uri.get())]() -> NError { - int32_t ret = CloudSyncManager::GetInstance().StartFileCache(uri); + auto cbExec = [uri{uri}, callbackImpl{callbackImpl}]() -> NError { + if (callbackImpl == nullptr) { + LOGE("Failed to get download callback"); + return NError(EINVAL); + } + int32_t ret = callbackImpl->StartDownloadInner(uri, FieldKey::FIELDKEY_CONTENT); if (ret != E_OK) { - LOGE("Start Download failed! ret = %{public}d", ret); - if (ret != E_INVAL_ARG) { - ret = E_BROKEN_IPC; - } + ret = (ret == E_CLOUD_SDK) ? E_SERVICE_INNER_ERROR : ret; return NError(Convert2JsErrNum(ret)); } - LOGI("Start Download Success!"); return NError(ERRNO_NOERR); }; @@ -186,20 +182,9 @@ napi_value CloudFileCacheNapi::StartFileCache(napi_env env, napi_callback_info i return asyncWork == nullptr ? nullptr : asyncWork->Schedule(procedureName, cbExec, cbCompl).val_; } -napi_value CloudFileCacheNapi::StopFileCache(napi_env env, napi_callback_info info) +static tuple GetCleanFlagForStop(napi_env env, NFuncArg &funcArg) { - NFuncArg funcArg(env, info); - if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::THREE)) { - LOGE("Stop Number of arguments unmatched"); - NError(E_PARAMS).ThrowErr(env); - return nullptr; - } - auto [succ, uri, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String(); - if (!succ) { - LOGE("Stop get uri parameter failed!"); - NError(E_PARAMS).ThrowErr(env); - return nullptr; - } + bool succ = false; bool needClean = false; size_t maxArgSize = static_cast(NARG_CNT::TWO); if (funcArg.GetArgc() >= NARG_CNT::TWO) { @@ -208,19 +193,67 @@ napi_value CloudFileCacheNapi::StopFileCache(napi_env env, napi_callback_info in tie(succ, needClean) = option.ToBool(); if (!succ) { LOGE("Failed to get clean flag!"); - NError(E_PARAMS).ThrowErr(env); - return nullptr; + return {EINVAL, false, false}; + ; } maxArgSize = static_cast(NARG_CNT::THREE); } } - auto cbExec = [uri = string(uri.get()), env = env, needClean]() -> NError { - int32_t ret = CloudSyncManager::GetInstance().StopDownloadFile(uri, needClean); + return {E_OK, needClean, maxArgSize}; +} + +static std::tuple> GetCallbackForStop(napi_env env, + NFuncArg &funcArg) +{ + auto fileCacheEntity = NClass::GetEntityOf(env, funcArg.GetThisVar()); + if (!fileCacheEntity) { + LOGE("Failed to get file cache entity."); + return {EINVAL, nullptr}; + } + + std::lock_guard lock(fileCacheEntity->registerMutex); + auto iter = fileCacheEntity->registerMap.find(PROGRESS); + if (iter != fileCacheEntity->registerMap.end()) { + auto callbackImpl = std::dynamic_pointer_cast(iter->second); + return {E_OK, callbackImpl}; + } + return {EINVAL, nullptr}; +} + +napi_value CloudFileCacheNapi::StopFileCache(napi_env env, napi_callback_info info) +{ + NFuncArg funcArg(env, info); + if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::THREE)) { + LOGE("Stop Number of arguments unmatched"); + NError(EINVAL).ThrowErr(env); + return nullptr; + } + auto [succUri, uri, size] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String(); + if (!succUri || size == 0) { + LOGE("Stop get uri parameter failed!"); + NError(EINVAL).ThrowErr(env); + return nullptr; + } + + auto [ret, needClean, maxArgSize] = GetCleanFlagForStop(env, funcArg); + if (ret != E_OK) { + NError(ret).ThrowErr(env); + return nullptr; + } + auto [res, callbackImpl] = GetCallbackForStop(env, funcArg); + if (res != E_OK) { + NError(res).ThrowErr(env); + return nullptr; + } + + auto cbExec = [uri{string(uri.get())}, needClean{needClean}, callbackImpl{callbackImpl}]() -> NError { + if (callbackImpl == nullptr) { + LOGE("Failed to get download callback"); + return NError(Convert2JsErrNum(E_SERVICE_INNER_ERROR)); + } + int32_t ret = callbackImpl->StopDownloadInner(uri, needClean); if (ret != E_OK) { - LOGE("Stop Download failed! ret = %{public}d", ret); - if (ret != E_INVAL_ARG) { - ret = E_BROKEN_IPC; - } + ret = (ret == E_CLOUD_SDK) ? E_SERVICE_INNER_ERROR : ret; return NError(Convert2JsErrNum(ret)); } return NError(ERRNO_NOERR); @@ -239,43 +272,78 @@ napi_value CloudFileCacheNapi::StopFileCache(napi_env env, napi_callback_info in } struct FileCacheArg { - vector uriList; - int64_t downloadId; + vector uriList; + int64_t downloadId = 0; + std::shared_ptr callbackImpl = nullptr; + int32_t fieldKey = FieldKey::FIELDKEY_CONTENT; }; +static std::shared_ptr FillParamForBatchStart(napi_env env, NFuncArg &funcArg) +{ + auto [succ, uriArray, size] = NVal(env, funcArg[NARG_POS::FIRST]).ToStringArray(); + if (!succ || size == 0) { + LOGE("Start get uri array parameter failed!"); + return nullptr; + } + + // Todo: get fieldKey + auto fileCacheEntity = NClass::GetEntityOf(env, funcArg.GetThisVar()); + if (fileCacheEntity == nullptr) { + LOGE("Failed to get file cache entity"); + return nullptr; + } + auto fileCache = std::make_shared(); + { + std::lock_guard lock(fileCacheEntity->registerMutex); + auto iter = fileCacheEntity->registerMap.find(MULTI_PROGRESS); + if (iter == fileCacheEntity->registerMap.end()) { + std::shared_ptr normal = std::make_shared(); + fileCache->callbackImpl = std::make_shared(normal); + fileCache->callbackImpl->registerMgr_ = std::make_shared(env); + fileCacheEntity->registerMap.insert(std::make_pair(MULTI_PROGRESS, fileCache->callbackImpl)); + } else { + fileCache->callbackImpl = std::dynamic_pointer_cast(iter->second); + } + } + + fileCache->uriList.swap(uriArray); + return fileCache; +} + napi_value CloudFileCacheNapi::StartBatchFileCache(napi_env env, napi_callback_info info) { NFuncArg funcArg(env, info); if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) { LOGE("Start Number of arguments unmatched"); - NError(E_PARAMS).ThrowErr(env); + NError(EINVAL).ThrowErr(env); return nullptr; } - auto fileUris = std::make_shared(); - auto [resGetUris, uriArray, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToStringArray(); - if (!resGetUris) { - LOGE("Start get uri array parameter failed!"); - NError(E_PARAMS).ThrowErr(env); + auto fileCache = FillParamForBatchStart(env, funcArg); + if (fileCache == nullptr) { + NError(Convert2JsErrNum(E_SERVICE_INNER_ERROR)).ThrowErr(env); return nullptr; } - fileUris->uriList = uriArray; - auto cbExec = [fileUris]() -> NError { - int32_t ret = CloudSyncManager::GetInstance().StartFileCache(fileUris->uriList, fileUris->downloadId); + auto cbExec = [fileCache]() -> NError { + if (fileCache == nullptr || fileCache->callbackImpl == nullptr) { + LOGE("Failed to get download callback"); + return NError(Convert2JsErrNum(E_SERVICE_INNER_ERROR)); + } + int32_t ret = + fileCache->callbackImpl->StartDownloadInner(fileCache->uriList, fileCache->fieldKey, fileCache->downloadId); if (ret != E_OK) { - LOGE("Batch start file cache failed! ret = %{public}d", ret); - ret = (ret == E_CLOUD_SDK) ? E_UNKNOWN_ERR : ret; + ret = (ret == E_CLOUD_SDK) ? E_SERVICE_INNER_ERROR : ret; return NError(Convert2JsErrNum(ret)); } return NError(ERRNO_NOERR); }; - auto cbCompl = [fileUris](napi_env env, NError err) -> NVal { + auto cbCompl = [fileCache](napi_env env, NError err) -> NVal { if (err) { return {env, err.GetNapiErr(env)}; } - return NVal::CreateInt64(env, fileUris->downloadId); + return NVal::CreateInt64(env, fileCache->downloadId); }; string procedureName = "cloudFileCache"; @@ -283,42 +351,59 @@ napi_value CloudFileCacheNapi::StartBatchFileCache(napi_env env, napi_callback_i return asyncWork == nullptr ? nullptr : asyncWork->Schedule(procedureName, cbExec, cbCompl).val_; } +static std::tuple> GetCallbackForStopBatch(napi_env env, + NFuncArg &funcArg) +{ + auto fileCacheEntity = NClass::GetEntityOf(env, funcArg.GetThisVar()); + if (!fileCacheEntity) { + LOGE("Failed to get file cache entity."); + return {EINVAL, nullptr}; + } + + std::lock_guard lock(fileCacheEntity->registerMutex); + auto iter = fileCacheEntity->registerMap.find(PROGRESS); + if (iter != fileCacheEntity->registerMap.end()) { + auto callbackImpl = std::dynamic_pointer_cast(iter->second); + return {E_OK, callbackImpl}; + } + return {EINVAL, nullptr}; +} + napi_value CloudFileCacheNapi::StopBatchFileCache(napi_env env, napi_callback_info info) { NFuncArg funcArg(env, info); if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::THREE)) { LOGE("Start Number of arguments unmatched"); - NError(E_PARAMS).ThrowErr(env); + NError(EINVAL).ThrowErr(env); return nullptr; } auto [succ, downloadId] = NVal(env, funcArg[NARG_POS::FIRST]).ToInt64(); if (!succ || downloadId <= 0) { - LOGE("Start get download ID parameter failed!"); - NError(E_PARAMS).ThrowErr(env); + LOGE("Start get taskId parameter failed!"); + NError(EINVAL).ThrowErr(env); return nullptr; } - bool needClean = false; - size_t maxArgSize = static_cast(NARG_CNT::TWO); - if (funcArg.GetArgc() >= NARG_CNT::TWO) { - NVal option(env, funcArg[NARG_POS::SECOND]); - if (!option.TypeIs(napi_function)) { - tie(succ, needClean) = option.ToBool(); - if (!succ) { - LOGE("Failed to get clean flag!"); - NError(E_PARAMS).ThrowErr(env); - return nullptr; - } - maxArgSize = static_cast(NARG_CNT::THREE); - } + auto [ret, needClean, maxArgSize] = GetCleanFlagForStop(env, funcArg); + if (ret != E_OK) { + NError(Convert2JsErrNum(ret)).ThrowErr(env); + return nullptr; + } + auto [res, callbackImpl] = GetCallbackForStopBatch(env, funcArg); + if (res != E_OK) { + NError(Convert2JsErrNum(res)).ThrowErr(env); + return nullptr; } - auto cbExec = [downloadId = downloadId, needClean]() -> NError { - int32_t ret = CloudSyncManager::GetInstance().StopFileCache(downloadId, needClean); + auto cbExec = [downloadId{downloadId}, needClean{needClean}, callbackImpl{callbackImpl}]() -> NError { + if (callbackImpl == nullptr) { + LOGE("Failed to get download callback"); + return NError(Convert2JsErrNum(E_SERVICE_INNER_ERROR)); + } + int32_t ret = callbackImpl->StopDownloadInner(downloadId, needClean); if (ret != E_OK) { - LOGE("Batch stop file cache failed! ret = %{public}d", ret); - ret = (ret == E_CLOUD_SDK) ? E_UNKNOWN_ERR : ret; + ret = (ret == E_CLOUD_SDK) ? E_SERVICE_INNER_ERROR : ret; return NError(Convert2JsErrNum(ret)); } return NError(ERRNO_NOERR); @@ -336,56 +421,71 @@ napi_value CloudFileCacheNapi::StopBatchFileCache(napi_env env, napi_callback_in return asyncWork == nullptr ? nullptr : asyncWork->Schedule(procedureName, cbExec, cbCompl).val_; } +static std::shared_ptr + InitCallbackImpl(napi_env env, NFuncArg &funcArg, const std::string &eventType) +{ + auto fileCacheEntity = NClass::GetEntityOf(env, funcArg.GetThisVar()); + if (!fileCacheEntity) { + LOGE("Failed to get file cache entity."); + return nullptr; + } + + std::shared_ptr callbackImpl = nullptr; + std::lock_guard lock(fileCacheEntity->registerMutex); + auto iter = fileCacheEntity->registerMap.find(eventType); + if (iter == fileCacheEntity->registerMap.end() || iter->second == nullptr) { + std::shared_ptr normal = std::make_shared(); + if (eventType == PROGRESS) { + callbackImpl = std::make_shared(normal); + } else { + callbackImpl = std::make_shared(normal); + } + callbackImpl->registerMgr_ = std::make_shared(env); + fileCacheEntity->registerMap.insert(make_pair(eventType, callbackImpl)); + } else { + callbackImpl = iter->second; + } + return callbackImpl; +} + napi_value CloudFileCacheNapi::On(napi_env env, napi_callback_info info) { NFuncArg funcArg(env, info); if (!funcArg.InitArgs(NARG_CNT::TWO)) { LOGE("Batch-On Number of arguments unmatched"); - NError(E_PARAMS).ThrowErr(env); + NError(EINVAL).ThrowErr(env); return nullptr; } auto [succProgress, progress, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String(); string eventType(progress.get()); + if (eventType == "multiProgress") { + eventType = MULTI_PROGRESS; + } if (!succProgress || (eventType != PROGRESS && eventType != MULTI_PROGRESS)) { LOGE("Batch-On get progress failed!"); - NError(E_PARAMS).ThrowErr(env); + NError(EINVAL).ThrowErr(env); return nullptr; } if (!NVal(env, funcArg[NARG_POS::SECOND]).TypeIs(napi_function)) { LOGE("Batch-On argument type mismatch"); - NError(E_PARAMS).ThrowErr(env); - return nullptr; - } - - auto fileCacheEntity = NClass::GetEntityOf(env, funcArg.GetThisVar()); - if (!fileCacheEntity) { - LOGE("Failed to get file cache entity."); - NError(E_PARAMS).ThrowErr(env); + NError(EINVAL).ThrowErr(env); return nullptr; } - auto arg = make_shared(); - arg->eventType = eventType; - if (eventType == PROGRESS) { - arg->callback = make_shared(env, NVal(env, funcArg[(int)NARG_POS::SECOND]).val_); - } else { - arg->callback = - make_shared(env, NVal(env, funcArg[(int)NARG_POS::SECOND]).val_, true); - } - - if (!fileCacheEntity->registerMgr.AddRegisterInfo(arg)) { - LOGE("Batch-On register callback fail, callback already exist"); - NError(E_PARAMS).ThrowErr(env); + std::shared_ptr callbackImpl = InitCallbackImpl(env, funcArg, eventType); + if (callbackImpl == nullptr || callbackImpl->registerMgr_ == nullptr) { + LOGE("Failed to init callback"); + NError(Convert2JsErrNum(E_SERVICE_INNER_ERROR)).ThrowErr(env); return nullptr; } - - int32_t ret = CloudSyncManager::GetInstance().RegisterFileCacheCallback(arg->callback); - if (ret != E_OK) { - LOGE("Failed to register callback, error: %{public}d", ret); - (void)fileCacheEntity->registerMgr.RemoveRegisterInfo(eventType); - NError(Convert2JsErrNum(ret)).ThrowErr(env); - return nullptr; + if (auto registerMgr = std::dynamic_pointer_cast(callbackImpl->registerMgr_)) { + auto status = registerMgr->RegisterCallback(funcArg[NARG_POS::SECOND]); + if (status != napi_ok) { + LOGE("Failed to register callback, status: %{public}d", status); + NError(E_SERVICE_INNER_ERROR).ThrowErr(env); + return nullptr; + } } return NVal::CreateUndefined(env).val_; @@ -396,47 +496,56 @@ napi_value CloudFileCacheNapi::Off(napi_env env, napi_callback_info info) NFuncArg funcArg(env, info); if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) { LOGE("Off Number of arguments unmatched"); - NError(E_PARAMS).ThrowErr(env); + NError(EINVAL).ThrowErr(env); return nullptr; } auto [succProgress, progress, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String(); string eventType(progress.get()); + if (eventType == "multiProgress") { + eventType = MULTI_PROGRESS; + } if (!succProgress || (eventType != PROGRESS && eventType != MULTI_PROGRESS)) { - LOGE("Batch-Off get progress failed!"); - NError(E_PARAMS).ThrowErr(env); + LOGE("Off get progress failed!"); + NError(EINVAL).ThrowErr(env); return nullptr; } - if (funcArg.GetArgc() == (uint)NARG_CNT::TWO && !NVal(env, funcArg[NARG_POS::SECOND]).TypeIs(napi_function)) { - LOGE("Batch-Off argument type mismatch"); - NError(E_PARAMS).ThrowErr(env); - return nullptr; + napi_value callbackVel = nullptr; + if (funcArg.GetArgc() == (uint)NARG_CNT::TWO) { + if (!NVal(env, funcArg[NARG_POS::SECOND]).TypeIs(napi_function)) { + LOGE("Off argument type mismatch"); + NError(EINVAL).ThrowErr(env); + return nullptr; + } + callbackVel = funcArg[NARG_POS::SECOND]; } auto fileCacheEntity = NClass::GetEntityOf(env, funcArg.GetThisVar()); if (!fileCacheEntity) { LOGE("Failed to get file cache entity."); - NError(E_PARAMS).ThrowErr(env); + NError(Convert2JsErrNum(E_SERVICE_INNER_ERROR)).ThrowErr(env); return nullptr; } - - if (!fileCacheEntity->registerMgr.HasEvent(eventType)) { - LOGE("Batch-Off no callback is registered for this event type: %{public}s.", eventType.c_str()); - NError(E_PARAMS).ThrowErr(env); - return nullptr; + std::shared_ptr callbackImpl = nullptr; + { + std::unique_lock lock(fileCacheEntity->registerMutex); + auto iter = fileCacheEntity->registerMap.find(eventType); + if (iter != fileCacheEntity->registerMap.end()) { + callbackImpl = iter->second; + } } - - int32_t ret = CloudSyncManager::GetInstance().UnregisterFileCacheCallback(); - if (ret != E_OK) { - LOGE("Failed to unregister callback, error: %{public}d", ret); - NError(Convert2JsErrNum(ret)).ThrowErr(env); + if (callbackImpl == nullptr || callbackImpl->registerMgr_ == nullptr) { + LOGE("Failed to unregister callback, for eventType: %{public}s.", eventType.c_str()); + NError(Convert2JsErrNum(E_SERVICE_INNER_ERROR)).ThrowErr(env); return nullptr; } - - if (!fileCacheEntity->registerMgr.RemoveRegisterInfo(eventType)) { - LOGE("Batch-Off no callback is registered for this event type: %{public}s.", eventType.c_str()); - NError(E_PARAMS).ThrowErr(env); - return nullptr; + if (auto registerMgr = std::dynamic_pointer_cast(callbackImpl->registerMgr_)) { + auto status = registerMgr->UnregisterCallback(callbackVel); + if (status != napi_ok) { + LOGE("Failed to unregister callback, status: %{public}d", status); + NError(E_SERVICE_INNER_ERROR).ThrowErr(env); + return nullptr; + } } return NVal::CreateUndefined(env).val_; diff --git a/interfaces/kits/js/cloudfilesync/cloud_file_cache_napi.h b/interfaces/kits/js/cloudfilesync/cloud_file_cache_napi.h index 06661dc7bbdfae0b5e70c0b4d9f0942572fc6d97..f171ff5cb83942a06567cc51a88f7fdf6c08d382 100644 --- a/interfaces/kits/js/cloudfilesync/cloud_file_cache_napi.h +++ b/interfaces/kits/js/cloudfilesync/cloud_file_cache_napi.h @@ -17,8 +17,11 @@ #define OHOS_FILEMGMT_CLOUD_FILE_CACHE_NAPI_H #include -#include -#include "cloud_file_napi.h" +#include +#include + +#include "cloud_download_callback_middle.h" +#include "filemgmt_libn.h" namespace OHOS::FileManagement::CloudSync { const std::string PROGRESS = "progress"; @@ -43,33 +46,10 @@ public: private: inline static std::string className_ = "CloudFileCache"; }; -struct RegisterInfoArg { - std::string eventType; - std::shared_ptr callback; - ~RegisterInfoArg() - { - if (callback != nullptr) { - callback->DeleteReference(); - callback = nullptr; - } - } -}; - -class RegisterManager { -public: - RegisterManager() = default; - ~RegisterManager() = default; - bool AddRegisterInfo(std::shared_ptr info); - bool RemoveRegisterInfo(const std::string &eventType); - bool HasEvent(const std::string &eventType); - -private: - std::mutex registerMutex_; - std::unordered_set> registerInfo_; -}; struct FileCacheEntity { - RegisterManager registerMgr; + std::mutex registerMutex; + std::unordered_map> registerMap; }; } // namespace OHOS::FileManagement::CloudSync #endif // OHOS_FILEMGMT_CLOUD_FILE_DOWNLOAD_NAPI_H \ No newline at end of file diff --git a/interfaces/kits/js/cloudfilesync/cloud_file_napi.cpp b/interfaces/kits/js/cloudfilesync/cloud_file_napi.cpp index fd6988281337ef73e4a6083783b3a85be3d9352d..19ec291c1314d89b9a642acbda12479fe378db7d 100644 --- a/interfaces/kits/js/cloudfilesync/cloud_file_napi.cpp +++ b/interfaces/kits/js/cloudfilesync/cloud_file_napi.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Huawei Device Co., Ltd. + * Copyright (c) 2023-2025 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -13,37 +13,67 @@ * limitations under the License. */ -#include "cloud_file_download_napi.h" +#include "cloud_file_napi.h" +#include #include #include "async_work.h" -#include "cloud_file_napi.h" #include "cloud_sync_manager.h" #include "dfs_error.h" +#include "dfsu_access_token_helper.h" +#include "download_progress.h" +#include "filemgmt_libn.h" +#include "n_napi.h" +#include "register_callback_manager_napi.h" #include "utils_log.h" -#include "uv.h" namespace OHOS::FileManagement::CloudSync { using namespace FileManagement::LibN; using namespace std; -const int32_t ARGS_ONE = 1; - -napi_value CloudFileNapi::Constructor(napi_env env, napi_callback_info info) +napi_value CloudFileDownloadNapi::Constructor(napi_env env, napi_callback_info info) { - LOGI("CloudFileNapi::Constructor begin"); + LOGI("CloudFileDownloadNapi::Constructor begin"); NFuncArg funcArg(env, info); if (!funcArg.InitArgs(NARG_CNT::ZERO)) { LOGE("Start Number of arguments unmatched"); NError(E_PARAMS).ThrowErr(env); return nullptr; } - isDownloadCallbackRegistered_ = false; - LOGI("CloudFileNapi::Constructor end"); + auto downloadEntity = make_unique(); + if (!NClass::SetEntityFor(env, funcArg.GetThisVar(), move(downloadEntity))) { + LOGE("Failed to set download entity."); + NError(E_UNKNOWN_ERR).ThrowErr(env); + return nullptr; + } + LOGI("CloudFileDownloadNapi::Constructor end"); return funcArg.GetThisVar(); } -napi_value CloudFileNapi::Start(napi_env env, napi_callback_info info) +static std::tuple> + ParseParamForStart(napi_env env, NFuncArg &funcArg) +{ + auto [succUri, uri, size] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String(); + if (!succUri || size == 0) { + LOGE("Start get uri parameter failed!"); + return {E_PARAMS, "", nullptr}; + } + + auto downloadEntity = NClass::GetEntityOf(env, funcArg.GetThisVar()); + if (downloadEntity == nullptr) { + LOGE("Failed to get file cache entity"); + return {E_PARAMS, "", nullptr}; + } + + if (downloadEntity->callbackImpl == nullptr) { + std::shared_ptr system = std::make_shared(); + downloadEntity->callbackImpl = std::make_shared(system); + downloadEntity->callbackImpl->registerMgr_ = std::make_shared(env); + } + return {E_OK, string(uri.get()), downloadEntity->callbackImpl}; +} + +napi_value CloudFileDownloadNapi::Start(napi_env env, napi_callback_info info) { LOGI("Start begin"); NFuncArg funcArg(env, info); @@ -52,25 +82,18 @@ napi_value CloudFileNapi::Start(napi_env env, napi_callback_info info) NError(E_PARAMS).ThrowErr(env); return nullptr; } - auto [succUri, uri, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String(); - if (!succUri) { - LOGE("Start get uri parameter failed!"); - NError(E_PARAMS).ThrowErr(env); + + auto [ret, uri, callbackImpl] = ParseParamForStart(env, funcArg); + if (ret != E_OK) { + NError(ret).ThrowErr(env); return nullptr; } - - auto cbExec = [uri = string(uri.get()), env = env]() -> NError { - lock_guard lock(mtx_); - if (callback_ != nullptr && !isDownloadCallbackRegistered_) { - int32_t ret = CloudSyncManager::GetInstance().RegisterDownloadFileCallback(callback_); - if (ret != E_OK) { - LOGE("RegisterDownloadFileCallback error, ret: %{public}d", ret); - return NError(Convert2JsErrNum(ret)); - } - isDownloadCallbackRegistered_ = true; + auto cbExec = [uri{uri}, callbackImpl{callbackImpl}]() -> NError { + if (callbackImpl == nullptr) { + LOGE("Failed to get download callback"); + return NError(Convert2JsErrNum(E_PARAMS)); } - - int32_t ret = CloudSyncManager::GetInstance().StartDownloadFile(uri); + int32_t ret = callbackImpl->StartDownloadInner(uri, FieldKey::FIELDKEY_CONTENT); if (ret != E_OK) { LOGE("Start Download failed! ret = %{public}d", ret); return NError(Convert2JsErrNum(ret)); @@ -91,96 +114,20 @@ napi_value CloudFileNapi::Start(napi_env env, napi_callback_info info) return asyncWork == nullptr ? nullptr : asyncWork->Schedule(procedureName, cbExec, cbCompl).val_; } -CloudDownloadCallbackImpl::CloudDownloadCallbackImpl(napi_env env, napi_value fun, bool isBatch) : env_(env) +static int32_t CheckPermissions(const string &permission, bool isSystemApp) { - if (fun != nullptr) { - napi_create_reference(env_, fun, 1, &cbOnRef_); + if (!permission.empty() && !DfsuAccessTokenHelper::CheckCallerPermission(permission)) { + LOGE("permission denied"); + return E_PERMISSION_DENIED; } - isBatch_ = isBatch; -} - -void CloudDownloadCallbackImpl::OnComplete(UvChangeMsg *msg) -{ - auto downloadcCallback = msg->CloudDownloadCallback_.lock(); - if (downloadcCallback == nullptr || downloadcCallback->cbOnRef_ == nullptr) { - LOGE("downloadcCallback->cbOnRef_ is nullptr"); - return; - } - auto env = downloadcCallback->env_; - auto ref = downloadcCallback->cbOnRef_; - napi_handle_scope scope = nullptr; - napi_open_handle_scope(env, &scope); - napi_value jsCallback = nullptr; - napi_status status = napi_get_reference_value(env, ref, &jsCallback); - if (status != napi_ok) { - LOGE("Create reference failed, status: %{public}d", status); - napi_close_handle_scope(env, scope); - return; - } - NVal obj = NVal::CreateObject(env); - if (!msg->isBatch_) { - obj.AddProp("state", NVal::CreateInt32(env, (int32_t)msg->downloadProgress_.state).val_); - obj.AddProp("processed", NVal::CreateInt64(env, msg->downloadProgress_.downloadedSize).val_); - obj.AddProp("size", NVal::CreateInt64(env, msg->downloadProgress_.totalSize).val_); - obj.AddProp("uri", NVal::CreateUTF8String(env, msg->downloadProgress_.path).val_); - } else { - LOGI("Batch download callback items"); - obj.AddProp("state", NVal::CreateInt32(env, (int32_t)msg->downloadProgress_.batchState).val_); - obj.AddProp("downloadedSize", NVal::CreateInt64(env, msg->downloadProgress_.batchDownloadSize).val_); - obj.AddProp("totalSize", NVal::CreateInt64(env, msg->downloadProgress_.batchTotalSize).val_); - obj.AddProp("successfulNum", NVal::CreateInt64(env, msg->downloadProgress_.batchSuccNum).val_); - obj.AddProp("failedNum", NVal::CreateInt64(env, msg->downloadProgress_.batchFailNum).val_); - obj.AddProp("totalNum", NVal::CreateInt64(env, msg->downloadProgress_.batchTotalNum).val_); - } - obj.AddProp("taskId", NVal::CreateInt64(env, msg->downloadProgress_.downloadId).val_); - obj.AddProp("error", NVal::CreateInt32(env, (int32_t)msg->downloadProgress_.downloadErrorType).val_); - - LOGI("OnComplete callback start for taskId: %{public}lld", - static_cast(msg->downloadProgress_.downloadId)); - napi_value retVal = nullptr; - napi_value global = nullptr; - napi_get_global(env, &global); - status = napi_call_function(env, global, jsCallback, ARGS_ONE, &(obj.val_), &retVal); - if (status != napi_ok) { - LOGE("napi call function failed, status: %{public}d", status); - } - napi_close_handle_scope(env, scope); -} - -void CloudDownloadCallbackImpl::OnDownloadProcess(const DownloadProgressObj &progress) -{ - UvChangeMsg *msg = new (std::nothrow) UvChangeMsg(shared_from_this(), progress, isBatch_); - if (msg == nullptr) { - LOGE("Failed to create uv message object"); - return; - } - auto task = [msg]() { - if (msg->CloudDownloadCallback_.expired()) { - LOGE("CloudDownloadCallback_ is expired"); - delete msg; - return; - } - msg->CloudDownloadCallback_.lock()->OnComplete(msg); - delete msg; - }; - napi_status ret = napi_send_event(env_, task, napi_event_priority::napi_eprio_immediate); - if (ret != napi_ok) { - LOGE("Failed to execute libuv work queue, ret: %{public}d", ret); - delete msg; - return; - } -} - -void CloudDownloadCallbackImpl::DeleteReference() -{ - if (cbOnRef_ != nullptr) { - napi_delete_reference(env_, cbOnRef_); - cbOnRef_ = nullptr; - return; + if (isSystemApp && !DfsuAccessTokenHelper::IsSystemApp()) { + LOGE("caller hap is not system hap"); + return E_PERMISSION_SYSTEM; } + return E_OK; } -napi_value CloudFileNapi::On(napi_env env, napi_callback_info info) +napi_value CloudFileDownloadNapi::On(napi_env env, napi_callback_info info) { LOGI("On begin"); NFuncArg funcArg(env, info); @@ -189,6 +136,12 @@ napi_value CloudFileNapi::On(napi_env env, napi_callback_info info) NError(E_PARAMS).ThrowErr(env); return nullptr; } + int32_t ret = CheckPermissions(PERM_CLOUD_SYNC, true); + if (ret != E_OK) { + LOGE("On get progress failed!"); + NError(Convert2JsErrNum(ret)).ThrowErr(env); + return nullptr; + } auto [succProgress, progress, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String(); if (!succProgress || std::string(progress.get()) != "progress") { LOGE("On get progress failed!"); @@ -201,18 +154,32 @@ napi_value CloudFileNapi::On(napi_env env, napi_callback_info info) NError(E_PARAMS).ThrowErr(env); return nullptr; } - - if (callback_ != nullptr) { - LOGI("callback already exist"); - return NVal::CreateUndefined(env).val_; + auto downloadEntity = NClass::GetEntityOf(env, funcArg.GetThisVar()); + if (downloadEntity == nullptr) { + LOGE("Failed to get file cache entity"); + NError(E_PARAMS).ThrowErr(env); + return nullptr; } - callback_ = make_shared(env, NVal(env, funcArg[(int)NARG_POS::SECOND]).val_); + if (downloadEntity->callbackImpl == nullptr) { + std::shared_ptr system = std::make_shared(); + downloadEntity->callbackImpl = std::make_shared(system); + downloadEntity->callbackImpl->registerMgr_ = std::make_shared(env); + } + if (auto registerMgr = + std::dynamic_pointer_cast(downloadEntity->callbackImpl->registerMgr_)) { + auto status = registerMgr->RegisterCallback(funcArg[NARG_POS::SECOND]); + if (status != napi_ok) { + LOGE("On register callback fail, status: %{public}d", status); + NError(E_PARAMS).ThrowErr(env); + return nullptr; + } + } return NVal::CreateUndefined(env).val_; } -napi_value CloudFileNapi::Off(napi_env env, napi_callback_info info) +napi_value CloudFileDownloadNapi::Off(napi_env env, napi_callback_info info) { LOGI("Off begin"); NFuncArg funcArg(env, info); @@ -221,6 +188,12 @@ napi_value CloudFileNapi::Off(napi_env env, napi_callback_info info) NError(E_PARAMS).ThrowErr(env); return nullptr; } + int32_t ret = CheckPermissions(PERM_CLOUD_SYNC, true); + if (ret != E_OK) { + LOGE("On get progress failed!"); + NError(Convert2JsErrNum(ret)).ThrowErr(env); + return nullptr; + } auto [succProgress, progress, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String(); if (!succProgress || std::string(progress.get()) != "progress") { LOGE("Off get progress failed!"); @@ -228,62 +201,85 @@ napi_value CloudFileNapi::Off(napi_env env, napi_callback_info info) return nullptr; } - if (funcArg.GetArgc() == (uint)NARG_CNT::TWO && !NVal(env, funcArg[NARG_POS::SECOND]).TypeIs(napi_function)) { - LOGE("Argument type mismatch"); - NError(E_PARAMS).ThrowErr(env); - return nullptr; + napi_value callbackVel = nullptr; + if (funcArg.GetArgc() == (uint)NARG_CNT::TWO) { + if (!NVal(env, funcArg[NARG_POS::SECOND]).TypeIs(napi_function)) { + LOGE("Off argument type mismatch"); + NError(E_PARAMS).ThrowErr(env); + return nullptr; + } + callbackVel = funcArg[NARG_POS::SECOND]; } - /* callback_ may be nullptr */ - int32_t ret = CloudSyncManager::GetInstance().UnregisterDownloadFileCallback(); - if (ret != E_OK) { - LOGE("UnregisterDownloadFileCallback error, ret: %{public}d", ret); - NError(Convert2JsErrNum(ret)).ThrowErr(env); + auto downloadEntity = NClass::GetEntityOf(env, funcArg.GetThisVar()); + if (downloadEntity == nullptr || downloadEntity->callbackImpl == nullptr || + downloadEntity->callbackImpl->registerMgr_ == nullptr) { + LOGE("Failed to get file cache entity, is entity null: %{public}d", (downloadEntity == nullptr)); + NError(E_PARAMS).ThrowErr(env); return nullptr; } - if (callback_ != nullptr) { - /* napi delete reference */ - callback_->DeleteReference(); - callback_ = nullptr; + + if (auto registerMgr = + std::dynamic_pointer_cast(downloadEntity->callbackImpl->registerMgr_)) { + auto status = registerMgr->UnregisterCallback(callbackVel); + if (status != napi_ok) { + LOGE("Off no callback is registered for this event type, status: %{public}d", status); + NError(E_PARAMS).ThrowErr(env); + return nullptr; + } } + return NVal::CreateUndefined(env).val_; } -napi_value CloudFileNapi::Stop(napi_env env, napi_callback_info info) +static std::tuple> + ParseParamForStop(napi_env env, NFuncArg &funcArg) +{ + auto [succUri, uri, size] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String(); + if (!succUri || size == 0) { + LOGE("Stop get uri parameter failed!"); + return {E_PARAMS, "", nullptr}; + } + + auto downloadEntity = NClass::GetEntityOf(env, funcArg.GetThisVar()); + if (downloadEntity == nullptr) { + LOGE("Failed to get file cache entity"); + return {E_PARAMS, "", nullptr}; + } + + if (downloadEntity->callbackImpl == nullptr) { + LOGE("Failed to get file cache entity"); + return {E_PARAMS, "", nullptr}; + } + return {E_OK, string(uri.get()), downloadEntity->callbackImpl}; +} + +napi_value CloudFileDownloadNapi::Stop(napi_env env, napi_callback_info info) { LOGI("Stop begin"); NFuncArg funcArg(env, info); - if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::THREE)) { + if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) { LOGE("Stop Number of arguments unmatched"); NError(E_PARAMS).ThrowErr(env); return nullptr; } - auto [succUri, uri, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String(); - if (!succUri) { - LOGE("Stop get uri parameter failed!"); - NError(E_PARAMS).ThrowErr(env); + + auto [ret, uri, callbackImpl] = ParseParamForStop(env, funcArg); + if (ret != E_OK) { + NError(ret).ThrowErr(env); return nullptr; } - bool needClean = false; - size_t maxArgSize = static_cast(NARG_CNT::TWO); - if (funcArg.GetArgc() >= NARG_CNT::TWO) { - NVal ui(env, NVal(env, funcArg[(int)NARG_POS::SECOND]).val_); - if (ui.TypeIs(napi_boolean)) { - bool needCleanIgnore; - std::tie(needCleanIgnore, needClean) = NVal(env, funcArg[NARG_POS::SECOND]).ToBool(); - maxArgSize = static_cast(NARG_CNT::THREE); + + auto cbExec = [uri{uri}, callbackImpl{callbackImpl}]() -> NError { + if (callbackImpl == nullptr) { + LOGE("Failed to get download callback"); + return NError(E_PARAMS); } - } - if (funcArg.GetArgc() == NARG_CNT::THREE) { - maxArgSize = static_cast(NARG_CNT::THREE); - } - auto cbExec = [uri = string(uri.get()), env = env, needClean]() -> NError { - int32_t ret = CloudSyncManager::GetInstance().StopDownloadFile(uri, needClean); + int32_t ret = callbackImpl->StopDownloadInner(uri, true); if (ret != E_OK) { LOGE("Stop Download failed! ret = %{public}d", ret); return NError(Convert2JsErrNum(ret)); } - LOGI("Stop Download Success!"); return NError(ERRNO_NOERR); }; @@ -295,15 +291,14 @@ napi_value CloudFileNapi::Stop(napi_env env, napi_callback_info info) }; string procedureName = "cloudFileDownload"; - auto asyncWork = GetPromiseOrCallBackWork(env, funcArg, maxArgSize); + auto asyncWork = GetPromiseOrCallBackWork(env, funcArg, static_cast(NARG_CNT::TWO)); return asyncWork == nullptr ? nullptr : asyncWork->Schedule(procedureName, cbExec, cbCompl).val_; } -bool CloudFileNapi::ToExport(std::vector props) +bool CloudFileDownloadNapi::ToExport(std::vector props) { std::string className = GetClassName(); - auto [succ, classValue] = - NClass::DefineClass(exports_.env_, className, Constructor, std::move(props)); + auto [succ, classValue] = NClass::DefineClass(exports_.env_, className, Constructor, std::move(props)); if (!succ) { NError(E_GETRESULT).ThrowErr(exports_.env_); LOGE("Failed to define GallerySync class"); @@ -320,23 +315,23 @@ bool CloudFileNapi::ToExport(std::vector props) return exports_.AddProp(className, classValue); } -bool CloudFileNapi::Export() +bool CloudFileDownloadNapi::Export() { vector props = { - NVal::DeclareNapiFunction("start", CloudFileNapi::Start), - NVal::DeclareNapiFunction("on", CloudFileNapi::On), - NVal::DeclareNapiFunction("off", CloudFileNapi::Off), - NVal::DeclareNapiFunction("stop", CloudFileNapi::Stop), + NVal::DeclareNapiFunction("start", CloudFileDownloadNapi::Start), + NVal::DeclareNapiFunction("on", CloudFileDownloadNapi::On), + NVal::DeclareNapiFunction("off", CloudFileDownloadNapi::Off), + NVal::DeclareNapiFunction("stop", CloudFileDownloadNapi::Stop), }; return ToExport(props); } -void CloudFileNapi::SetClassName(std::string classname) +void CloudFileDownloadNapi::SetClassName(std::string classname) { className_ = classname; } -string CloudFileNapi::GetClassName() +string CloudFileDownloadNapi::GetClassName() { return className_; } diff --git a/interfaces/kits/js/cloudfilesync/cloud_file_napi.h b/interfaces/kits/js/cloudfilesync/cloud_file_napi.h index 3eb1e22c30f8d930101eaf19161ac7a8b97fa9aa..210ef009d3acaec2306a15e10673d1f1a5ec8e0e 100644 --- a/interfaces/kits/js/cloudfilesync/cloud_file_napi.h +++ b/interfaces/kits/js/cloudfilesync/cloud_file_napi.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Huawei Device Co., Ltd. + * Copyright (c) 2023-2025 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -16,15 +16,19 @@ #ifndef OHOS_FILEMGMT_CLOUD_FILE_NAPI_H #define OHOS_FILEMGMT_CLOUD_FILE_NAPI_H -#include "cloud_download_callback.h" +#include +#include +#include + +#include "cloud_download_callback_impl.h" #include "filemgmt_libn.h" +#include "register_callback_manager.h" namespace OHOS::FileManagement::CloudSync { -class CloudDownloadCallbackImpl; -class CloudFileNapi : public LibN::NExporter { +class CloudFileDownloadNapi : public LibN::NExporter { public: - CloudFileNapi(napi_env env, napi_value exports) : NExporter(env, exports) {} - ~CloudFileNapi() = default; + CloudFileDownloadNapi(napi_env env, napi_value exports) : NExporter(env, exports) {} + ~CloudFileDownloadNapi() = default; bool Export() override; bool ToExport(std::vector props); @@ -37,39 +41,11 @@ public: static napi_value Stop(napi_env env, napi_callback_info info); private: - inline static std::mutex mtx_; - inline static bool isDownloadCallbackRegistered_; - static inline std::shared_ptr callback_; - inline static std::string className_ = "CloudFileNapi"; + inline static std::string className_ = "Download"; }; -class CloudDownloadCallbackImpl : public CloudDownloadCallback, - public std::enable_shared_from_this { -public: - CloudDownloadCallbackImpl(napi_env env, napi_value fun, bool isBatch = false); - ~CloudDownloadCallbackImpl() = default; - void OnDownloadProcess(const DownloadProgressObj &progress) override; - void DeleteReference(); - - class UvChangeMsg { - public: - UvChangeMsg(std::shared_ptr CloudDownloadCallbackIn, - DownloadProgressObj downloadProgress, - bool isBatch) - : CloudDownloadCallback_(CloudDownloadCallbackIn), downloadProgress_(downloadProgress), isBatch_(isBatch) - { - } - ~UvChangeMsg() {} - std::weak_ptr CloudDownloadCallback_; - DownloadProgressObj downloadProgress_; - bool isBatch_; - }; - -private: - static void OnComplete(UvChangeMsg *msg); - napi_env env_; - napi_ref cbOnRef_ = nullptr; - bool isBatch_; +struct DownloadEntity { + std::shared_ptr callbackImpl{nullptr}; }; } // namespace OHOS::FileManagement::CloudSync #endif // OHOS_FILEMGMT_CLOUD_FILE_NAPI_H \ No newline at end of file diff --git a/interfaces/kits/js/cloudfilesync/multi_download_progress_napi.cpp b/interfaces/kits/js/cloudfilesync/multi_download_progress_napi.cpp new file mode 100644 index 0000000000000000000000000000000000000000..247af6e7e0ff252b031ca615be2dde77b9d09296 --- /dev/null +++ b/interfaces/kits/js/cloudfilesync/multi_download_progress_napi.cpp @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "multi_download_progress_napi.h" + +#include "dfs_error.h" +#include "utils_log.h" + +namespace OHOS::FileManagement::CloudSync { +using namespace FileManagement::LibN; +using namespace std; +napi_value MultiDlProgressNapi::Constructor(napi_env env, napi_callback_info info) +{ + NFuncArg funcArg(env, info); + funcArg.InitArgs(NARG_CNT::ZERO); + auto progressEntity = make_unique(); + if (progressEntity == nullptr) { + LOGE("Failed to request heap memory."); + return nullptr; + } + if (!NClass::SetEntityFor(env, funcArg.GetThisVar(), move(progressEntity))) { + LOGE("Failed to set progressEntity."); + return nullptr; + } + return funcArg.GetThisVar(); +} + +napi_value MultiDlProgressNapi::GetStatus(napi_env env, napi_callback_info info) +{ + NFuncArg funcArg(env, info); + funcArg.InitArgs(NARG_CNT::ZERO); + int32_t state = static_cast(DownloadProgressObj::Status::FAILED); + auto progressEntity = NClass::GetEntityOf(env, funcArg.GetThisVar()); + if (progressEntity == nullptr || progressEntity->downloadProgress == nullptr) { + LOGE("Failed to get MultiDlProgressEntity."); + } else { + state = progressEntity->downloadProgress->GetState(); + } + + return NVal::CreateInt32(env, state).val_; +} + +napi_value MultiDlProgressNapi::GetTaskId(napi_env env, napi_callback_info info) +{ + int64_t taskId = 0; + NFuncArg funcArg(env, info); + funcArg.InitArgs(NARG_CNT::ZERO); + auto progressEntity = NClass::GetEntityOf(env, funcArg.GetThisVar()); + if (progressEntity == nullptr || progressEntity->downloadProgress == nullptr) { + LOGE("Failed to get MultiDlProgressEntity."); + } else { + taskId = progressEntity->downloadProgress->GetTaskId(); + } + + return NVal::CreateInt64(env, taskId).val_; +} + +napi_value MultiDlProgressNapi::GetDownloadedNum(napi_env env, napi_callback_info info) +{ + int64_t succNum = -1; + NFuncArg funcArg(env, info); + funcArg.InitArgs(NARG_CNT::ZERO); + auto progressEntity = NClass::GetEntityOf(env, funcArg.GetThisVar()); + if (progressEntity == nullptr || progressEntity->downloadProgress == nullptr) { + LOGE("Failed to get MultiDlProgressEntity."); + } else { + succNum = static_cast(progressEntity->downloadProgress->GetSuccNum()); + } + + return NVal::CreateInt64(env, succNum).val_; +} + +napi_value MultiDlProgressNapi::GetFailedNum(napi_env env, napi_callback_info info) +{ + int64_t failedNum = -1; + NFuncArg funcArg(env, info); + funcArg.InitArgs(NARG_CNT::ZERO); + auto progressEntity = NClass::GetEntityOf(env, funcArg.GetThisVar()); + if (progressEntity == nullptr || progressEntity->downloadProgress == nullptr) { + LOGE("Failed to get MultiDlProgressEntity."); + } else { + failedNum = static_cast(progressEntity->downloadProgress->GetFailedNum()); + } + + return NVal::CreateInt64(env, failedNum).val_; +} + +napi_value MultiDlProgressNapi::GetTotalNum(napi_env env, napi_callback_info info) +{ + int64_t totalNum = -1; + NFuncArg funcArg(env, info); + funcArg.InitArgs(NARG_CNT::ZERO); + auto progressEntity = NClass::GetEntityOf(env, funcArg.GetThisVar()); + if (progressEntity == nullptr || progressEntity->downloadProgress == nullptr) { + LOGE("Failed to get MultiDlProgressEntity."); + } else { + totalNum = progressEntity->downloadProgress->GetTotalNum(); + } + + return NVal::CreateInt64(env, totalNum).val_; +} + +napi_value MultiDlProgressNapi::GetDownloadedSize(napi_env env, napi_callback_info info) +{ + int64_t downloadSize = INT64_MAX; + NFuncArg funcArg(env, info); + funcArg.InitArgs(NARG_CNT::ZERO); + auto progressEntity = NClass::GetEntityOf(env, funcArg.GetThisVar()); + if (progressEntity == nullptr || progressEntity->downloadProgress == nullptr) { + LOGE("Failed to get MultiDlProgressEntity."); + } else { + downloadSize = progressEntity->downloadProgress->GetDownloadedSize(); + } + + return NVal::CreateInt64(env, downloadSize).val_; +} + +napi_value MultiDlProgressNapi::GetTotalSize(napi_env env, napi_callback_info info) +{ + int64_t totalSize = INT64_MAX; + NFuncArg funcArg(env, info); + funcArg.InitArgs(NARG_CNT::ZERO); + auto progressEntity = NClass::GetEntityOf(env, funcArg.GetThisVar()); + if (progressEntity == nullptr || progressEntity->downloadProgress == nullptr) { + LOGE("Failed to get MultiDlProgressEntity."); + } else { + totalSize = progressEntity->downloadProgress->GetTotalSize(); + } + + return NVal::CreateInt64(env, totalSize).val_; +} + +napi_value MultiDlProgressNapi::GetErrorType(napi_env env, napi_callback_info info) +{ + NFuncArg funcArg(env, info); + funcArg.InitArgs(NARG_CNT::ZERO); + int32_t errorType = static_cast(DownloadProgressObj::DownloadErrorType::UNKNOWN_ERROR); + auto progressEntity = NClass::GetEntityOf(env, funcArg.GetThisVar()); + if (progressEntity == nullptr || progressEntity->downloadProgress == nullptr) { + LOGE("Failed to get MultiDlProgressEntity."); + } else { + errorType = progressEntity->downloadProgress->GetErrorType(); + } + + return NVal::CreateInt32(env, errorType).val_; +} + +napi_value MultiDlProgressNapi::GetFailedFileList(napi_env env, napi_callback_info info) +{ + NFuncArg funcArg(env, info); + if (!funcArg.InitArgs(NARG_CNT::ZERO)) { + LOGE("Failed to get param."); + NError(Convert2JsErrNum(E_SERVICE_INNER_ERROR)).ThrowErr(env); + return nullptr; + } + auto progressEntity = NClass::GetEntityOf(env, funcArg.GetThisVar()); + if (progressEntity == nullptr || progressEntity->downloadProgress == nullptr) { + LOGE("Failed to get MultiDlProgressEntity."); + NError(Convert2JsErrNum(E_SERVICE_INNER_ERROR)).ThrowErr(env); + return nullptr; + } + napi_value res = nullptr; + napi_status status = napi_create_array(env, &res); + if (status != napi_ok) { + HILOGE("Failed to creat array"); + NError(Convert2JsErrNum(E_SERVICE_INNER_ERROR)).ThrowErr(env); + return nullptr; + } + + size_t index = 0; + auto failedFiles = progressEntity->downloadProgress->GetFailedFiles(); + for (const auto &iter : failedFiles) { + NVal obj = NVal::CreateObject(env); + obj.AddProp("uri", NVal::CreateUTF8String(env, iter.first).val_); + obj.AddProp("error", NVal::CreateInt32(env, iter.second).val_); + status = napi_set_element(env, res, index, obj.val_); + if (status != napi_ok) { + HILOGE("Failed to set element on data, %{public}s", GetAnonyString(iter.first).c_str()); + NError(Convert2JsErrNum(E_SERVICE_INNER_ERROR)).ThrowErr(env); + return nullptr; + } + index++; + } + + return res; +} + +napi_value MultiDlProgressNapi::GetDownloadedFileList(napi_env env, napi_callback_info info) +{ + NFuncArg funcArg(env, info); + if (!funcArg.InitArgs(NARG_CNT::ZERO)) { + LOGE("Failed to get param."); + NError(Convert2JsErrNum(E_SERVICE_INNER_ERROR)).ThrowErr(env); + return nullptr; + } + auto progressEntity = NClass::GetEntityOf(env, funcArg.GetThisVar()); + if (progressEntity == nullptr || progressEntity->downloadProgress == nullptr) { + LOGE("Failed to get MultiDlProgressEntity."); + NError(Convert2JsErrNum(E_SERVICE_INNER_ERROR)).ThrowErr(env); + return nullptr; + } + napi_value res = nullptr; + napi_status status = napi_create_array(env, &res); + if (status != napi_ok) { + HILOGE("Failed to creat array"); + NError(Convert2JsErrNum(E_SERVICE_INNER_ERROR)).ThrowErr(env); + return nullptr; + } + + size_t index = 0; + auto downloadedFiles = progressEntity->downloadProgress->GetDownloadedFiles(); + for (const auto &item : downloadedFiles) { + status = napi_set_element(env, res, index, NVal::CreateUTF8String(env, item).val_); + if (status != napi_ok) { + HILOGE("Failed to set element on data, %{public}s", GetAnonyString(item).c_str()); + NError(Convert2JsErrNum(E_SERVICE_INNER_ERROR)).ThrowErr(env); + return nullptr; + } + index++; + } + + return res; +} + +std::string MultiDlProgressNapi::GetClassName() +{ + return className_; +} + +bool MultiDlProgressNapi::Export() +{ + vector props = { + NVal::DeclareNapiGetter("state", GetStatus), + NVal::DeclareNapiGetter("taskId", GetTaskId), + NVal::DeclareNapiGetter("successfulCount", GetDownloadedNum), + NVal::DeclareNapiGetter("failedCount", GetFailedNum), + NVal::DeclareNapiGetter("totalCount", GetTotalNum), + NVal::DeclareNapiGetter("downloadedSize", GetDownloadedSize), + NVal::DeclareNapiGetter("totalSize", GetTotalSize), + NVal::DeclareNapiGetter("errType", GetErrorType), + NVal::DeclareNapiFunction("getFailedFiles", GetFailedFileList), + NVal::DeclareNapiFunction("getSuccessfulFiles", GetDownloadedFileList), + }; + + string className = GetClassName(); + bool succ = false; + napi_value classValue = nullptr; + tie(succ, classValue) = + NClass::DefineClass(exports_.env_, className, MultiDlProgressNapi::Constructor, move(props)); + if (!succ) { + LOGE("Define class exceptions"); + NError(Convert2JsErrNum(E_SERVICE_INNER_ERROR)).ThrowErr(exports_.env_); + return false; + } + succ = NClass::SaveClass(exports_.env_, className, classValue); + if (!succ) { + LOGE("Save class exceptions"); + NError(Convert2JsErrNum(E_SERVICE_INNER_ERROR)).ThrowErr(exports_.env_); + return false; + } + + return exports_.AddProp(className, classValue); +} + +} // namespace OHOS::FileManagement::CloudSync \ No newline at end of file diff --git a/interfaces/kits/js/cloudfilesync/multi_download_progress_napi.h b/interfaces/kits/js/cloudfilesync/multi_download_progress_napi.h new file mode 100644 index 0000000000000000000000000000000000000000..b1f1b564f433576ab3dac97de1cab4725f548e11 --- /dev/null +++ b/interfaces/kits/js/cloudfilesync/multi_download_progress_napi.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OHOS_FILEMGMT_MULTI_DOWNLOAD_PROGRESS_NAPI_H +#define OHOS_FILEMGMT_MULTI_DOWNLOAD_PROGRESS_NAPI_H + +#include + +#include "download_progress.h" +#include "filemgmt_libn.h" + +namespace OHOS::FileManagement::CloudSync { +class MultiDlProgressNapi final : public LibN::NExporter { +public: + MultiDlProgressNapi(napi_env env, napi_value exports) : NExporter(env, exports) {} + ~MultiDlProgressNapi() = default; + + bool Export() override; + std::string GetClassName() override; + static napi_value Constructor(napi_env env, napi_callback_info info); + static napi_value GetStatus(napi_env env, napi_callback_info info); + static napi_value GetTaskId(napi_env env, napi_callback_info info); + static napi_value GetDownloadedNum(napi_env env, napi_callback_info info); + static napi_value GetFailedNum(napi_env env, napi_callback_info info); + static napi_value GetTotalNum(napi_env env, napi_callback_info info); + static napi_value GetDownloadedSize(napi_env env, napi_callback_info info); + static napi_value GetTotalSize(napi_env env, napi_callback_info info); + static napi_value GetErrorType(napi_env env, napi_callback_info info); + static napi_value GetFailedFileList(napi_env env, napi_callback_info info); + static napi_value GetDownloadedFileList(napi_env env, napi_callback_info info); + + inline static const std::string className_ = "MultiDownloadProgress"; +}; + +struct MultiDlProgressEntity { + std::shared_ptr downloadProgress{nullptr}; +}; +} // namespace OHOS::FileManagement::CloudSync +#endif // OHOS_FILEMGMT_MULTI_DOWNLOAD_PROGRESS_NAPI_H \ No newline at end of file diff --git a/interfaces/kits/js/cloudfilesync/register_callback_manager.cpp b/interfaces/kits/js/cloudfilesync/register_callback_manager_napi.cpp similarity index 50% rename from interfaces/kits/js/cloudfilesync/register_callback_manager.cpp rename to interfaces/kits/js/cloudfilesync/register_callback_manager_napi.cpp index 3d3cbb6239f6f2e73b6c6fcce1ec7daf794bbc84..79bb99fe6ba55f09266e3d209148a0b93e8f6a2a 100644 --- a/interfaces/kits/js/cloudfilesync/register_callback_manager.cpp +++ b/interfaces/kits/js/cloudfilesync/register_callback_manager_napi.cpp @@ -13,12 +13,52 @@ * limitations under the License. */ -#include "register_callback_manager.h" +#include "register_callback_manager_napi.h" + +#include + +#include "cloud_download_callback_middle.h" +#include "download_progress.h" +#include "multi_download_progress_napi.h" #include "utils_log.h" namespace OHOS::FileManagement::CloudSync { +using namespace FileManagement::LibN; using namespace std; -napi_status RegisterCallbackManager::RegisterCallback(napi_value callback) +static napi_value ConvertDlProgressToValue(napi_env env, std::shared_ptr &progress) +{ + if (progress == nullptr) { + LOGE("Progress is null"); + return nullptr; + } + if (auto singleProgress = std::dynamic_pointer_cast(progress)) { + NVal obj = NVal::CreateObject(env); + obj.AddProp("state", NVal::CreateInt32(env, singleProgress->GetState()).val_); + obj.AddProp("processed", NVal::CreateInt64(env, singleProgress->GetDownloadedSize()).val_); + obj.AddProp("size", NVal::CreateInt64(env, singleProgress->GetTotalSize()).val_); + obj.AddProp("uri", NVal::CreateUTF8String(env, singleProgress->GetUri()).val_); + obj.AddProp("error", NVal::CreateInt32(env, singleProgress->GetErrorType()).val_); + return obj.val_; + } + if (auto batchProgress = std::dynamic_pointer_cast(progress)) { + napi_value progressVal = NClass::InstantiateClass(env, MultiDlProgressNapi::className_, {}); + if (progressVal == nullptr) { + LOGE("Failed to instantiate class"); + return nullptr; + } + auto progressEntity = NClass::GetEntityOf(env, progressVal); + if (progressEntity == nullptr) { + LOGE("Failed to get progressEntity."); + return nullptr; + } + progressEntity->downloadProgress = batchProgress; + return progressVal; + } + LOGE("Failed to convert progress to nvalue"); + return nullptr; +} + +napi_status RegisterCallbackManagerNapi::RegisterCallback(napi_value callback) { if (callback == nullptr) { return napi_invalid_arg; @@ -39,7 +79,7 @@ napi_status RegisterCallbackManager::RegisterCallback(napi_value callback) return napi_ok; } -napi_status RegisterCallbackManager::UnregisterCallback(napi_value callback) +napi_status RegisterCallbackManagerNapi::UnregisterCallback(napi_value callback) { if (validRefNum_ == 0) { return napi_ok; @@ -78,7 +118,7 @@ napi_status RegisterCallbackManager::UnregisterCallback(napi_value callback) } // No need to lock -bool RegisterCallbackManager::IsExisted(napi_value callback) +bool RegisterCallbackManagerNapi::IsExisted(napi_value callback) { bool isExisted = false; napi_value val = nullptr; @@ -101,8 +141,43 @@ bool RegisterCallbackManager::IsExisted(napi_value callback) return false; } +void RegisterCallbackManagerNapi::OnDownloadCallback(std::shared_ptr progress, + std::shared_ptr callbackImpl) +{ + std::shared_ptr registerMgr = shared_from_this(); + napi_status status = napi_send_event( + env_, + [progress, callbackImpl, registerMgr]() mutable { + if (progress == nullptr || callbackImpl == nullptr || registerMgr == nullptr) { + LOGE("Failed to callback, is callbackImpl null: %{public}d", (callbackImpl == nullptr)); + return; + } + napi_handle_scope scope = nullptr; + napi_status status = napi_open_handle_scope(registerMgr->env_, &scope); + if (status != napi_ok) { + LOGE("Failed to open handle scope, status: %{public}d", status); + napi_close_handle_scope(registerMgr->env_, scope); + return; + } + napi_value jsProgress = ConvertDlProgressToValue(registerMgr->env_, progress); + if (jsProgress == nullptr) { + napi_close_handle_scope(registerMgr->env_, scope); + return; + } + registerMgr->OnJsCallback(&jsProgress, 1); + napi_close_handle_scope(registerMgr->env_, scope); + if (progress->IsNeedClean()) { + callbackImpl->RemoveDownloadInfo(progress->GetTaskId()); + } + }, + napi_eprio_immediate); + if (status != napi_ok) { + LOGE("Failed to execute libuv work queue, status: %{public}d", status); + } +} + // Running in JS thread -void RegisterCallbackManager::OnJsCallback(napi_value *value, uint32_t argc) +void RegisterCallbackManagerNapi::OnJsCallback(napi_value *value, uint32_t argc) { std::lock_guard lock(callbackMtx_); for (auto iter = callbackList_.begin(); iter != callbackList_.end();) { diff --git a/interfaces/kits/js/cloudfilesync/register_callback_manager_napi.h b/interfaces/kits/js/cloudfilesync/register_callback_manager_napi.h new file mode 100644 index 0000000000000000000000000000000000000000..152c6f70782106341081931a3a611af4b97632de --- /dev/null +++ b/interfaces/kits/js/cloudfilesync/register_callback_manager_napi.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OHOS_FILEMGMT_REGISTER_CALLBACK_MANAGER_NAPI_H +#define OHOS_FILEMGMT_REGISTER_CALLBACK_MANAGER_NAPI_H + +#include +#include +#include + +#include "filemgmt_libn.h" +#include "n_napi.h" +#include "register_callback_manager.h" + +namespace OHOS::FileManagement::CloudSync { +class RegisterCallbackManagerNapi : public RegisterCallbackManager, + public std::enable_shared_from_this { +public: + explicit RegisterCallbackManagerNapi(napi_env env) : env_(env) {} + bool IsExisted(napi_value callback); + napi_status RegisterCallback(napi_value callback); + napi_status UnregisterCallback(napi_value callback); + void OnJsCallback(napi_value *value, uint32_t argc); + void OnDownloadCallback(std::shared_ptr progress, + std::shared_ptr callbackImpl) override; + +protected: + napi_env env_; + std::recursive_mutex callbackMtx_; + std::atomic validRefNum_{0}; + std::list> callbackList_; +}; +} // namespace OHOS::FileManagement::CloudSync +#endif // OHOS_FILEMGMT_REGISTER_CALLBACK_MANAGER_NAPI_H \ No newline at end of file diff --git a/interfaces/kits/js/common/cloud_download_callback_impl.cpp b/interfaces/kits/js/common/cloud_download_callback_impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e57c803bbf385980c5d325e8037026484db530e6 --- /dev/null +++ b/interfaces/kits/js/common/cloud_download_callback_impl.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "cloud_download_callback_impl.h" + +#include +#include + +#include "dfs_error.h" +#include "download_progress.h" +#include "utils_log.h" + +namespace OHOS::FileManagement::CloudSync { +using namespace std; +BatchDownloadCallbackImpl::BatchDownloadCallbackImpl(std::shared_ptr strategy) + : CloudDownloadCallbackMiddle(strategy) +{ +} + +int32_t BatchDownloadCallbackImpl::StartDownloadInner(const std::vector &uriList, + int32_t fieldKey, + int64_t &downloadId) +{ + std::shared_ptr fileCacheInfo = std::make_shared(); + std::lock_guard lock(downloadInfoMtx_); + int32_t ret = GetStrategy()->Start(uriList, fieldKey, downloadId, shared_from_this()); + if (ret != E_OK) { + LOGE("Start batch download failed! ret = %{public}d", ret); + return ret; + } + + downloadInfos_.insert(std::make_pair(downloadId, fileCacheInfo)); + return ret; +} + +int32_t BatchDownloadCallbackImpl::StopDownloadInner(int64_t downloadId, bool needClean) +{ + int32_t ret = GetStrategy()->Stop(downloadId, needClean); + if (ret != E_OK) { + LOGE("Batch stop file cache failed! ret = %{public}d", ret); + } + return ret; +} + +SingleDownloadCallbackImpl::SingleDownloadCallbackImpl(std::shared_ptr strategy) + : CloudDownloadCallbackMiddle(strategy) +{ +} + +int32_t SingleDownloadCallbackImpl::StartDownloadInner(const std::string &uri, int32_t fieldKey) +{ + int64_t downloadId = 0; + std::lock_guard lock(downloadInfoMtx_); + std::shared_ptr fileCacheInfo = std::make_shared(); + int32_t ret = GetStrategy()->Start({uri}, fieldKey, downloadId, shared_from_this()); + if (ret != E_OK) { + LOGE("Start single download failed! ret = %{public}d", ret); + return ret; + } + downloadInfos_.insert(std::make_pair(downloadId, fileCacheInfo)); + return ret; +} + +int32_t SingleDownloadCallbackImpl::StopDownloadInner(const std::string &uri, bool needClean) +{ + auto downloadIdList = GetDownloadIdsByUri(uri); + int32_t ret = E_OK; + int32_t resErr = E_OK; + LOGI("Stop Download downloadId list size: %{public}zu", downloadIdList.size()); + for (auto taskId : downloadIdList) { + resErr = GetStrategy()->Stop(taskId, needClean); + if (resErr != E_OK) { + ret = resErr; + continue; + } + } + if (ret != E_OK) { + LOGE("Stop Download failed! ret = %{public}d", ret); + } + return ret; +} + +std::vector SingleDownloadCallbackImpl::GetDownloadIdsByUri(const std::string &uri) +{ + std::vector ids; + std::lock_guard lock(downloadInfoMtx_); + for (const auto &[id, progress] : downloadInfos_) { + if (progress->GetUri() == uri) { + ids.push_back(id); + } + } + return ids; +} +} // namespace OHOS::FileManagement::CloudSync diff --git a/interfaces/kits/js/ani/file_cloud_sync/include/cloud_download_callback_ani.h b/interfaces/kits/js/common/cloud_download_callback_impl.h similarity index 46% rename from interfaces/kits/js/ani/file_cloud_sync/include/cloud_download_callback_ani.h rename to interfaces/kits/js/common/cloud_download_callback_impl.h index 5bebbca8662ad879b2729add09a6e1f4266f2474..209a5de16b30fcc77ba0faf76f71f514d4d165b3 100644 --- a/interfaces/kits/js/ani/file_cloud_sync/include/cloud_download_callback_ani.h +++ b/interfaces/kits/js/common/cloud_download_callback_impl.h @@ -13,29 +13,30 @@ * limitations under the License. */ -#ifndef OHOS_FILEMGMT_CLOUD_DOWNLOAD_CALLBACK_ANI_H -#define OHOS_FILEMGMT_CLOUD_DOWNLOAD_CALLBACK_ANI_H +#ifndef OHOS_FILEMGMT_CLOUD_DOWNLOAD_CALLBACK_IMPL_H +#define OHOS_FILEMGMT_CLOUD_DOWNLOAD_CALLBACK_IMPL_H -#include -#include +#include #include "cloud_download_callback_middle.h" namespace OHOS::FileManagement::CloudSync { +class BatchDownloadCallbackImpl : public CloudDownloadCallbackMiddle { +public: + explicit BatchDownloadCallbackImpl(std::shared_ptr strategy); + + int32_t StartDownloadInner(const std::vector &uriList, int32_t fieldKey, int64_t &downloadId); + int32_t StopDownloadInner(int64_t downloadId, bool needClean); +}; -class CloudDownloadCallbackAniImpl : public CloudDownloadCallbackMiddle, - public std::enable_shared_from_this { +class SingleDownloadCallbackImpl : public CloudDownloadCallbackMiddle { public: - CloudDownloadCallbackAniImpl(ani_env *env, ani_ref fun, bool isBatch = false); - ~CloudDownloadCallbackAniImpl() override = default; - void OnDownloadProcess(const DownloadProgressObj &progress) override; - void DeleteReference() override; + explicit SingleDownloadCallbackImpl(std::shared_ptr strategy); + int32_t StartDownloadInner(const std::string &uri, int32_t fieldKey); + int32_t StopDownloadInner(const std::string &uri, bool needClean); private: - void GetDownloadProgress(const DownloadProgressObj &progress, const ani_class &cls, ani_object &pg); - ani_env *env_; - ani_ref cbOnRef_ = nullptr; - bool isBatch_; + std::vector GetDownloadIdsByUri(const std::string &uri); }; } // namespace OHOS::FileManagement::CloudSync -#endif // OHOS_FILEMGMT_CLOUD_DOWNLOAD_CALLBACK_ANI_H +#endif // OHOS_FILEMGMT_CLOUD_DOWNLOAD_CALLBACK_IMPL_H \ No newline at end of file diff --git a/interfaces/kits/js/common/cloud_download_callback_middle.cpp b/interfaces/kits/js/common/cloud_download_callback_middle.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bc1057cf49f9bc177b78c258806cf07fffeb925e --- /dev/null +++ b/interfaces/kits/js/common/cloud_download_callback_middle.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "cloud_download_callback_middle.h" +#include + +#include "cloud_sync_manager.h" +#include "dfs_error.h" +#include "download_progress.h" +#include "utils_log.h" + +namespace OHOS::FileManagement::CloudSync { +int32_t NormalDownload::Start(const std::vector &uriList, + int32_t fieldKey, + int64_t &downloadId, + const std::shared_ptr &callback) +{ + return CloudSyncManager::GetInstance().StartFileCache(uriList, downloadId, fieldKey, callback); +} + +int32_t NormalDownload::Stop(int64_t downloadId, bool needClean) +{ + return CloudSyncManager::GetInstance().StopFileCache(downloadId, needClean); +} + +int32_t SystemDownload::Start(const std::vector &uriList, + int32_t fieldKey, + int64_t &downloadId, + const std::shared_ptr &callback) +{ + if (uriList.empty()) { + LOGE("Invalid argument, uri list is empty."); + return E_INVAL_ARG; + } + return CloudSyncManager::GetInstance().StartDownloadFile(uriList[0], callback, downloadId); +} + +int32_t SystemDownload::Stop(int64_t downloadId, bool needClean) +{ + return CloudSyncManager::GetInstance().StopDownloadFile(downloadId, needClean); +} + +CloudDownloadCallbackMiddle::CloudDownloadCallbackMiddle(std::shared_ptr &strategy) + : strategy_(strategy) +{ +} + +void CloudDownloadCallbackMiddle::RemoveDownloadInfo(int64_t downloadId) +{ + std::lock_guard lock(downloadInfoMtx_); + downloadInfos_.erase(downloadId); +} + +std::shared_ptr CloudDownloadCallbackMiddle::GetDownloadInfo(int64_t downloadId) +{ + std::lock_guard lock(downloadInfoMtx_); + auto it = downloadInfos_.find(downloadId); + if (it != downloadInfos_.end()) { + return it->second; + } + return nullptr; +} + +void CloudDownloadCallbackMiddle::OnDownloadProcess(const DownloadProgressObj &progress) +{ + auto fileCacheInfo = GetDownloadInfo(progress.downloadId); + if (fileCacheInfo == nullptr) { + LOGE("Failed to callback, no such taskId: %{public}lld", static_cast(progress.downloadId)); + return; + } + fileCacheInfo->Update(progress); + if (registerMgr_ == nullptr) { + LOGE("Skip to callback, register manager is null"); + return; + } + registerMgr_->OnDownloadCallback(fileCacheInfo, shared_from_this()); +} +} // namespace OHOS::FileManagement::CloudSync \ No newline at end of file diff --git a/interfaces/kits/js/common/cloud_download_callback_middle.h b/interfaces/kits/js/common/cloud_download_callback_middle.h new file mode 100644 index 0000000000000000000000000000000000000000..831f06337dc9dd67bb855a4c26653cb651295880 --- /dev/null +++ b/interfaces/kits/js/common/cloud_download_callback_middle.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OHOS_FILEMGMT_CLOUD_DOWNLOAD_CALLBACK_MIDDLE_H +#define OHOS_FILEMGMT_CLOUD_DOWNLOAD_CALLBACK_MIDDLE_H + +#include +#include +#include +#include +#include + +#include "cloud_download_callback.h" +#include "download_progress.h" +#include "register_callback_manager.h" + +namespace OHOS::FileManagement::CloudSync { +class CloudDownloadCallbackMiddle; +class DownloadStrategy { +public: + virtual ~DownloadStrategy() = default; + virtual int32_t Start(const std::vector &uriList, + int32_t fieldKey, + int64_t &downloadId, + const std::shared_ptr &callback) = 0; + virtual int32_t Stop(int64_t downloadId, bool needClean) = 0; +}; + +class NormalDownload : public DownloadStrategy { +public: + int32_t Start(const std::vector &uriList, + int32_t fieldKey, + int64_t &downloadId, + const std::shared_ptr &callback) override; + int32_t Stop(int64_t downloadId, bool needClean) override; +}; + +class SystemDownload : public DownloadStrategy { +public: + int32_t Start(const std::vector &uriList, + int32_t fieldKey, + int64_t &downloadId, + const std::shared_ptr &callback) override; + int32_t Stop(int64_t downloadId, bool needClean) override; +}; + +class CloudDownloadCallbackMiddle : public CloudDownloadCallback, + public std::enable_shared_from_this { +public: + explicit CloudDownloadCallbackMiddle(std::shared_ptr &strategy); + virtual ~CloudDownloadCallbackMiddle() = default; + void OnDownloadProcess(const DownloadProgressObj &progress) override; + void RemoveDownloadInfo(int64_t downloadId); + +public: + std::shared_ptr registerMgr_{nullptr}; + +protected: + std::shared_ptr GetDownloadInfo(int64_t downloadId); + std::shared_ptr GetStrategy() const + { + return strategy_; + } + +protected: + std::shared_ptr strategy_{nullptr}; + std::mutex downloadInfoMtx_; + std::unordered_map> downloadInfos_; +}; + +} // namespace OHOS::FileManagement::CloudSync +#endif // OHOS_FILEMGMT_CLOUD_DOWNLOAD_CALLBACK_MIDDLE_H \ No newline at end of file diff --git a/interfaces/kits/js/common/download_progress.cpp b/interfaces/kits/js/common/download_progress.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fd4ba0fc8c544e8094097d62231dd693ce366281 --- /dev/null +++ b/interfaces/kits/js/common/download_progress.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "download_progress.h" + +namespace OHOS::FileManagement::CloudSync { +void SingleDownloadProgress::Update(const DownloadProgressObj &progress) +{ + if (taskId_ != progress.downloadId) { + return; + } + uri_ = progress.path; + totalSize_ = progress.totalSize; + downloadedSize_ = progress.downloadedSize; + state_ = progress.state; + errorType_ = progress.downloadErrorType; + needClean_ = (progress.state != DownloadProgressObj::RUNNING); +} + +void BatchDownloadProgress::Update(const DownloadProgressObj &progress) +{ + if (taskId_ != progress.downloadId) { + return; + } + state_ = static_cast(progress.batchState); + taskId_ = progress.downloadId; + downloadedSize_ = progress.batchDownloadSize; + totalSize_ = progress.batchTotalSize; + totalNum_ = progress.batchTotalNum; + errorType_ = static_cast(progress.downloadErrorType); + if (progress.state == DownloadProgressObj::COMPLETED) { + downloadedFiles_.insert(progress.path); + } else if (progress.state != DownloadProgressObj::RUNNING) { + failedFiles_.insert(std::make_pair(progress.path, static_cast(progress.downloadErrorType))); + } + needClean_ = ((progress.batchTotalNum == progress.batchFailNum + progress.batchSuccNum) && + progress.batchState != DownloadProgressObj::RUNNING); +} +} // namespace OHOS::FileManagement::CloudSync diff --git a/interfaces/kits/js/common/download_progress.h b/interfaces/kits/js/common/download_progress.h new file mode 100644 index 0000000000000000000000000000000000000000..42561e35e59c94e0c279201f270475f7e6368e93 --- /dev/null +++ b/interfaces/kits/js/common/download_progress.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OHOS_FILEMGMT_DOWNLOAD_PROGRESS_H +#define OHOS_FILEMGMT_DOWNLOAD_PROGRESS_H + +#include +#include +#include + +#include "cloud_sync_common.h" + +namespace OHOS::FileManagement::CloudSync { +class DownloadProgress { +public: + virtual ~DownloadProgress() = default; + virtual void Update(const DownloadProgressObj &progress) = 0; + + // Use default inline + int64_t GetTaskId() const + { + return taskId_; + } + int64_t GetTotalSize() const + { + return totalSize_; + } + int64_t GetDownloadedSize() const + { + return downloadedSize_; + } + int32_t GetState() const + { + return state_; + } + int32_t GetErrorType() const + { + return errorType_; + } + bool IsNeedClean() const + { + return needClean_; + } + std::string GetUri() const + { + return uri_; + } + +protected: + std::string uri_; + // taskId_ is used to identify the download task, it is unique for each download + int64_t taskId_{0}; + int64_t totalSize_{0}; + int64_t downloadedSize_{0}; + int32_t state_{0}; + int32_t errorType_{0}; + bool needClean_{false}; +}; + +class SingleDownloadProgress : public DownloadProgress { +public: + void Update(const DownloadProgressObj &progress) override; +}; + +class BatchDownloadProgress : public DownloadProgress { +public: + void Update(const DownloadProgressObj &progress) override; + + // Getters for batch download progress + int64_t GetTotalNum() const + { + return totalNum_; + } + std::size_t GetSuccNum() const + { + return downloadedFiles_.size(); + } + std::size_t GetFailedNum() const + { + return failedFiles_.size(); + } + std::unordered_set GetDownloadedFiles() const + { + return downloadedFiles_; + } + std::unordered_map GetFailedFiles() const + { + return failedFiles_; + } + +private: + int64_t totalNum_{0}; + std::unordered_set downloadedFiles_; + std::unordered_map failedFiles_; +}; +} // namespace OHOS::FileManagement::CloudSync +#endif // OHOS_FILEMGMT_DOWNLOAD_PROGRESS_H diff --git a/interfaces/kits/js/cloudfilesync/register_callback_manager.h b/interfaces/kits/js/common/register_callback_manager.h similarity index 64% rename from interfaces/kits/js/cloudfilesync/register_callback_manager.h rename to interfaces/kits/js/common/register_callback_manager.h index 51aa3a8af5d326313ef00791bfcb54d7ccdffc51..5c985d17b4c1c99eff1fe08f3b7ae84c712de5fb 100644 --- a/interfaces/kits/js/cloudfilesync/register_callback_manager.h +++ b/interfaces/kits/js/common/register_callback_manager.h @@ -15,26 +15,16 @@ #ifndef OHOS_FILEMGMT_REGISTER_CALLBACK_MANAGER_H #define OHOS_FILEMGMT_REGISTER_CALLBACK_MANAGER_H - -#include -#include - -#include "n_napi.h" +#include namespace OHOS::FileManagement::CloudSync { +class DownloadProgress; +class CloudDownloadCallbackMiddle; class RegisterCallbackManager { public: - explicit RegisterCallbackManager(napi_env env) : env_(env) {} - bool IsExisted(napi_value callback); - napi_status RegisterCallback(napi_value callback); - napi_status UnregisterCallback(napi_value callback); - void OnJsCallback(napi_value *value, uint32_t argc); - -protected: - napi_env env_; - std::recursive_mutex callbackMtx_; - std::atomic validRefNum_{0}; - std::list> callbackList_; + virtual ~RegisterCallbackManager() = default; + virtual void OnDownloadCallback(std::shared_ptr progress, + std::shared_ptr callbackImpl) {}; }; } // namespace OHOS::FileManagement::CloudSync #endif // OHOS_FILEMGMT_REGISTER_CALLBACK_MANAGER_H \ No newline at end of file