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