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 #define MLOG_TAG "MediaLibraryBackupNapi"
17 
18 #include <napi_base_context.h>
19 #include "medialibrary_backup_napi.h"
20 #include "application_context.h"
21 #include "backup_restore_service.h"
22 #include "js_native_api.h"
23 #include "medialibrary_client_errno.h"
24 #include "medialibrary_errno.h"
25 #include "medialibrary_napi_log.h"
26 #include "medialibrary_napi_utils.h"
27 
28 namespace OHOS {
29 namespace Media {
30 
31 using RestoreBlock = struct {
32     napi_env env;
33     std::weak_ptr<OHOS::AbilityRuntime::Context> context;
34     RestoreInfo restoreInfo;
35     napi_deferred nativeDeferred;
36 };
37 
38 using RestoreExBlock = struct {
39     napi_env env;
40     std::weak_ptr<OHOS::AbilityRuntime::Context> context;
41     RestoreInfo restoreInfo;
42     std::string restoreExInfo;
43     napi_deferred nativeDeferred;
44 };
45 
46 using BackupBlock = struct {
47     napi_env env;
48     int32_t sceneCode;
49     std::string galleryAppName;
50     std::string mediaAppName;
51     napi_deferred nativeDeferred;
52 };
53 
Init(napi_env env,napi_value exports)54 napi_value MediaLibraryBackupNapi::Init(napi_env env, napi_value exports)
55 {
56     NAPI_INFO_LOG("Init, MediaLibraryBackupNapi has been used.");
57     napi_property_descriptor media_library_properties[] = {
58         DECLARE_NAPI_FUNCTION("startRestore", JSStartRestore),
59         DECLARE_NAPI_FUNCTION("startRestoreEx", JSStartRestoreEx),
60         DECLARE_NAPI_FUNCTION("getBackupInfo", JSGetBackupInfo),
61         DECLARE_NAPI_FUNCTION("getProgressInfo", JSGetProgressInfo),
62         DECLARE_NAPI_FUNCTION("startBackup", JSStartBackup),
63     };
64 
65     NAPI_CALL(env, napi_define_properties(env, exports, sizeof(media_library_properties) /
66         sizeof(media_library_properties[0]), media_library_properties));
67     return exports;
68 }
69 
GetIntFromParams(napi_env env,const napi_value args[],size_t index)70 static int32_t GetIntFromParams(napi_env env, const napi_value args[], size_t index)
71 {
72     int32_t result = -1;
73     napi_valuetype valueType = napi_undefined;
74     if (napi_typeof(env, args[index], &valueType) != napi_ok || valueType != napi_number) {
75         NapiError::ThrowError(env, JS_ERR_PARAMETER_INVALID);
76         return result;
77     }
78     napi_get_value_int32(env, args[index], &result);
79     return result;
80 }
81 
GetStringFromParams(napi_env env,const napi_value args[],size_t index)82 static std::string GetStringFromParams(napi_env env, const napi_value args[], size_t index)
83 {
84     napi_valuetype valueType = napi_undefined;
85     if (napi_typeof(env, args[index], &valueType) != napi_ok || valueType != napi_string) {
86         NapiError::ThrowError(env, JS_ERR_PARAMETER_INVALID);
87         return "";
88     }
89 
90     size_t resultLength;
91     napi_get_value_string_utf8(env, args[index], nullptr, 0, &resultLength);
92     std::string result(resultLength, '\0');
93     napi_get_value_string_utf8(env, args[index], &result[0], resultLength + 1, &resultLength);
94     return result;
95 }
96 
CheckPermission(void)97 static int32_t CheckPermission(void)
98 {
99     auto context = AbilityRuntime::Context::GetApplicationContext();
100     if (context == nullptr) {
101         NAPI_ERR_LOG("Failed to get context");
102         return E_FAIL;
103     }
104     std::string bundleName = context->GetBundleName();
105     if (bundleName.compare(BUNDLE_NAME) != 0) {
106         NAPI_ERR_LOG("bundleName is invalid, %{public}s", bundleName.c_str());
107         return E_FAIL;
108     }
109     return E_OK;
110 }
111 
UvQueueWork(uv_loop_s * loop,uv_work_t * work)112 void MediaLibraryBackupNapi::UvQueueWork(uv_loop_s *loop, uv_work_t *work)
113 {
114     uv_queue_work(loop, work, [](uv_work_t *work) {
115         RestoreBlock *block = reinterpret_cast<RestoreBlock *> (work->data);
116         if (block == nullptr) {
117             delete work;
118             return;
119         }
120         BackupRestoreService::GetInstance().StartRestore(block->context.lock(), block->restoreInfo);
121     }, [](uv_work_t *work, int _status) {
122         RestoreBlock *block = reinterpret_cast<RestoreBlock *> (work->data);
123         napi_handle_scope scope = nullptr;
124         napi_open_handle_scope(block->env, &scope);
125         if (scope == nullptr) {
126             delete work;
127             return;
128         }
129         napi_value resultCode = nullptr;
130         napi_create_int32(block->env, 1, &resultCode);
131         napi_resolve_deferred(block->env, block->nativeDeferred, resultCode);
132         napi_close_handle_scope(block->env, scope);
133         delete block;
134         delete work;
135     });
136 }
137 
ParseContext(const napi_env & env,const napi_value & input,std::shared_ptr<OHOS::AbilityRuntime::Context> & context)138 bool ParseContext(const napi_env &env, const napi_value &input,
139     std::shared_ptr<OHOS::AbilityRuntime::Context> &context)
140 {
141     bool isStageMode = false;
142     napi_status status = OHOS::AbilityRuntime::IsStageContext(env, input, isStageMode);
143     if (status != napi_ok) {
144         NAPI_ERR_LOG("parse context status error, status:%{public}d", status);
145         return false;
146     }
147     if (!isStageMode) {
148         NAPI_ERR_LOG("parse context failed, not stage context");
149         return false;
150     }
151     context = OHOS::AbilityRuntime::GetStageModeContext(env, input);
152     if (context == nullptr) {
153         NAPI_ERR_LOG("parse context failed, context is null");
154         return false;
155     }
156 
157     return true;
158 }
159 
JSStartRestore(napi_env env,napi_callback_info info)160 napi_value MediaLibraryBackupNapi::JSStartRestore(napi_env env, napi_callback_info info)
161 {
162     napi_value result = nullptr;
163     if (CheckPermission() != E_OK) {
164         return result;
165     }
166 
167     size_t argc = ARGS_FIVE;
168     napi_value argv[ARGS_FIVE] = {0};
169     napi_value thisVar = nullptr;
170 
171     GET_JS_ARGS(env, info, argc, argv, thisVar);
172     NAPI_ASSERT(env, (argc == ARGS_FIVE), "requires 5 parameters");
173     napi_get_undefined(env, &result);
174 
175     // get ability context
176     std::shared_ptr<OHOS::AbilityRuntime::Context> context;
177     if (!ParseContext(env, argv[PARAM0], context)) {
178         return result;
179     }
180     RestoreInfo restoreInfo;
181     restoreInfo.sceneCode = GetIntFromParams(env, argv, PARAM1);
182     restoreInfo.galleryAppName = GetStringFromParams(env, argv, PARAM2);
183     restoreInfo.mediaAppName = GetStringFromParams(env, argv, PARAM3);
184     restoreInfo.backupDir = GetStringFromParams(env, argv, PARAM4);
185     NAPI_INFO_LOG("StartRestore, sceneCode = %{public}d", restoreInfo.sceneCode);
186 
187     if (restoreInfo.sceneCode < 0) {
188         NAPI_ERR_LOG("Parameters error, sceneCode = %{public}d", restoreInfo.sceneCode);
189         return result;
190     }
191     uv_loop_s *loop = nullptr;
192     napi_get_uv_event_loop(env, &loop);
193     uv_work_t *work = new (std::nothrow) uv_work_t;
194     if (work == nullptr) {
195         NAPI_ERR_LOG("Failed to new uv_work");
196         return result;
197     }
198     napi_deferred nativeDeferred = nullptr;
199     napi_create_promise(env, &nativeDeferred, &result);
200     RestoreBlock *block = new (std::nothrow) RestoreBlock { env, context, restoreInfo, nativeDeferred };
201     if (block == nullptr) {
202         NAPI_ERR_LOG("Failed to new block");
203         delete work;
204         return result;
205     }
206     work->data = reinterpret_cast<void *>(block);
207     UvQueueWork(loop, work);
208     return result;
209 }
210 
UvQueueWorkEx(uv_loop_s * loop,uv_work_t * work)211 void MediaLibraryBackupNapi::UvQueueWorkEx(uv_loop_s *loop, uv_work_t *work)
212 {
213     uv_queue_work(loop, work, [](uv_work_t *work) {
214         RestoreExBlock *block = reinterpret_cast<RestoreExBlock *> (work->data);
215         BackupRestoreService::GetInstance().StartRestoreEx(block->context.lock(), block->restoreInfo,
216             block->restoreExInfo);
217     }, [](uv_work_t *work, int _status) {
218         RestoreExBlock *block = reinterpret_cast<RestoreExBlock *> (work->data);
219         if (block == nullptr) {
220             delete work;
221             return;
222         }
223         napi_handle_scope scope = nullptr;
224         napi_open_handle_scope(block->env, &scope);
225         if (scope == nullptr) {
226             delete work;
227             return;
228         }
229         napi_value restoreExResult = nullptr;
230         napi_create_string_utf8(block->env, block->restoreExInfo.c_str(), NAPI_AUTO_LENGTH, &restoreExResult);
231         napi_resolve_deferred(block->env, block->nativeDeferred, restoreExResult);
232         napi_close_handle_scope(block->env, scope);
233         delete block;
234         delete work;
235     });
236 }
237 
JSStartRestoreEx(napi_env env,napi_callback_info info)238 napi_value MediaLibraryBackupNapi::JSStartRestoreEx(napi_env env, napi_callback_info info)
239 {
240     napi_value result = nullptr;
241     if (CheckPermission() != E_OK) {
242         return result;
243     }
244 
245     size_t argc = ARGS_SIX;
246     napi_value argv[ARGS_SIX] = {0};
247     napi_value thisVar = nullptr;
248 
249     GET_JS_ARGS(env, info, argc, argv, thisVar);
250     NAPI_ASSERT(env, (argc == ARGS_SIX), "requires 6 parameters");
251     napi_get_undefined(env, &result);
252     // get ability context
253     std::shared_ptr<OHOS::AbilityRuntime::Context> context;
254     if (!ParseContext(env, argv[PARAM0], context)) {
255         return result;
256     }
257     RestoreInfo restoreInfo;
258     restoreInfo.sceneCode = GetIntFromParams(env, argv, PARAM1);
259     restoreInfo.galleryAppName = GetStringFromParams(env, argv, PARAM2);
260     restoreInfo.mediaAppName = GetStringFromParams(env, argv, PARAM3);
261     restoreInfo.backupDir = GetStringFromParams(env, argv, PARAM4);
262     restoreInfo.bundleInfo = GetStringFromParams(env, argv, PARAM5);
263     std::string restoreExInfo;
264     NAPI_INFO_LOG("StartRestoreEx, sceneCode = %{public}d", restoreInfo.sceneCode);
265     if (restoreInfo.sceneCode < 0) {
266         NAPI_INFO_LOG("Parameters error, sceneCode = %{public}d", restoreInfo.sceneCode);
267         return result;
268     }
269     uv_loop_s *loop = nullptr;
270     napi_get_uv_event_loop(env, &loop);
271     uv_work_t *work = new (std::nothrow) uv_work_t;
272     if (work == nullptr) {
273         NAPI_ERR_LOG("Failed to new uv_work");
274         return result;
275     }
276     napi_deferred nativeDeferred = nullptr;
277     napi_create_promise(env, &nativeDeferred, &result);
278     RestoreExBlock *block = new (std::nothrow) RestoreExBlock {
279         env, context, restoreInfo, restoreExInfo, nativeDeferred };
280     if (block == nullptr) {
281         NAPI_ERR_LOG("Failed to new block");
282         delete work;
283         return result;
284     }
285     work->data = reinterpret_cast<void *>(block);
286     UvQueueWorkEx(loop, work);
287     return result;
288 }
289 
JSGetBackupInfo(napi_env env,napi_callback_info info)290 napi_value MediaLibraryBackupNapi::JSGetBackupInfo(napi_env env, napi_callback_info info)
291 {
292     napi_value result = nullptr;
293     if (CheckPermission() != E_OK) {
294         return result;
295     }
296 
297     size_t argc = ARGS_ONE;
298     napi_value argv[ARGS_ONE] = {0};
299     napi_value thisVar = nullptr;
300 
301     GET_JS_ARGS(env, info, argc, argv, thisVar);
302     NAPI_ASSERT(env, (argc == ARGS_ONE), "requires 1 parameters");
303     napi_get_undefined(env, &result);
304     int32_t sceneCode = GetIntFromParams(env, argv, PARAM0);
305     NAPI_INFO_LOG("GetBackupInfo, sceneCode = %{public}d", sceneCode);
306     if (sceneCode < 0) {
307         NAPI_INFO_LOG("Parameters error, sceneCode = %{public}d", sceneCode);
308         return result;
309     }
310     std::string backupInfo;
311     BackupRestoreService::GetInstance().GetBackupInfo(sceneCode, backupInfo);
312     CHECK_ARGS(env, napi_create_string_utf8(env, backupInfo.c_str(), NAPI_AUTO_LENGTH, &result), JS_INNER_FAIL);
313     return result;
314 }
315 
JSGetProgressInfo(napi_env env,napi_callback_info info)316 napi_value MediaLibraryBackupNapi::JSGetProgressInfo(napi_env env, napi_callback_info info)
317 {
318     napi_value result = nullptr;
319     if (CheckPermission() != E_OK) {
320         return result;
321     }
322 
323     std::string progressInfo;
324     BackupRestoreService::GetInstance().GetProgressInfo(progressInfo);
325     CHECK_ARGS(env, napi_create_string_utf8(env, progressInfo.c_str(), NAPI_AUTO_LENGTH, &result), JS_INNER_FAIL);
326     return result;
327 }
328 
UvBackupWork(uv_loop_s * loop,uv_work_t * work)329 void MediaLibraryBackupNapi::UvBackupWork(uv_loop_s *loop, uv_work_t *work)
330 {
331     uv_queue_work(loop, work, [](uv_work_t *work) {
332         BackupBlock *block = reinterpret_cast<BackupBlock *> (work->data);
333         BackupRestoreService::GetInstance().StartBackup(block->sceneCode, block->galleryAppName,
334             block->mediaAppName);
335     }, [](uv_work_t *work, int _status) {
336         BackupBlock *block = reinterpret_cast<BackupBlock *> (work->data);
337         if (block == nullptr) {
338             delete work;
339             return;
340         }
341         napi_handle_scope scope = nullptr;
342         napi_open_handle_scope(block->env, &scope);
343         if (scope == nullptr) {
344             delete work;
345             return;
346         }
347         napi_value resultCode = nullptr;
348         napi_create_int32(block->env, 1, &resultCode);
349         napi_resolve_deferred(block->env, block->nativeDeferred, resultCode);
350         napi_close_handle_scope(block->env, scope);
351         delete block;
352         delete work;
353     });
354 }
355 
JSStartBackup(napi_env env,napi_callback_info info)356 napi_value MediaLibraryBackupNapi::JSStartBackup(napi_env env, napi_callback_info info)
357 {
358     napi_value result = nullptr;
359     if (CheckPermission() != E_OK) {
360         return result;
361     }
362 
363     size_t argc = ARGS_THREE;
364     napi_value argv[ARGS_THREE] = {0};
365     napi_value thisVar = nullptr;
366 
367     GET_JS_ARGS(env, info, argc, argv, thisVar);
368     NAPI_ASSERT(env, (argc == ARGS_THREE), "requires 3 parameters");
369     napi_get_undefined(env, &result);
370     int32_t sceneCode = GetIntFromParams(env, argv, PARAM0);
371     std::string galleryAppName = GetStringFromParams(env, argv, PARAM1);
372     std::string mediaAppName = GetStringFromParams(env, argv, PARAM2);
373     NAPI_INFO_LOG("StartBackup, sceneCode = %{public}d", sceneCode);
374     if (sceneCode < 0) {
375         NAPI_INFO_LOG("Parameters error, sceneCode = %{public}d", sceneCode);
376         return result;
377     }
378     uv_loop_s *loop = nullptr;
379     napi_get_uv_event_loop(env, &loop);
380     uv_work_t *work = new (std::nothrow) uv_work_t;
381     if (work == nullptr) {
382         NAPI_ERR_LOG("Failed to new uv_work");
383         return result;
384     }
385     napi_deferred nativeDeferred = nullptr;
386     napi_create_promise(env, &nativeDeferred, &result);
387     BackupBlock *block = new (std::nothrow) BackupBlock {
388         env, sceneCode, galleryAppName, mediaAppName, nativeDeferred };
389     if (block == nullptr) {
390         NAPI_ERR_LOG("Failed to new block");
391         delete work;
392         return result;
393     }
394     work->data = reinterpret_cast<void *>(block);
395     UvBackupWork(loop, work);
396     return result;
397 }
398 } // namespace Media
399 } // namespace OHOS
400