1 /*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "cloud_file_download_napi.h"
17
18 #include <sys/types.h>
19
20 #include "async_work.h"
21 #include "cloud_file_napi.h"
22 #include "cloud_sync_manager.h"
23 #include "dfs_error.h"
24 #include "utils_log.h"
25 #include "uv.h"
26
27 namespace OHOS::FileManagement::CloudSync {
28 using namespace FileManagement::LibN;
29 using namespace std;
30 const int32_t ARGS_ONE = 1;
31
Constructor(napi_env env,napi_callback_info info)32 napi_value CloudFileNapi::Constructor(napi_env env, napi_callback_info info)
33 {
34 LOGI("CloudFileNapi::Constructor begin");
35 NFuncArg funcArg(env, info);
36 if (!funcArg.InitArgs(NARG_CNT::ZERO)) {
37 LOGE("Start Number of arguments unmatched");
38 NError(E_PARAMS).ThrowErr(env);
39 return nullptr;
40 }
41
42 LOGI("CloudFileNapi::Constructor end");
43 return funcArg.GetThisVar();
44 }
45
Start(napi_env env,napi_callback_info info)46 napi_value CloudFileNapi::Start(napi_env env, napi_callback_info info)
47 {
48 LOGI("Start begin");
49 NFuncArg funcArg(env, info);
50 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) {
51 LOGE("Start Number of arguments unmatched");
52 NError(E_PARAMS).ThrowErr(env);
53 return nullptr;
54 }
55 auto [succUri, uri, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String();
56 if (!succUri) {
57 LOGE("Start get uri parameter failed!");
58 NError(E_PARAMS).ThrowErr(env);
59 return nullptr;
60 }
61
62 auto cbExec = [uri = string(uri.get()), env = env]() -> NError {
63 int32_t ret = CloudSyncManager::GetInstance().StartDownloadFile(uri);
64 if (ret != E_OK) {
65 LOGE("Start Download failed! ret = %{public}d", ret);
66 return NError(Convert2JsErrNum(ret));
67 }
68 LOGI("Start Download Success!");
69 return NError(ERRNO_NOERR);
70 };
71
72 auto cbCompl = [](napi_env env, NError err) -> NVal {
73 if (err) {
74 return {env, err.GetNapiErr(env)};
75 }
76 return NVal::CreateUndefined(env);
77 };
78
79 string procedureName = "cloudFileDownload";
80 auto asyncWork = GetPromiseOrCallBackWork(env, funcArg, static_cast<size_t>(NARG_CNT::TWO));
81 return asyncWork == nullptr ? nullptr : asyncWork->Schedule(procedureName, cbExec, cbCompl).val_;
82 }
83
CloudDownloadCallbackImpl(napi_env env,napi_value fun,bool isBatch)84 CloudDownloadCallbackImpl::CloudDownloadCallbackImpl(napi_env env, napi_value fun, bool isBatch) : env_(env)
85 {
86 if (fun != nullptr) {
87 napi_create_reference(env_, fun, 1, &cbOnRef_);
88 }
89 isBatch_ = isBatch;
90 }
91
OnComplete(UvChangeMsg * msg)92 void CloudDownloadCallbackImpl::OnComplete(UvChangeMsg *msg)
93 {
94 auto downloadcCallback = msg->CloudDownloadCallback_.lock();
95 if (downloadcCallback == nullptr || downloadcCallback->cbOnRef_ == nullptr) {
96 LOGE("downloadcCallback->cbOnRef_ is nullptr");
97 return;
98 }
99 auto env = downloadcCallback->env_;
100 auto ref = downloadcCallback->cbOnRef_;
101 napi_handle_scope scope = nullptr;
102 napi_open_handle_scope(env, &scope);
103 napi_value jsCallback = nullptr;
104 napi_status status = napi_get_reference_value(env, ref, &jsCallback);
105 if (status != napi_ok) {
106 LOGE("Create reference failed, status: %{public}d", status);
107 napi_close_handle_scope(env, scope);
108 return;
109 }
110 NVal obj = NVal::CreateObject(env);
111 if (!msg->isBatch_) {
112 obj.AddProp("state", NVal::CreateInt32(env, (int32_t)msg->downloadProgress_.state).val_);
113 obj.AddProp("processed", NVal::CreateInt64(env, msg->downloadProgress_.downloadedSize).val_);
114 obj.AddProp("size", NVal::CreateInt64(env, msg->downloadProgress_.totalSize).val_);
115 obj.AddProp("uri", NVal::CreateUTF8String(env, msg->downloadProgress_.path).val_);
116 } else {
117 LOGI("Batch download callback items");
118 obj.AddProp("state", NVal::CreateInt32(env, (int32_t)msg->downloadProgress_.batchState).val_);
119 obj.AddProp("downloadedSize", NVal::CreateInt64(env, msg->downloadProgress_.batchDownloadSize).val_);
120 obj.AddProp("totalSize", NVal::CreateInt64(env, msg->downloadProgress_.batchTotalSize).val_);
121 obj.AddProp("successfulNum", NVal::CreateInt64(env, msg->downloadProgress_.batchSuccNum).val_);
122 obj.AddProp("failedNum", NVal::CreateInt64(env, msg->downloadProgress_.batchFailNum).val_);
123 obj.AddProp("totalNum", NVal::CreateInt64(env, msg->downloadProgress_.batchTotalNum).val_);
124 }
125 obj.AddProp("taskId", NVal::CreateInt64(env, msg->downloadProgress_.downloadId).val_);
126 obj.AddProp("error", NVal::CreateInt32(env, (int32_t)msg->downloadProgress_.downloadErrorType).val_);
127
128 napi_value retVal = nullptr;
129 napi_value global = nullptr;
130 napi_get_global(env, &global);
131 status = napi_call_function(env, global, jsCallback, ARGS_ONE, &(obj.val_), &retVal);
132 if (status != napi_ok) {
133 LOGE("napi call function failed, status: %{public}d", status);
134 }
135 napi_close_handle_scope(env, scope);
136 }
137
OnDownloadProcess(const DownloadProgressObj & progress)138 void CloudDownloadCallbackImpl::OnDownloadProcess(const DownloadProgressObj &progress)
139 {
140 uv_loop_s *loop = nullptr;
141 napi_get_uv_event_loop(env_, &loop);
142 if (loop == nullptr) {
143 return;
144 }
145
146 uv_work_t *work = new (nothrow) uv_work_t;
147 if (work == nullptr) {
148 LOGE("Failed to create uv work");
149 return;
150 }
151
152 UvChangeMsg *msg = new (std::nothrow) UvChangeMsg(shared_from_this(), progress, isBatch_);
153 if (msg == nullptr) {
154 delete work;
155 return;
156 }
157 work->data = reinterpret_cast<void *>(msg);
158 int ret = uv_queue_work(
159 loop, work, [](uv_work_t *work) {},
160 [](uv_work_t *work, int status) {
161 auto msg = reinterpret_cast<UvChangeMsg *>(work->data);
162 OnComplete(msg);
163 delete msg;
164 delete work;
165 });
166 if (ret != 0) {
167 LOGE("Failed to execute libuv work queue, ret: %{public}d", ret);
168 delete msg;
169 delete work;
170 }
171 }
172
DeleteReference()173 void CloudDownloadCallbackImpl::DeleteReference()
174 {
175 if (cbOnRef_ != nullptr) {
176 napi_delete_reference(env_, cbOnRef_);
177 cbOnRef_ = nullptr;
178 }
179 }
180
On(napi_env env,napi_callback_info info)181 napi_value CloudFileNapi::On(napi_env env, napi_callback_info info)
182 {
183 LOGI("On begin");
184 NFuncArg funcArg(env, info);
185 if (!funcArg.InitArgs(NARG_CNT::TWO)) {
186 LOGE("On Number of arguments unmatched");
187 NError(E_PARAMS).ThrowErr(env);
188 return nullptr;
189 }
190 auto [succProgress, progress, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String();
191 if (!succProgress || std::string(progress.get()) != "progress") {
192 LOGE("On get progress failed!");
193 NError(E_PARAMS).ThrowErr(env);
194 return nullptr;
195 }
196
197 if (!NVal(env, funcArg[NARG_POS::SECOND]).TypeIs(napi_function)) {
198 LOGE("Argument type mismatch");
199 NError(E_PARAMS).ThrowErr(env);
200 return nullptr;
201 }
202
203 if (callback_ != nullptr) {
204 LOGI("callback already exist");
205 return NVal::CreateUndefined(env).val_;
206 }
207
208 callback_ = make_shared<CloudDownloadCallbackImpl>(env, NVal(env, funcArg[(int)NARG_POS::SECOND]).val_);
209 int32_t ret = CloudSyncManager::GetInstance().RegisterDownloadFileCallback(callback_);
210 if (ret != E_OK) {
211 LOGE("RegisterDownloadFileCallback error, ret: %{public}d", ret);
212 NError(Convert2JsErrNum(ret)).ThrowErr(env);
213 return nullptr;
214 }
215
216 return NVal::CreateUndefined(env).val_;
217 }
218
Off(napi_env env,napi_callback_info info)219 napi_value CloudFileNapi::Off(napi_env env, napi_callback_info info)
220 {
221 LOGI("Off begin");
222 NFuncArg funcArg(env, info);
223 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) {
224 LOGE("Off Number of arguments unmatched");
225 NError(E_PARAMS).ThrowErr(env);
226 return nullptr;
227 }
228 auto [succProgress, progress, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String();
229 if (!succProgress || std::string(progress.get()) != "progress") {
230 LOGE("Off get progress failed!");
231 NError(E_PARAMS).ThrowErr(env);
232 return nullptr;
233 }
234
235 if (funcArg.GetArgc() == (uint)NARG_CNT::TWO && !NVal(env, funcArg[NARG_POS::SECOND]).TypeIs(napi_function)) {
236 LOGE("Argument type mismatch");
237 NError(E_PARAMS).ThrowErr(env);
238 return nullptr;
239 }
240
241 /* callback_ may be nullptr */
242 int32_t ret = CloudSyncManager::GetInstance().UnregisterDownloadFileCallback();
243 if (ret != E_OK) {
244 LOGE("UnregisterDownloadFileCallback error, ret: %{public}d", ret);
245 NError(Convert2JsErrNum(ret)).ThrowErr(env);
246 return nullptr;
247 }
248 if (callback_ != nullptr) {
249 /* napi delete reference */
250 callback_->DeleteReference();
251 callback_ = nullptr;
252 }
253 return NVal::CreateUndefined(env).val_;
254 }
255
Stop(napi_env env,napi_callback_info info)256 napi_value CloudFileNapi::Stop(napi_env env, napi_callback_info info)
257 {
258 LOGI("Stop begin");
259 NFuncArg funcArg(env, info);
260 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::THREE)) {
261 LOGE("Stop Number of arguments unmatched");
262 NError(E_PARAMS).ThrowErr(env);
263 return nullptr;
264 }
265 auto [succUri, uri, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String();
266 if (!succUri) {
267 LOGE("Stop get uri parameter failed!");
268 NError(E_PARAMS).ThrowErr(env);
269 return nullptr;
270 }
271 bool needClean = false;
272 bool needCleanIgnore;
273 size_t maxArgSize = static_cast<size_t>(NARG_CNT::TWO);
274 if (funcArg.GetArgc() >= NARG_CNT::TWO) {
275 NVal ui(env, NVal(env, funcArg[(int)NARG_POS::SECOND]).val_);
276 if (ui.TypeIs(napi_boolean)) {
277 std::tie(needCleanIgnore, needClean) = NVal(env, funcArg[NARG_POS::SECOND]).ToBool();
278 maxArgSize = static_cast<size_t>(NARG_CNT::THREE);
279 }
280 }
281 if (funcArg.GetArgc() == NARG_CNT::THREE) {
282 maxArgSize = static_cast<size_t>(NARG_CNT::THREE);
283 }
284 auto cbExec = [uri = string(uri.get()), env = env, needClean = needClean]() -> NError {
285 int32_t ret = CloudSyncManager::GetInstance().StopDownloadFile(uri, needClean);
286 if (ret != E_OK) {
287 LOGE("Stop Download failed! ret = %{public}d", ret);
288 return NError(Convert2JsErrNum(ret));
289 }
290 LOGI("Stop Download Success!");
291 return NError(ERRNO_NOERR);
292 };
293
294 auto cbCompl = [](napi_env env, NError err) -> NVal {
295 if (err) {
296 return {env, err.GetNapiErr(env)};
297 }
298 return NVal::CreateUndefined(env);
299 };
300
301 string procedureName = "cloudFileDownload";
302 auto asyncWork = GetPromiseOrCallBackWork(env, funcArg, maxArgSize);
303 return asyncWork == nullptr ? nullptr : asyncWork->Schedule(procedureName, cbExec, cbCompl).val_;
304 }
305
ToExport(std::vector<napi_property_descriptor> props)306 bool CloudFileNapi::ToExport(std::vector<napi_property_descriptor> props)
307 {
308 std::string className = GetClassName();
309 auto [succ, classValue] =
310 NClass::DefineClass(exports_.env_, className, Constructor, std::move(props));
311 if (!succ) {
312 NError(E_GETRESULT).ThrowErr(exports_.env_);
313 LOGE("Failed to define GallerySync class");
314 return false;
315 }
316
317 succ = NClass::SaveClass(exports_.env_, className, classValue);
318 if (!succ) {
319 NError(E_GETRESULT).ThrowErr(exports_.env_);
320 LOGE("Failed to save GallerySync class");
321 return false;
322 }
323
324 return exports_.AddProp(className, classValue);
325 }
326
Export()327 bool CloudFileNapi::Export()
328 {
329 vector<napi_property_descriptor> props = {
330 NVal::DeclareNapiFunction("start", CloudFileNapi::Start),
331 NVal::DeclareNapiFunction("on", CloudFileNapi::On),
332 NVal::DeclareNapiFunction("off", CloudFileNapi::Off),
333 NVal::DeclareNapiFunction("stop", CloudFileNapi::Stop),
334 };
335 return ToExport(props);
336 }
337
SetClassName(std::string classname)338 void CloudFileNapi::SetClassName(std::string classname)
339 {
340 className_ = classname;
341 }
342
GetClassName()343 string CloudFileNapi::GetClassName()
344 {
345 return className_;
346 }
347 } // namespace OHOS::FileManagement::CloudSync