• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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 "SendablePhotoAccessHelper"
17 
18 #include "sendable_photo_access_helper_napi.h"
19 
20 #include <fcntl.h>
21 #include <functional>
22 #include <sys/sendfile.h>
23 
24 #include "ability_context.h"
25 #include "context.h"
26 #include "directory_ex.h"
27 #include "file_ex.h"
28 #include "hitrace_meter.h"
29 #include "location_column.h"
30 #include "media_device_column.h"
31 #include "media_directory_type_column.h"
32 #include "media_file_asset_columns.h"
33 #include "media_change_request_napi.h"
34 #include "media_column.h"
35 #include "media_app_uri_permission_column.h"
36 #include "media_file_uri.h"
37 #include "media_file_utils.h"
38 #include "media_smart_album_column.h"
39 #include "media_smart_map_column.h"
40 #include "medialibrary_client_errno.h"
41 #include "medialibrary_data_manager.h"
42 #include "medialibrary_db_const.h"
43 #include "medialibrary_errno.h"
44 #include "medialibrary_napi_log.h"
45 #include "medialibrary_napi_utils.h"
46 #include "medialibrary_tracer.h"
47 #include "napi_base_context.h"
48 #include "photo_album_column.h"
49 #include "photo_album_napi.h"
50 #include "result_set_utils.h"
51 #include "safe_map.h"
52 #include "search_column.h"
53 #include "sendable_medialibrary_napi_utils.h"
54 #include "sendable_photo_album_napi.h"
55 #include "smart_album_napi.h"
56 #include "story_album_column.h"
57 #include "string_ex.h"
58 #include "string_wrapper.h"
59 #include "userfile_client.h"
60 #include "form_map.h"
61 #include "userfile_manager_types.h"
62 
63 using namespace std;
64 using namespace OHOS::AppExecFwk;
65 using namespace OHOS::NativeRdb;
66 using namespace OHOS::DataShare;
67 
68 namespace OHOS {
69 namespace Media {
70 using ChangeType = AAFwk::ChangeInfo::ChangeType;
71 const string DATE_FUNCTION = "DATE(";
72 
73 mutex SendablePhotoAccessHelper::sUserFileClientMutex_;
74 mutex SendablePhotoAccessHelper::sOnOffMutex_;
75 
76 const std::string SUBTYPE = "subType";
77 const std::string PAH_SUBTYPE = "subtype";
78 const std::string CAMERA_SHOT_KEY = "cameraShotKey";
79 const std::map<std::string, std::string> PHOTO_CREATE_OPTIONS_PARAM = {
80     { SUBTYPE, PhotoColumn::PHOTO_SUBTYPE },
81     { CAMERA_SHOT_KEY, PhotoColumn::CAMERA_SHOT_KEY },
82     { PAH_SUBTYPE, PhotoColumn::PHOTO_SUBTYPE }
83 
84 };
85 
86 const std::string TITLE = "title";
87 const std::map<std::string, std::string> CREATE_OPTIONS_PARAM = {
88     { TITLE, MediaColumn::MEDIA_TITLE }
89 };
90 
91 using CompleteCallback = napi_async_complete_callback;
92 using Context = SendablePhotoAccessHelperAsyncContext* ;
93 
94 thread_local napi_ref SendablePhotoAccessHelper::photoAccessHelperConstructor_ = nullptr;
95 thread_local napi_ref SendablePhotoAccessHelper::sMediaTypeEnumRef_ = nullptr;
96 thread_local napi_ref SendablePhotoAccessHelper::sPhotoSubType_ = nullptr;
97 thread_local napi_ref SendablePhotoAccessHelper::sPositionTypeEnumRef_ = nullptr;
98 thread_local napi_ref SendablePhotoAccessHelper::sAlbumType_ = nullptr;
99 thread_local napi_ref SendablePhotoAccessHelper::sAlbumSubType_ = nullptr;
100 thread_local napi_ref SendablePhotoAccessHelper::sMovingPhotoEffectModeEnumRef_ = nullptr;
101 
SendablePhotoAccessHelper()102 SendablePhotoAccessHelper::SendablePhotoAccessHelper()
103     : env_(nullptr) {}
104 
105 SendablePhotoAccessHelper::~SendablePhotoAccessHelper() = default;
106 
MediaLibraryNapiDestructor(napi_env env,void * nativeObject,void * finalize_hint)107 void SendablePhotoAccessHelper::MediaLibraryNapiDestructor(napi_env env, void *nativeObject, void *finalize_hint)
108 {
109     SendablePhotoAccessHelper *sendablePhotoAccessHelper = reinterpret_cast<SendablePhotoAccessHelper*>(nativeObject);
110     if (sendablePhotoAccessHelper != nullptr) {
111         delete sendablePhotoAccessHelper;
112         sendablePhotoAccessHelper = nullptr;
113     }
114 }
115 
Init(napi_env env,napi_value exports)116 napi_value SendablePhotoAccessHelper::Init(napi_env env, napi_value exports)
117 {
118     napi_value ctorObj;
119 
120     napi_property_descriptor props[] = {
121         DECLARE_NAPI_FUNCTION("getAssets", PhotoAccessGetPhotoAssets),
122         DECLARE_NAPI_FUNCTION("getBurstAssets", PhotoAccessGetBurstAssets),
123         DECLARE_NAPI_FUNCTION("createAsset", PhotoAccessHelperCreatePhotoAsset),
124         DECLARE_NAPI_FUNCTION("release", JSRelease),
125         DECLARE_NAPI_FUNCTION("getAlbums", PhotoAccessGetPhotoAlbums),
126         DECLARE_NAPI_FUNCTION("getHiddenAlbums", PahGetHiddenAlbums),
127         DECLARE_NAPI_FUNCTION("getSharedPhotoAssets", PhotoAccessGetSharedPhotoAssets),
128     };
129     napi_define_sendable_class(env, SENDABLE_PHOTOACCESSHELPER_NAPI_CLASS_NAME.c_str(), NAPI_AUTO_LENGTH,
130                                MediaLibraryNapiConstructor, nullptr, sizeof(props) / sizeof(props[0]),
131                                props, nullptr, &ctorObj);
132     NAPI_CALL(env, napi_create_reference(env, ctorObj, NAPI_INIT_REF_COUNT, &photoAccessHelperConstructor_));
133     NAPI_CALL(env, napi_set_named_property(env, exports, SENDABLE_PHOTOACCESSHELPER_NAPI_CLASS_NAME.c_str(), ctorObj));
134 
135     const vector<napi_property_descriptor> staticProps = {
136         DECLARE_NAPI_STATIC_FUNCTION("getPhotoAccessHelper", GetPhotoAccessHelper),
137         DECLARE_NAPI_PROPERTY("PhotoType", CreateMediaTypeUserFileEnum(env)),
138         DECLARE_NAPI_PROPERTY("AlbumType", CreateAlbumTypeEnum(env)),
139         DECLARE_NAPI_PROPERTY("AlbumSubtype", CreateAlbumSubTypeEnum(env)),
140         DECLARE_NAPI_PROPERTY("PositionType", CreatePositionTypeEnum(env)),
141         DECLARE_NAPI_PROPERTY("PhotoSubtype", CreatePhotoSubTypeEnum(env)),
142         DECLARE_NAPI_PROPERTY("MovingPhotoEffectMode", CreateMovingPhotoEffectModeEnum(env)),
143     };
144     MediaLibraryNapiUtils::NapiAddStaticProps(env, exports, staticProps);
145     return exports;
146 }
147 
CheckWhetherAsync(napi_env env,napi_callback_info info,bool & isAsync)148 static napi_status CheckWhetherAsync(napi_env env, napi_callback_info info, bool &isAsync)
149 {
150     isAsync = false;
151     size_t argc = ARGS_TWO;
152     napi_value argv[ARGS_TWO] = {0};
153     napi_status status = napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
154     if (status != napi_ok) {
155         NAPI_ERR_LOG("Error while obtaining js environment information");
156         return status;
157     }
158 
159     if (argc == ARGS_ONE) {
160         return napi_ok;
161     } else if (argc == ARGS_TWO) {
162         napi_valuetype valueType = napi_undefined;
163         status = napi_typeof(env, argv[ARGS_ONE], &valueType);
164         if (status != napi_ok) {
165             NAPI_ERR_LOG("Error while obtaining js environment information");
166             return status;
167         }
168         if (valueType == napi_boolean) {
169             isAsync = true;
170         }
171         status = napi_get_value_bool(env, argv[ARGS_ONE], &isAsync);
172         return status;
173     } else {
174         NAPI_ERR_LOG("argc %{public}d, is invalid", static_cast<int>(argc));
175         return napi_invalid_arg;
176     }
177 }
178 
179 // Constructor callback
MediaLibraryNapiConstructor(napi_env env,napi_callback_info info)180 napi_value SendablePhotoAccessHelper::MediaLibraryNapiConstructor(napi_env env, napi_callback_info info)
181 {
182     napi_status status;
183     napi_value result = nullptr;
184     napi_value thisVar = nullptr;
185     MediaLibraryTracer tracer;
186 
187     tracer.Start("MediaLibraryNapiConstructor");
188 
189     NAPI_CALL(env, napi_get_undefined(env, &result));
190     GET_JS_OBJ_WITH_ZERO_ARGS(env, info, status, thisVar);
191     if (status != napi_ok || thisVar == nullptr) {
192         NAPI_ERR_LOG("Error while obtaining js environment information, status: %{public}d", status);
193         return result;
194     }
195 
196     unique_ptr<SendablePhotoAccessHelper> obj = make_unique<SendablePhotoAccessHelper>();
197     if (obj == nullptr) {
198         return result;
199     }
200     obj->env_ = env;
201 
202     bool isAsync = false;
203     NAPI_CALL(env, CheckWhetherAsync(env, info, isAsync));
204     if (!isAsync) {
205         unique_lock<mutex> helperLock(sUserFileClientMutex_);
206         if (!UserFileClient::IsValid()) {
207             UserFileClient::Init(env, info);
208             if (!UserFileClient::IsValid()) {
209                 NAPI_ERR_LOG("UserFileClient creation failed");
210                 helperLock.unlock();
211                 return result;
212             }
213         }
214         helperLock.unlock();
215     }
216 
217     status = napi_wrap_sendable(env, thisVar, reinterpret_cast<void *>(obj.get()),
218         SendablePhotoAccessHelper::MediaLibraryNapiDestructor, nullptr);
219     if (status == napi_ok) {
220         obj.release();
221         return thisVar;
222     } else {
223         NAPI_ERR_LOG("Failed to wrap the native media lib client object with JS, status: %{public}d", status);
224     }
225 
226     return result;
227 }
228 
CheckWhetherInitSuccess(napi_env env,napi_value value,bool checkIsValid)229 static bool CheckWhetherInitSuccess(napi_env env, napi_value value, bool checkIsValid)
230 {
231     napi_value propertyNames;
232     uint32_t propertyLength;
233     napi_valuetype valueType = napi_undefined;
234     NAPI_CALL_BASE(env, napi_typeof(env, value, &valueType), false);
235     if (valueType != napi_object) {
236         return false;
237     }
238 
239     NAPI_CALL_BASE(env, napi_get_property_names(env, value, &propertyNames), false);
240     NAPI_CALL_BASE(env, napi_get_array_length(env, propertyNames, &propertyLength), false);
241     if (propertyLength == 0) {
242         return false;
243     }
244     if (checkIsValid && (!UserFileClient::IsValid())) {
245         NAPI_ERR_LOG("UserFileClient is not valid");
246         return false;
247     }
248     return true;
249 }
250 
CreateNewInstance(napi_env env,napi_callback_info info,napi_ref ref,bool isAsync=false)251 static napi_value CreateNewInstance(napi_env env, napi_callback_info info, napi_ref ref,
252     bool isAsync = false)
253 {
254     constexpr size_t argContext = 1;
255     size_t argc = argContext;
256     napi_value argv[ARGS_TWO] = {0};
257 
258     napi_value thisVar = nullptr;
259     napi_value ctor = nullptr;
260     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
261     NAPI_CALL(env, napi_get_reference_value(env, ref, &ctor));
262 
263     if (isAsync) {
264         argc = ARGS_TWO;
265         NAPI_CALL(env, napi_get_boolean(env, true, &argv[ARGS_ONE]));
266         argv[ARGS_ONE] = argv[argContext];
267     }
268 
269     napi_value result = nullptr;
270     NAPI_CALL(env, napi_new_instance(env, ctor, argc, argv, &result));
271     if (!CheckWhetherInitSuccess(env, result, !isAsync)) {
272         NAPI_ERR_LOG("Init MediaLibrary Instance is failed");
273         NAPI_CALL(env, napi_get_undefined(env, &result));
274     }
275     return result;
276 }
277 
GetPhotoAccessHelper(napi_env env,napi_callback_info info)278 napi_value SendablePhotoAccessHelper::GetPhotoAccessHelper(napi_env env, napi_callback_info info)
279 {
280     MediaLibraryTracer tracer;
281     tracer.Start("GetPhotoAccessHelper");
282     if (photoAccessHelperConstructor_ == nullptr) {
283         napi_value exports = nullptr;
284         napi_create_object(env, &exports);
285         SendablePhotoAccessHelper::Init(env, exports);
286     }
287 
288     return CreateNewInstance(env, info, photoAccessHelperConstructor_);
289 }
290 
AddIntegerNamedProperty(napi_env env,napi_value object,const string & name,int32_t enumValue)291 static napi_status AddIntegerNamedProperty(napi_env env, napi_value object,
292     const string &name, int32_t enumValue)
293 {
294     napi_value enumNapiValue;
295     napi_status status = napi_create_int32(env, enumValue, &enumNapiValue);
296     if (status == napi_ok) {
297         status = napi_set_named_property(env, object, name.c_str(), enumNapiValue);
298     }
299     return status;
300 }
301 
CreateNumberEnumProperty(napi_env env,vector<string> properties,napi_ref & ref,int32_t offset=0)302 static napi_value CreateNumberEnumProperty(napi_env env, vector<string> properties, napi_ref &ref, int32_t offset = 0)
303 {
304     napi_value result = nullptr;
305     NAPI_CALL(env, napi_create_object(env, &result));
306     for (size_t i = 0; i < properties.size(); i++) {
307         NAPI_CALL(env, AddIntegerNamedProperty(env, result, properties[i], i + offset));
308     }
309     NAPI_CALL(env, napi_create_reference(env, result, NAPI_INIT_REF_COUNT, &ref));
310     return result;
311 }
312 
GetNapiFileResult(napi_env env,SendablePhotoAccessHelperAsyncContext * context,unique_ptr<SendableJSAsyncContextOutput> & jsContext)313 static void GetNapiFileResult(napi_env env, SendablePhotoAccessHelperAsyncContext *context,
314     unique_ptr<SendableJSAsyncContextOutput> &jsContext)
315 {
316     // Create FetchResult object using the contents of resultSet
317     if (context->fetchFileResult == nullptr) {
318         NAPI_ERR_LOG("No fetch file result found!");
319         SendableMediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, ERR_INVALID_OUTPUT,
320             "Failed to obtain Fetch File Result");
321         return;
322     }
323     napi_value fileResult = SendableFetchFileResultNapi::CreateFetchFileResult(env, move(context->fetchFileResult));
324     if (fileResult == nullptr) {
325         SendableMediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, ERR_INVALID_OUTPUT,
326             "Failed to create js object for Fetch File Result");
327     } else {
328         jsContext->data = fileResult;
329         jsContext->status = true;
330         napi_get_undefined(env, &jsContext->error);
331     }
332 }
333 
GetFileAssetsAsyncCallbackComplete(napi_env env,napi_status status,void * data)334 static void GetFileAssetsAsyncCallbackComplete(napi_env env, napi_status status, void *data)
335 {
336     MediaLibraryTracer tracer;
337     tracer.Start("GetFileAssetsAsyncCallbackComplete");
338 
339     SendablePhotoAccessHelperAsyncContext *context = static_cast<SendablePhotoAccessHelperAsyncContext*>(data);
340     CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
341 
342     unique_ptr<SendableJSAsyncContextOutput> jsContext = make_unique<SendableJSAsyncContextOutput>();
343     jsContext->status = false;
344     napi_get_undefined(env, &jsContext->data);
345 
346     if (context->error != ERR_DEFAULT) {
347         context->HandleError(env, jsContext->error);
348     } else {
349         GetNapiFileResult(env, context, jsContext);
350     }
351 
352     tracer.Finish();
353     if (context->work != nullptr) {
354         SendableMediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
355             context->work, *jsContext);
356     }
357     delete context;
358 }
359 
360 #ifdef MEDIALIBRARY_COMPATIBILITY
SetCompatAlbumName(AlbumAsset * albumData)361 static void SetCompatAlbumName(AlbumAsset *albumData)
362 {
363     string albumName;
364     switch (albumData->GetAlbumSubType()) {
365         case PhotoAlbumSubType::CAMERA:
366             albumName = CAMERA_ALBUM_NAME;
367             break;
368         case PhotoAlbumSubType::SCREENSHOT:
369             albumName = SCREEN_SHOT_ALBUM_NAME;
370             break;
371         default:
372             NAPI_WARN_LOG("Ignore unsupported compat album type: %{public}d", albumData->GetAlbumSubType());
373     }
374     albumData->SetAlbumName(albumName);
375 }
376 #else
SetAlbumCoverUri(SendablePhotoAccessHelperAsyncContext * context,unique_ptr<AlbumAsset> & album)377 static void SetAlbumCoverUri(SendablePhotoAccessHelperAsyncContext *context, unique_ptr<AlbumAsset> &album)
378 {
379     MediaLibraryTracer tracer;
380     tracer.Start("SetAlbumCoverUri");
381     CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
382     DataShare::DataSharePredicates predicates;
383     predicates.SetWhereClause(MEDIA_DATA_DB_BUCKET_ID + " = ? ");
384     predicates.SetWhereArgs({ to_string(album->GetAlbumId()) });
385     predicates.SetOrder(MEDIA_DATA_DB_DATE_ADDED + " DESC LIMIT 0,1 ");
386     vector<string> columns;
387     string queryUri = MEDIALIBRARY_DATA_URI;
388     if (!context->networkId.empty()) {
389         queryUri = MEDIALIBRARY_DATA_ABILITY_PREFIX + context->networkId + MEDIALIBRARY_DATA_URI_IDENTIFIER;
390         NAPI_DEBUG_LOG("querycoverUri is = %{private}s", queryUri.c_str());
391     }
392     Uri uri(queryUri);
393     int errCode = 0;
394     shared_ptr<DataShare::DataShareResultSet> resultSet = UserFileClient::Query(
395         uri, predicates, columns, errCode);
396     if (resultSet == nullptr) {
397         NAPI_ERR_LOG("Query for Album uri failed! errorCode is = %{public}d", errCode);
398         return;
399     }
400     unique_ptr<FetchResult<FileAsset>> fetchFileResult = make_unique<FetchResult<FileAsset>>(move(resultSet));
401     fetchFileResult->SetNetworkId(context->networkId);
402     unique_ptr<FileAsset> fileAsset = fetchFileResult->GetFirstObject();
403     CHECK_NULL_PTR_RETURN_VOID(fileAsset, "SetAlbumCoverUr:FileAsset is nullptr");
404     string coverUri = fileAsset->GetUri();
405     album->SetCoverUri(coverUri);
406     NAPI_DEBUG_LOG("coverUri is = %{private}s", album->GetCoverUri().c_str());
407 }
408 #endif
409 
SetAlbumData(AlbumAsset * albumData,shared_ptr<DataShare::DataShareResultSet> resultSet,const string & networkId)410 void SetAlbumData(AlbumAsset* albumData, shared_ptr<DataShare::DataShareResultSet> resultSet,
411     const string &networkId)
412 {
413 #ifdef MEDIALIBRARY_COMPATIBILITY
414     albumData->SetAlbumId(get<int32_t>(ResultSetUtils::GetValFromColumn(PhotoAlbumColumns::ALBUM_ID, resultSet,
415         TYPE_INT32)));
416     albumData->SetAlbumType(static_cast<PhotoAlbumType>(
417         get<int32_t>(ResultSetUtils::GetValFromColumn(PhotoAlbumColumns::ALBUM_TYPE, resultSet, TYPE_INT32))));
418     albumData->SetAlbumSubType(static_cast<PhotoAlbumSubType>(
419         get<int32_t>(ResultSetUtils::GetValFromColumn(PhotoAlbumColumns::ALBUM_SUBTYPE, resultSet, TYPE_INT32))));
420     SetCompatAlbumName(albumData);
421 #else
422     // Get album id index and value
423     albumData->SetAlbumId(get<int32_t>(ResultSetUtils::GetValFromColumn(MEDIA_DATA_DB_BUCKET_ID, resultSet,
424         TYPE_INT32)));
425 
426     // Get album title index and value
427     albumData->SetAlbumName(get<string>(ResultSetUtils::GetValFromColumn(MEDIA_DATA_DB_TITLE, resultSet,
428         TYPE_STRING)));
429 #endif
430 
431     // Get album asset count index and value
432     albumData->SetCount(get<int32_t>(ResultSetUtils::GetValFromColumn(MEDIA_DATA_DB_COUNT, resultSet, TYPE_INT32)));
433     MediaFileUri fileUri(MEDIA_TYPE_ALBUM, to_string(albumData->GetAlbumId()), networkId,
434         MEDIA_API_VERSION_DEFAULT);
435     albumData->SetAlbumUri(fileUri.ToString());
436     // Get album relativePath index and value
437     albumData->SetAlbumRelativePath(get<string>(ResultSetUtils::GetValFromColumn(MEDIA_DATA_DB_RELATIVE_PATH,
438         resultSet, TYPE_STRING)));
439     albumData->SetAlbumDateModified(get<int64_t>(ResultSetUtils::GetValFromColumn(MEDIA_DATA_DB_DATE_MODIFIED,
440         resultSet, TYPE_INT64)));
441 }
442 
443 #ifdef MEDIALIBRARY_COMPATIBILITY
ReplaceAlbumName(const string & arg,string & argInstead)444 static void ReplaceAlbumName(const string &arg, string &argInstead)
445 {
446     if (arg == CAMERA_ALBUM_NAME) {
447         argInstead = to_string(PhotoAlbumSubType::CAMERA);
448     } else if (arg == SCREEN_SHOT_ALBUM_NAME) {
449         argInstead = to_string(PhotoAlbumSubType::SCREENSHOT);
450     } else if (arg == SCREEN_RECORD_ALBUM_NAME) {
451         argInstead = to_string(PhotoAlbumSubType::SCREENSHOT);
452     } else {
453         argInstead = arg;
454     }
455 }
456 
DoReplaceRelativePath(const string & arg,string & argInstead)457 static bool DoReplaceRelativePath(const string &arg, string &argInstead)
458 {
459     if (arg == CAMERA_PATH) {
460         argInstead = to_string(PhotoAlbumSubType::CAMERA);
461     } else if (arg == SCREEN_SHOT_PATH) {
462         argInstead = to_string(PhotoAlbumSubType::SCREENSHOT);
463     } else if (arg == SCREEN_RECORD_PATH) {
464         argInstead = to_string(PhotoAlbumSubType::SCREENSHOT);
465     } else if (arg.empty()) {
466         argInstead = arg;
467         return false;
468     } else {
469         argInstead = arg;
470     }
471     return true;
472 }
473 
ReplaceRelativePath(string & selection,size_t pos,const string & keyInstead,const string & arg,string & argInstead)474 static inline void ReplaceRelativePath(string &selection, size_t pos, const string &keyInstead, const string &arg,
475     string &argInstead)
476 {
477     bool shouldReplace = DoReplaceRelativePath(arg, argInstead);
478     if (shouldReplace) {
479         selection.replace(pos, MEDIA_DATA_DB_RELATIVE_PATH.length(), keyInstead);
480     }
481 }
482 
ReplaceSelection(string & selection,vector<string> & selectionArgs,const string & key,const string & keyInstead,const int32_t mode)483 void SendablePhotoAccessHelper::ReplaceSelection(string &selection, vector<string> &selectionArgs,
484     const string &key, const string &keyInstead, const int32_t mode)
485 {
486     for (size_t pos = 0; pos != string::npos;) {
487         pos = selection.find(key, pos);
488         if (pos == string::npos) {
489             break;
490         }
491 
492         size_t argPos = selection.find('?', pos);
493         if (argPos == string::npos) {
494             break;
495         }
496         size_t argIndex = 0;
497         for (size_t i = 0; i < argPos; i++) {
498             if (selection[i] == '?') {
499                 argIndex++;
500             }
501         }
502         if (argIndex > selectionArgs.size() - 1) {
503             NAPI_WARN_LOG("SelectionArgs size is not valid, selection format maybe incorrect: %{private}s",
504                 selection.c_str());
505             break;
506         }
507         const string &arg = selectionArgs[argIndex];
508         string argInstead = arg;
509         if (key == MEDIA_DATA_DB_RELATIVE_PATH) {
510             if (mode == SendableReplaceSelectionMode::SENDABLE_ADD_DOCS_TO_RELATIVE_PATH) {
511                 argInstead = MediaFileUtils::AddDocsToRelativePath(arg);
512             } else {
513                 ReplaceRelativePath(selection, pos, keyInstead, arg, argInstead);
514             }
515         } else if (key == MEDIA_DATA_DB_BUCKET_NAME) {
516             ReplaceAlbumName(arg, argInstead);
517             selection.replace(pos, key.length(), keyInstead);
518         } else if (key == MEDIA_DATA_DB_BUCKET_ID) {
519             selection.replace(pos, key.length(), keyInstead);
520         }
521         selectionArgs[argIndex] = argInstead;
522         argPos = selection.find('?', pos);
523         if (argPos == string::npos) {
524             break;
525         }
526         pos = argPos + 1;
527     }
528 }
529 #endif
530 
GetJSArgsForCreateAsset(napi_env env,size_t argc,const napi_value argv[],SendablePhotoAccessHelperAsyncContext & asyncContext)531 napi_value GetJSArgsForCreateAsset(napi_env env, size_t argc, const napi_value argv[],
532                                    SendablePhotoAccessHelperAsyncContext &asyncContext)
533 {
534     const int32_t refCount = 1;
535     napi_value result = nullptr;
536     auto context = &asyncContext;
537     CHECK_NULL_PTR_RETURN_UNDEFINED(env, context, result, "Async context is null");
538     int32_t fileMediaType = 0;
539     size_t res = 0;
540     char relativePathBuffer[PATH_MAX];
541     char titleBuffer[PATH_MAX];
542     NAPI_ASSERT(env, argv != nullptr, "Argument list is empty");
543 
544     for (size_t i = PARAM0; i < argc; i++) {
545         napi_valuetype valueType = napi_undefined;
546         napi_typeof(env, argv[i], &valueType);
547         if (i == PARAM0 && valueType == napi_number) {
548             napi_get_value_int32(env, argv[i], &fileMediaType);
549         } else if (i == PARAM1 && valueType == napi_string) {
550             napi_get_value_string_utf8(env, argv[i], titleBuffer, PATH_MAX, &res);
551             NAPI_DEBUG_LOG("displayName = %{private}s", string(titleBuffer).c_str());
552         } else if (i == PARAM2 && valueType == napi_string) {
553             napi_get_value_string_utf8(env, argv[i], relativePathBuffer, PATH_MAX, &res);
554             NAPI_DEBUG_LOG("relativePath = %{private}s", string(relativePathBuffer).c_str());
555         } else if (i == PARAM3 && valueType == napi_function) {
556             napi_create_reference(env, argv[i], refCount, &context->callbackRef);
557         } else {
558             NAPI_DEBUG_LOG("type mismatch, valueType: %{public}d", valueType);
559             return result;
560     }
561     }
562 
563     context->valuesBucket.Put(MEDIA_DATA_DB_MEDIA_TYPE, fileMediaType);
564     context->valuesBucket.Put(MEDIA_DATA_DB_NAME, string(titleBuffer));
565     context->valuesBucket.Put(MEDIA_DATA_DB_RELATIVE_PATH, string(relativePathBuffer));
566 
567     context->assetType = TYPE_DEFAULT;
568     if (fileMediaType == MediaType::MEDIA_TYPE_IMAGE || fileMediaType == MediaType::MEDIA_TYPE_VIDEO) {
569         context->assetType = TYPE_PHOTO;
570     } else if (fileMediaType == MediaType::MEDIA_TYPE_AUDIO) {
571         context->assetType = TYPE_AUDIO;
572     }
573 
574     NAPI_DEBUG_LOG("GetJSArgsForCreateAsset END");
575     // Return true napi_value if params are successfully obtained
576     napi_get_boolean(env, true, &result);
577     return result;
578 }
579 
GetJSArgsForDeleteAsset(napi_env env,size_t argc,const napi_value argv[],SendablePhotoAccessHelperAsyncContext & asyncContext)580 napi_value GetJSArgsForDeleteAsset(napi_env env, size_t argc, const napi_value argv[],
581                                    SendablePhotoAccessHelperAsyncContext &asyncContext)
582 {
583     const int32_t refCount = 1;
584     napi_value result = nullptr;
585     auto context = &asyncContext;
586     CHECK_NULL_PTR_RETURN_UNDEFINED(env, context, result, "Async context is null");
587     size_t res = 0;
588     char buffer[PATH_MAX];
589 
590     NAPI_ASSERT(env, argv != nullptr, "Argument list is empty");
591 
592     for (size_t i = PARAM0; i < argc; i++) {
593         napi_valuetype valueType = napi_undefined;
594         napi_typeof(env, argv[i], &valueType);
595 
596         if (i == PARAM0 && valueType == napi_string) {
597             napi_get_value_string_utf8(env, argv[i], buffer, PATH_MAX, &res);
598         } else if (i == PARAM1 && valueType == napi_function) {
599             napi_create_reference(env, argv[i], refCount, &context->callbackRef);
600             break;
601         } else {
602             NAPI_ASSERT(env, false, "type mismatch");
603         }
604     }
605 
606     context->valuesBucket.Put(MEDIA_DATA_DB_URI, string(buffer));
607 
608     // Return true napi_value if params are successfully obtained
609     napi_get_boolean(env, true, &result);
610     return result;
611 }
612 
JSReleaseCompleteCallback(napi_env env,napi_status status,SendablePhotoAccessHelperAsyncContext * context)613 static void JSReleaseCompleteCallback(napi_env env, napi_status status,
614                                       SendablePhotoAccessHelperAsyncContext *context)
615 {
616     MediaLibraryTracer tracer;
617     tracer.Start("JSReleaseCompleteCallback");
618 
619     CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
620 
621     unique_ptr<SendableJSAsyncContextOutput> jsContext = make_unique<SendableJSAsyncContextOutput>();
622     jsContext->status = false;
623     if (context->objectInfo != nullptr) {
624         napi_create_int32(env, E_SUCCESS, &jsContext->data);
625         jsContext->status = true;
626         napi_get_undefined(env, &jsContext->error);
627     } else {
628         NAPI_ERR_LOG("JSReleaseCompleteCallback context->objectInfo == nullptr");
629         SendableMediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, ERR_INVALID_OUTPUT,
630             "UserFileClient is invalid");
631         napi_get_undefined(env, &jsContext->data);
632     }
633 
634     tracer.Finish();
635     if (context->work != nullptr) {
636         SendableMediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
637             context->work, *jsContext);
638     }
639 
640     delete context;
641 }
642 
JSRelease(napi_env env,napi_callback_info info)643 napi_value SendablePhotoAccessHelper::JSRelease(napi_env env, napi_callback_info info)
644 {
645     napi_status status;
646     napi_value result = nullptr;
647     size_t argc = ARGS_ONE;
648     napi_value argv[ARGS_ONE] = {0};
649     napi_value thisVar = nullptr;
650     napi_value resource = nullptr;
651     int32_t refCount = 1;
652 
653     MediaLibraryTracer tracer;
654     tracer.Start("JSRelease");
655 
656     GET_JS_ARGS(env, info, argc, argv, thisVar);
657     NAPI_ASSERT(env, (argc == ARGS_ONE || argc == ARGS_ZERO), "requires 1 parameters maximum");
658     napi_get_undefined(env, &result);
659 
660     unique_ptr<SendablePhotoAccessHelperAsyncContext> asyncContext =
661         make_unique<SendablePhotoAccessHelperAsyncContext>();
662     status = napi_unwrap_sendable(env, thisVar, reinterpret_cast<void **>(&asyncContext->objectInfo));
663     NAPI_ASSERT(env, status == napi_ok && asyncContext->objectInfo != nullptr, "Failed to get object info");
664 
665     if (argc == PARAM1) {
666         napi_valuetype valueType = napi_undefined;
667         napi_typeof(env, argv[PARAM0], &valueType);
668         if (valueType == napi_function) {
669             napi_create_reference(env, argv[PARAM0], refCount, &asyncContext->callbackRef);
670         }
671     }
672     CHECK_NULL_PTR_RETURN_UNDEFINED(env, result, result, "Failed to obtain arguments");
673 
674     NAPI_CALL(env, napi_remove_wrap_sendable(env, thisVar, reinterpret_cast<void**>(&asyncContext->objectInfo)));
675     NAPI_CREATE_PROMISE(env, asyncContext->callbackRef, asyncContext->deferred, result);
676     NAPI_CREATE_RESOURCE_NAME(env, resource, "JSRelease", asyncContext);
677 
678     status = napi_create_async_work(
679         env, nullptr, resource, [](napi_env env, void *data) {},
680         reinterpret_cast<CompleteCallback>(JSReleaseCompleteCallback),
681         static_cast<void *>(asyncContext.get()), &asyncContext->work);
682     if (status != napi_ok) {
683         napi_get_undefined(env, &result);
684     } else {
685         napi_queue_async_work(env, asyncContext->work);
686         asyncContext.release();
687     }
688 
689     return result;
690 }
691 
AddDefaultPhotoAlbumColumns(napi_env env,vector<string> & fetchColumn)692 static napi_value AddDefaultPhotoAlbumColumns(napi_env env, vector<string> &fetchColumn)
693 {
694     auto validFetchColumns = PhotoAlbumColumns::DEFAULT_FETCH_COLUMNS;
695     for (const auto &column : fetchColumn) {
696         if (PhotoAlbumColumns::IsPhotoAlbumColumn(column)) {
697             validFetchColumns.insert(column);
698         } else if (column.compare(MEDIA_DATA_DB_URI) == 0) {
699             // uri is default property of album
700             continue;
701         } else {
702             NAPI_ERR_LOG("unknown columns:%{public}s", column.c_str());
703             NapiError::ThrowError(env, JS_ERR_PARAMETER_INVALID);
704             return nullptr;
705         }
706     }
707     fetchColumn.assign(validFetchColumns.begin(), validFetchColumns.end());
708 
709     napi_value result = nullptr;
710     CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
711     return result;
712 }
713 
GetJSArgsForCreateSmartAlbum(napi_env env,size_t argc,const napi_value argv[],SendablePhotoAccessHelperAsyncContext & asyncContext)714 napi_value GetJSArgsForCreateSmartAlbum(napi_env env, size_t argc, const napi_value argv[],
715                                         SendablePhotoAccessHelperAsyncContext &asyncContext)
716 {
717     const int32_t refCount = 1;
718     napi_value result = nullptr;
719     auto context = &asyncContext;
720     CHECK_NULL_PTR_RETURN_UNDEFINED(env, context, result, "Async context is null");
721     size_t res = 0;
722     char buffer[PATH_MAX];
723     NAPI_ASSERT(env, argv != nullptr, "Argument list is empty");
724     for (size_t i = 0; i < argc; i++) {
725         napi_valuetype valueType = napi_undefined;
726         napi_typeof(env, argv[i], &valueType);
727         if (i == 0 && valueType == napi_number) {
728             napi_get_value_int32(env, argv[i], &context->parentSmartAlbumId);
729         } else if (i == PARAM1 && valueType == napi_string) {
730             napi_get_value_string_utf8(env, argv[i], buffer, PATH_MAX, &res);
731         } else if (i == PARAM2 && valueType == napi_function) {
732             napi_create_reference(env, argv[i], refCount, &context->callbackRef);
733             break;
734         } else {
735             NAPI_ASSERT(env, false, "type mismatch");
736         }
737     }
738     if (context->parentSmartAlbumId < 0) {
739         NAPI_ASSERT(env, false, "type mismatch");
740     }
741     string smartName = string(buffer);
742     if (smartName.empty()) {
743         NAPI_ASSERT(env, false, "type mismatch");
744     }
745     context->valuesBucket.Put(SMARTALBUM_DB_NAME, smartName);
746     napi_get_boolean(env, true, &result);
747     return result;
748 }
749 
ParseArgsGetAssets(napi_env env,napi_callback_info info,unique_ptr<SendablePhotoAccessHelperAsyncContext> & context)750 static napi_value ParseArgsGetAssets(napi_env env, napi_callback_info info,
751     unique_ptr<SendablePhotoAccessHelperAsyncContext> &context)
752 {
753     constexpr size_t minArgs = ARGS_ONE;
754     constexpr size_t maxArgs = ARGS_TWO;
755     CHECK_ARGS(env, SendableMediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs),
756         JS_ERR_PARAMETER_INVALID);
757 
758     /* Parse the first argument */
759     CHECK_ARGS(env, SendableMediaLibraryNapiUtils::GetFetchOption(env, context->argv[PARAM0], ASSET_FETCH_OPT, context),
760         JS_INNER_FAIL);
761     auto &predicates = context->predicates;
762     switch (context->assetType) {
763         case TYPE_AUDIO: {
764             CHECK_NULLPTR_RET(SendableMediaLibraryNapiUtils::AddDefaultAssetColumns(env, context->fetchColumn,
765                 AudioColumn::IsAudioColumn, TYPE_AUDIO));
766             break;
767         }
768         case TYPE_PHOTO: {
769             CHECK_NULLPTR_RET(SendableMediaLibraryNapiUtils::AddDefaultAssetColumns(env, context->fetchColumn,
770                 PhotoColumn::IsPhotoColumn, TYPE_PHOTO));
771             break;
772         }
773         default: {
774             NapiError::ThrowError(env, JS_ERR_PARAMETER_INVALID);
775             return nullptr;
776         }
777     }
778     predicates.And()->EqualTo(MediaColumn::MEDIA_DATE_TRASHED, to_string(0));
779     predicates.And()->EqualTo(MediaColumn::MEDIA_TIME_PENDING, to_string(0));
780     if (context->assetType == TYPE_PHOTO) {
781         predicates.And()->EqualTo(MediaColumn::MEDIA_HIDDEN, to_string(0));
782         predicates.And()->EqualTo(PhotoColumn::PHOTO_IS_TEMP, to_string(false));
783         predicates.EqualTo(PhotoColumn::PHOTO_BURST_COVER_LEVEL,
784             to_string(static_cast<int32_t>(BurstCoverLevelType::COVER)));
785     }
786 
787     napi_value result = nullptr;
788     CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
789     return result;
790 }
791 
PhotoAccessGetAssetsExecute(napi_env env,void * data)792 static void PhotoAccessGetAssetsExecute(napi_env env, void *data)
793 {
794     MediaLibraryTracer tracer;
795     tracer.Start("PhotoAccessGetAssetsExecute");
796 
797     auto *context = static_cast<SendablePhotoAccessHelperAsyncContext*>(data);
798     string queryUri;
799     switch (context->assetType) {
800         case TYPE_PHOTO: {
801             queryUri = PAH_QUERY_PHOTO;
802             SendableMediaLibraryNapiUtils::UriAppendKeyValue(queryUri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
803             break;
804         }
805         default: {
806             context->SaveError(-EINVAL);
807             return;
808         }
809     }
810 
811     Uri uri(queryUri);
812     int errCode = 0;
813     shared_ptr<DataShare::DataShareResultSet> resultSet = UserFileClient::Query(uri,
814         context->predicates, context->fetchColumn, errCode);
815     if (resultSet == nullptr && !context->uri.empty() && errCode == E_PERMISSION_DENIED) {
816         Uri queryWithUri(context->uri);
817         resultSet = UserFileClient::Query(queryWithUri, context->predicates, context->fetchColumn, errCode);
818     }
819     if (resultSet == nullptr) {
820         context->SaveError(errCode);
821         return;
822     }
823     context->fetchFileResult = make_unique<FetchResult<FileAsset>>(move(resultSet));
824     context->fetchFileResult->SetResultNapiType(ResultNapiType::TYPE_PHOTOACCESS_HELPER);
825 }
826 
PhotoAccessGetPhotoAssets(napi_env env,napi_callback_info info)827 napi_value SendablePhotoAccessHelper::PhotoAccessGetPhotoAssets(napi_env env, napi_callback_info info)
828 {
829     unique_ptr<SendablePhotoAccessHelperAsyncContext> asyncContext =
830         make_unique<SendablePhotoAccessHelperAsyncContext>();
831     asyncContext->assetType = TYPE_PHOTO;
832     CHECK_NULLPTR_RET(ParseArgsGetAssets(env, info, asyncContext));
833 
834     return SendableMediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSGetPhotoAssets",
835         PhotoAccessGetAssetsExecute, GetFileAssetsAsyncCallbackComplete);
836 }
837 
GetPhotoAlbumQueryResult(napi_env env,SendablePhotoAccessHelperAsyncContext * context,unique_ptr<SendableJSAsyncContextOutput> & jsContext)838 static void GetPhotoAlbumQueryResult(napi_env env, SendablePhotoAccessHelperAsyncContext *context,
839     unique_ptr<SendableJSAsyncContextOutput> &jsContext)
840 {
841     napi_value fileResult = SendableFetchFileResultNapi::CreateFetchFileResult(env,
842         move(context->fetchPhotoAlbumResult));
843     if (fileResult == nullptr) {
844         CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->data), JS_INNER_FAIL);
845         SendableMediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, ERR_INVALID_OUTPUT,
846             "Failed to create js object for Fetch Album Result");
847         return;
848     }
849     jsContext->data = fileResult;
850     jsContext->status = true;
851     CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), JS_INNER_FAIL);
852 }
853 
JSGetPhotoAlbumsExecute(napi_env env,void * data)854 static void JSGetPhotoAlbumsExecute(napi_env env, void *data)
855 {
856     MediaLibraryTracer tracer;
857     tracer.Start("JSGetPhotoAlbumsExecute");
858 
859     auto *context = static_cast<SendablePhotoAccessHelperAsyncContext*>(data);
860     string queryUri;
861     if (context->hiddenOnly || context->hiddenAlbumFetchMode == ASSETS_MODE) {
862         queryUri = (context->resultNapiType == ResultNapiType::TYPE_USERFILE_MGR) ?
863             UFM_QUERY_HIDDEN_ALBUM : PAH_QUERY_HIDDEN_ALBUM;
864     } else if (context->isAnalysisAlbum) {
865         queryUri = context->isLocationAlbum == PhotoAlbumSubType::GEOGRAPHY_LOCATION ?
866             PAH_QUERY_GEO_PHOTOS : PAH_QUERY_ANA_PHOTO_ALBUM;
867     } else {
868         queryUri = (context->resultNapiType == ResultNapiType::TYPE_USERFILE_MGR) ?
869             UFM_QUERY_PHOTO_ALBUM : PAH_QUERY_PHOTO_ALBUM;
870     }
871     Uri uri(queryUri);
872     int errCode = 0;
873     auto resultSet = UserFileClient::Query(uri, context->predicates, context->fetchColumn, errCode);
874     if (resultSet == nullptr) {
875         NAPI_ERR_LOG("resultSet == nullptr, errCode is %{public}d", errCode);
876         if (errCode == E_PERMISSION_DENIED) {
877             if (context->hiddenOnly || context->hiddenAlbumFetchMode == ASSETS_MODE) {
878                 context->error = OHOS_PERMISSION_DENIED_CODE;
879             } else {
880                 context->SaveError(E_HAS_DB_ERROR);
881             }
882         } else {
883             context->SaveError(E_HAS_DB_ERROR);
884         }
885         return;
886     }
887 
888     context->fetchPhotoAlbumResult = make_unique<FetchResult<PhotoAlbum>>(move(resultSet));
889     context->fetchPhotoAlbumResult->SetResultNapiType(context->resultNapiType);
890     context->fetchPhotoAlbumResult->SetHiddenOnly(context->hiddenOnly);
891     context->fetchPhotoAlbumResult->SetLocationOnly(context->isLocationAlbum ==
892         PhotoAlbumSubType::GEOGRAPHY_LOCATION);
893 }
894 
JSGetPhotoAlbumsCompleteCallback(napi_env env,napi_status status,void * data)895 static void JSGetPhotoAlbumsCompleteCallback(napi_env env, napi_status status, void *data)
896 {
897     MediaLibraryTracer tracer;
898     tracer.Start("JSGetPhotoAlbumsCompleteCallback");
899 
900     auto *context = static_cast<SendablePhotoAccessHelperAsyncContext*>(data);
901     unique_ptr<SendableJSAsyncContextOutput> jsContext = make_unique<SendableJSAsyncContextOutput>();
902     jsContext->status = false;
903     CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), JS_INNER_FAIL);
904     if (context->error != ERR_DEFAULT  || context->fetchPhotoAlbumResult == nullptr) {
905         CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->data), JS_INNER_FAIL);
906         context->HandleError(env, jsContext->error);
907     } else {
908         GetPhotoAlbumQueryResult(env, context, jsContext);
909     }
910 
911     tracer.Finish();
912     if (context->work != nullptr) {
913         SendableMediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
914             context->work, *jsContext);
915     }
916     delete context;
917 }
918 
CreateMediaTypeUserFileEnum(napi_env env)919 napi_value SendablePhotoAccessHelper::CreateMediaTypeUserFileEnum(napi_env env)
920 {
921     const int32_t startIdx = 1;
922     return CreateNumberEnumProperty(env, mediaTypesUserFileEnum, sMediaTypeEnumRef_, startIdx);
923 }
924 
CreateAlbumTypeEnum(napi_env env)925 napi_value SendablePhotoAccessHelper::CreateAlbumTypeEnum(napi_env env)
926 {
927     napi_value result = nullptr;
928     CHECK_ARGS(env, napi_create_object(env, &result), JS_INNER_FAIL);
929 
930     CHECK_ARGS(env, AddIntegerNamedProperty(env, result, "USER", PhotoAlbumType::USER), JS_INNER_FAIL);
931     CHECK_ARGS(env, AddIntegerNamedProperty(env, result, "SYSTEM", PhotoAlbumType::SYSTEM), JS_INNER_FAIL);
932     CHECK_ARGS(env, AddIntegerNamedProperty(env, result, "SMART", PhotoAlbumType::SMART), JS_INNER_FAIL);
933     CHECK_ARGS(env, AddIntegerNamedProperty(env, result, "SOURCE", PhotoAlbumType::SOURCE), JS_INNER_FAIL);
934 
935     CHECK_ARGS(env, napi_create_reference(env, result, NAPI_INIT_REF_COUNT, &sAlbumType_), JS_INNER_FAIL);
936     return result;
937 }
938 
CreateAlbumSubTypeEnum(napi_env env)939 napi_value SendablePhotoAccessHelper::CreateAlbumSubTypeEnum(napi_env env)
940 {
941     napi_value result = nullptr;
942     CHECK_ARGS(env, napi_create_object(env, &result), JS_INNER_FAIL);
943 
944     CHECK_ARGS(env, AddIntegerNamedProperty(env, result, "USER_GENERIC", PhotoAlbumSubType::USER_GENERIC),
945         JS_INNER_FAIL);
946     CHECK_ARGS(env, AddIntegerNamedProperty(env, result, "SOURCE_GENERIC", PhotoAlbumSubType::SOURCE_GENERIC),
947         JS_INNER_FAIL);
948     for (size_t i = 0; i < systemAlbumSubType.size(); i++) {
949         CHECK_ARGS(env, AddIntegerNamedProperty(env, result, systemAlbumSubType[i],
950             PhotoAlbumSubType::SYSTEM_START + i), JS_INNER_FAIL);
951     }
952     CHECK_ARGS(env, AddIntegerNamedProperty(env, result, "CLASSIFY", PhotoAlbumSubType::CLASSIFY),
953         JS_INNER_FAIL);
954     for (size_t i = 0; i < analysisAlbumSubType.size(); i++) {
955         CHECK_ARGS(env, AddIntegerNamedProperty(env, result, analysisAlbumSubType[i],
956             PhotoAlbumSubType::GEOGRAPHY_LOCATION + i), JS_INNER_FAIL);
957     }
958     CHECK_ARGS(env, AddIntegerNamedProperty(env, result, "ANY", PhotoAlbumSubType::ANY), JS_INNER_FAIL);
959 
960     CHECK_ARGS(env, napi_create_reference(env, result, NAPI_INIT_REF_COUNT, &sAlbumSubType_), JS_INNER_FAIL);
961     return result;
962 }
963 
CreateMovingPhotoEffectModeEnum(napi_env env)964 napi_value SendablePhotoAccessHelper::CreateMovingPhotoEffectModeEnum(napi_env env)
965 {
966     return CreateNumberEnumProperty(env, movingPhotoEffectModeEnum, sMovingPhotoEffectModeEnumRef_);
967 }
968 
GetAlbumFetchOption(napi_env env,unique_ptr<SendablePhotoAccessHelperAsyncContext> & context,bool hasCallback)969 static napi_value GetAlbumFetchOption(napi_env env, unique_ptr<SendablePhotoAccessHelperAsyncContext> &context,
970     bool hasCallback)
971 {
972     if (context->argc < (ARGS_ONE + hasCallback)) {
973         NAPI_ERR_LOG("No arguments to parse");
974         return nullptr;
975     }
976 
977     // The index of fetchOption should always be the last arg besides callback
978     napi_value fetchOption = context->argv[context->argc - 1 - hasCallback];
979     CHECK_ARGS(env, SendableMediaLibraryNapiUtils::GetFetchOption(env, fetchOption, ALBUM_FETCH_OPT,
980         context), JS_INNER_FAIL);
981     if (!context->uri.empty()) {
982         if (context->uri.find(PhotoAlbumColumns::ANALYSIS_ALBUM_URI_PREFIX) != std::string::npos) {
983             context->isAnalysisAlbum = 1; // 1:is an analysis album
984         }
985     }
986     napi_value result = nullptr;
987     CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
988     return result;
989 }
990 
ParseLocationAlbumTypes(unique_ptr<SendablePhotoAccessHelperAsyncContext> & context,const int32_t albumSubType)991 static bool ParseLocationAlbumTypes(unique_ptr<SendablePhotoAccessHelperAsyncContext> &context,
992     const int32_t albumSubType)
993 {
994     if (albumSubType == PhotoAlbumSubType::GEOGRAPHY_LOCATION) {
995         context->isLocationAlbum = PhotoAlbumSubType::GEOGRAPHY_LOCATION;
996         context->fetchColumn.insert(context->fetchColumn.end(),
997             PhotoAlbumColumns::LOCATION_DEFAULT_FETCH_COLUMNS.begin(),
998             PhotoAlbumColumns::LOCATION_DEFAULT_FETCH_COLUMNS.end());
999         SendableMediaLibraryNapiUtils::GetAllLocationPredicates(context->predicates);
1000         return false;
1001     } else if (albumSubType == PhotoAlbumSubType::GEOGRAPHY_CITY) {
1002         context->fetchColumn = PhotoAlbumColumns::CITY_DEFAULT_FETCH_COLUMNS;
1003         context->isLocationAlbum = PhotoAlbumSubType::GEOGRAPHY_CITY;
1004         string onClause = PhotoAlbumColumns::ALBUM_NAME  + " = " + CITY_ID;
1005         context->predicates.InnerJoin(GEO_DICTIONARY_TABLE)->On({ onClause });
1006         context->predicates.NotEqualTo(PhotoAlbumColumns::ALBUM_COUNT, to_string(0));
1007     }
1008     return true;
1009 }
1010 
ParseAlbumTypes(napi_env env,unique_ptr<SendablePhotoAccessHelperAsyncContext> & context)1011 static napi_value ParseAlbumTypes(napi_env env, unique_ptr<SendablePhotoAccessHelperAsyncContext> &context)
1012 {
1013     if (context->argc < ARGS_TWO) {
1014         NAPI_ERR_LOG("No arguments to parse");
1015         return nullptr;
1016     }
1017 
1018     /* Parse the first argument to photo album type */
1019     int32_t albumType;
1020     CHECK_NULLPTR_RET(SendableMediaLibraryNapiUtils::GetInt32Arg(env, context->argv[PARAM0], albumType));
1021     if (!PhotoAlbum::CheckPhotoAlbumType(static_cast<PhotoAlbumType>(albumType))) {
1022         NapiError::ThrowError(env, JS_ERR_PARAMETER_INVALID);
1023         return nullptr;
1024     }
1025     context->isAnalysisAlbum = (albumType == PhotoAlbumType::SMART) ? 1 : 0;
1026 
1027     /* Parse the second argument to photo album subType */
1028     int32_t albumSubType;
1029     CHECK_NULLPTR_RET(SendableMediaLibraryNapiUtils::GetInt32Arg(env, context->argv[PARAM1], albumSubType));
1030     if (!PhotoAlbum::CheckPhotoAlbumSubType(static_cast<PhotoAlbumSubType>(albumSubType))) {
1031         NapiError::ThrowError(env, JS_ERR_PARAMETER_INVALID);
1032         return nullptr;
1033     }
1034 
1035     if (!ParseLocationAlbumTypes(context, albumSubType)) {
1036         napi_value result = nullptr;
1037         CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
1038         return result;
1039     }
1040 
1041     context->predicates.And()->EqualTo(PhotoAlbumColumns::ALBUM_TYPE, to_string(albumType));
1042     if (albumSubType != ANY) {
1043         context->predicates.And()->EqualTo(PhotoAlbumColumns::ALBUM_SUBTYPE, to_string(albumSubType));
1044     }
1045     if (albumSubType == PhotoAlbumSubType::SHOOTING_MODE || albumSubType == PhotoAlbumSubType::GEOGRAPHY_CITY) {
1046         context->predicates.OrderByDesc(PhotoAlbumColumns::ALBUM_COUNT);
1047     }
1048     if (albumSubType == PhotoAlbumSubType::HIGHLIGHT || albumSubType == PhotoAlbumSubType::HIGHLIGHT_SUGGESTIONS) {
1049         context->isHighlightAlbum = albumSubType;
1050         vector<string> onClause = {
1051             ANALYSIS_ALBUM_TABLE + "." + PhotoAlbumColumns::ALBUM_ID + " = " +
1052             HIGHLIGHT_ALBUM_TABLE + "." + PhotoAlbumColumns::ALBUM_ID,
1053         };
1054         context->predicates.InnerJoin(HIGHLIGHT_ALBUM_TABLE)->On(onClause);
1055         context->predicates.OrderByDesc(MAX_DATE_ADDED + ", " + GENERATE_TIME);
1056     }
1057 
1058     napi_value result = nullptr;
1059     CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
1060     return result;
1061 }
1062 
RestrictAlbumSubtypeOptions(unique_ptr<SendablePhotoAccessHelperAsyncContext> & context)1063 static void RestrictAlbumSubtypeOptions(unique_ptr<SendablePhotoAccessHelperAsyncContext> &context)
1064 {
1065     if (!SendableMediaLibraryNapiUtils::IsSystemApp()) {
1066         context->predicates.And()->In(PhotoAlbumColumns::ALBUM_SUBTYPE, vector<string>({
1067             to_string(PhotoAlbumSubType::USER_GENERIC),
1068             to_string(PhotoAlbumSubType::FAVORITE),
1069             to_string(PhotoAlbumSubType::VIDEO),
1070             to_string(PhotoAlbumSubType::IMAGE),
1071         }));
1072     } else {
1073         context->predicates.And()->NotEqualTo(PhotoAlbumColumns::ALBUM_SUBTYPE, to_string(PhotoAlbumSubType::HIDDEN));
1074     }
1075 }
1076 
ParseArgsGetPhotoAlbum(napi_env env,napi_callback_info info,unique_ptr<SendablePhotoAccessHelperAsyncContext> & context)1077 static napi_value ParseArgsGetPhotoAlbum(napi_env env, napi_callback_info info,
1078     unique_ptr<SendablePhotoAccessHelperAsyncContext> &context)
1079 {
1080     constexpr size_t minArgs = ARGS_ZERO;
1081     constexpr size_t maxArgs = ARGS_FOUR;
1082     CHECK_ARGS(env, SendableMediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs),
1083         JS_ERR_PARAMETER_INVALID);
1084 
1085     bool hasCallback = false;
1086     CHECK_ARGS(env, SendableMediaLibraryNapiUtils::HasCallback(env, context->argc, context->argv, hasCallback),
1087         JS_ERR_PARAMETER_INVALID);
1088     if (context->argc == ARGS_THREE) {
1089         napi_valuetype valueType = napi_undefined;
1090         if (napi_typeof(env, context->argv[PARAM2], &valueType) == napi_ok &&
1091             (valueType == napi_undefined || valueType == napi_null)) {
1092             context->argc -= 1;
1093         }
1094     }
1095     switch (context->argc - hasCallback) {
1096         case ARGS_ZERO:
1097             break;
1098         case ARGS_ONE:
1099             CHECK_NULLPTR_RET(GetAlbumFetchOption(env, context, hasCallback));
1100             break;
1101         case ARGS_TWO:
1102             CHECK_NULLPTR_RET(ParseAlbumTypes(env, context));
1103             break;
1104         case ARGS_THREE:
1105             CHECK_NULLPTR_RET(GetAlbumFetchOption(env, context, hasCallback));
1106             CHECK_NULLPTR_RET(ParseAlbumTypes(env, context));
1107             break;
1108         default:
1109             return nullptr;
1110     }
1111     RestrictAlbumSubtypeOptions(context);
1112     if (context->isLocationAlbum != PhotoAlbumSubType::GEOGRAPHY_LOCATION &&
1113         context->isLocationAlbum != PhotoAlbumSubType::GEOGRAPHY_CITY) {
1114         CHECK_NULLPTR_RET(AddDefaultPhotoAlbumColumns(env, context->fetchColumn));
1115         if (!context->isAnalysisAlbum) {
1116             context->fetchColumn.push_back(PhotoAlbumColumns::ALBUM_IMAGE_COUNT);
1117             context->fetchColumn.push_back(PhotoAlbumColumns::ALBUM_VIDEO_COUNT);
1118         }
1119         if (context->isHighlightAlbum) {
1120             context->fetchColumn.erase(std::remove(context->fetchColumn.begin(), context->fetchColumn.end(),
1121                 PhotoAlbumColumns::ALBUM_ID), context->fetchColumn.end());
1122             context->fetchColumn.push_back(ANALYSIS_ALBUM_TABLE + "." + PhotoAlbumColumns::ALBUM_ID + " AS " +
1123             PhotoAlbumColumns::ALBUM_ID);
1124         }
1125     }
1126     napi_value result = nullptr;
1127     CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
1128     return result;
1129 }
1130 
PhotoAccessGetPhotoAlbums(napi_env env,napi_callback_info info)1131 napi_value SendablePhotoAccessHelper::PhotoAccessGetPhotoAlbums(napi_env env, napi_callback_info info)
1132 {
1133     unique_ptr<SendablePhotoAccessHelperAsyncContext> asyncContext =
1134         make_unique<SendablePhotoAccessHelperAsyncContext>();
1135     asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
1136     CHECK_NULLPTR_RET(ParseArgsGetPhotoAlbum(env, info, asyncContext));
1137 
1138     return SendableMediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "GetPhotoAlbums",
1139         JSGetPhotoAlbumsExecute, JSGetPhotoAlbumsCompleteCallback);
1140 }
1141 
CreatePositionTypeEnum(napi_env env)1142 napi_value SendablePhotoAccessHelper::CreatePositionTypeEnum(napi_env env)
1143 {
1144     const int32_t startIdx = 1;
1145     return CreateNumberEnumProperty(env, positionTypeEnum, sPositionTypeEnumRef_, startIdx);
1146 }
1147 
CreatePhotoSubTypeEnum(napi_env env)1148 napi_value SendablePhotoAccessHelper::CreatePhotoSubTypeEnum(napi_env env)
1149 {
1150     return CreateNumberEnumProperty(env, photoSubTypeEnum, sPhotoSubType_);
1151 }
1152 
ParseHiddenPhotosDisplayMode(napi_env env,const unique_ptr<SendablePhotoAccessHelperAsyncContext> & context,const int32_t fetchMode)1153 napi_value ParseHiddenPhotosDisplayMode(napi_env env,
1154     const unique_ptr<SendablePhotoAccessHelperAsyncContext> &context, const int32_t fetchMode)
1155 {
1156     switch (fetchMode) {
1157         case ASSETS_MODE:
1158             context->predicates.EqualTo(PhotoAlbumColumns::ALBUM_SUBTYPE, PhotoAlbumSubType::HIDDEN);
1159             break;
1160         case ALBUMS_MODE:
1161             context->predicates.EqualTo(PhotoAlbumColumns::CONTAINS_HIDDEN, to_string(1));
1162             break;
1163         default:
1164             NapiError::ThrowError(
1165                 env, OHOS_INVALID_PARAM_CODE, "Invalid fetch mode: " + to_string(fetchMode));
1166             return nullptr;
1167     }
1168     napi_value result = nullptr;
1169     CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
1170     return result;
1171 }
1172 
ParseArgsGetHiddenAlbums(napi_env env,napi_callback_info info,unique_ptr<SendablePhotoAccessHelperAsyncContext> & context)1173 napi_value ParseArgsGetHiddenAlbums(napi_env env, napi_callback_info info,
1174     unique_ptr<SendablePhotoAccessHelperAsyncContext> &context)
1175 {
1176     if (!SendableMediaLibraryNapiUtils::IsSystemApp()) {
1177         NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
1178         return nullptr;
1179     }
1180     napi_value result = nullptr;
1181     CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
1182     constexpr size_t minArgs = ARGS_ONE;
1183     constexpr size_t maxArgs = ARGS_THREE;
1184     CHECK_ARGS(env, SendableMediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs),
1185         OHOS_INVALID_PARAM_CODE);
1186     bool hasCallback = false;
1187     CHECK_ARGS(env, SendableMediaLibraryNapiUtils::HasCallback(env, context->argc, context->argv, hasCallback),
1188         OHOS_INVALID_PARAM_CODE);
1189     if (context->argc == ARGS_THREE) {
1190         napi_valuetype valueType = napi_undefined;
1191         if (napi_typeof(env, context->argv[PARAM2], &valueType) == napi_ok &&
1192             (valueType == napi_undefined || valueType == napi_null)) {
1193             context->argc -= 1;
1194         }
1195     }
1196     int32_t fetchMode = 0;
1197     switch (context->argc - hasCallback) {
1198         case ARGS_ONE:
1199             CHECK_ARGS(env, SendableMediaLibraryNapiUtils::GetInt32(env, context->argv[PARAM0], fetchMode),
1200                 OHOS_INVALID_PARAM_CODE);
1201             break;
1202         case ARGS_TWO:
1203             CHECK_ARGS(env, SendableMediaLibraryNapiUtils::GetInt32(env, context->argv[PARAM0], fetchMode),
1204                 OHOS_INVALID_PARAM_CODE);
1205             CHECK_ARGS(env, SendableMediaLibraryNapiUtils::GetFetchOption(
1206                 env, context->argv[PARAM1], ALBUM_FETCH_OPT, context), OHOS_INVALID_PARAM_CODE);
1207             break;
1208         default:
1209             NapiError::ThrowError(
1210                 env, OHOS_INVALID_PARAM_CODE, "Invalid parameter count: " + to_string(context->argc));
1211             return nullptr;
1212     }
1213     CHECK_NULLPTR_RET(ParseHiddenPhotosDisplayMode(env, context, fetchMode));
1214     CHECK_NULLPTR_RET(AddDefaultPhotoAlbumColumns(env, context->fetchColumn));
1215     context->hiddenAlbumFetchMode = fetchMode;
1216     if (fetchMode == HiddenPhotosDisplayMode::ASSETS_MODE) {
1217         return result;
1218     }
1219     context->hiddenOnly = true;
1220     context->fetchColumn.push_back(PhotoAlbumColumns::HIDDEN_COUNT);
1221     context->fetchColumn.push_back(PhotoAlbumColumns::HIDDEN_COVER);
1222     return result;
1223 }
1224 
PahGetHiddenAlbums(napi_env env,napi_callback_info info)1225 napi_value SendablePhotoAccessHelper::PahGetHiddenAlbums(napi_env env, napi_callback_info info)
1226 {
1227     auto asyncContext = make_unique<SendablePhotoAccessHelperAsyncContext>();
1228     asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
1229     CHECK_NULLPTR_RET(ParseArgsGetHiddenAlbums(env, info, asyncContext));
1230     return SendableMediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "PahGetHiddenAlbums",
1231         JSGetPhotoAlbumsExecute, JSGetPhotoAlbumsCompleteCallback);
1232 }
1233 
1234 template <class AsyncContext>
AsyncContextSetStaticObjectInfo(napi_env env,napi_callback_info info,AsyncContext & asyncContext,const size_t minArgs,const size_t maxArgs)1235 static napi_status AsyncContextSetStaticObjectInfo(napi_env env, napi_callback_info info,
1236     AsyncContext &asyncContext, const size_t minArgs, const size_t maxArgs)
1237 {
1238     NAPI_INFO_LOG("AsyncContextSetStaticObjectInfo start");
1239     napi_value thisVar = nullptr;
1240     asyncContext->argc = maxArgs;
1241     CHECK_STATUS_RET(napi_get_cb_info(env, info, &asyncContext->argc, &(asyncContext->argv[ARGS_ZERO]), &thisVar,
1242         nullptr), "Failed to get cb info");
1243     CHECK_COND_RET(((asyncContext->argc >= minArgs) && (asyncContext->argc <= maxArgs)), napi_invalid_arg,
1244         "Number of args is invalid");
1245     if (minArgs > 0) {
1246         CHECK_COND_RET(asyncContext->argv[ARGS_ZERO] != nullptr, napi_invalid_arg, "Argument list is empty");
1247     }
1248     CHECK_STATUS_RET(SendableMediaLibraryNapiUtils::GetParamCallback(env, asyncContext),
1249         "Failed to get callback param!");
1250     return napi_ok;
1251 }
1252 
CheckDisplayNameParams(SendablePhotoAccessHelperAsyncContext * context)1253 static bool CheckDisplayNameParams(SendablePhotoAccessHelperAsyncContext *context)
1254 {
1255     if (context == nullptr) {
1256         NAPI_ERR_LOG("Async context is null");
1257         return false;
1258     }
1259     if (!context->isCreateByComponent) {
1260         bool isValid = false;
1261         string displayName = context->valuesBucket.Get(MEDIA_DATA_DB_NAME, isValid);
1262         if (!isValid) {
1263             NAPI_ERR_LOG("getting displayName is invalid");
1264             return false;
1265         }
1266         if (displayName.empty()) {
1267             return false;
1268         }
1269     }
1270 
1271     return true;
1272 }
1273 
CheckCreateOption(SendablePhotoAccessHelperAsyncContext & context)1274 static napi_status CheckCreateOption(SendablePhotoAccessHelperAsyncContext &context)
1275 {
1276     bool isValid = false;
1277     int32_t subtype = context.valuesBucket.Get(PhotoColumn::PHOTO_SUBTYPE, isValid);
1278     string cameraShotKey = context.valuesBucket.Get(PhotoColumn::CAMERA_SHOT_KEY, isValid);
1279     if (isValid) {
1280         if (cameraShotKey.size() < CAMERA_SHOT_KEY_SIZE) {
1281             NAPI_ERR_LOG("cameraShotKey is not null with but is less than CAMERA_SHOT_KEY_SIZE");
1282             return napi_invalid_arg;
1283         }
1284         if (subtype == static_cast<int32_t>(PhotoSubType::SCREENSHOT)) {
1285             NAPI_ERR_LOG("cameraShotKey is not null with subtype is SCREENSHOT");
1286             return napi_invalid_arg;
1287         } else {
1288             context.valuesBucket.Put(PhotoColumn::PHOTO_SUBTYPE, static_cast<int32_t>(PhotoSubType::CAMERA));
1289         }
1290     }
1291 
1292     return napi_ok;
1293 }
1294 
ParsePhotoAssetCreateOption(napi_env env,napi_value arg,SendablePhotoAccessHelperAsyncContext & context)1295 static napi_status ParsePhotoAssetCreateOption(napi_env env, napi_value arg,
1296     SendablePhotoAccessHelperAsyncContext &context)
1297 {
1298     for (const auto &iter : PHOTO_CREATE_OPTIONS_PARAM) {
1299         string param = iter.first;
1300         bool present = false;
1301         napi_status result = napi_has_named_property(env, arg, param.c_str(), &present);
1302         CHECK_COND_RET(result == napi_ok, result, "failed to check named property");
1303         if (!present) {
1304             continue;
1305         }
1306         napi_value value;
1307         result = napi_get_named_property(env, arg, param.c_str(), &value);
1308         CHECK_COND_RET(result == napi_ok, result, "failed to get named property");
1309         napi_valuetype valueType = napi_undefined;
1310         result = napi_typeof(env, value, &valueType);
1311         CHECK_COND_RET(result == napi_ok, result, "failed to get value type");
1312         if (valueType == napi_number) {
1313             int32_t number = 0;
1314             result = napi_get_value_int32(env, value, &number);
1315             CHECK_COND_RET(result == napi_ok, result, "failed to get int32_t");
1316             context.valuesBucket.Put(iter.second, number);
1317         } else if (valueType == napi_boolean) {
1318             bool isTrue = false;
1319             result = napi_get_value_bool(env, value, &isTrue);
1320             CHECK_COND_RET(result == napi_ok, result, "failed to get bool");
1321             context.valuesBucket.Put(iter.second, isTrue);
1322         } else if (valueType == napi_string) {
1323             char buffer[ARG_BUF_SIZE];
1324             size_t res = 0;
1325             result = napi_get_value_string_utf8(env, value, buffer, ARG_BUF_SIZE, &res);
1326             CHECK_COND_RET(result == napi_ok, result, "failed to get string");
1327             context.valuesBucket.Put(iter.second, string(buffer));
1328         } else if (valueType == napi_undefined || valueType == napi_null) {
1329             continue;
1330         } else {
1331             NAPI_ERR_LOG("valueType %{public}d is unaccepted", static_cast<int>(valueType));
1332             return napi_invalid_arg;
1333         }
1334     }
1335 
1336     return CheckCreateOption(context);
1337 }
1338 
ParseArgsCreatePhotoAssetSystem(napi_env env,napi_callback_info info,unique_ptr<SendablePhotoAccessHelperAsyncContext> & context)1339 static napi_value ParseArgsCreatePhotoAssetSystem(napi_env env, napi_callback_info info,
1340     unique_ptr<SendablePhotoAccessHelperAsyncContext> &context)
1341 {
1342     /* Parse the first argument into displayName */
1343     napi_valuetype valueType;
1344     MediaType mediaType;
1345     string displayName;
1346     NAPI_ASSERT(env, SendableMediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[ARGS_ZERO], displayName) ==
1347         napi_ok, "Failed to get displayName");
1348     mediaType = MediaFileUtils::GetMediaType(displayName);
1349     NAPI_ASSERT(env, (mediaType == MEDIA_TYPE_IMAGE || mediaType == MEDIA_TYPE_VIDEO), "invalid file type");
1350     context->valuesBucket.Put(MEDIA_DATA_DB_NAME, displayName);
1351 
1352     /* Parse the second argument into albumUri if exists */
1353     string albumUri;
1354     if ((context->argc >= ARGS_TWO)) {
1355         NAPI_ASSERT(env, napi_typeof(env, context->argv[ARGS_ONE], &valueType) == napi_ok, "Failed to get napi type");
1356         if (valueType == napi_string) {
1357             if (SendableMediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[ARGS_ONE],
1358                 albumUri) == napi_ok) {
1359                 context->valuesBucket.Put(MEDIA_DATA_DB_ALARM_URI, albumUri);
1360             }
1361         } else if (valueType == napi_object) {
1362             NAPI_ASSERT(env, ParsePhotoAssetCreateOption(env, context->argv[ARGS_ONE], *context) == napi_ok,
1363                 "Parse asset create option failed");
1364         }
1365     }
1366 
1367     context->valuesBucket.Put(MEDIA_DATA_DB_MEDIA_TYPE, static_cast<int32_t>(mediaType));
1368     NAPI_ASSERT(env, SendableMediaLibraryNapiUtils::GetParamCallback(env, context) == napi_ok,
1369         "Failed to get callback");
1370 
1371     napi_value result = nullptr;
1372     NAPI_CALL(env, napi_get_boolean(env, true, &result));
1373     return result;
1374 }
1375 
ParseCreateOptions(napi_env env,napi_value arg,SendablePhotoAccessHelperAsyncContext & context)1376 static napi_status ParseCreateOptions(napi_env env, napi_value arg, SendablePhotoAccessHelperAsyncContext &context)
1377 {
1378     for (const auto &iter : CREATE_OPTIONS_PARAM) {
1379         string param = iter.first;
1380         bool present = false;
1381         napi_status result = napi_has_named_property(env, arg, param.c_str(), &present);
1382         CHECK_COND_RET(result == napi_ok, result, "failed to check named property");
1383         if (!present) {
1384             continue;
1385         }
1386         napi_value value;
1387         result = napi_get_named_property(env, arg, param.c_str(), &value);
1388         CHECK_COND_RET(result == napi_ok, result, "failed to get named property");
1389         napi_valuetype valueType = napi_undefined;
1390         result = napi_typeof(env, value, &valueType);
1391         CHECK_COND_RET(result == napi_ok, result, "failed to get value type");
1392         if (valueType == napi_number) {
1393             int32_t number = 0;
1394             result = napi_get_value_int32(env, value, &number);
1395             CHECK_COND_RET(result == napi_ok, result, "failed to get int32_t");
1396             context.valuesBucket.Put(iter.second, number);
1397         } else if (valueType == napi_boolean) {
1398             bool isTrue = false;
1399             result = napi_get_value_bool(env, value, &isTrue);
1400             CHECK_COND_RET(result == napi_ok, result, "failed to get bool");
1401             context.valuesBucket.Put(iter.second, isTrue);
1402         } else if (valueType == napi_string) {
1403             char buffer[ARG_BUF_SIZE];
1404             size_t res = 0;
1405             result = napi_get_value_string_utf8(env, value, buffer, ARG_BUF_SIZE, &res);
1406             CHECK_COND_RET(result == napi_ok, result, "failed to get string");
1407             context.valuesBucket.Put(iter.second, string(buffer));
1408         } else if (valueType == napi_undefined || valueType == napi_null) {
1409             continue;
1410         } else {
1411             NAPI_ERR_LOG("ParseCreateOptions failed, valueType %{public}d is unaccepted",
1412                 static_cast<int>(valueType));
1413             return napi_invalid_arg;
1414         }
1415     }
1416 
1417     return napi_ok;
1418 }
1419 
1420 
ParseArgsCreatePhotoAssetComponent(napi_env env,napi_callback_info info,unique_ptr<SendablePhotoAccessHelperAsyncContext> & context)1421 static napi_value ParseArgsCreatePhotoAssetComponent(napi_env env, napi_callback_info info,
1422     unique_ptr<SendablePhotoAccessHelperAsyncContext> &context)
1423 {
1424     /* Parse the first argument into displayName */
1425     napi_valuetype valueType;
1426     MediaType mediaType;
1427     int32_t type = 0;
1428     NAPI_ASSERT(env, napi_get_value_int32(env, context->argv[ARGS_ZERO], &type) == napi_ok,
1429         "Failed to get type value");
1430     mediaType = static_cast<MediaType>(type);
1431     NAPI_ASSERT(env, (mediaType == MEDIA_TYPE_IMAGE || mediaType == MEDIA_TYPE_VIDEO), "invalid file type");
1432 
1433     /* Parse the second argument into albumUri if exists */
1434     string extention;
1435     NAPI_ASSERT(env, SendableMediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[ARGS_ONE], extention) ==
1436         napi_ok, "Failed to get extention");
1437     context->valuesBucket.Put(ASSET_EXTENTION, extention);
1438 
1439     /* Parse the third argument into albumUri if exists */
1440     if (context->argc >= ARGS_THREE) {
1441         NAPI_ASSERT(env, napi_typeof(env, context->argv[ARGS_TWO], &valueType) == napi_ok, "Failed to get napi type");
1442         if (valueType == napi_object) {
1443             NAPI_ASSERT(env, ParseCreateOptions(env, context->argv[ARGS_TWO], *context) == napi_ok,
1444                 "Parse asset create option failed");
1445         } else if (valueType != napi_function) {
1446             NAPI_ERR_LOG("Napi type is wrong in create options");
1447             return nullptr;
1448         }
1449     }
1450 
1451     context->valuesBucket.Put(MEDIA_DATA_DB_MEDIA_TYPE, static_cast<int32_t>(mediaType));
1452 
1453     napi_value result = nullptr;
1454     NAPI_CALL(env, napi_get_boolean(env, true, &result));
1455     return result;
1456 }
1457 
PhotoAccessSetFileAssetByIdV10(int32_t id,const string & networkId,const string & uri,SendablePhotoAccessHelperAsyncContext * context)1458 static void PhotoAccessSetFileAssetByIdV10(int32_t id, const string &networkId, const string &uri,
1459                                            SendablePhotoAccessHelperAsyncContext *context)
1460 {
1461     bool isValid = false;
1462     string displayName = context->valuesBucket.Get(MEDIA_DATA_DB_NAME, isValid);
1463     if (!isValid) {
1464         NAPI_ERR_LOG("getting title is invalid");
1465         return;
1466     }
1467     auto fileAsset = make_unique<FileAsset>();
1468     fileAsset->SetId(id);
1469     MediaType mediaType = MediaFileUtils::GetMediaType(displayName);
1470     fileAsset->SetUri(uri);
1471     fileAsset->SetMediaType(mediaType);
1472     fileAsset->SetDisplayName(displayName);
1473     fileAsset->SetTitle(MediaFileUtils::GetTitleFromDisplayName(displayName));
1474     fileAsset->SetResultNapiType(ResultNapiType::TYPE_PHOTOACCESS_HELPER);
1475     fileAsset->SetTimePending(UNCREATE_FILE_TIMEPENDING);
1476     context->fileAsset = move(fileAsset);
1477 }
1478 
GetCreateUri(SendablePhotoAccessHelperAsyncContext * context,string & uri)1479 static void GetCreateUri(SendablePhotoAccessHelperAsyncContext *context, string &uri)
1480 {
1481     if (context->resultNapiType == ResultNapiType::TYPE_USERFILE_MGR ||
1482         context->resultNapiType == ResultNapiType::TYPE_PHOTOACCESS_HELPER) {
1483         switch (context->assetType) {
1484             case TYPE_PHOTO:
1485                 uri = (context->resultNapiType == ResultNapiType::TYPE_USERFILE_MGR) ?
1486                     ((context->isCreateByComponent) ? UFM_CREATE_PHOTO_COMPONENT : UFM_CREATE_PHOTO) :
1487                     ((context->isCreateByComponent) ? PAH_CREATE_PHOTO_COMPONENT : PAH_CREATE_PHOTO);
1488                 break;
1489             case TYPE_AUDIO:
1490                 uri = (context->isCreateByComponent) ? UFM_CREATE_AUDIO_COMPONENT : UFM_CREATE_AUDIO;
1491                 break;
1492             default:
1493                 NAPI_ERR_LOG("Unsupported creation napitype %{public}d", static_cast<int32_t>(context->assetType));
1494                 return;
1495         }
1496         SendableMediaLibraryNapiUtils::UriAppendKeyValue(uri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
1497     } else {
1498 #ifdef MEDIALIBRARY_COMPATIBILITY
1499         bool isValid = false;
1500         string relativePath = context->valuesBucket.Get(MEDIA_DATA_DB_RELATIVE_PATH, isValid);
1501         if (MediaFileUtils::StartsWith(relativePath, DOCS_PATH + DOC_DIR_VALUES) ||
1502             MediaFileUtils::StartsWith(relativePath, DOCS_PATH + DOWNLOAD_DIR_VALUES)) {
1503             uri = MEDIALIBRARY_DATA_URI + "/" + MEDIA_FILEOPRN + "/" + MEDIA_FILEOPRN_CREATEASSET;
1504             SendableMediaLibraryNapiUtils::UriAppendKeyValue(uri, API_VERSION, to_string(MEDIA_API_VERSION_V9));
1505             return;
1506         }
1507         switch (context->assetType) {
1508             case TYPE_PHOTO:
1509                 uri = MEDIALIBRARY_DATA_URI + "/" + MEDIA_PHOTOOPRN + "/" + MEDIA_FILEOPRN_CREATEASSET;
1510                 break;
1511             case TYPE_AUDIO:
1512                 uri = MEDIALIBRARY_DATA_URI + "/" + MEDIA_AUDIOOPRN + "/" + MEDIA_FILEOPRN_CREATEASSET;
1513                 break;
1514             case TYPE_DEFAULT:
1515                 uri = MEDIALIBRARY_DATA_URI + "/" + MEDIA_FILEOPRN + "/" + MEDIA_FILEOPRN_CREATEASSET;
1516                 break;
1517             default:
1518                 NAPI_ERR_LOG("Unsupported creation napi type %{public}d", static_cast<int32_t>(context->assetType));
1519                 return;
1520         }
1521         SendableMediaLibraryNapiUtils::UriAppendKeyValue(uri, API_VERSION, to_string(MEDIA_API_VERSION_V9));
1522 #else
1523         uri = MEDIALIBRARY_DATA_URI + "/" + MEDIA_FILEOPRN + "/" + MEDIA_FILEOPRN_CREATEASSET;
1524 #endif
1525     }
1526 }
1527 
PhotoAccessCreateAssetExecute(napi_env env,void * data)1528 static void PhotoAccessCreateAssetExecute(napi_env env, void *data)
1529 {
1530     MediaLibraryTracer tracer;
1531     tracer.Start("JSCreateAssetExecute");
1532 
1533     auto *context = static_cast<SendablePhotoAccessHelperAsyncContext*>(data);
1534     if (!CheckDisplayNameParams(context)) {
1535         context->error = JS_E_DISPLAYNAME;
1536         return;
1537     }
1538 
1539     string uri;
1540     GetCreateUri(context, uri);
1541     Uri createFileUri(uri);
1542     string outUri;
1543     int index = UserFileClient::InsertExt(createFileUri, context->valuesBucket, outUri);
1544     if (index < 0) {
1545         context->SaveError(index);
1546         NAPI_ERR_LOG("InsertExt fail, index: %{public}d.", index);
1547     } else {
1548         if (context->isCreateByComponent) {
1549             context->uri = outUri;
1550         } else {
1551             PhotoAccessSetFileAssetByIdV10(index, "", outUri, context);
1552         }
1553     }
1554 }
1555 
ParseArgsCreatePhotoAsset(napi_env env,napi_callback_info info,unique_ptr<SendablePhotoAccessHelperAsyncContext> & context)1556 static napi_value ParseArgsCreatePhotoAsset(napi_env env, napi_callback_info info,
1557     unique_ptr<SendablePhotoAccessHelperAsyncContext> &context)
1558 {
1559     constexpr size_t minArgs = ARGS_ONE;
1560     constexpr size_t maxArgs = ARGS_FOUR;
1561     NAPI_ASSERT(env, SendableMediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs) ==
1562         napi_ok, "Failed to get object info");
1563 
1564     napi_valuetype valueType;
1565     NAPI_ASSERT(env, napi_typeof(env, context->argv[ARGS_ZERO], &valueType) == napi_ok, "Failed to get napi type");
1566     if (valueType == napi_string) {
1567         context->isCreateByComponent = false;
1568         if (!SendableMediaLibraryNapiUtils::IsSystemApp()) {
1569             NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
1570             return nullptr;
1571         }
1572         return ParseArgsCreatePhotoAssetSystem(env, info, context);
1573     } else if (valueType == napi_number) {
1574         context->isCreateByComponent = true;
1575         return ParseArgsCreatePhotoAssetComponent(env, info, context);
1576     } else {
1577         NAPI_ERR_LOG("JS param type %{public}d is wrong", static_cast<int32_t>(valueType));
1578         return nullptr;
1579     }
1580 }
1581 
JSCreateUriInCallback(napi_env env,SendablePhotoAccessHelperAsyncContext * context,unique_ptr<SendableJSAsyncContextOutput> & jsContext)1582 static void JSCreateUriInCallback(napi_env env, SendablePhotoAccessHelperAsyncContext *context,
1583     unique_ptr<SendableJSAsyncContextOutput> &jsContext)
1584 {
1585     napi_value jsObject = nullptr;
1586     if (context->uri.empty()) {
1587         SendableMediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, ERR_INVALID_OUTPUT,
1588             "Obtain file asset uri failed");
1589         napi_get_undefined(env, &jsContext->data);
1590     } else {
1591         napi_status status = napi_create_string_utf8(env, context->uri.c_str(), NAPI_AUTO_LENGTH, &jsObject);
1592         if (status != napi_ok || jsObject == nullptr) {
1593             NAPI_ERR_LOG("Failed to get file asset uri napi object");
1594             napi_get_undefined(env, &jsContext->data);
1595             SendableMediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, JS_INNER_FAIL,
1596                 "System inner fail");
1597         } else {
1598             jsContext->data = jsObject;
1599             napi_get_undefined(env, &jsContext->error);
1600             jsContext->status = true;
1601         }
1602     }
1603 }
1604 
JSCreateAssetInCallback(napi_env env,SendablePhotoAccessHelperAsyncContext * context,unique_ptr<SendableJSAsyncContextOutput> & jsContext)1605 static void JSCreateAssetInCallback(napi_env env, SendablePhotoAccessHelperAsyncContext *context,
1606     unique_ptr<SendableJSAsyncContextOutput> &jsContext)
1607 {
1608     napi_value jsFileAsset = nullptr;
1609     if (context->fileAsset == nullptr) {
1610         SendableMediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, ERR_INVALID_OUTPUT,
1611             "Obtain file asset failed");
1612         napi_get_undefined(env, &jsContext->data);
1613     } else {
1614         jsFileAsset = SendableFileAssetNapi::CreateFileAsset(env, context->fileAsset);
1615         if (jsFileAsset == nullptr) {
1616             NAPI_ERR_LOG("Failed to get file asset napi object");
1617             napi_get_undefined(env, &jsContext->data);
1618             SendableMediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, JS_INNER_FAIL,
1619                 "System inner fail");
1620         } else {
1621             NAPI_DEBUG_LOG("JSCreateAssetCompleteCallback jsFileAsset != nullptr");
1622             jsContext->data = jsFileAsset;
1623             napi_get_undefined(env, &jsContext->error);
1624             jsContext->status = true;
1625         }
1626     }
1627 }
1628 
JSCreateAssetCompleteCallback(napi_env env,napi_status status,void * data)1629 static void JSCreateAssetCompleteCallback(napi_env env, napi_status status, void *data)
1630 {
1631     MediaLibraryTracer tracer;
1632     tracer.Start("JSCreateAssetCompleteCallback");
1633 
1634     auto *context = static_cast<SendablePhotoAccessHelperAsyncContext*>(data);
1635     auto jsContext = make_unique<SendableJSAsyncContextOutput>();
1636     jsContext->status = false;
1637 
1638     if (context->error == ERR_DEFAULT) {
1639         if (context->isCreateByComponent) {
1640             JSCreateUriInCallback(env, context, jsContext);
1641         } else {
1642             JSCreateAssetInCallback(env, context, jsContext);
1643         }
1644     } else {
1645         context->HandleError(env, jsContext->error);
1646         napi_get_undefined(env, &jsContext->data);
1647     }
1648 
1649     tracer.Finish();
1650     if (context->work != nullptr) {
1651         SendableMediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
1652             context->work, *jsContext);
1653     }
1654     delete context;
1655 }
1656 
PhotoAccessHelperCreatePhotoAsset(napi_env env,napi_callback_info info)1657 napi_value SendablePhotoAccessHelper::PhotoAccessHelperCreatePhotoAsset(napi_env env, napi_callback_info info)
1658 {
1659     MediaLibraryTracer tracer;
1660     tracer.Start("PhotoAccessHelperCreatePhotoAsset");
1661 
1662     NAPI_INFO_LOG("enter");
1663 
1664     unique_ptr<SendablePhotoAccessHelperAsyncContext> asyncContext =
1665         make_unique<SendablePhotoAccessHelperAsyncContext>();
1666     asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
1667     asyncContext->assetType = TYPE_PHOTO;
1668     NAPI_ASSERT(env, ParseArgsCreatePhotoAsset(env, info, asyncContext), "Failed to parse js args");
1669 
1670     return SendableMediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "PhotoAccessHelperCreatePhotoAsset",
1671         PhotoAccessCreateAssetExecute, JSCreateAssetCompleteCallback);
1672 }
1673 
ParseArgsGetBurstAssets(napi_env env,napi_callback_info info,unique_ptr<SendablePhotoAccessHelperAsyncContext> & context)1674 static napi_value ParseArgsGetBurstAssets(napi_env env, napi_callback_info info,
1675     unique_ptr<SendablePhotoAccessHelperAsyncContext> &context)
1676 {
1677     constexpr size_t minArgs = ARGS_ONE;
1678     constexpr size_t maxArgs = ARGS_TWO;
1679     CHECK_ARGS(env, SendableMediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs),
1680         OHOS_INVALID_PARAM_CODE);
1681 
1682     /* Parse the first argument */
1683     std::string burstKey;
1684     CHECK_ARGS(env, SendableMediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[PARAM0], burstKey),
1685         OHOS_INVALID_PARAM_CODE);
1686     if (burstKey.empty()) {
1687         NAPI_ERR_LOG("The input burstkey cannot be empty");
1688         return nullptr;
1689     }
1690     /* Parse the second argument */
1691     CHECK_ARGS(env, SendableMediaLibraryNapiUtils::GetFetchOption(env, context->argv[PARAM1], ASSET_FETCH_OPT,
1692         context), JS_INNER_FAIL);
1693 
1694     auto &predicates = context->predicates;
1695     if (context->assetType != TYPE_PHOTO) {
1696         return nullptr;
1697     }
1698     CHECK_NULLPTR_RET(SendableMediaLibraryNapiUtils::AddDefaultAssetColumns(env, context->fetchColumn,
1699         PhotoColumn::IsPhotoColumn, TYPE_PHOTO));
1700     predicates.And()->EqualTo(PhotoColumn::PHOTO_BURST_KEY, burstKey);
1701     predicates.And()->EqualTo(MediaColumn::MEDIA_TIME_PENDING, to_string(0));
1702     predicates.And()->EqualTo(PhotoColumn::PHOTO_IS_TEMP, to_string(0));
1703     predicates.OrderByAsc(MediaColumn::MEDIA_NAME);
1704 
1705     napi_value result = nullptr;
1706     CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
1707     return result;
1708 }
1709 
PhotoAccessGetBurstAssets(napi_env env,napi_callback_info info)1710 napi_value SendablePhotoAccessHelper::PhotoAccessGetBurstAssets(napi_env env, napi_callback_info info)
1711 {
1712     NAPI_INFO_LOG("PhotoAccessHelper::PhotoAccessGetBurstAssets start");
1713     unique_ptr<SendablePhotoAccessHelperAsyncContext> asyncContext =
1714         make_unique<SendablePhotoAccessHelperAsyncContext>();
1715     asyncContext->assetType = TYPE_PHOTO;
1716     CHECK_NULLPTR_RET(ParseArgsGetBurstAssets(env, info, asyncContext));
1717 
1718     return SendableMediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSGetPhotoAssets",
1719         PhotoAccessGetAssetsExecute, GetFileAssetsAsyncCallbackComplete);
1720 }
1721 
PhotoAccessGetSharedPhotoAssets(napi_env env,napi_callback_info info)1722 napi_value SendablePhotoAccessHelper::PhotoAccessGetSharedPhotoAssets(napi_env env, napi_callback_info info)
1723 {
1724     MediaLibraryTracer tracer;
1725     tracer.Start("PhotoAccessGetSharedPhotoAssets");
1726     unique_ptr<SendablePhotoAccessHelperAsyncContext> asyncContext =
1727         make_unique<SendablePhotoAccessHelperAsyncContext>();
1728     asyncContext->assetType = TYPE_PHOTO;
1729     CHECK_NULLPTR_RET(ParseArgsGetAssets(env, info, asyncContext));
1730 
1731     SendablePhotoAccessHelperAsyncContext* context =
1732         static_cast<SendablePhotoAccessHelperAsyncContext*>((asyncContext.get()));
1733     string queryUri = PAH_QUERY_PHOTO;
1734     SendableMediaLibraryNapiUtils::UriAppendKeyValue(queryUri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
1735 
1736     Uri uri(queryUri);
1737     shared_ptr<NativeRdb::AbsSharedResultSet> resultSet = UserFileClient::QueryRdb(uri,
1738         context->predicates, context->fetchColumn);
1739     CHECK_NULLPTR_RET(resultSet);
1740 
1741     napi_value jsFileArray = 0;
1742     napi_create_array(env, &jsFileArray);
1743 
1744     int count = 0;
1745     int err = resultSet->GoToFirstRow();
1746     if (err != napi_ok) {
1747         NAPI_ERR_LOG("Failed GoToFirstRow %{public}d", err);
1748         return jsFileArray;
1749     }
1750     do {
1751         napi_value item = SendableMediaLibraryNapiUtils::GetNextRowObject(env, resultSet);
1752         napi_set_element(env, jsFileArray, count++, item);
1753     } while (!resultSet->GoToNextRow());
1754     resultSet->Close();
1755     return jsFileArray;
1756 }
1757 } // namespace Media
1758 } // namespace OHOS