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 LOGI("OnComplete callback start for taskId: %{public}lld",
129 static_cast<long long>(msg->downloadProgress_.downloadId));
130 napi_value retVal = nullptr;
131 napi_value global = nullptr;
132 napi_get_global(env, &global);
133 status = napi_call_function(env, global, jsCallback, ARGS_ONE, &(obj.val_), &retVal);
134 if (status != napi_ok) {
135 LOGE("napi call function failed, status: %{public}d", status);
136 }
137 napi_close_handle_scope(env, scope);
138 }
139
OnDownloadProcess(const DownloadProgressObj & progress)140 void CloudDownloadCallbackImpl::OnDownloadProcess(const DownloadProgressObj &progress)
141 {
142 uv_loop_s *loop = nullptr;
143 napi_status status = napi_get_uv_event_loop(env_, &loop);
144 if (status != napi_ok) {
145 LOGE("Failed to get uv event loop");
146 return;
147 }
148
149 uv_work_t *work = new (nothrow) uv_work_t;
150 if (work == nullptr) {
151 LOGE("Failed to create uv work");
152 return;
153 }
154
155 UvChangeMsg *msg = new (std::nothrow) UvChangeMsg(shared_from_this(), progress, isBatch_);
156 if (msg == nullptr) {
157 LOGE("Failed to create uv message object");
158 delete work;
159 return;
160 }
161 work->data = reinterpret_cast<void *>(msg);
162 int ret = uv_queue_work(
163 loop, work, [](uv_work_t *work) {},
164 [](uv_work_t *work, int status) {
165 auto msg = reinterpret_cast<UvChangeMsg *>(work->data);
166 OnComplete(msg);
167 delete msg;
168 delete work;
169 });
170 if (ret != 0) {
171 LOGE("Failed to execute libuv work queue, ret: %{public}d", ret);
172 delete msg;
173 delete work;
174 }
175 }
176
DeleteReference()177 void CloudDownloadCallbackImpl::DeleteReference()
178 {
179 if (cbOnRef_ != nullptr) {
180 napi_delete_reference(env_, cbOnRef_);
181 cbOnRef_ = nullptr;
182 }
183 }
184
On(napi_env env,napi_callback_info info)185 napi_value CloudFileNapi::On(napi_env env, napi_callback_info info)
186 {
187 LOGI("On begin");
188 NFuncArg funcArg(env, info);
189 if (!funcArg.InitArgs(NARG_CNT::TWO)) {
190 LOGE("On Number of arguments unmatched");
191 NError(E_PARAMS).ThrowErr(env);
192 return nullptr;
193 }
194 auto [succProgress, progress, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String();
195 if (!succProgress || std::string(progress.get()) != "progress") {
196 LOGE("On get progress failed!");
197 NError(E_PARAMS).ThrowErr(env);
198 return nullptr;
199 }
200
201 if (!NVal(env, funcArg[NARG_POS::SECOND]).TypeIs(napi_function)) {
202 LOGE("Argument type mismatch");
203 NError(E_PARAMS).ThrowErr(env);
204 return nullptr;
205 }
206
207 if (callback_ != nullptr) {
208 LOGI("callback already exist");
209 return NVal::CreateUndefined(env).val_;
210 }
211
212 callback_ = make_shared<CloudDownloadCallbackImpl>(env, NVal(env, funcArg[(int)NARG_POS::SECOND]).val_);
213 int32_t ret = CloudSyncManager::GetInstance().RegisterDownloadFileCallback(callback_);
214 if (ret != E_OK) {
215 LOGE("RegisterDownloadFileCallback error, ret: %{public}d", ret);
216 NError(Convert2JsErrNum(ret)).ThrowErr(env);
217 return nullptr;
218 }
219
220 return NVal::CreateUndefined(env).val_;
221 }
222
Off(napi_env env,napi_callback_info info)223 napi_value CloudFileNapi::Off(napi_env env, napi_callback_info info)
224 {
225 LOGI("Off begin");
226 NFuncArg funcArg(env, info);
227 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) {
228 LOGE("Off Number of arguments unmatched");
229 NError(E_PARAMS).ThrowErr(env);
230 return nullptr;
231 }
232 auto [succProgress, progress, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String();
233 if (!succProgress || std::string(progress.get()) != "progress") {
234 LOGE("Off get progress failed!");
235 NError(E_PARAMS).ThrowErr(env);
236 return nullptr;
237 }
238
239 if (funcArg.GetArgc() == (uint)NARG_CNT::TWO && !NVal(env, funcArg[NARG_POS::SECOND]).TypeIs(napi_function)) {
240 LOGE("Argument type mismatch");
241 NError(E_PARAMS).ThrowErr(env);
242 return nullptr;
243 }
244
245 /* callback_ may be nullptr */
246 int32_t ret = CloudSyncManager::GetInstance().UnregisterDownloadFileCallback();
247 if (ret != E_OK) {
248 LOGE("UnregisterDownloadFileCallback error, ret: %{public}d", ret);
249 NError(Convert2JsErrNum(ret)).ThrowErr(env);
250 return nullptr;
251 }
252 if (callback_ != nullptr) {
253 /* napi delete reference */
254 callback_->DeleteReference();
255 callback_ = nullptr;
256 }
257 return NVal::CreateUndefined(env).val_;
258 }
259
Stop(napi_env env,napi_callback_info info)260 napi_value CloudFileNapi::Stop(napi_env env, napi_callback_info info)
261 {
262 LOGI("Stop begin");
263 NFuncArg funcArg(env, info);
264 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::THREE)) {
265 LOGE("Stop Number of arguments unmatched");
266 NError(E_PARAMS).ThrowErr(env);
267 return nullptr;
268 }
269 auto [succUri, uri, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String();
270 if (!succUri) {
271 LOGE("Stop get uri parameter failed!");
272 NError(E_PARAMS).ThrowErr(env);
273 return nullptr;
274 }
275 bool needClean = false;
276 size_t maxArgSize = static_cast<size_t>(NARG_CNT::TWO);
277 if (funcArg.GetArgc() >= NARG_CNT::TWO) {
278 NVal ui(env, NVal(env, funcArg[(int)NARG_POS::SECOND]).val_);
279 if (ui.TypeIs(napi_boolean)) {
280 bool needCleanIgnore;
281 std::tie(needCleanIgnore, needClean) = NVal(env, funcArg[NARG_POS::SECOND]).ToBool();
282 maxArgSize = static_cast<size_t>(NARG_CNT::THREE);
283 }
284 }
285 if (funcArg.GetArgc() == NARG_CNT::THREE) {
286 maxArgSize = static_cast<size_t>(NARG_CNT::THREE);
287 }
288 auto cbExec = [uri = string(uri.get()), env = env, needClean]() -> NError {
289 int32_t ret = CloudSyncManager::GetInstance().StopDownloadFile(uri, needClean);
290 if (ret != E_OK) {
291 LOGE("Stop Download failed! ret = %{public}d", ret);
292 return NError(Convert2JsErrNum(ret));
293 }
294 LOGI("Stop Download Success!");
295 return NError(ERRNO_NOERR);
296 };
297
298 auto cbCompl = [](napi_env env, NError err) -> NVal {
299 if (err) {
300 return {env, err.GetNapiErr(env)};
301 }
302 return NVal::CreateUndefined(env);
303 };
304
305 string procedureName = "cloudFileDownload";
306 auto asyncWork = GetPromiseOrCallBackWork(env, funcArg, maxArgSize);
307 return asyncWork == nullptr ? nullptr : asyncWork->Schedule(procedureName, cbExec, cbCompl).val_;
308 }
309
ToExport(std::vector<napi_property_descriptor> props)310 bool CloudFileNapi::ToExport(std::vector<napi_property_descriptor> props)
311 {
312 std::string className = GetClassName();
313 auto [succ, classValue] =
314 NClass::DefineClass(exports_.env_, className, Constructor, std::move(props));
315 if (!succ) {
316 NError(E_GETRESULT).ThrowErr(exports_.env_);
317 LOGE("Failed to define GallerySync class");
318 return false;
319 }
320
321 succ = NClass::SaveClass(exports_.env_, className, classValue);
322 if (!succ) {
323 NError(E_GETRESULT).ThrowErr(exports_.env_);
324 LOGE("Failed to save GallerySync class");
325 return false;
326 }
327
328 return exports_.AddProp(className, classValue);
329 }
330
Export()331 bool CloudFileNapi::Export()
332 {
333 vector<napi_property_descriptor> props = {
334 NVal::DeclareNapiFunction("start", CloudFileNapi::Start),
335 NVal::DeclareNapiFunction("on", CloudFileNapi::On),
336 NVal::DeclareNapiFunction("off", CloudFileNapi::Off),
337 NVal::DeclareNapiFunction("stop", CloudFileNapi::Stop),
338 };
339 return ToExport(props);
340 }
341
SetClassName(std::string classname)342 void CloudFileNapi::SetClassName(std::string classname)
343 {
344 className_ = classname;
345 }
346
GetClassName()347 string CloudFileNapi::GetClassName()
348 {
349 return className_;
350 }
351 } // namespace OHOS::FileManagement::CloudSync