1 /*
2 * Copyright (c) 2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "downgrade_download_napi.h"
17
18 #include <cstddef>
19 #include <sys/types.h>
20
21 #include "async_work.h"
22 #include "cloud_sync_manager.h"
23 #include "dfs_error.h"
24 #include "dfsu_access_token_helper.h"
25 #include "utils_log.h"
26
27 namespace OHOS::FileManagement::CloudSync {
28 using namespace FileManagement::LibN;
29 using namespace std;
CheckPermissions(const string & permission,bool isSystemApp)30 static int32_t CheckPermissions(const string &permission, bool isSystemApp)
31 {
32 if (!permission.empty() && !DfsuAccessTokenHelper::CheckCallerPermission(permission)) {
33 LOGE("permission denied");
34 return E_PERMISSION_DENIED;
35 }
36 if (isSystemApp && !DfsuAccessTokenHelper::IsSystemApp()) {
37 LOGE("caller hap is not system hap");
38 return E_PERMISSION_SYSTEM;
39 }
40 return E_OK;
41 }
42
DowngradeDlCallbackImpl(napi_env env,napi_value func)43 DowngradeDlCallbackImpl::DowngradeDlCallbackImpl(napi_env env, napi_value func) : env_(env)
44 {
45 napi_status status = napi_create_reference(env_, func, 1, &cbOnRef_);
46 if (status != napi_ok) {
47 LOGE("Failed to create napi ref, %{public}d", status);
48 }
49 }
50
~DowngradeDlCallbackImpl()51 DowngradeDlCallbackImpl::~DowngradeDlCallbackImpl()
52 {
53 if (cbOnRef_ != nullptr) {
54 napi_status status = napi_delete_reference(env_, cbOnRef_);
55 if (status != napi_ok) {
56 LOGE("Failed to delete napi ref, %{public}d", status);
57 }
58 cbOnRef_ = nullptr;
59 }
60 }
61
ConvertToValue()62 napi_value DowngradeDlCallbackImpl::ConvertToValue()
63 {
64 napi_value progressVal = NClass::InstantiateClass(env_, DowngradeProgressNapi::className_, {});
65 if (progressVal == nullptr) {
66 LOGE("Failed to instantiate class");
67 return nullptr;
68 }
69 auto progressEntity = NClass::GetEntityOf<DowngradeProgressEntity>(env_, progressVal);
70 if (progressEntity == nullptr) {
71 LOGE("Failed to get progressEntity.");
72 return nullptr;
73 }
74
75 progressEntity->progress = dlProgress_;
76 return progressVal;
77 }
78
UpdateDownloadProgress(const DowngradeProgress & progress)79 void DowngradeDlCallbackImpl::UpdateDownloadProgress(const DowngradeProgress &progress)
80 {
81 if (dlProgress_ == nullptr) {
82 dlProgress_ = std::make_shared<SingleBundleProgress>();
83 }
84
85 dlProgress_->state = static_cast<int32_t>(progress.state);
86 dlProgress_->downloadedSize = progress.downloadedSize;
87 dlProgress_->totalSize = progress.totalSize;
88 dlProgress_->successfulCount = progress.successfulCount;
89 dlProgress_->failedCount = progress.failedCount;
90 dlProgress_->totalCount = progress.totalCount;
91 dlProgress_->stopReason = static_cast<int32_t>(progress.stopReason);
92 }
93
OnDownloadProcess(const DowngradeProgress & progress)94 void DowngradeDlCallbackImpl::OnDownloadProcess(const DowngradeProgress &progress)
95 {
96 UpdateDownloadProgress(progress);
97 std::shared_ptr<DowngradeDlCallbackImpl> callbackImpl = shared_from_this();
98 napi_status status = napi_send_event(
99 callbackImpl->env_,
100 [callbackImpl]() mutable {
101 auto env = callbackImpl->env_;
102 auto ref = callbackImpl->cbOnRef_;
103 if (env == nullptr || ref == nullptr) {
104 LOGE("The env context is invalid");
105 return;
106 }
107 napi_handle_scope scope = nullptr;
108 napi_status status = napi_open_handle_scope(env, &scope);
109 if (status != napi_ok) {
110 LOGE("Failed to open handle scope, status: %{public}d", status);
111 return;
112 }
113 napi_value jsCallback = nullptr;
114 status = napi_get_reference_value(env, ref, &jsCallback);
115 if (status != napi_ok) {
116 LOGE("Create reference failed, status: %{public}d", status);
117 napi_close_handle_scope(env, scope);
118 return;
119 }
120 napi_value jsProgress = callbackImpl->ConvertToValue();
121 if (jsProgress == nullptr) {
122 napi_close_handle_scope(env, scope);
123 return;
124 }
125 napi_value jsResult = nullptr;
126 status = napi_call_function(env, nullptr, jsCallback, 1, &jsProgress, &jsResult);
127 if (status != napi_ok) {
128 LOGE("napi call function failed, status: %{public}d", status);
129 }
130 napi_close_handle_scope(env, scope);
131 },
132 napi_eprio_immediate);
133 if (status != napi_ok) {
134 LOGE("Failed to execute libuv work queue, status: %{public}d", status);
135 }
136 }
137
Constructor(napi_env env,napi_callback_info info)138 napi_value DowngradeDownloadNapi::Constructor(napi_env env, napi_callback_info info)
139 {
140 NFuncArg funcArg(env, info);
141 if (!funcArg.InitArgs(NARG_CNT::ONE)) {
142 LOGE("Start Number of arguments unmatched");
143 NError(EINVAL).ThrowErr(env);
144 return nullptr;
145 }
146 int32_t ret = CheckPermissions(PERM_CLOUD_SYNC_MANAGER, true);
147 if (ret != E_OK) {
148 NError(Convert2JsErrNum(ret)).ThrowErr(env);
149 return nullptr;
150 }
151 auto [succ, bundle, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String();
152 std::string bundleName(bundle.get());
153 if (!succ || bundleName.empty()) {
154 LOGE("Failed to get bundleName.");
155 NError(EINVAL).ThrowErr(env);
156 return nullptr;
157 }
158 auto downgradeEntity = make_unique<DowngradeEntity>(bundleName);
159 if (!NClass::SetEntityFor<DowngradeEntity>(env, funcArg.GetThisVar(), move(downgradeEntity))) {
160 LOGE("Failed to set file cache entity.");
161 NError(EINVAL).ThrowErr(env);
162 return nullptr;
163 }
164
165 return funcArg.GetThisVar();
166 }
167
ToExport(std::vector<napi_property_descriptor> props)168 bool DowngradeDownloadNapi::ToExport(std::vector<napi_property_descriptor> props)
169 {
170 std::string className = GetClassName();
171 auto [succ, classValue] = NClass::DefineClass(exports_.env_, className, Constructor, std::move(props));
172 if (!succ) {
173 NError(Convert2JsErrNum(E_SERVICE_INNER_ERROR)).ThrowErr(exports_.env_);
174 LOGE("Failed to define GallerySync class");
175 return false;
176 }
177
178 succ = NClass::SaveClass(exports_.env_, className, classValue);
179 if (!succ) {
180 NError(Convert2JsErrNum(E_SERVICE_INNER_ERROR)).ThrowErr(exports_.env_);
181 LOGE("Failed to save GallerySync class");
182 return false;
183 }
184
185 return exports_.AddProp(className, classValue);
186 }
187
Export()188 bool DowngradeDownloadNapi::Export()
189 {
190 std::vector<napi_property_descriptor> props = {
191 NVal::DeclareNapiFunction("startDownload", DowngradeDownloadNapi::StartDownload),
192 NVal::DeclareNapiFunction("stopDownload", DowngradeDownloadNapi::StopDownload),
193 NVal::DeclareNapiFunction("getCloudFileInfo", DowngradeDownloadNapi::GetCloudFileInfo),
194 };
195
196 return ToExport(props);
197 }
198
StartDownload(napi_env env,napi_callback_info info)199 napi_value DowngradeDownloadNapi::StartDownload(napi_env env, napi_callback_info info)
200 {
201 NFuncArg funcArg(env, info);
202 if (!funcArg.InitArgs(NARG_CNT::ONE)) {
203 LOGE("Start Number of arguments unmatched");
204 NError(EINVAL).ThrowErr(env);
205 return nullptr;
206 }
207 NVal callbackVal(env, funcArg[NARG_POS::FIRST]);
208 if (!callbackVal.TypeIs(napi_function)) {
209 LOGE("Batch-On argument type mismatch");
210 NError(EINVAL).ThrowErr(env);
211 return nullptr;
212 }
213
214 auto downgradeEntity = NClass::GetEntityOf<DowngradeEntity>(env, funcArg.GetThisVar());
215 if (!downgradeEntity) {
216 LOGE("Failed to get downgrade entity.");
217 NError(Convert2JsErrNum(E_SERVICE_INNER_ERROR)).ThrowErr(env);
218 return nullptr;
219 }
220
221 if (downgradeEntity->callbackImpl == nullptr) {
222 downgradeEntity->callbackImpl = make_shared<DowngradeDlCallbackImpl>(env, callbackVal.val_);
223 }
224
225 auto cbExec = [callbackImpl{downgradeEntity->callbackImpl}, bundleName{downgradeEntity->bundleName}]() -> NError {
226 if (callbackImpl == nullptr) {
227 LOGE("Failed to get download callback");
228 return NError(Convert2JsErrNum(E_SERVICE_INNER_ERROR));
229 }
230 int32_t ret = CloudSyncManager::GetInstance().StartDowngrade(bundleName, callbackImpl);
231 if (ret != E_OK) {
232 LOGE("Start downgrade failed! ret = %{public}d", ret);
233 return NError(Convert2JsErrNum(ret));
234 }
235
236 LOGI("Start downgrade success!");
237 return NError(ERRNO_NOERR);
238 };
239
240 auto cbCompl = [](napi_env env, NError err) -> NVal {
241 if (err) {
242 return {env, err.GetNapiErr(env)};
243 }
244 return NVal::CreateUndefined(env);
245 };
246
247 string procedureName = "downgradeDownload";
248 auto asyncWork = GetPromiseOrCallBackWork(env, funcArg, static_cast<size_t>(NARG_CNT::TWO));
249 return asyncWork == nullptr ? nullptr : asyncWork->Schedule(procedureName, cbExec, cbCompl).val_;
250 }
251
StopDownload(napi_env env,napi_callback_info info)252 napi_value DowngradeDownloadNapi::StopDownload(napi_env env, napi_callback_info info)
253 {
254 NFuncArg funcArg(env, info);
255 funcArg.InitArgs(NARG_CNT::ZERO);
256 auto downgradeEntity = NClass::GetEntityOf<DowngradeEntity>(env, funcArg.GetThisVar());
257 if (!downgradeEntity || downgradeEntity->callbackImpl == nullptr) {
258 LOGE("Failed to get downgrade entity.");
259 NError(Convert2JsErrNum(E_SERVICE_INNER_ERROR)).ThrowErr(env);
260 return nullptr;
261 }
262
263 auto cbExec = [bundleName{downgradeEntity->bundleName}]() -> NError {
264 int32_t ret = CloudSyncManager::GetInstance().StopDowngrade(bundleName);
265 if (ret != E_OK) {
266 LOGE("Stop downgrade failed! ret = %{public}d", ret);
267 return NError(Convert2JsErrNum(ret));
268 }
269 LOGI("Stop downgrade success!");
270 return NError(ERRNO_NOERR);
271 };
272
273 auto cbCompl = [](napi_env env, NError err) -> NVal {
274 if (err) {
275 return {env, err.GetNapiErr(env)};
276 }
277 return NVal::CreateUndefined(env);
278 };
279
280 string procedureName = "downgradeDownload";
281 auto asyncWork = GetPromiseOrCallBackWork(env, funcArg, static_cast<size_t>(NARG_CNT::TWO));
282 return asyncWork == nullptr ? nullptr : asyncWork->Schedule(procedureName, cbExec, cbCompl).val_;
283 }
284
GetCloudFileInfo(napi_env env,napi_callback_info info)285 napi_value DowngradeDownloadNapi::GetCloudFileInfo(napi_env env, napi_callback_info info)
286 {
287 NFuncArg funcArg(env, info);
288 funcArg.InitArgs(NARG_CNT::ZERO);
289 auto downgradeEntity = NClass::GetEntityOf<DowngradeEntity>(env, funcArg.GetThisVar());
290 if (!downgradeEntity) {
291 LOGE("Failed to get downgrade entity.");
292 NError(Convert2JsErrNum(E_SERVICE_INNER_ERROR)).ThrowErr(env);
293 return nullptr;
294 }
295
296 auto fileInfo = std::make_shared<CloudFileInfo>();
297 std::string bundleName = downgradeEntity->bundleName;
298 auto cbExec = [bundleName{downgradeEntity->bundleName}, fileInfo]() -> NError {
299 int32_t ret = CloudSyncManager::GetInstance().GetCloudFileInfo(bundleName, *fileInfo);
300 if (ret != E_OK) {
301 LOGE("Start Download failed! ret = %{public}d", ret);
302 return NError(Convert2JsErrNum(ret));
303 }
304 LOGI("Start Download Success!");
305 return NError(ERRNO_NOERR);
306 };
307
308 auto cbCompl = [fileInfo](napi_env env, NError err) -> NVal {
309 if (err) {
310 return {env, err.GetNapiErr(env)};
311 }
312 NVal obj = NVal::CreateObject(env);
313 obj.AddProp("cloudfileCount", NVal::CreateInt32(env, fileInfo->cloudfileCount).val_);
314 obj.AddProp("cloudFileTotalSize", NVal::CreateInt64(env, fileInfo->cloudFileTotalSize).val_);
315 obj.AddProp("localFileCount", NVal::CreateInt32(env, fileInfo->localFileCount).val_);
316 obj.AddProp("localFileTotalSize", NVal::CreateInt64(env, fileInfo->localFileTotalSize).val_);
317 obj.AddProp("bothFileCount", NVal::CreateInt32(env, fileInfo->bothFileCount).val_);
318 obj.AddProp("bothFileTotalSize", NVal::CreateInt64(env, fileInfo->bothFileTotalSize).val_);
319 return obj;
320 };
321
322 string procedureName = "downgradeDownload";
323 auto asyncWork = GetPromiseOrCallBackWork(env, funcArg, static_cast<size_t>(NARG_CNT::ONE));
324 return asyncWork == nullptr ? nullptr : asyncWork->Schedule(procedureName, cbExec, cbCompl).val_;
325 }
326
GetClassName()327 string DowngradeDownloadNapi::GetClassName()
328 {
329 return className_;
330 }
331 } // namespace OHOS::FileManagement::CloudSync