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_cache_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 "utils_log.h"
25 #include "uv.h"
26
27 namespace OHOS::FileManagement::CloudSync {
28 using namespace FileManagement::LibN;
29 using namespace std;
30
AddRegisterInfo(shared_ptr<RegisterInfoArg> info)31 bool RegisterManager::AddRegisterInfo(shared_ptr<RegisterInfoArg> info)
32 {
33 unique_lock<mutex> mutex_;
34 bool hasEvent = false;
35 for (auto &iter : registerInfo_) {
36 if (iter->eventType == info->eventType) {
37 hasEvent = true;
38 break;
39 }
40 }
41 if (!hasEvent) {
42 registerInfo_.insert(info);
43 return true;
44 }
45 return false;
46 }
47
RemoveRegisterInfo(const string &eventType)48 bool RegisterManager::RemoveRegisterInfo(const string &eventType)
49 {
50 bool isFound = false;
51 unique_lock<mutex> mutex_;
52 for (auto iter = registerInfo_.begin(); iter != registerInfo_.end();) {
53 if ((*iter)->eventType == eventType) {
54 isFound = true;
55 iter = registerInfo_.erase(iter);
56 } else {
57 iter++;
58 }
59 }
60 return isFound;
61 }
62
Constructor(napi_env env, napi_callback_info info)63 napi_value CloudFileCacheNapi::Constructor(napi_env env, napi_callback_info info)
64 {
65 NFuncArg funcArg(env, info);
66 if (!funcArg.InitArgs(NARG_CNT::ZERO)) {
67 LOGE("Start Number of arguments unmatched");
68 NError(E_PARAMS).ThrowErr(env);
69 return nullptr;
70 }
71
72 auto fileCacheEntity = make_unique<FileCacheEntity>();
73 if (!NClass::SetEntityFor<FileCacheEntity>(env, funcArg.GetThisVar(), move(fileCacheEntity))) {
74 LOGE("Failed to set file cache entity.");
75 NError(E_UNKNOWN_ERR).ThrowErr(env);
76 return nullptr;
77 }
78 return funcArg.GetThisVar();
79 }
80
ToExport(std::vector<napi_property_descriptor> props)81 bool CloudFileCacheNapi::ToExport(std::vector<napi_property_descriptor> props)
82 {
83 std::string className = GetClassName();
84 auto [succ, classValue] = NClass::DefineClass(exports_.env_, className, Constructor, std::move(props));
85 if (!succ) {
86 NError(E_UNKNOWN_ERR).ThrowErr(exports_.env_);
87 LOGE("Failed to define GallerySync class");
88 return false;
89 }
90
91 succ = NClass::SaveClass(exports_.env_, className, classValue);
92 if (!succ) {
93 NError(E_UNKNOWN_ERR).ThrowErr(exports_.env_);
94 LOGE("Failed to save GallerySync class");
95 return false;
96 }
97
98 return exports_.AddProp(className, classValue);
99 }
100
CleanCloudFileCache(napi_env env, napi_callback_info info)101 napi_value CloudFileCacheNapi::CleanCloudFileCache(napi_env env, napi_callback_info info)
102 {
103 LOGI("CleanCache start");
104 NFuncArg funcArg(env, info);
105
106 if (!funcArg.InitArgs(NARG_CNT::ONE)) {
107 NError(E_PARAMS).ThrowErr(env);
108 return nullptr;
109 }
110
111 auto [succ, uri, ignore] = NVal(env, funcArg[(int)NARG_POS::FIRST]).ToUTF8String();
112 if (!succ) {
113 LOGE("Get uri error");
114 NError(EINVAL).ThrowErr(env);
115 return nullptr;
116 }
117
118 int32_t ret = CloudSyncManager::GetInstance().CleanCache(uri.get());
119 if (ret != E_OK) {
120 NError(Convert2JsErrNum(ret)).ThrowErr(env);
121 return nullptr;
122 }
123 return NVal::CreateUndefined(env).val_;
124 }
125
Export()126 bool CloudFileCacheNapi::Export()
127 {
128 std::vector<napi_property_descriptor> props = {
129 NVal::DeclareNapiFunction("on", CloudFileCacheNapi::On),
130 NVal::DeclareNapiFunction("off", CloudFileCacheNapi::Off),
131 NVal::DeclareNapiFunction("start", CloudFileCacheNapi::StartFileCache),
132 NVal::DeclareNapiFunction("startBatch", CloudFileCacheNapi::StartBatchFileCache),
133 NVal::DeclareNapiFunction("stop", CloudFileCacheNapi::StopFileCache),
134 NVal::DeclareNapiFunction("stopBatch", CloudFileCacheNapi::StopBatchFileCache),
135 NVal::DeclareNapiFunction("cleanCache", CloudFileCacheNapi::CleanCloudFileCache),
136 };
137
138 return ToExport(props);
139 }
140
StartFileCache(napi_env env, napi_callback_info info)141 napi_value CloudFileCacheNapi::StartFileCache(napi_env env, napi_callback_info info)
142 {
143 NFuncArg funcArg(env, info);
144 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) {
145 LOGE("Start Number of arguments unmatched");
146 NError(E_PARAMS).ThrowErr(env);
147 return nullptr;
148 }
149 auto [succUri, uri, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String();
150 if (!succUri) {
151 LOGE("Start get uri parameter failed!");
152 NError(E_PARAMS).ThrowErr(env);
153 return nullptr;
154 }
155
156 auto cbExec = [uri = string(uri.get())]() -> NError {
157 int32_t ret = CloudSyncManager::GetInstance().StartFileCache(uri);
158 if (ret != E_OK) {
159 LOGE("Start Download failed! ret = %{public}d", ret);
160 return NError(Convert2JsErrNum(ret));
161 }
162 LOGI("Start Download Success!");
163 return NError(ERRNO_NOERR);
164 };
165
166 auto cbCompl = [](napi_env env, NError err) -> NVal {
167 if (err) {
168 return {env, err.GetNapiErr(env)};
169 }
170 return NVal::CreateUndefined(env);
171 };
172
173 string procedureName = "cloudFileCache";
174 auto asyncWork = GetPromiseOrCallBackWork(env, funcArg, static_cast<size_t>(NARG_CNT::TWO));
175 return asyncWork == nullptr ? nullptr : asyncWork->Schedule(procedureName, cbExec, cbCompl).val_;
176 }
177
StopFileCache(napi_env env, napi_callback_info info)178 napi_value CloudFileCacheNapi::StopFileCache(napi_env env, napi_callback_info info)
179 {
180 NFuncArg funcArg(env, info);
181 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::THREE)) {
182 LOGE("Stop Number of arguments unmatched");
183 NError(E_PARAMS).ThrowErr(env);
184 return nullptr;
185 }
186 auto [succ, uri, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String();
187 if (!succ) {
188 LOGE("Stop get uri parameter failed!");
189 NError(E_PARAMS).ThrowErr(env);
190 return nullptr;
191 }
192 bool needClean = false;
193 size_t maxArgSize = static_cast<size_t>(NARG_CNT::TWO);
194 if (funcArg.GetArgc() >= NARG_CNT::TWO) {
195 NVal option(env, funcArg[NARG_POS::SECOND]);
196 if (!option.TypeIs(napi_function)) {
197 tie(succ, needClean) = option.ToBool();
198 if (!succ) {
199 LOGE("Failed to get clean flag!");
200 NError(E_PARAMS).ThrowErr(env);
201 return nullptr;
202 }
203 maxArgSize = static_cast<size_t>(NARG_CNT::THREE);
204 }
205 }
206 auto cbExec = [uri = string(uri.get()), env = env, needClean]() -> NError {
207 int32_t ret = CloudSyncManager::GetInstance().StopDownloadFile(uri, needClean);
208 if (ret != E_OK) {
209 LOGE("Stop Download failed! ret = %{public}d", ret);
210 return NError(Convert2JsErrNum(ret));
211 }
212 return NError(ERRNO_NOERR);
213 };
214
215 auto cbCompl = [](napi_env env, NError err) -> NVal {
216 if (err) {
217 return {env, err.GetNapiErr(env)};
218 }
219 return NVal::CreateUndefined(env);
220 };
221
222 string procedureName = "cloudFileCache";
223 auto asyncWork = GetPromiseOrCallBackWork(env, funcArg, maxArgSize);
224 return asyncWork == nullptr ? nullptr : asyncWork->Schedule(procedureName, cbExec, cbCompl).val_;
225 }
226
227 struct FileCacheArg {
228 vector<string> uriList;
229 int64_t downloadId;
230 };
231
StartBatchFileCache(napi_env env, napi_callback_info info)232 napi_value CloudFileCacheNapi::StartBatchFileCache(napi_env env, napi_callback_info info)
233 {
234 NFuncArg funcArg(env, info);
235 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) {
236 LOGE("Start Number of arguments unmatched");
237 NError(E_PARAMS).ThrowErr(env);
238 return nullptr;
239 }
240
241 auto fileUris = std::make_shared<FileCacheArg>();
242 auto [resGetUris, uriArray, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToStringArray();
243 if (!resGetUris) {
244 LOGE("Start get uri array parameter failed!");
245 NError(E_PARAMS).ThrowErr(env);
246 return nullptr;
247 }
248
249 fileUris->uriList = uriArray;
250 auto cbExec = [fileUris]() -> NError {
251 int32_t ret = CloudSyncManager::GetInstance().StartFileCache(fileUris->uriList, fileUris->downloadId);
252 if (ret != E_OK) {
253 LOGE("Batch start file cache failed! ret = %{public}d", ret);
254 ret = (ret == E_CLOUD_SDK) ? E_UNKNOWN_ERR : ret;
255 return NError(Convert2JsErrNum(ret));
256 }
257 return NError(ERRNO_NOERR);
258 };
259
260 auto cbCompl = [fileUris](napi_env env, NError err) -> NVal {
261 if (err) {
262 return {env, err.GetNapiErr(env)};
263 }
264 return NVal::CreateInt64(env, fileUris->downloadId);
265 };
266
267 string procedureName = "cloudFileCache";
268 auto asyncWork = GetPromiseOrCallBackWork(env, funcArg, static_cast<size_t>(NARG_CNT::TWO));
269 return asyncWork == nullptr ? nullptr : asyncWork->Schedule(procedureName, cbExec, cbCompl).val_;
270 }
271
StopBatchFileCache(napi_env env, napi_callback_info info)272 napi_value CloudFileCacheNapi::StopBatchFileCache(napi_env env, napi_callback_info info)
273 {
274 NFuncArg funcArg(env, info);
275 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::THREE)) {
276 LOGE("Start Number of arguments unmatched");
277 NError(E_PARAMS).ThrowErr(env);
278 return nullptr;
279 }
280
281 auto [succ, downloadId] = NVal(env, funcArg[NARG_POS::FIRST]).ToInt64();
282 if (!succ || downloadId <= 0) {
283 LOGE("Start get download ID parameter failed!");
284 NError(E_PARAMS).ThrowErr(env);
285 return nullptr;
286 }
287
288 bool needClean = false;
289 size_t maxArgSize = static_cast<size_t>(NARG_CNT::TWO);
290 if (funcArg.GetArgc() >= NARG_CNT::TWO) {
291 NVal option(env, funcArg[NARG_POS::SECOND]);
292 if (!option.TypeIs(napi_function)) {
293 tie(succ, needClean) = option.ToBool();
294 if (!succ) {
295 LOGE("Failed to get clean flag!");
296 NError(E_PARAMS).ThrowErr(env);
297 return nullptr;
298 }
299 maxArgSize = static_cast<size_t>(NARG_CNT::THREE);
300 }
301 }
302
303 auto cbExec = [downloadId = downloadId, needClean]() -> NError {
304 int32_t ret = CloudSyncManager::GetInstance().StopFileCache(downloadId, needClean);
305 if (ret != E_OK) {
306 LOGE("Batch stop file cache failed! ret = %{public}d", ret);
307 ret = (ret == E_CLOUD_SDK) ? E_UNKNOWN_ERR : ret;
308 return NError(Convert2JsErrNum(ret));
309 }
310 return NError(ERRNO_NOERR);
311 };
312
313 auto cbCompl = [](napi_env env, NError err) -> NVal {
314 if (err) {
315 return {env, err.GetNapiErr(env)};
316 }
317 return NVal::CreateUndefined(env);
318 };
319
320 string procedureName = "cloudFileCache";
321 auto asyncWork = GetPromiseOrCallBackWork(env, funcArg, maxArgSize);
322 return asyncWork == nullptr ? nullptr : asyncWork->Schedule(procedureName, cbExec, cbCompl).val_;
323 }
324
On(napi_env env, napi_callback_info info)325 napi_value CloudFileCacheNapi::On(napi_env env, napi_callback_info info)
326 {
327 NFuncArg funcArg(env, info);
328 if (!funcArg.InitArgs(NARG_CNT::TWO)) {
329 LOGE("Batch-On Number of arguments unmatched");
330 NError(E_PARAMS).ThrowErr(env);
331 return nullptr;
332 }
333 auto [succProgress, progress, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String();
334 string eventType(progress.get());
335 if (!succProgress || (eventType != PROGRESS && eventType != MULTI_PROGRESS)) {
336 LOGE("Batch-On get progress failed!");
337 NError(E_PARAMS).ThrowErr(env);
338 return nullptr;
339 }
340
341 if (!NVal(env, funcArg[NARG_POS::SECOND]).TypeIs(napi_function)) {
342 LOGE("Batch-On argument type mismatch");
343 NError(E_PARAMS).ThrowErr(env);
344 return nullptr;
345 }
346
347 auto fileCacheEntity = NClass::GetEntityOf<FileCacheEntity>(env, funcArg.GetThisVar());
348 if (!fileCacheEntity) {
349 LOGE("Failed to get file cache entity.");
350 NError(E_PARAMS).ThrowErr(env);
351 return nullptr;
352 }
353
354 auto arg = make_shared<RegisterInfoArg>();
355 arg->eventType = eventType;
356 if (eventType == PROGRESS) {
357 arg->callback = make_shared<CloudDownloadCallbackImpl>(env, NVal(env, funcArg[(int)NARG_POS::SECOND]).val_);
358 } else {
359 arg->callback =
360 make_shared<CloudDownloadCallbackImpl>(env, NVal(env, funcArg[(int)NARG_POS::SECOND]).val_, true);
361 }
362
363 if (!fileCacheEntity->registerMgr.AddRegisterInfo(arg)) {
364 LOGE("Batch-On register callback fail, callback already exist");
365 NError(E_PARAMS).ThrowErr(env);
366 return nullptr;
367 }
368
369 int32_t ret = CloudSyncManager::GetInstance().RegisterDownloadFileCallback(arg->callback);
370 if (ret != E_OK) {
371 LOGE("Failed to register callback, error: %{public}d", ret);
372 (void)fileCacheEntity->registerMgr.RemoveRegisterInfo(eventType);
373 NError(Convert2JsErrNum(ret)).ThrowErr(env);
374 return nullptr;
375 }
376
377 return NVal::CreateUndefined(env).val_;
378 }
379
Off(napi_env env, napi_callback_info info)380 napi_value CloudFileCacheNapi::Off(napi_env env, napi_callback_info info)
381 {
382 NFuncArg funcArg(env, info);
383 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) {
384 LOGE("Off Number of arguments unmatched");
385 NError(E_PARAMS).ThrowErr(env);
386 return nullptr;
387 }
388 auto [succProgress, progress, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String();
389 string eventType(progress.get());
390 if (!succProgress || (eventType != PROGRESS && eventType != MULTI_PROGRESS)) {
391 LOGE("Batch-Off get progress failed!");
392 NError(E_PARAMS).ThrowErr(env);
393 return nullptr;
394 }
395
396 if (funcArg.GetArgc() == (uint)NARG_CNT::TWO && !NVal(env, funcArg[NARG_POS::SECOND]).TypeIs(napi_function)) {
397 LOGE("Batch-Off argument type mismatch");
398 NError(E_PARAMS).ThrowErr(env);
399 return nullptr;
400 }
401
402 auto fileCacheEntity = NClass::GetEntityOf<FileCacheEntity>(env, funcArg.GetThisVar());
403 if (!fileCacheEntity) {
404 LOGE("Failed to get file cache entity.");
405 NError(E_PARAMS).ThrowErr(env);
406 return nullptr;
407 }
408
409 if (!fileCacheEntity->registerMgr.RemoveRegisterInfo(eventType)) {
410 LOGE("Batch-Off no callback is registered for this event type: %{public}s.", eventType.c_str());
411 NError(E_PARAMS).ThrowErr(env);
412 return nullptr;
413 }
414
415 int32_t ret = CloudSyncManager::GetInstance().UnregisterDownloadFileCallback();
416 if (ret != E_OK) {
417 LOGE("Failed to unregister callback, error: %{public}d", ret);
418 NError(Convert2JsErrNum(ret)).ThrowErr(env);
419 return nullptr;
420 }
421
422 return NVal::CreateUndefined(env).val_;
423 }
424
GetClassName()425 string CloudFileCacheNapi::GetClassName()
426 {
427 return className_;
428 }
429 } // namespace OHOS::FileManagement::CloudSync