• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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