• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2025 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 "PhotoAssetCustomRecordManagerNapi"
17 
18 #include "photo_asset_custom_record_manager_napi.h"
19 
20 #include <cstdlib>
21 #include "custom_record_uri.h"
22 #include "custom_records_column.h"
23 #include "medialibrary_errno.h"
24 #include "medialibrary_client_errno.h"
25 #include "userfile_client.h"
26 #include "medialibrary_napi_utils.h"
27 #include "media_library_napi.h"
28 #include "fetch_file_result_napi.h"
29 #include "js_proxy.h"
30 #include "result_set_utils.h"
31 #include "medialibrary_errno.h"
32 
33 namespace OHOS::Media {
34 static const std::string PHOTO_ASSET_CUSTOM_RECORD_MANAGER = "PhotoAssetCustomRecordManager";
35 thread_local napi_ref PhotoAssetCustomRecordManager::constructor_ = nullptr;
36 
37 const int32_t OPERATION_MAX_LEN = 200;
38 const int32_t OPERATION_MAX_FILE_IDS_LEN = 500;
39 const int32_t OPERATION_MIN_LEN = 0;
40 const std::string AUTO_INCREMENT = "1";
41 const std::string NapiCustomRecordStr::FILE_ID = "file_id";
42 const std::string NapiCustomRecordStr::SHARE_COUNT = "share_count";
43 const std::string NapiCustomRecordStr::LCD_JUMP_COUNT = "lcd_jump_count";
44 
Init(napi_env env,napi_value exports)45 napi_value PhotoAssetCustomRecordManager::Init(napi_env env, napi_value exports)
46 {
47     NapiClassInfo info = {
48         .name = PHOTO_ASSET_CUSTOM_RECORD_MANAGER,
49         .ref = &constructor_,
50         .constructor = Constructor,
51         .props = {
52             DECLARE_NAPI_STATIC_FUNCTION("getCustomRecordManagerInstance", JSGetCustomRecordsInstance),
53             DECLARE_NAPI_FUNCTION("createCustomRecords", JSCreateCustomRecords),
54             DECLARE_NAPI_FUNCTION("getCustomRecords", JSGetCustomRecords),
55             DECLARE_NAPI_FUNCTION("setCustomRecords", JSSetCustomRecords),
56             DECLARE_NAPI_FUNCTION("removeCustomRecords", JSRemoveCustomRecords),
57             DECLARE_NAPI_FUNCTION("addShareCount", JSAddShareCount),
58             DECLARE_NAPI_FUNCTION("addLcdJumpCount", JSAddLCDJumpCount),
59         } };
60     MediaLibraryNapiUtils::NapiDefineClass(env, exports, info);
61     return exports;
62 }
63 
ParseUserIdFormCbInfo(napi_env env,napi_callback_info info)64 static int32_t ParseUserIdFormCbInfo(napi_env env, napi_callback_info info)
65 {
66     size_t argc = ARGS_TWO;
67     napi_value argv[ARGS_TWO] = { 0 };
68     napi_status status;
69     int32_t userId = -1;
70     status = napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
71     if (status == napi_ok) {
72         napi_valuetype valueType = napi_undefined;
73         status = napi_typeof(env, argv[ARGS_ONE], &valueType);
74         if (status == napi_ok && valueType == napi_number) {
75             napi_get_value_int32(env, argv[ARGS_ONE], &userId);
76         }
77     }
78     return userId;
79 }
80 
Constructor(napi_env env,napi_callback_info info)81 napi_value PhotoAssetCustomRecordManager::Constructor(napi_env env, napi_callback_info info)
82 {
83     if (!MediaLibraryNapiUtils::IsSystemApp()) {
84         NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL,
85             "The custom record asset manager instance can be called only by system apps");
86         return nullptr;
87     }
88     napi_value newTarget = nullptr;
89     CHECK_ARGS(env, napi_get_new_target(env, info, &newTarget), JS_E_INIT_FAIL);
90     if (!InitUserFileClient(env, info)) {
91         NapiError::ThrowError(env, JS_E_INIT_FAIL, "user file client init fail");
92         return nullptr;
93     }
94     CHECK_COND_RET(newTarget != nullptr, nullptr, "Failed to check new.target");
95     int32_t userId = ParseUserIdFormCbInfo(env, info);
96     UserFileClient::SetUserId(userId);
97 
98     size_t argc = ARGS_ONE;
99     napi_value argv[ARGS_ONE] = { 0 };
100     napi_value thisVar = nullptr;
101     CHECK_ARGS(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr), JS_INNER_FAIL);
102     CHECK_COND_WITH_MESSAGE(env, argc == ARGS_ONE, "Number of args is invalid");
103 
104     std::unique_ptr<PhotoAssetCustomRecordManager> obj = std::make_unique<PhotoAssetCustomRecordManager>();
105     CHECK_COND(env, obj != nullptr, JS_INNER_FAIL);
106     CHECK_ARGS(env,
107         napi_wrap(env, thisVar, reinterpret_cast<void*>(obj.get()), PhotoAssetCustomRecordManager::Destructor, nullptr,
108             nullptr),
109         JS_INNER_FAIL);
110     obj.release();
111     return thisVar;
112 }
113 
Destructor(napi_env env,void * nativeObject,void * finalizeHint)114 void PhotoAssetCustomRecordManager::Destructor(napi_env env, void* nativeObject, void* finalizeHint)
115 {
116     auto* photoAssetCustomRecordManager = reinterpret_cast<PhotoAssetCustomRecordManager*>(nativeObject);
117     if (photoAssetCustomRecordManager == nullptr) {
118         NAPI_ERR_LOG("photoAssetCustomRecordManager is nullptr");
119         return;
120     }
121     delete photoAssetCustomRecordManager;
122     photoAssetCustomRecordManager = nullptr;
123 }
124 
CheckWhetherInitSuccess(napi_env env,napi_value value,bool checkIsValid)125 static bool CheckWhetherInitSuccess(napi_env env, napi_value value, bool checkIsValid)
126 {
127     napi_value propertyNames;
128     uint32_t propertyLength;
129     napi_valuetype valueType = napi_undefined;
130     NAPI_CALL_BASE(env, napi_typeof(env, value, &valueType), false);
131     if (valueType != napi_object) {
132         NAPI_ERR_LOG("valueType is not valid");
133         return false;
134     }
135 
136     NAPI_CALL_BASE(env, napi_get_property_names(env, value, &propertyNames), false);
137     NAPI_CALL_BASE(env, napi_get_array_length(env, propertyNames, &propertyLength), false);
138     if (propertyLength == 0) {
139         NAPI_ERR_LOG("propertyLength is 0");
140         return false;
141     }
142     if (checkIsValid && (!UserFileClient::IsValid())) {
143         NAPI_ERR_LOG("UserFileClient is not valid");
144         return false;
145     }
146     return true;
147 }
148 
InitUserFileClient(napi_env env,napi_callback_info info)149 bool PhotoAssetCustomRecordManager::InitUserFileClient(napi_env env, napi_callback_info info)
150 {
151     if (UserFileClient::IsValid()) {
152         return true;
153     }
154     std::unique_lock<std::mutex> helperLock(MediaLibraryNapi::sUserFileClientMutex_);
155     if (!UserFileClient::IsValid()) {
156         UserFileClient::Init(env, info);
157     }
158     helperLock.unlock();
159     return UserFileClient::IsValid();
160 }
161 
JSGetCustomRecordsInstance(napi_env env,napi_callback_info info)162 napi_value PhotoAssetCustomRecordManager::JSGetCustomRecordsInstance(napi_env env, napi_callback_info info)
163 {
164     if (!MediaLibraryNapiUtils::IsSystemApp()) {
165         NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL,
166             "The custom record asset manager instance can be called only by system apps");
167         return nullptr;
168     }
169     constexpr size_t ARG_CONTEXT = 1;
170     size_t argc = ARG_CONTEXT;
171     napi_value argv[ARGS_TWO] = { 0 };
172 
173     napi_value thisVar = nullptr;
174     napi_value ctor = nullptr;
175     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
176     CHECK_COND_WITH_MESSAGE(env, argc == ARGS_ONE, "Number of args is invalid");
177     NAPI_CALL(env, napi_get_reference_value(env, constructor_, &ctor));
178 
179     napi_value result = nullptr;
180 
181     NAPI_CALL(env, napi_new_instance(env, ctor, argc, argv, &result));
182     if (!CheckWhetherInitSuccess(env, result, false)) {
183         NAPI_ERR_LOG("GetCustomRecordsInstance is failed");
184         NAPI_CALL(env, napi_get_undefined(env, &result));
185     }
186     return result;
187 }
188 
ParseArgsCustomRecords(napi_env env,napi_callback_info info,std::unique_ptr<CustomRecordAsyncContext> & context)189 static napi_value ParseArgsCustomRecords(napi_env env, napi_callback_info info,
190     std::unique_ptr<CustomRecordAsyncContext>& context)
191 {
192     napi_value result = nullptr;
193     CHECK_NULL_PTR_RETURN_UNDEFINED(env, context, result, "async context is null");
194     context->argc = ARGS_TWO;
195     CHECK_ARGS(env, napi_get_cb_info(env, info, &context->argc, &(context->argv[ARGS_ZERO]), NULL, NULL),
196         JS_E_PARAM_INVALID);
197     uint32_t len = 0;
198     CHECK_ARGS(env, napi_get_array_length(env, context->argv[ARGS_ZERO], &len), JS_E_PARAM_INVALID);
199     if (context->argc != ARGS_ONE || len > OPERATION_MAX_LEN || len == OPERATION_MIN_LEN) {
200         context->error = JS_E_PARAM_INVALID;
201         return nullptr;
202     }
203     std::vector<PhotoAssetCustomRecord> customRecords;
204     for (uint32_t i = 0; i < len; i++) {
205         napi_value element = nullptr;
206         CHECK_ARGS(env, napi_get_element(env, context->argv[ARGS_ZERO], i, &element), JS_E_PARAM_INVALID);
207         PhotoAssetCustomRecord customRecord;
208         napi_value jsFileId;
209         napi_value jsShareCount;
210         napi_value jsLcdJumpCount;
211         CHECK_ARGS(env, napi_get_named_property(env, element, "fileId", &jsFileId), JS_E_PARAM_INVALID);
212         CHECK_ARGS(env, napi_get_named_property(env, element, "shareCount", &jsShareCount), JS_E_PARAM_INVALID);
213         CHECK_ARGS(env, napi_get_named_property(env, element, "lcdJumpCount", &jsLcdJumpCount),
214             JS_E_PARAM_INVALID);
215         DataShare::DataShareValuesBucket valueBucket;
216         int32_t fileId;
217         int32_t shareCount;
218         int32_t lcdJumpCount;
219         CHECK_ARGS(env, napi_get_value_int32(env, jsFileId, &fileId), JS_E_PARAM_INVALID);
220         CHECK_ARGS(env, napi_get_value_int32(env, jsShareCount, &shareCount), JS_E_PARAM_INVALID);
221         CHECK_ARGS(env, napi_get_value_int32(env, jsLcdJumpCount, &lcdJumpCount), JS_E_PARAM_INVALID);
222         if (fileId <= 0 || shareCount <= 0 || lcdJumpCount <= 0) {
223             context->error = JS_E_PARAM_INVALID;
224         }
225         customRecord.SetFileId(fileId);
226         customRecord.SetShareCount(shareCount);
227         customRecord.SetLcdJumpCount(lcdJumpCount);
228         customRecords.push_back(customRecord);
229         valueBucket.Put(CustomRecordsColumns::FILE_ID, fileId);
230         valueBucket.Put(CustomRecordsColumns::SHARE_COUNT, shareCount);
231         valueBucket.Put(CustomRecordsColumns::LCD_JUMP_COUNT, lcdJumpCount);
232         context->valuesBuckets.push_back(valueBucket);
233     }
234     context->updateRecords = customRecords;
235     CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
236     return result;
237 }
238 
CreateCustomRecordsExecute(napi_env env,void * data)239 static void CreateCustomRecordsExecute(napi_env env, void* data)
240 {
241     auto* context = static_cast<CustomRecordAsyncContext*>(data);
242     CHECK_NULL_PTR_RETURN_VOID(context, "async context is null");
243     if (context->error != ERR_DEFAULT) {
244         return;
245     }
246     std::vector<DataShare::DataShareValuesBucket> valueBuckets = context->valuesBuckets;
247     Uri uri(CUSTOM_RECORDS_CREATE_URI);
248     int32_t ret = UserFileClient::BatchInsert(uri, valueBuckets);
249     if (ret <= 0) {
250         context->error = JS_E_PARAM_INVALID;
251         return;
252     }
253 }
254 
CustomRecordsCallback(napi_env env,napi_status status,void * data)255 static void CustomRecordsCallback(napi_env env, napi_status status, void* data)
256 {
257     auto* context = static_cast<CustomRecordAsyncContext*>(data);
258     CHECK_NULL_PTR_RETURN_VOID(context, "async context is null");
259     auto jsContext = std::make_unique<JSAsyncContextOutput>();
260     jsContext->status = false;
261     napi_get_undefined(env, &jsContext->data);
262     napi_get_undefined(env, &jsContext->error);
263     if (context->error == ERR_DEFAULT) {
264         jsContext->status = true;
265     } else {
266         context->HandleError(env, jsContext->error);
267     }
268     if (context->work != nullptr) {
269         MediaLibraryNapiUtils::InvokeJSAsyncMethod(
270             env, context->deferred, context->callbackRef, context->work, *jsContext);
271     }
272     delete context;
273 }
274 
JSCreateCustomRecords(napi_env env,napi_callback_info info)275 napi_value PhotoAssetCustomRecordManager::JSCreateCustomRecords(napi_env env, napi_callback_info info)
276 {
277     if (!MediaLibraryNapiUtils::IsSystemApp()) {
278         NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL,
279             "The custom record asset manager instance can be called only by system apps");
280         return nullptr;
281     }
282     std::unique_ptr<CustomRecordAsyncContext> context = std::make_unique<CustomRecordAsyncContext>();
283     ParseArgsCustomRecords(env, info, context);
284     return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, context, "JSCreateCustomRecords",
285         CreateCustomRecordsExecute, CustomRecordsCallback);
286 }
287 
GetFetchOption(napi_env env,napi_callback_info info,std::unique_ptr<CustomRecordAsyncContext> & context)288 static bool GetFetchOption(napi_env env, napi_callback_info info,
289     std::unique_ptr<CustomRecordAsyncContext>& context)
290 {
291     if (context == nullptr) {
292         NAPI_ERR_LOG("async context is nullptr");
293         return false;
294     }
295     context->argc = ARGS_TWO;
296     CHECK_STATUS_RET(napi_get_cb_info(env, info, &context->argc, &(context->argv[ARGS_ZERO]), NULL, NULL),
297         "Failed to napi_get_cb_info");
298     if (context->argc != ARGS_ONE) {
299         NAPI_ERR_LOG("Number of args is invalid");
300         return false;
301     }
302     bool present = false;
303     CHECK_STATUS_RET(napi_has_named_property(env, context->argv[ARGS_ZERO], "fetchColumns", &present),
304         "Failed to napi_has_named_property");
305     if (!present) {
306         NAPI_ERR_LOG("napi params error");
307         return false;
308     }
309     napi_value property = nullptr;
310     CHECK_STATUS_RET(napi_get_named_property(env, context->argv[ARGS_ZERO], "fetchColumns", &property),
311         "Failed to napi_get_named_property");
312 
313     std::vector<std::string> fetchColumns;
314     CHECK_STATUS_RET(MediaLibraryNapiUtils::GetStringArray(env, property, fetchColumns),
315         "Failed to napi_get_named_property");
316     context->fetchColumn = fetchColumns;
317     return true;
318 }
319 
GetPredicate(napi_env env,const napi_value arg,const string & propName,std::unique_ptr<CustomRecordAsyncContext> & context)320 static bool GetPredicate(napi_env env, const napi_value arg, const string &propName,
321     std::unique_ptr<CustomRecordAsyncContext>& context)
322 {
323     if (context == nullptr) {
324         NAPI_ERR_LOG("async context is nullptr");
325         return false;
326     }
327     bool present = false;
328     napi_value property = nullptr;
329     CHECK_STATUS_RET(napi_has_named_property(env, arg, propName.c_str(), &present),
330         "Failed to check property name");
331     if (!present) {
332         NAPI_ERR_LOG("napi_has_named_property is invalid");
333         return false;
334     }
335     CHECK_STATUS_RET(napi_get_named_property(env, arg, propName.c_str(), &property), "Failed to get property");
336     JSProxy::JSProxy<DataShare::DataShareAbsPredicates> *jsProxy = nullptr;
337     napi_unwrap(env, property, reinterpret_cast<void **>(&jsProxy));
338     if (jsProxy == nullptr) {
339         NAPI_ERR_LOG("jsProxy is invalid");
340         return false;
341     }
342     std::shared_ptr<DataShare::DataShareAbsPredicates> predicate = jsProxy->GetInstance();
343     if (predicate == nullptr) {
344         NAPI_ERR_LOG("predicate is nullptr");
345         return false;
346     }
347     context->predicates = DataShare::DataSharePredicates(predicate->GetOperationList());
348     return true;
349 }
350 
CheckColumns(std::vector<std::string> fetchColumns)351 bool PhotoAssetCustomRecordManager::CheckColumns(std::vector<std::string> fetchColumns)
352 {
353     static std::unordered_set<std::string> CustomRecordsColumns = {
354         NapiCustomRecordStr::FILE_ID,
355         NapiCustomRecordStr::SHARE_COUNT,
356         NapiCustomRecordStr::LCD_JUMP_COUNT,
357     };
358     if (fetchColumns.size() == 0) {
359         return true;
360     }
361     for (auto column : fetchColumns) {
362         if (!CustomRecordsColumns.count(column)) {
363             NAPI_ERR_LOG("fetchColumns is invalid");
364             return false;
365         }
366     }
367     return true;
368 }
369 
ParserArgsFetchOptions(napi_env env,napi_callback_info info,std::unique_ptr<CustomRecordAsyncContext> & context)370 static napi_value ParserArgsFetchOptions(napi_env env, napi_callback_info info,
371     std::unique_ptr<CustomRecordAsyncContext>& context)
372 {
373     napi_value result = nullptr;
374     CHECK_NULL_PTR_RETURN_UNDEFINED(env, context, result, "async context is null");
375     context->argc = ARGS_TWO;
376     CHECK_ARGS(env, napi_get_cb_info(env, info, &context->argc, &(context->argv[ARGS_ZERO]), NULL, NULL),
377         JS_E_PARAM_INVALID);
378     if (context->argc != ARGS_ONE) {
379         NAPI_ERR_LOG("Number of args is invalid");
380         context->error = JS_E_PARAM_INVALID;
381         return nullptr;
382     }
383 
384     if (!GetFetchOption(env, info, context) || !PhotoAssetCustomRecordManager::CheckColumns(context->fetchColumn) ||
385         !GetPredicate(env, context->argv[ARGS_ZERO], "predicates", context)) {
386         NAPI_ERR_LOG("param is invalid");
387         context->error = JS_E_PARAM_INVALID;
388         return nullptr;
389     }
390 
391     CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
392     return result;
393 }
394 
JSGetCustomRecordsExecute(napi_env env,void * data)395 static void JSGetCustomRecordsExecute(napi_env env, void* data)
396 {
397     auto* context = static_cast<CustomRecordAsyncContext*>(data);
398     CHECK_NULL_PTR_RETURN_VOID(context, "async context is null");
399     std::string queryUri = CUSTOM_RECORDS_QUERY_URI;
400     Uri uri(queryUri);
401     int32_t errCode = 0;
402     std::shared_ptr<DataShare::DataShareResultSet> resultSet = UserFileClient::Query(uri,
403         context->predicates, context->fetchColumn, errCode);
404 
405     if (resultSet == nullptr) {
406         NAPI_ERR_LOG("resultSet is nullptr, errCode is %{public}d", errCode);
407         context->error = errCode;
408         return;
409     }
410     context->fetchCustomRecordsResult = std::make_unique<FetchResult<PhotoAssetCustomRecord>>(move(resultSet));
411     if (context->fetchCustomRecordsResult == nullptr) {
412         NAPI_ERR_LOG("fetchCustomRecordsResult is nullptr, errCode is %{public}d", errCode);
413         context->error = errCode;
414         return;
415     }
416     context->fetchCustomRecordsResult->SetResultNapiType(ResultNapiType::TYPE_PHOTOACCESS_HELPER);
417 }
418 
GetNapiFetchResult(napi_env env,CustomRecordAsyncContext * context,unique_ptr<JSAsyncContextOutput> & jsContext)419 static void GetNapiFetchResult(napi_env env, CustomRecordAsyncContext *context,
420     unique_ptr<JSAsyncContextOutput> &jsContext)
421 {
422     CHECK_NULL_PTR_RETURN_VOID(context, "async context is null");
423     CHECK_NULL_PTR_RETURN_VOID(jsContext, "jsContext context is null");
424     // Create FetchResult object using the contents of resultSet
425     if (context->fetchCustomRecordsResult == nullptr) {
426         NAPI_ERR_LOG("No fetch file result found!");
427         MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, ERR_INVALID_OUTPUT,
428             "Failed to obtain Fetch File Result");
429         return;
430     }
431     napi_value fileResult = FetchFileResultNapi::CreateFetchFileResult(env, move(context->fetchCustomRecordsResult));
432     if (fileResult == nullptr) {
433         MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, ERR_INVALID_OUTPUT,
434             "Failed to create js object for Fetch File Result");
435     } else {
436         jsContext->data = fileResult;
437         jsContext->status = true;
438         napi_get_undefined(env, &jsContext->error);
439     }
440 }
441 
GetFileAssetsAsyncCallbackComplete(napi_env env,napi_status status,void * data)442 static void GetFileAssetsAsyncCallbackComplete(napi_env env, napi_status status, void *data)
443 {
444     CustomRecordAsyncContext *context = static_cast<CustomRecordAsyncContext*>(data);
445     CHECK_NULL_PTR_RETURN_VOID(context, "async context is null");
446 
447     unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
448     jsContext->status = false;
449     napi_get_undefined(env, &jsContext->data);
450 
451     if (context->error != ERR_DEFAULT) {
452         context->HandleError(env, jsContext->error);
453     } else {
454         GetNapiFetchResult(env, context, jsContext);
455     }
456 
457     if (context->work != nullptr) {
458         MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
459                                                    context->work, *jsContext);
460     }
461     delete context;
462 }
463 
JSGetCustomRecords(napi_env env,napi_callback_info info)464 napi_value PhotoAssetCustomRecordManager::JSGetCustomRecords(napi_env env, napi_callback_info info)
465 {
466     if (!MediaLibraryNapiUtils::IsSystemApp()) {
467         NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL,
468             "The custom record asset manager instance can be called only by system apps");
469         return nullptr;
470     }
471     std::unique_ptr<CustomRecordAsyncContext> context = std::make_unique<CustomRecordAsyncContext>();
472     ParserArgsFetchOptions(env, info, context);
473     return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, context, "JSGetCustomRecords",
474         JSGetCustomRecordsExecute, GetFileAssetsAsyncCallbackComplete);
475 }
476 
JSRemoveCustomRecordsExecute(napi_env env,void * data)477 static void JSRemoveCustomRecordsExecute(napi_env env, void* data)
478 {
479     auto* context = static_cast<CustomRecordAsyncContext*>(data);
480     std::string deleteUri = CUSTOM_RECORDS_DELETE_URI;
481     Uri uri(deleteUri);
482     int32_t errcode = UserFileClient::Delete(uri, context->predicates);
483     if (errcode < 0) {
484         NAPI_ERR_LOG("UserFileClient::Delete fail.");
485         context->error = JS_E_PARAM_INVALID;
486     }
487 }
488 
JSRemoveCustomRecordsCompleteCallback(napi_env env,napi_status status,void * data)489 static void JSRemoveCustomRecordsCompleteCallback(napi_env env, napi_status status, void* data)
490 {
491     auto* context = static_cast<CustomRecordAsyncContext*>(data);
492     CHECK_NULL_PTR_RETURN_VOID(context, "async context is null");
493     auto jsContext = make_unique<JSAsyncContextOutput>();
494     CHECK_NULL_PTR_RETURN_VOID(jsContext, "jsContext is null");
495     jsContext->status = false;
496     napi_get_undefined(env, &jsContext->data);
497     napi_get_undefined(env, &jsContext->error);
498     if (context->error == ERR_DEFAULT) {
499         jsContext->status = true;
500     } else {
501         context->HandleError(env, jsContext->error);
502     }
503 
504     if (context->work != nullptr) {
505         MediaLibraryNapiUtils::InvokeJSAsyncMethod(
506             env, context->deferred, context->callbackRef, context->work, *jsContext);
507     }
508     delete context;
509 }
510 
JSRemoveCustomRecords(napi_env env,napi_callback_info info)511 napi_value PhotoAssetCustomRecordManager::JSRemoveCustomRecords(napi_env env, napi_callback_info info)
512 {
513     if (!MediaLibraryNapiUtils::IsSystemApp()) {
514         NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL,
515             "The custom record asset manager instance can be called only by system apps");
516         return nullptr;
517     }
518     std::unique_ptr<CustomRecordAsyncContext> context = std::make_unique<CustomRecordAsyncContext>();
519     context->userId_ = UserFileClient::GetUserId();
520     ParserArgsFetchOptions(env, info, context);
521     return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, context, "JSRemoveCustomRecords",
522         JSRemoveCustomRecordsExecute, JSRemoveCustomRecordsCompleteCallback);
523 }
524 
JSAddShareCountExecute(napi_env env,void * data)525 static void JSAddShareCountExecute(napi_env env, void* data)
526 {
527     auto* context = static_cast<CustomRecordAsyncContext*>(data);
528     CHECK_NULL_PTR_RETURN_VOID(context, "async context is null");
529     std::set<int32_t> deduplicationIds(context->fileIds.begin(), context->fileIds.end());
530     for (auto id : deduplicationIds) {
531         int32_t errcode = 0;
532         std::string queryUriStr = CUSTOM_RECORDS_QUERY_URI;
533         Uri queryuri(queryUriStr);
534         OHOS::DataShare::DataSharePredicates predicates;
535         context->fetchColumn.push_back(CustomRecordsColumns::SHARE_COUNT);
536         predicates.EqualTo(CustomRecordsColumns::FILE_ID, std::to_string(id));
537         std::shared_ptr<DataShare::DataShareResultSet> resultSet = UserFileClient::Query(queryuri,
538             predicates, context->fetchColumn, errcode);
539         if (resultSet == nullptr || resultSet->GoToFirstRow() != E_OK) {
540             NAPI_ERR_LOG("Resultset is nullptr");
541             context->failFileIds.push_back(id);
542             continue;
543         }
544         if (errcode == E_HAS_DB_ERROR ||
545             (errcode >= E_MEDIA_IPC_OFFSET && errcode <= E_IPC_SEVICE_UNMARSHALLING_FAIL)) {
546             NAPI_ERR_LOG("JSAddShareCountExecute inner fail");
547             context->error = JS_E_INNER_FAIL;
548         }
549         int32_t shareCount = get<int32_t>(ResultSetUtils::GetValFromColumn(CustomRecordsColumns::SHARE_COUNT,
550             resultSet, TYPE_INT32));
551         Uri updateUri(CUSTOM_RECORDS_UPDATE_URI);
552         DataShare::DataShareValuesBucket valuesBucket;
553         valuesBucket.Put(CustomRecordsColumns::SHARE_COUNT, ++shareCount);
554         int32_t result = UserFileClient::Update(updateUri, predicates, valuesBucket, context->userId_);
555         if (result == E_HAS_DB_ERROR || (result >= E_MEDIA_IPC_OFFSET && result <= E_IPC_SEVICE_UNMARSHALLING_FAIL)) {
556             NAPI_ERR_LOG("JSAddShareCountExecute inner fail");
557             context->error = JS_E_INNER_FAIL;
558         } else if (result < 0) {
559             NAPI_ERR_LOG("JSAddShareCountExecute Update fail");
560             context->failFileIds.push_back(id);
561         }
562     }
563 }
564 
JSAddCustomRecordCompleteCallback(napi_env env,napi_status status,void * data)565 static void JSAddCustomRecordCompleteCallback(napi_env env, napi_status status, void* data)
566 {
567     auto* context = static_cast<CustomRecordAsyncContext*>(data);
568     CHECK_NULL_PTR_RETURN_VOID(context, "async context is null");
569     auto jsContext = make_unique<JSAsyncContextOutput>();
570     jsContext->status = false;
571     napi_get_undefined(env, &jsContext->data);
572     napi_get_undefined(env, &jsContext->error);
573     if (context->error == ERR_DEFAULT) {
574         CHECK_ARGS_RET_VOID(env, napi_create_array_with_length(env, context->failFileIds.size(), &jsContext->data),
575             JS_INNER_FAIL);
576         for (size_t i = 0; i < context->failFileIds.size(); i++) {
577             napi_value fileId = nullptr;
578             CHECK_ARGS_RET_VOID(env, napi_create_int32(env, context->failFileIds[i], &fileId), JS_INNER_FAIL);
579             CHECK_ARGS_RET_VOID(env, napi_set_element(env, jsContext->data, i, fileId), JS_INNER_FAIL);
580         }
581         jsContext->status = true;
582     } else {
583         context->HandleError(env, jsContext->error);
584     }
585 
586     if (context->work != nullptr) {
587         MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
588                                                    context->work, *jsContext);
589     }
590     delete context;
591 }
592 
ParserArgsCustomRecordsFileIds(napi_env env,napi_callback_info info,std::unique_ptr<CustomRecordAsyncContext> & context)593 static napi_value ParserArgsCustomRecordsFileIds(napi_env env, napi_callback_info info,
594     std::unique_ptr<CustomRecordAsyncContext>& context)
595 {
596     context->argc = ARGS_TWO;
597     CHECK_ARGS(env, napi_get_cb_info(env, info, &context->argc, &(context->argv[ARGS_ZERO]), NULL, NULL),
598         JS_E_PARAM_INVALID);
599     if (context->argc != ARGS_ONE) {
600         NAPI_ERR_LOG("Number of args is invalid");
601         context->error = JS_E_PARAM_INVALID;
602         return nullptr;
603     }
604     uint32_t len = 0;
605     std::vector<int32_t> fileIds;
606     CHECK_ARGS(env, napi_get_array_length(env, context->argv[ARGS_ZERO], &len), JS_E_PARAM_INVALID);
607     if (len > OPERATION_MAX_FILE_IDS_LEN || len == OPERATION_MIN_LEN) {
608         NAPI_ERR_LOG("param count exceeds the predefined maximum");
609         context->error = JS_E_PARAM_INVALID;
610         return nullptr;
611     }
612     CHECK_ARGS(env, MediaLibraryNapiUtils::GetInt32Array(env, context->argv[ARGS_ZERO], fileIds), JS_E_PARAM_INVALID);
613     context->fileIds = fileIds;
614 
615     napi_value result = nullptr;
616     CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
617     return result;
618 }
619 
JSAddShareCount(napi_env env,napi_callback_info info)620 napi_value PhotoAssetCustomRecordManager::JSAddShareCount(napi_env env, napi_callback_info info)
621 {
622     if (!MediaLibraryNapiUtils::IsSystemApp()) {
623         NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL,
624             "The custom record asset manager instance can be called only by system apps");
625         return nullptr;
626     }
627     std::unique_ptr<CustomRecordAsyncContext> context = std::make_unique<CustomRecordAsyncContext>();
628     ParserArgsCustomRecordsFileIds(env, info, context);
629     return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, context, "JSAddShareCount",
630         JSAddShareCountExecute, JSAddCustomRecordCompleteCallback);
631 }
632 
JSAddLcdJumpCountExecute(napi_env env,void * data)633 static void JSAddLcdJumpCountExecute(napi_env env, void* data)
634 {
635     auto* context = static_cast<CustomRecordAsyncContext*>(data);
636     CHECK_NULL_PTR_RETURN_VOID(context, "async context is nullptr");
637     std::set<int32_t> deduplicationIds(context->fileIds.begin(), context->fileIds.end());
638     for (auto id : deduplicationIds) {
639         int32_t errcode = 0;
640         Uri queryuri(CUSTOM_RECORDS_QUERY_URI);
641         context->fetchColumn.push_back(CustomRecordsColumns::LCD_JUMP_COUNT);
642         DataShare::DataSharePredicates predicates;
643         predicates.EqualTo(CustomRecordsColumns::FILE_ID, std::to_string(id));
644         std::shared_ptr<DataShare::DataShareResultSet> resultSet = UserFileClient::Query(queryuri,
645             predicates, context->fetchColumn, errcode);
646         if (resultSet == nullptr || resultSet->GoToFirstRow() != E_OK) {
647             NAPI_ERR_LOG("Resultset is nullptr");
648             context->failFileIds.push_back(id);
649             continue;
650         }
651         int32_t lcdJumpCount = get<int32_t>(ResultSetUtils::GetValFromColumn(CustomRecordsColumns::LCD_JUMP_COUNT,
652             resultSet, TYPE_INT32));
653         Uri updateUri(CUSTOM_RECORDS_UPDATE_URI);
654         DataShare::DataShareValuesBucket valuesBucket;
655         valuesBucket.Put(CustomRecordsColumns::LCD_JUMP_COUNT, ++lcdJumpCount);
656         int32_t result = UserFileClient::Update(updateUri, predicates, valuesBucket, context->userId_);
657         if (result == E_HAS_DB_ERROR || (result >= E_MEDIA_IPC_OFFSET && result <= E_IPC_SEVICE_UNMARSHALLING_FAIL)) {
658             NAPI_ERR_LOG("JSAddLcdJumpCountExecute inner fail");
659             context->error = JS_E_INNER_FAIL;
660         } else if (result < 0) {
661             NAPI_ERR_LOG("JSAddLcdJumpCountExecute Update fail");
662             context->failFileIds.push_back(id);
663         }
664     }
665 }
666 
JSAddLCDJumpCount(napi_env env,napi_callback_info info)667 napi_value PhotoAssetCustomRecordManager::JSAddLCDJumpCount(napi_env env, napi_callback_info info)
668 {
669     if (!MediaLibraryNapiUtils::IsSystemApp()) {
670         NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL,
671             "The custom record asset manager instance can be called only by system apps");
672         return nullptr;
673     }
674     std::unique_ptr<CustomRecordAsyncContext> context = std::make_unique<CustomRecordAsyncContext>();
675     ParserArgsCustomRecordsFileIds(env, info, context);
676     return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, context, "JSAddLCDJumpCount",
677         JSAddLcdJumpCountExecute, JSAddCustomRecordCompleteCallback);
678 }
679 
JSSetCustomRecordsExecute(napi_env env,void * data)680 static void JSSetCustomRecordsExecute(napi_env env, void* data)
681 {
682     auto* context = static_cast<CustomRecordAsyncContext*>(data);
683     CHECK_NULL_PTR_RETURN_VOID(context, "async context is nullptr");
684     for (auto& customRecord : context->updateRecords) {
685         int32_t fileId = customRecord.GetFileId();
686         int32_t shareCount = customRecord.GetShareCount();
687         int32_t lcdJumpCount = customRecord.GetLcdJumpCount();
688         DataShare::DataShareValuesBucket valuesBucket;
689         valuesBucket.Put(CustomRecordsColumns::SHARE_COUNT, shareCount);
690         valuesBucket.Put(CustomRecordsColumns::LCD_JUMP_COUNT, lcdJumpCount);
691         DataShare::DataSharePredicates predicate;
692         predicate.EqualTo(CustomRecordsColumns::FILE_ID, fileId);
693         Uri updateUri(CUSTOM_RECORDS_UPDATE_URI);
694         int32_t result = UserFileClient::Update(updateUri, predicate, valuesBucket);
695         if (result <= 0) {
696             NAPI_ERR_LOG("JSSetCustomRecordsExecute Update fail");
697             context->failFileIds.push_back(fileId);
698         }
699     }
700 }
701 
JSSetCustomRecords(napi_env env,napi_callback_info info)702 napi_value PhotoAssetCustomRecordManager::JSSetCustomRecords(napi_env env, napi_callback_info info)
703 {
704     if (!MediaLibraryNapiUtils::IsSystemApp()) {
705         NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL,
706             "The custom record asset manager instance can be called only by system apps");
707         return nullptr;
708     }
709     std::unique_ptr<CustomRecordAsyncContext> context = std::make_unique<CustomRecordAsyncContext>();
710     ParseArgsCustomRecords(env, info, context);
711     return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, context, "JSSetCustomRecords",
712         JSSetCustomRecordsExecute, JSAddCustomRecordCompleteCallback);
713 }
714 } // namespace OHOS::Media