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