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