1 /*
2 * Copyright (C) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #define MLOG_TAG "MediaAssetChangeRequestNapi"
17
18 #include "media_asset_change_request_napi.h"
19
20 #include <fcntl.h>
21 #include <functional>
22 #include <sys/sendfile.h>
23 #include <sys/stat.h>
24 #include <unordered_map>
25 #include <unordered_set>
26
27 #include "ability_context.h"
28 #include "access_token.h"
29 #include "accesstoken_kit.h"
30 #include "delete_callback.h"
31 #include "directory_ex.h"
32 #include "delete_permanently_operations_uri.h"
33 #include "file_uri.h"
34 #include "image_packer.h"
35 #include "ipc_skeleton.h"
36 #include "js_native_api.h"
37 #include "js_native_api_types.h"
38 #include "media_asset_edit_data_napi.h"
39 #include "media_column.h"
40 #include "media_file_utils.h"
41 #include "medialibrary_client_errno.h"
42 #include "medialibrary_errno.h"
43 #include "medialibrary_napi_log.h"
44 #include "medialibrary_tracer.h"
45 #include "modal_ui_extension_config.h"
46 #include "permission_utils.h"
47 #include "photo_proxy_napi.h"
48 #include "securec.h"
49 #ifdef HAS_ACE_ENGINE_PART
50 #include "ui_content.h"
51 #endif
52 #include "unique_fd.h"
53 #include "userfile_client.h"
54 #include "userfile_manager_types.h"
55 #include "want.h"
56
57 using namespace std;
58 using namespace OHOS::Security::AccessToken;
59
60 namespace OHOS::Media {
61 static const string MEDIA_ASSET_CHANGE_REQUEST_CLASS = "MediaAssetChangeRequest";
62 thread_local napi_ref MediaAssetChangeRequestNapi::constructor_ = nullptr;
63 std::atomic<uint32_t> MediaAssetChangeRequestNapi::cacheFileId_ = 0;
64 const std::string SET_LOCATION_KEY = "set_location";
65 const std::string SET_LOCATION_VALUE = "1";
66 const std::string SET_USER_ID_VALUE = "1";
67
68 const std::string SET_DISPLAY_NAME_KEY = "set_displayName";
69 const std::string CAN_FALLBACK = "can_fallback";
70 const std::string OLD_DISPLAY_NAME = "old_displayName";
71 const std::string DEFAULT_MIME_TYPE = "application/octet-stream";
72
73 static const std::array<int, 4> ORIENTATION_ARRAY = {0, 90, 180, 270};
74
75 constexpr int64_t CREATE_ASSET_REQUEST_PENDING = -4;
76
77 constexpr int32_t YES = 1;
78 constexpr int32_t NO = 0;
79
80 constexpr int32_t USER_COMMENT_MAX_LEN = 420;
81 constexpr int32_t MAX_DELETE_NUMBER = 300;
82 constexpr int32_t MAX_PHOTO_ID_LEN = 32;
83
84 const std::string PAH_SUBTYPE = "subtype";
85 const std::string CAMERA_SHOT_KEY = "cameraShotKey";
86 const std::string USER_ID = "userId";
87 const std::map<std::string, std::string> PHOTO_CREATE_OPTIONS_PARAM = {
88 { PAH_SUBTYPE, PhotoColumn::PHOTO_SUBTYPE },
89 { CAMERA_SHOT_KEY, PhotoColumn::CAMERA_SHOT_KEY },
90 { USER_ID, SET_USER_ID_VALUE },
91 };
92
93 const std::string TITLE = "title";
94 const std::map<std::string, std::string> CREATE_OPTIONS_PARAM = {
95 { TITLE, PhotoColumn::MEDIA_TITLE },
96 { PAH_SUBTYPE, PhotoColumn::PHOTO_SUBTYPE },
97 { USER_ID, SET_USER_ID_VALUE },
98 };
99
100 const std::string DEFAULT_TITLE_TIME_FORMAT = "%Y%m%d_%H%M%S";
101 const std::string DEFAULT_TITLE_IMG_PREFIX = "IMG_";
102 const std::string DEFAULT_TITLE_VIDEO_PREFIX = "VID_";
103 const std::string MOVING_PHOTO_VIDEO_EXTENSION = "mp4";
104 static const size_t BATCH_DELETE_MAX_NUMBER = 500;
105
ReadData(const shared_ptr<AVSharedMemory> & mem,uint32_t length)106 int32_t MediaDataSource::ReadData(const shared_ptr<AVSharedMemory>& mem, uint32_t length)
107 {
108 if (readPos_ >= size_) {
109 NAPI_ERR_LOG("Failed to check read position");
110 return SOURCE_ERROR_EOF;
111 }
112
113 if (memcpy_s(mem->GetBase(), mem->GetSize(), (char*)buffer_ + readPos_, length) != E_OK) {
114 NAPI_ERR_LOG("Failed to copy buffer to mem");
115 return SOURCE_ERROR_IO;
116 }
117 readPos_ += static_cast<int64_t>(length);
118 return static_cast<int32_t>(length);
119 }
120
ReadAt(const std::shared_ptr<AVSharedMemory> & mem,uint32_t length,int64_t pos)121 int32_t MediaDataSource::ReadAt(const std::shared_ptr<AVSharedMemory>& mem, uint32_t length, int64_t pos)
122 {
123 readPos_ = pos;
124 return ReadData(mem, length);
125 }
126
ReadAt(int64_t pos,uint32_t length,const std::shared_ptr<AVSharedMemory> & mem)127 int32_t MediaDataSource::ReadAt(int64_t pos, uint32_t length, const std::shared_ptr<AVSharedMemory>& mem)
128 {
129 readPos_ = pos;
130 return ReadData(mem, length);
131 }
132
ReadAt(uint32_t length,const std::shared_ptr<AVSharedMemory> & mem)133 int32_t MediaDataSource::ReadAt(uint32_t length, const std::shared_ptr<AVSharedMemory>& mem)
134 {
135 return ReadData(mem, length);
136 }
137
GetSize(int64_t & size)138 int32_t MediaDataSource::GetSize(int64_t& size)
139 {
140 size = size_;
141 return E_OK;
142 }
143
Init(napi_env env,napi_value exports)144 napi_value MediaAssetChangeRequestNapi::Init(napi_env env, napi_value exports)
145 {
146 NapiClassInfo info = { .name = MEDIA_ASSET_CHANGE_REQUEST_CLASS,
147 .ref = &constructor_,
148 .constructor = Constructor,
149 .props = {
150 DECLARE_NAPI_STATIC_FUNCTION("createAssetRequest", JSCreateAssetRequest),
151 DECLARE_NAPI_STATIC_FUNCTION("createImageAssetRequest", JSCreateImageAssetRequest),
152 DECLARE_NAPI_STATIC_FUNCTION("createVideoAssetRequest", JSCreateVideoAssetRequest),
153 DECLARE_NAPI_STATIC_FUNCTION("deleteLocalAssetsPermanently", JSDeleteLocalAssetsPermanently),
154 DECLARE_NAPI_STATIC_FUNCTION("deleteAssets", JSDeleteAssets),
155 DECLARE_NAPI_FUNCTION("getAsset", JSGetAsset),
156 DECLARE_NAPI_FUNCTION("setEditData", JSSetEditData),
157 DECLARE_NAPI_FUNCTION("setFavorite", JSSetFavorite),
158 DECLARE_NAPI_FUNCTION("setHidden", JSSetHidden),
159 DECLARE_NAPI_FUNCTION("setTitle", JSSetTitle),
160 DECLARE_NAPI_FUNCTION("setDisplayName", JSSetDisplayName),
161 DECLARE_NAPI_FUNCTION("setUserComment", JSSetUserComment),
162 DECLARE_NAPI_FUNCTION("getWriteCacheHandler", JSGetWriteCacheHandler),
163 DECLARE_NAPI_FUNCTION("setLocation", JSSetLocation),
164 DECLARE_NAPI_FUNCTION("addResource", JSAddResource),
165 DECLARE_NAPI_FUNCTION("setEffectMode", JSSetEffectMode),
166 DECLARE_NAPI_FUNCTION("setCameraShotKey", JSSetCameraShotKey),
167 DECLARE_NAPI_FUNCTION("saveCameraPhoto", JSSaveCameraPhoto),
168 DECLARE_NAPI_FUNCTION("discardCameraPhoto", JSDiscardCameraPhoto),
169 DECLARE_NAPI_FUNCTION("setOrientation", JSSetOrientation),
170 DECLARE_NAPI_FUNCTION("setSupportedWatermarkType", JSSetSupportedWatermarkType),
171 DECLARE_NAPI_FUNCTION("setVideoEnhancementAttr", JSSetVideoEnhancementAttr),
172 } };
173 MediaLibraryNapiUtils::NapiDefineClass(env, exports, info);
174 return exports;
175 }
176
Constructor(napi_env env,napi_callback_info info)177 napi_value MediaAssetChangeRequestNapi::Constructor(napi_env env, napi_callback_info info)
178 {
179 napi_value newTarget = nullptr;
180 CHECK_ARGS(env, napi_get_new_target(env, info, &newTarget), JS_INNER_FAIL);
181 CHECK_COND_RET(newTarget != nullptr, nullptr, "Failed to check new.target");
182
183 size_t argc = ARGS_ONE;
184 napi_value argv[ARGS_ONE] = { 0 };
185 napi_value thisVar = nullptr;
186 napi_valuetype valueType;
187 FileAssetNapi* fileAssetNapi;
188 CHECK_ARGS(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr), JS_INNER_FAIL);
189 CHECK_COND_WITH_MESSAGE(env, argc == ARGS_ONE, "Number of args is invalid");
190 CHECK_ARGS(env, napi_typeof(env, argv[PARAM0], &valueType), JS_INNER_FAIL);
191 CHECK_COND_WITH_MESSAGE(env, valueType == napi_object, "Invalid argument type");
192 CHECK_ARGS(env, napi_unwrap(env, argv[PARAM0], reinterpret_cast<void**>(&fileAssetNapi)), JS_INNER_FAIL);
193 CHECK_COND_WITH_MESSAGE(env, fileAssetNapi != nullptr, "Failed to get FileAssetNapi object");
194
195 auto fileAssetPtr = fileAssetNapi->GetFileAssetInstance();
196 CHECK_COND_WITH_MESSAGE(env, fileAssetPtr != nullptr, "fileAsset is null");
197 CHECK_COND_WITH_MESSAGE(env,
198 fileAssetPtr->GetResultNapiType() == ResultNapiType::TYPE_PHOTOACCESS_HELPER &&
199 (fileAssetPtr->GetMediaType() == MEDIA_TYPE_IMAGE || fileAssetPtr->GetMediaType() == MEDIA_TYPE_VIDEO),
200 "Unsupported type of fileAsset");
201
202 unique_ptr<MediaAssetChangeRequestNapi> obj = make_unique<MediaAssetChangeRequestNapi>();
203 CHECK_COND(env, obj != nullptr, JS_INNER_FAIL);
204 obj->fileAsset_ = fileAssetPtr;
205 CHECK_ARGS(env,
206 napi_wrap(env, thisVar, reinterpret_cast<void*>(obj.get()), MediaAssetChangeRequestNapi::Destructor, nullptr,
207 nullptr),
208 JS_INNER_FAIL);
209 obj.release();
210 return thisVar;
211 }
212
DeleteCache(const string & cacheFileName)213 static void DeleteCache(const string& cacheFileName)
214 {
215 if (cacheFileName.empty()) {
216 return;
217 }
218
219 string uri = PhotoColumn::PHOTO_CACHE_URI_PREFIX + cacheFileName;
220 MediaFileUtils::UriAppendKeyValue(uri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
221 Uri deleteCacheUri(uri);
222 DataShare::DataSharePredicates predicates;
223 int32_t ret = UserFileClient::Delete(deleteCacheUri, predicates);
224 if (ret < 0) {
225 NAPI_WARN_LOG("Failed to delete cache: %{private}s, error: %{public}d", cacheFileName.c_str(), ret);
226 }
227 }
228
Destructor(napi_env env,void * nativeObject,void * finalizeHint)229 void MediaAssetChangeRequestNapi::Destructor(napi_env env, void* nativeObject, void* finalizeHint)
230 {
231 auto* assetChangeRequest = reinterpret_cast<MediaAssetChangeRequestNapi*>(nativeObject);
232 if (assetChangeRequest == nullptr) {
233 return;
234 }
235
236 DeleteCache(assetChangeRequest->cacheFileName_);
237 DeleteCache(assetChangeRequest->cacheMovingPhotoVideoName_);
238
239 delete assetChangeRequest;
240 assetChangeRequest = nullptr;
241 }
242
GetFileAssetInstance() const243 shared_ptr<FileAsset> MediaAssetChangeRequestNapi::GetFileAssetInstance() const
244 {
245 return fileAsset_;
246 }
247
GetPhotoProxyObj()248 sptr<PhotoProxy> MediaAssetChangeRequestNapi::GetPhotoProxyObj()
249 {
250 return photoProxy_;
251 }
252
ReleasePhotoProxyObj()253 void MediaAssetChangeRequestNapi::ReleasePhotoProxyObj()
254 {
255 photoProxy_->Release();
256 photoProxy_ = nullptr;
257 }
258
RecordChangeOperation(AssetChangeOperation changeOperation)259 void MediaAssetChangeRequestNapi::RecordChangeOperation(AssetChangeOperation changeOperation)
260 {
261 if ((changeOperation == AssetChangeOperation::GET_WRITE_CACHE_HANDLER ||
262 changeOperation == AssetChangeOperation::ADD_RESOURCE ||
263 changeOperation == AssetChangeOperation::ADD_FILTERS) &&
264 Contains(AssetChangeOperation::CREATE_FROM_SCRATCH)) {
265 assetChangeOperations_.insert(assetChangeOperations_.begin() + 1, changeOperation);
266 return;
267 }
268 if (changeOperation == AssetChangeOperation::ADD_RESOURCE &&
269 Contains(AssetChangeOperation::SET_MOVING_PHOTO_EFFECT_MODE)) {
270 assetChangeOperations_.insert(assetChangeOperations_.begin(), changeOperation);
271 return;
272 }
273 assetChangeOperations_.push_back(changeOperation);
274 }
275
Contains(AssetChangeOperation changeOperation) const276 bool MediaAssetChangeRequestNapi::Contains(AssetChangeOperation changeOperation) const
277 {
278 return std::find(assetChangeOperations_.begin(), assetChangeOperations_.end(), changeOperation) !=
279 assetChangeOperations_.end();
280 }
281
ContainsResource(ResourceType resourceType) const282 bool MediaAssetChangeRequestNapi::ContainsResource(ResourceType resourceType) const
283 {
284 return std::find(addResourceTypes_.begin(), addResourceTypes_.end(), resourceType) != addResourceTypes_.end();
285 }
286
IsMovingPhoto() const287 bool MediaAssetChangeRequestNapi::IsMovingPhoto() const
288 {
289 return fileAsset_ != nullptr &&
290 (fileAsset_->GetPhotoSubType() == static_cast<int32_t>(PhotoSubType::MOVING_PHOTO) ||
291 (fileAsset_->GetPhotoSubType() == static_cast<int32_t>(PhotoSubType::DEFAULT) &&
292 fileAsset_->GetMovingPhotoEffectMode() == static_cast<int32_t>(MovingPhotoEffectMode::IMAGE_ONLY)));
293 }
294
CheckMovingPhotoResource(ResourceType resourceType) const295 bool MediaAssetChangeRequestNapi::CheckMovingPhotoResource(ResourceType resourceType) const
296 {
297 if (resourceType == ResourceType::INVALID_RESOURCE) {
298 NAPI_ERR_LOG("Invalid resource type");
299 return false;
300 }
301
302 bool isResourceTypeVaild = !ContainsResource(resourceType);
303 int addResourceTimes =
304 std::count(assetChangeOperations_.begin(), assetChangeOperations_.end(), AssetChangeOperation::ADD_RESOURCE);
305 return isResourceTypeVaild && addResourceTimes <= 1; // currently, add resource no more than once
306 }
307
308 static const unordered_map<MovingPhotoEffectMode, unordered_map<ResourceType, bool>> EFFECT_MODE_RESOURCE_CHECK = {
309 { MovingPhotoEffectMode::DEFAULT,
310 { { ResourceType::IMAGE_RESOURCE, false }, { ResourceType::VIDEO_RESOURCE, false } } },
311 { MovingPhotoEffectMode::BOUNCE_PLAY,
312 { { ResourceType::IMAGE_RESOURCE, false }, { ResourceType::VIDEO_RESOURCE, true } } },
313 { MovingPhotoEffectMode::LOOP_PLAY,
314 { { ResourceType::IMAGE_RESOURCE, false }, { ResourceType::VIDEO_RESOURCE, true } } },
315 { MovingPhotoEffectMode::CINEMA_GRAPH,
316 { { ResourceType::IMAGE_RESOURCE, false }, { ResourceType::VIDEO_RESOURCE, true } } },
317 { MovingPhotoEffectMode::LONG_EXPOSURE,
318 { { ResourceType::IMAGE_RESOURCE, true }, { ResourceType::VIDEO_RESOURCE, false } } },
319 { MovingPhotoEffectMode::MULTI_EXPOSURE,
320 { { ResourceType::IMAGE_RESOURCE, true }, { ResourceType::VIDEO_RESOURCE, false } } },
321 { MovingPhotoEffectMode::IMAGE_ONLY,
322 { { ResourceType::IMAGE_RESOURCE, false }, { ResourceType::VIDEO_RESOURCE, false } } },
323 };
324
CheckEffectModeWriteOperation()325 bool MediaAssetChangeRequestNapi::CheckEffectModeWriteOperation()
326 {
327 if (fileAsset_ == nullptr) {
328 NAPI_ERR_LOG("fileAsset is nullptr");
329 return false;
330 }
331
332 if (fileAsset_->GetTimePending() != 0) {
333 NAPI_ERR_LOG("Failed to check pending of fileAsset: %{public}" PRId64, fileAsset_->GetTimePending());
334 return false;
335 }
336
337 MovingPhotoEffectMode effectMode = static_cast<MovingPhotoEffectMode>(fileAsset_->GetMovingPhotoEffectMode());
338 auto iter = EFFECT_MODE_RESOURCE_CHECK.find(effectMode);
339 if (iter == EFFECT_MODE_RESOURCE_CHECK.end()) {
340 NAPI_ERR_LOG("Failed to check effect mode: %{public}d", static_cast<int32_t>(effectMode));
341 return false;
342 }
343
344 bool isImageExist = ContainsResource(ResourceType::IMAGE_RESOURCE);
345 bool isVideoExist = ContainsResource(ResourceType::VIDEO_RESOURCE);
346 if (iter->second.at(ResourceType::IMAGE_RESOURCE) && !isImageExist) {
347 NAPI_ERR_LOG("Failed to check image resource for effect mode: %{public}d", static_cast<int32_t>(effectMode));
348 return false;
349 }
350 if (iter->second.at(ResourceType::VIDEO_RESOURCE) && !isVideoExist) {
351 NAPI_ERR_LOG("Failed to check video resource for effect mode: %{public}d", static_cast<int32_t>(effectMode));
352 return false;
353 }
354 return true;
355 }
356
CheckMovingPhotoWriteOperation()357 bool MediaAssetChangeRequestNapi::CheckMovingPhotoWriteOperation()
358 {
359 if (Contains(AssetChangeOperation::SET_MOVING_PHOTO_EFFECT_MODE)) {
360 return CheckEffectModeWriteOperation();
361 }
362
363 bool containsAddResource = Contains(AssetChangeOperation::ADD_RESOURCE);
364 if (!containsAddResource) {
365 return true;
366 }
367
368 bool isCreation = Contains(AssetChangeOperation::CREATE_FROM_SCRATCH);
369 if (!isCreation) {
370 return true;
371 }
372
373 int addResourceTimes =
374 std::count(assetChangeOperations_.begin(), assetChangeOperations_.end(), AssetChangeOperation::ADD_RESOURCE);
375 bool isImageExist = ContainsResource(ResourceType::IMAGE_RESOURCE);
376 bool isVideoExist = ContainsResource(ResourceType::VIDEO_RESOURCE);
377 return addResourceTimes == 2 && isImageExist && isVideoExist; // must add resource 2 times with image and video
378 }
379
CheckChangeOperations(napi_env env)380 bool MediaAssetChangeRequestNapi::CheckChangeOperations(napi_env env)
381 {
382 if (assetChangeOperations_.empty()) {
383 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "None request to apply");
384 return false;
385 }
386
387 bool isCreateFromScratch = Contains(AssetChangeOperation::CREATE_FROM_SCRATCH);
388 bool isCreateFromUri = Contains(AssetChangeOperation::CREATE_FROM_URI);
389 bool containsEdit = Contains(AssetChangeOperation::SET_EDIT_DATA);
390 bool containsGetHandler = Contains(AssetChangeOperation::GET_WRITE_CACHE_HANDLER);
391 bool containsAddResource = Contains(AssetChangeOperation::ADD_RESOURCE);
392 bool isSaveCameraPhoto = Contains(AssetChangeOperation::SAVE_CAMERA_PHOTO);
393 if ((isCreateFromScratch || containsEdit) && !containsGetHandler && !containsAddResource && !isSaveCameraPhoto) {
394 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "Cannot create or edit asset without data to write");
395 return false;
396 }
397
398 if (containsEdit && (isCreateFromScratch || isCreateFromUri)) {
399 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "Cannot create together with edit");
400 return false;
401 }
402
403 auto fileAsset = GetFileAssetInstance();
404 if (fileAsset == nullptr) {
405 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "fileAsset is null");
406 return false;
407 }
408
409 AssetChangeOperation firstOperation = assetChangeOperations_.front();
410 if (fileAsset->GetId() <= 0 && firstOperation != AssetChangeOperation::CREATE_FROM_SCRATCH &&
411 firstOperation != AssetChangeOperation::CREATE_FROM_URI) {
412 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "Invalid asset change request");
413 return false;
414 }
415
416 bool isMovingPhoto = IsMovingPhoto();
417 if (isMovingPhoto && !CheckMovingPhotoWriteOperation()) {
418 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "Invalid write operation for moving photo");
419 return false;
420 }
421
422 return true;
423 }
424
FetchAddCacheFileId()425 uint32_t MediaAssetChangeRequestNapi::FetchAddCacheFileId()
426 {
427 uint32_t id = cacheFileId_.fetch_add(1);
428 return id;
429 }
430
SetCacheFileName(string & fileName)431 void MediaAssetChangeRequestNapi::SetCacheFileName(string& fileName)
432 {
433 cacheFileName_ = fileName;
434 }
435
GetCacheFileName()436 std::string MediaAssetChangeRequestNapi::GetCacheFileName()
437 {
438 return cacheFileName_;
439 }
440
SetCacheMovingPhotoVideoName(string & fileName)441 void MediaAssetChangeRequestNapi::SetCacheMovingPhotoVideoName(string& fileName)
442 {
443 cacheMovingPhotoVideoName_ = fileName;
444 }
445
GetFileRealPath() const446 string MediaAssetChangeRequestNapi::GetFileRealPath() const
447 {
448 return realPath_;
449 }
450
GetAddResourceMode() const451 AddResourceMode MediaAssetChangeRequestNapi::GetAddResourceMode() const
452 {
453 return addResourceMode_;
454 }
455
GetDataBuffer() const456 void* MediaAssetChangeRequestNapi::GetDataBuffer() const
457 {
458 return dataBuffer_;
459 }
460
GetDataBufferSize() const461 size_t MediaAssetChangeRequestNapi::GetDataBufferSize() const
462 {
463 return dataBufferSize_;
464 }
465
GetMovingPhotoVideoPath() const466 string MediaAssetChangeRequestNapi::GetMovingPhotoVideoPath() const
467 {
468 return movingPhotoVideoRealPath_;
469 }
470
GetMovingPhotoVideoMode() const471 AddResourceMode MediaAssetChangeRequestNapi::GetMovingPhotoVideoMode() const
472 {
473 return movingPhotoVideoResourceMode_;
474 }
475
GetMovingPhotoVideoBuffer() const476 void* MediaAssetChangeRequestNapi::GetMovingPhotoVideoBuffer() const
477 {
478 return movingPhotoVideoDataBuffer_;
479 }
480
GetMovingPhotoVideoSize() const481 size_t MediaAssetChangeRequestNapi::GetMovingPhotoVideoSize() const
482 {
483 return movingPhotoVideoBufferSize_;
484 }
485
GetCacheMovingPhotoVideoName() const486 string MediaAssetChangeRequestNapi::GetCacheMovingPhotoVideoName() const
487 {
488 return cacheMovingPhotoVideoName_;
489 }
490
SetImageFileType(int32_t imageFileType)491 void MediaAssetChangeRequestNapi::SetImageFileType(int32_t imageFileType)
492 {
493 imageFileType_ = imageFileType;
494 }
495
GetImageFileType()496 int32_t MediaAssetChangeRequestNapi::GetImageFileType()
497 {
498 return imageFileType_;
499 }
500
SetIsWriteGpsAdvanced(bool val)501 void MediaAssetChangeRequestNapi::SetIsWriteGpsAdvanced(bool val)
502 {
503 isWriteGpsAdvanced_ = val;
504 }
505
GetIsWriteGpsAdvanced()506 bool MediaAssetChangeRequestNapi::GetIsWriteGpsAdvanced()
507 {
508 return isWriteGpsAdvanced_;
509 }
510
SetIsEditDisplayName(bool val)511 void MediaAssetChangeRequestNapi::SetIsEditDisplayName(bool val)
512 {
513 isEditDisplayName_ = val;
514 }
515
GetIsEditDisplayName()516 bool MediaAssetChangeRequestNapi::GetIsEditDisplayName()
517 {
518 return isEditDisplayName_;
519 }
520
SetOldDisplayName(const std::string & oldDisplayName)521 void MediaAssetChangeRequestNapi::SetOldDisplayName(const std::string &oldDisplayName)
522 {
523 oldDisplayName_ = oldDisplayName;
524 }
525
GetOldDisplayName()526 std::string MediaAssetChangeRequestNapi::GetOldDisplayName()
527 {
528 return oldDisplayName_;
529 }
530
PutStringToCreationValue(const std::string & columnName,const std::string & val)531 void MediaAssetChangeRequestNapi::PutStringToCreationValue(const std::string &columnName, const std::string &val)
532 {
533 creationValuesBucket_.Put(columnName, val);
534 }
535
JSGetAsset(napi_env env,napi_callback_info info)536 napi_value MediaAssetChangeRequestNapi::JSGetAsset(napi_env env, napi_callback_info info)
537 {
538 auto asyncContext = make_unique<MediaAssetChangeRequestAsyncContext>();
539 CHECK_ARGS_THROW_INVALID_PARAM(env,
540 MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, asyncContext, ARGS_ZERO, ARGS_ZERO));
541
542 auto changeRequest = asyncContext->objectInfo;
543 auto fileAsset = changeRequest->GetFileAssetInstance();
544 CHECK_COND(env, fileAsset != nullptr, JS_INNER_FAIL);
545 if (fileAsset->GetId() > 0) {
546 return FileAssetNapi::CreatePhotoAsset(env, fileAsset);
547 }
548
549 // FileAsset object has not been actually created, return null.
550 napi_value nullValue;
551 napi_get_null(env, &nullValue);
552 return nullValue;
553 }
554
HasWritePermission()555 static bool HasWritePermission()
556 {
557 AccessTokenID tokenCaller = IPCSkeleton::GetSelfTokenID();
558 int result = AccessTokenKit::VerifyAccessToken(tokenCaller, PERM_WRITE_IMAGEVIDEO);
559 return result == PermissionState::PERMISSION_GRANTED;
560 }
561
CheckMovingPhotoCreationArgs(MediaAssetChangeRequestAsyncContext & context)562 static bool CheckMovingPhotoCreationArgs(MediaAssetChangeRequestAsyncContext& context)
563 {
564 bool isValid = false;
565 int32_t mediaType = context.valuesBucket.Get(MEDIA_DATA_DB_MEDIA_TYPE, isValid);
566 if (!isValid) {
567 NAPI_ERR_LOG("Failed to get media type");
568 return false;
569 }
570
571 if (mediaType != static_cast<int32_t>(MEDIA_TYPE_IMAGE)) {
572 NAPI_ERR_LOG("Failed to check media type (%{public}d) for moving photo", mediaType);
573 return false;
574 }
575
576 string extension = context.valuesBucket.Get(ASSET_EXTENTION, isValid);
577 if (isValid) {
578 return MediaFileUtils::CheckMovingPhotoExtension(extension);
579 }
580
581 string displayName = context.valuesBucket.Get(MEDIA_DATA_DB_NAME, isValid);
582 return isValid && MediaFileUtils::CheckMovingPhotoExtension(MediaFileUtils::GetExtensionFromPath(displayName));
583 }
584
CheckCreateOption(MediaAssetChangeRequestAsyncContext & context,bool isSystemApi)585 static napi_status CheckCreateOption(MediaAssetChangeRequestAsyncContext& context, bool isSystemApi)
586 {
587 bool isValid = false;
588 int32_t subtype = context.valuesBucket.Get(PhotoColumn::PHOTO_SUBTYPE, isValid);
589 if (isValid) {
590 if (subtype < static_cast<int32_t>(PhotoSubType::DEFAULT) ||
591 subtype >= static_cast<int32_t>(PhotoSubType::SUBTYPE_END)) {
592 NAPI_ERR_LOG("Failed to check subtype: %{public}d", subtype);
593 return napi_invalid_arg;
594 }
595
596 // check media type and extension for moving photo
597 if (subtype == static_cast<int32_t>(PhotoSubType::MOVING_PHOTO) &&
598 !CheckMovingPhotoCreationArgs(context)) {
599 NAPI_ERR_LOG("Failed to check creation args for moving photo");
600 return napi_invalid_arg;
601 }
602
603 // check subtype for public api
604 if (!isSystemApi && subtype != static_cast<int32_t>(PhotoSubType::DEFAULT) &&
605 subtype != static_cast<int32_t>(PhotoSubType::MOVING_PHOTO)) {
606 NAPI_ERR_LOG("Failed to check subtype: %{public}d", subtype);
607 return napi_invalid_arg;
608 }
609 }
610
611 string cameraShotKey = context.valuesBucket.Get(PhotoColumn::CAMERA_SHOT_KEY, isValid);
612 if (isValid) {
613 if (cameraShotKey.size() < CAMERA_SHOT_KEY_SIZE) {
614 NAPI_ERR_LOG("cameraShotKey is not null but is less than CAMERA_SHOT_KEY_SIZE");
615 return napi_invalid_arg;
616 }
617 if (subtype == static_cast<int32_t>(PhotoSubType::SCREENSHOT)) {
618 NAPI_ERR_LOG("cameraShotKey is not null with subtype is SCREENSHOT");
619 return napi_invalid_arg;
620 } else {
621 context.valuesBucket.Put(PhotoColumn::PHOTO_SUBTYPE, static_cast<int32_t>(PhotoSubType::CAMERA));
622 }
623 }
624 return napi_ok;
625 }
626
ParseAssetCreateOptions(napi_env env,napi_value arg,MediaAssetChangeRequestAsyncContext & context,const map<string,string> & createOptionsMap,bool isSystemApi)627 static napi_status ParseAssetCreateOptions(napi_env env, napi_value arg, MediaAssetChangeRequestAsyncContext& context,
628 const map<string, string>& createOptionsMap, bool isSystemApi)
629 {
630 for (const auto& iter : createOptionsMap) {
631 string param = iter.first;
632 bool present = false;
633 napi_status result = napi_has_named_property(env, arg, param.c_str(), &present);
634 CHECK_COND_RET(result == napi_ok, result, "Failed to check named property");
635 if (!present) {
636 continue;
637 }
638 napi_value value;
639 result = napi_get_named_property(env, arg, param.c_str(), &value);
640 CHECK_COND_RET(result == napi_ok, result, "Failed to get named property");
641 napi_valuetype valueType = napi_undefined;
642 result = napi_typeof(env, value, &valueType);
643 CHECK_COND_RET(result == napi_ok, result, "Failed to get value type");
644 if (param == USER_ID && valueType == napi_number) {
645 int32_t number = -1;
646 result = napi_get_value_int32(env, value, &number);
647 CHECK_COND_RET(result == napi_ok, result, "Failed to get int32_t");
648 context.userId_ = number;
649 }
650 if (valueType == napi_number) {
651 int32_t number = 0;
652 result = napi_get_value_int32(env, value, &number);
653 CHECK_COND_RET(result == napi_ok, result, "Failed to get int32_t");
654 context.valuesBucket.Put(iter.second, number);
655 } else if (valueType == napi_boolean) {
656 bool isTrue = false;
657 result = napi_get_value_bool(env, value, &isTrue);
658 CHECK_COND_RET(result == napi_ok, result, "Failed to get bool");
659 context.valuesBucket.Put(iter.second, isTrue);
660 } else if (valueType == napi_string) {
661 char buffer[ARG_BUF_SIZE];
662 size_t res = 0;
663 result = napi_get_value_string_utf8(env, value, buffer, ARG_BUF_SIZE, &res);
664 CHECK_COND_RET(result == napi_ok, result, "Failed to get string");
665 context.valuesBucket.Put(iter.second, string(buffer));
666 } else if (valueType == napi_undefined || valueType == napi_null) {
667 continue;
668 } else {
669 NAPI_ERR_LOG("valueType %{public}d is unaccepted", static_cast<int>(valueType));
670 return napi_invalid_arg;
671 }
672 }
673 return CheckCreateOption(context, isSystemApi);
674 }
675
ParseArgsCreateAssetSystem(napi_env env,napi_callback_info info,unique_ptr<MediaAssetChangeRequestAsyncContext> & context)676 static napi_value ParseArgsCreateAssetSystem(
677 napi_env env, napi_callback_info info, unique_ptr<MediaAssetChangeRequestAsyncContext>& context)
678 {
679 // Parse displayName.
680 string displayName;
681 MediaType mediaType;
682 CHECK_COND_WITH_MESSAGE(env,
683 MediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[PARAM1], displayName) == napi_ok,
684 "Failed to get displayName");
685 CHECK_COND_WITH_MESSAGE(env, MediaFileUtils::CheckDisplayName(displayName) == E_OK, "Failed to check displayName");
686 mediaType = MediaFileUtils::GetMediaType(displayName);
687 CHECK_COND_WITH_MESSAGE(env, mediaType == MEDIA_TYPE_IMAGE || mediaType == MEDIA_TYPE_VIDEO, "Invalid file type");
688 context->valuesBucket.Put(MEDIA_DATA_DB_NAME, displayName);
689 context->valuesBucket.Put(MEDIA_DATA_DB_MEDIA_TYPE, static_cast<int32_t>(mediaType));
690
691 // Parse options if exists.
692 if (context->argc == ARGS_THREE) {
693 napi_valuetype valueType;
694 napi_value createOptionsNapi = context->argv[PARAM2];
695 CHECK_COND_WITH_MESSAGE(
696 env, napi_typeof(env, createOptionsNapi, &valueType) == napi_ok, "Failed to get napi type");
697 if (valueType != napi_object) {
698 NAPI_ERR_LOG("Napi type is wrong in PhotoCreateOptions");
699 return nullptr;
700 }
701
702 CHECK_COND_WITH_MESSAGE(env,
703 ParseAssetCreateOptions(env, createOptionsNapi, *context, PHOTO_CREATE_OPTIONS_PARAM, true) == napi_ok,
704 "Parse PhotoCreateOptions failed");
705 }
706 RETURN_NAPI_TRUE(env);
707 }
708
ParseArgsCreateAssetCommon(napi_env env,napi_callback_info info,unique_ptr<MediaAssetChangeRequestAsyncContext> & context)709 static napi_value ParseArgsCreateAssetCommon(
710 napi_env env, napi_callback_info info, unique_ptr<MediaAssetChangeRequestAsyncContext>& context)
711 {
712 // Parse photoType.
713 MediaType mediaType;
714 int32_t type = 0;
715 CHECK_COND_WITH_MESSAGE(
716 env, napi_get_value_int32(env, context->argv[PARAM1], &type) == napi_ok, "Failed to get photoType");
717 mediaType = static_cast<MediaType>(type);
718 CHECK_COND_WITH_MESSAGE(env, mediaType == MEDIA_TYPE_IMAGE || mediaType == MEDIA_TYPE_VIDEO, "Invalid photoType");
719
720 // Parse extension.
721 string extension;
722 CHECK_COND_WITH_MESSAGE(env,
723 MediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[PARAM2], extension) == napi_ok,
724 "Failed to get extension");
725 CHECK_COND_WITH_MESSAGE(
726 env, mediaType == MediaFileUtils::GetMediaType("." + extension), "Failed to check extension");
727 context->valuesBucket.Put(ASSET_EXTENTION, extension);
728 context->valuesBucket.Put(MEDIA_DATA_DB_MEDIA_TYPE, static_cast<int32_t>(mediaType));
729
730 // Parse options if exists.
731 if (context->argc == ARGS_FOUR) {
732 napi_valuetype valueType;
733 napi_value createOptionsNapi = context->argv[PARAM3];
734 CHECK_COND_WITH_MESSAGE(
735 env, napi_typeof(env, createOptionsNapi, &valueType) == napi_ok, "Failed to get napi type");
736 if (valueType != napi_object) {
737 NAPI_ERR_LOG("Napi type is wrong in CreateOptions");
738 return nullptr;
739 }
740
741 CHECK_COND_WITH_MESSAGE(env,
742 ParseAssetCreateOptions(env, createOptionsNapi, *context, CREATE_OPTIONS_PARAM, false) == napi_ok,
743 "Parse CreateOptions failed");
744 }
745
746 bool isValid = false;
747 string title = context->valuesBucket.Get(PhotoColumn::MEDIA_TITLE, isValid);
748 if (!isValid) {
749 title = mediaType == MEDIA_TYPE_IMAGE ? DEFAULT_TITLE_IMG_PREFIX : DEFAULT_TITLE_VIDEO_PREFIX;
750 title += MediaFileUtils::StrCreateTime(DEFAULT_TITLE_TIME_FORMAT, MediaFileUtils::UTCTimeSeconds());
751 context->valuesBucket.Put(PhotoColumn::MEDIA_TITLE, title);
752 }
753
754 string displayName = title + "." + extension;
755 CHECK_COND_WITH_MESSAGE(env, MediaFileUtils::CheckDisplayName(displayName, true) == E_OK,
756 "Failed to check displayName");
757 context->valuesBucket.Put(MEDIA_DATA_DB_NAME, displayName);
758 RETURN_NAPI_TRUE(env);
759 }
760
ParseArgsCreateAsset(napi_env env,napi_callback_info info,unique_ptr<MediaAssetChangeRequestAsyncContext> & context)761 static napi_value ParseArgsCreateAsset(
762 napi_env env, napi_callback_info info, unique_ptr<MediaAssetChangeRequestAsyncContext>& context)
763 {
764 constexpr size_t minArgs = ARGS_TWO;
765 constexpr size_t maxArgs = ARGS_FOUR;
766 napi_value result = nullptr;
767 CHECK_COND_WITH_MESSAGE(env,
768 MediaLibraryNapiUtils::AsyncContextGetArgs(env, info, context, minArgs, maxArgs) == napi_ok,
769 "Failed to get args");
770
771 napi_valuetype valueType;
772 CHECK_COND_WITH_MESSAGE(
773 env, napi_typeof(env, context->argv[PARAM1], &valueType) == napi_ok, "Failed to get napi type");
774 if (valueType == napi_string) {
775 if (!MediaLibraryNapiUtils::IsSystemApp()) {
776 NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
777 return nullptr;
778 }
779 CHECK_COND_WITH_MESSAGE(env, context->argc <= ARGS_THREE, "Number of args is invalid");
780 result = ParseArgsCreateAssetSystem(env, info, context);
781 } else if (valueType == napi_number) {
782 result = ParseArgsCreateAssetCommon(env, info, context);
783 } else {
784 NAPI_ERR_LOG("param type %{public}d is invalid", static_cast<int32_t>(valueType));
785 return nullptr;
786 }
787 CHECK_COND(env, MediaAssetChangeRequestNapi::InitUserFileClient(env, info, context->userId_), JS_INNER_FAIL);
788 return result;
789 }
790
JSCreateAssetRequest(napi_env env,napi_callback_info info)791 napi_value MediaAssetChangeRequestNapi::JSCreateAssetRequest(napi_env env, napi_callback_info info)
792 {
793 NAPI_INFO_LOG("CreateAssetRequest Start");
794 auto asyncContext = make_unique<MediaAssetChangeRequestAsyncContext>();
795 CHECK_COND_WITH_MESSAGE(env, ParseArgsCreateAsset(env, info, asyncContext), "Failed to parse args");
796
797 bool isValid = false;
798 string displayName = asyncContext->valuesBucket.Get(MEDIA_DATA_DB_NAME, isValid);
799 int32_t subtype = asyncContext->valuesBucket.Get(PhotoColumn::PHOTO_SUBTYPE, isValid); // default is 0
800 auto emptyFileAsset = make_unique<FileAsset>();
801 emptyFileAsset->SetDisplayName(displayName);
802 emptyFileAsset->SetTitle(MediaFileUtils::GetTitleFromDisplayName(displayName));
803 emptyFileAsset->SetMediaType(MediaFileUtils::GetMediaType(displayName));
804 emptyFileAsset->SetPhotoSubType(subtype);
805 emptyFileAsset->SetTimePending(CREATE_ASSET_REQUEST_PENDING);
806 emptyFileAsset->SetResultNapiType(ResultNapiType::TYPE_PHOTOACCESS_HELPER);
807 emptyFileAsset->SetUserId(asyncContext->userId_);
808 napi_value fileAssetNapi = FileAssetNapi::CreateFileAsset(env, emptyFileAsset);
809 CHECK_COND(env, fileAssetNapi != nullptr, JS_INNER_FAIL);
810
811 napi_value constructor = nullptr;
812 napi_value instance = nullptr;
813 CHECK_ARGS(env, napi_get_reference_value(env, constructor_, &constructor), JS_INNER_FAIL);
814 CHECK_ARGS(env, napi_new_instance(env, constructor, 1, &fileAssetNapi, &instance), JS_INNER_FAIL);
815 CHECK_COND(env, instance != nullptr, JS_INNER_FAIL);
816
817 MediaAssetChangeRequestNapi* changeRequest = nullptr;
818 CHECK_ARGS(env, napi_unwrap(env, instance, reinterpret_cast<void**>(&changeRequest)), JS_INNER_FAIL);
819 CHECK_COND(env, changeRequest != nullptr, JS_INNER_FAIL);
820 changeRequest->creationValuesBucket_ = std::move(asyncContext->valuesBucket);
821 changeRequest->RecordChangeOperation(AssetChangeOperation::CREATE_FROM_SCRATCH);
822 return instance;
823 }
824
ParseFileUri(napi_env env,napi_value arg,MediaType mediaType,unique_ptr<MediaAssetChangeRequestAsyncContext> & context)825 static napi_value ParseFileUri(napi_env env, napi_value arg, MediaType mediaType,
826 unique_ptr<MediaAssetChangeRequestAsyncContext>& context)
827 {
828 string fileUriStr;
829 CHECK_COND_WITH_MESSAGE(
830 env, MediaLibraryNapiUtils::GetParamStringPathMax(env, arg, fileUriStr) == napi_ok, "Failed to get fileUri");
831 AppFileService::ModuleFileUri::FileUri fileUri(fileUriStr);
832 string path = fileUri.GetRealPath();
833 CHECK_COND(env, PathToRealPath(path, context->realPath), JS_ERR_NO_SUCH_FILE);
834
835 CHECK_COND_WITH_MESSAGE(env, mediaType == MediaFileUtils::GetMediaType(context->realPath), "Invalid file type");
836 RETURN_NAPI_TRUE(env);
837 }
838
ParseArgsCreateAssetFromFileUri(napi_env env,napi_callback_info info,MediaType mediaType,unique_ptr<MediaAssetChangeRequestAsyncContext> & context)839 static napi_value ParseArgsCreateAssetFromFileUri(napi_env env, napi_callback_info info, MediaType mediaType,
840 unique_ptr<MediaAssetChangeRequestAsyncContext>& context)
841 {
842 CHECK_COND_WITH_MESSAGE(env,
843 MediaLibraryNapiUtils::AsyncContextGetArgs(env, info, context, ARGS_TWO, ARGS_TWO) == napi_ok,
844 "Failed to get args");
845 CHECK_COND(env, MediaAssetChangeRequestNapi::InitUserFileClient(env, info), JS_INNER_FAIL);
846 return ParseFileUri(env, context->argv[PARAM1], mediaType, context);
847 }
848
CreateAssetRequestFromRealPath(napi_env env,const string & realPath)849 napi_value MediaAssetChangeRequestNapi::CreateAssetRequestFromRealPath(napi_env env, const string& realPath)
850 {
851 string displayName = MediaFileUtils::GetFileName(realPath);
852 CHECK_COND_WITH_MESSAGE(env, MediaFileUtils::CheckDisplayName(displayName, true) == E_OK, "Invalid fileName");
853 string title = MediaFileUtils::GetTitleFromDisplayName(displayName);
854 MediaType mediaType = MediaFileUtils::GetMediaType(displayName);
855 auto emptyFileAsset = make_unique<FileAsset>();
856 emptyFileAsset->SetDisplayName(displayName);
857 emptyFileAsset->SetTitle(title);
858 emptyFileAsset->SetMediaType(mediaType);
859 emptyFileAsset->SetPhotoSubType(static_cast<int32_t>(PhotoSubType::DEFAULT));
860 emptyFileAsset->SetTimePending(CREATE_ASSET_REQUEST_PENDING);
861 emptyFileAsset->SetResultNapiType(ResultNapiType::TYPE_PHOTOACCESS_HELPER);
862 napi_value fileAssetNapi = FileAssetNapi::CreateFileAsset(env, emptyFileAsset);
863 CHECK_COND(env, fileAssetNapi != nullptr, JS_INNER_FAIL);
864
865 napi_value constructor = nullptr;
866 napi_value instance = nullptr;
867 CHECK_ARGS(env, napi_get_reference_value(env, constructor_, &constructor), JS_INNER_FAIL);
868 CHECK_ARGS(env, napi_new_instance(env, constructor, 1, &fileAssetNapi, &instance), JS_INNER_FAIL);
869 CHECK_COND(env, instance != nullptr, JS_INNER_FAIL);
870
871 MediaAssetChangeRequestNapi* changeRequest = nullptr;
872 CHECK_ARGS(env, napi_unwrap(env, instance, reinterpret_cast<void**>(&changeRequest)), JS_INNER_FAIL);
873 CHECK_COND(env, changeRequest != nullptr, JS_INNER_FAIL);
874 changeRequest->realPath_ = realPath;
875 changeRequest->creationValuesBucket_.Put(MEDIA_DATA_DB_NAME, displayName);
876 changeRequest->creationValuesBucket_.Put(ASSET_EXTENTION, MediaFileUtils::GetExtensionFromPath(displayName));
877 changeRequest->creationValuesBucket_.Put(MEDIA_DATA_DB_MEDIA_TYPE, static_cast<int32_t>(mediaType));
878 changeRequest->creationValuesBucket_.Put(PhotoColumn::MEDIA_TITLE, title);
879 changeRequest->RecordChangeOperation(AssetChangeOperation::CREATE_FROM_URI);
880 return instance;
881 }
882
JSCreateImageAssetRequest(napi_env env,napi_callback_info info)883 napi_value MediaAssetChangeRequestNapi::JSCreateImageAssetRequest(napi_env env, napi_callback_info info)
884 {
885 NAPI_INFO_LOG("CreateImageAssetRequest Start");
886 auto asyncContext = make_unique<MediaAssetChangeRequestAsyncContext>();
887 CHECK_COND_WITH_MESSAGE(env, ParseArgsCreateAssetFromFileUri(env, info, MediaType::MEDIA_TYPE_IMAGE, asyncContext),
888 "Failed to parse args");
889 return CreateAssetRequestFromRealPath(env, asyncContext->realPath);
890 }
891
JSCreateVideoAssetRequest(napi_env env,napi_callback_info info)892 napi_value MediaAssetChangeRequestNapi::JSCreateVideoAssetRequest(napi_env env, napi_callback_info info)
893 {
894 NAPI_INFO_LOG("CreateVideoAssetRequest start");
895 auto asyncContext = make_unique<MediaAssetChangeRequestAsyncContext>();
896 CHECK_COND_WITH_MESSAGE(env, ParseArgsCreateAssetFromFileUri(env, info, MediaType::MEDIA_TYPE_VIDEO, asyncContext),
897 "Failed to parse args");
898 return CreateAssetRequestFromRealPath(env, asyncContext->realPath);
899 }
900
initDeleteRequest(napi_env env,MediaAssetChangeRequestAsyncContext & context,OHOS::AAFwk::Want & request,shared_ptr<DeleteCallback> & callback)901 static napi_value initDeleteRequest(napi_env env, MediaAssetChangeRequestAsyncContext& context,
902 OHOS::AAFwk::Want& request, shared_ptr<DeleteCallback>& callback)
903 {
904 request.SetElementName(DELETE_UI_PACKAGE_NAME, DELETE_UI_EXT_ABILITY_NAME);
905 request.SetParam(DELETE_UI_EXTENSION_TYPE, DELETE_UI_REQUEST_TYPE);
906
907 CHECK_COND(env, !context.appName.empty(), JS_INNER_FAIL);
908 request.SetParam(DELETE_UI_APPNAME, context.appName);
909
910 request.SetParam(DELETE_UI_URIS, context.uris);
911 callback->SetUris(context.uris);
912
913 napi_valuetype valueType = napi_undefined;
914 CHECK_COND_WITH_MESSAGE(env, context.argc >= ARGS_THREE && context.argc <= ARGS_FOUR, "Failed to check args");
915 napi_value func = context.argv[PARAM1];
916 CHECK_ARGS(env, napi_typeof(env, func, &valueType), JS_INNER_FAIL);
917 CHECK_COND_WITH_MESSAGE(env, valueType == napi_function, "Failed to check args");
918 callback->SetFunc(func);
919 RETURN_NAPI_TRUE(env);
920 }
921
ParseArgsDeleteAssets(napi_env env,napi_callback_info info,unique_ptr<MediaAssetChangeRequestAsyncContext> & context)922 static napi_value ParseArgsDeleteAssets(
923 napi_env env, napi_callback_info info, unique_ptr<MediaAssetChangeRequestAsyncContext>& context)
924 {
925 constexpr size_t minArgs = ARGS_THREE;
926 constexpr size_t maxArgs = ARGS_FOUR;
927 CHECK_COND_WITH_MESSAGE(env,
928 MediaLibraryNapiUtils::AsyncContextGetArgs(env, info, context, minArgs, maxArgs) == napi_ok,
929 "Failed to get args");
930 CHECK_COND(env, MediaAssetChangeRequestNapi::InitUserFileClient(env, info), JS_INNER_FAIL);
931
932 napi_valuetype valueType = napi_undefined;
933 CHECK_ARGS(env, napi_typeof(env, context->argv[PARAM1], &valueType), JS_INNER_FAIL);
934 CHECK_COND(env, valueType == napi_function, JS_INNER_FAIL);
935
936 vector<string> uris;
937 vector<napi_value> napiValues;
938 CHECK_NULLPTR_RET(MediaLibraryNapiUtils::GetNapiValueArray(env, context->argv[PARAM2], napiValues));
939 CHECK_COND_WITH_MESSAGE(env, !napiValues.empty(), "array is empty");
940 CHECK_ARGS(env, napi_typeof(env, napiValues.front(), &valueType), JS_INNER_FAIL);
941 if (valueType == napi_string) { // array of asset uri
942 CHECK_NULLPTR_RET(MediaLibraryNapiUtils::GetStringArray(env, napiValues, uris));
943 } else if (valueType == napi_object) { // array of asset object
944 CHECK_NULLPTR_RET(MediaLibraryNapiUtils::GetUriArrayFromAssets(env, napiValues, uris));
945 } else {
946 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "Invalid type");
947 return nullptr;
948 }
949
950 CHECK_COND_WITH_MESSAGE(env, !uris.empty(), "Failed to check empty array");
951 for (const auto& uri : uris) {
952 context->userId_ = stoi(MediaLibraryNapiUtils::GetUserIdFromUri(uri));
953 CHECK_COND(env, uri.find(PhotoColumn::PHOTO_URI_PREFIX) != string::npos, JS_E_URI);
954 }
955
956 NAPI_INFO_LOG("DeleteAssetsExecute size:%{public}zu", uris.size());
957 context->predicates.In(PhotoColumn::MEDIA_ID, uris);
958 context->valuesBucket.Put(PhotoColumn::MEDIA_DATE_TRASHED, MediaFileUtils::UTCTimeSeconds());
959 context->uris.assign(uris.begin(), uris.end());
960 RETURN_NAPI_TRUE(env);
961 }
962
DeleteAssetsExecute(napi_env env,void * data)963 static void DeleteAssetsExecute(napi_env env, void* data)
964 {
965 MediaLibraryTracer tracer;
966 tracer.Start("DeleteAssetsExecute");
967
968 auto* context = static_cast<MediaAssetChangeRequestAsyncContext*>(data);
969 string trashUri = PAH_SYS_TRASH_PHOTO;
970 MediaLibraryNapiUtils::UriAppendKeyValue(trashUri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
971 Uri updateAssetUri(trashUri);
972 int32_t changedRows = UserFileClient::Update(updateAssetUri, context->predicates, context->valuesBucket,
973 context->userId_);
974 if (changedRows < 0) {
975 context->SaveError(changedRows);
976 NAPI_ERR_LOG("Failed to delete assets, err: %{public}d", changedRows);
977 }
978 }
979
DeleteAssetsCompleteCallback(napi_env env,napi_status status,void * data)980 static void DeleteAssetsCompleteCallback(napi_env env, napi_status status, void* data)
981 {
982 auto* context = static_cast<MediaAssetChangeRequestAsyncContext*>(data);
983 CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
984 auto jsContext = make_unique<JSAsyncContextOutput>();
985 jsContext->status = false;
986 napi_get_undefined(env, &jsContext->data);
987 napi_get_undefined(env, &jsContext->error);
988 if (context->error == ERR_DEFAULT) {
989 jsContext->status = true;
990 } else {
991 context->HandleError(env, jsContext->error);
992 }
993
994 if (context->work != nullptr) {
995 MediaLibraryNapiUtils::InvokeJSAsyncMethod(
996 env, context->deferred, context->callbackRef, context->work, *jsContext);
997 }
998 delete context;
999 }
1000
JSDeleteAssets(napi_env env,napi_callback_info info)1001 napi_value MediaAssetChangeRequestNapi::JSDeleteAssets(napi_env env, napi_callback_info info)
1002 {
1003 NAPI_INFO_LOG("enter");
1004 MediaLibraryTracer tracer;
1005 tracer.Start("JSDeleteAssets");
1006
1007 auto asyncContext = make_unique<MediaAssetChangeRequestAsyncContext>();
1008 CHECK_COND_WITH_MESSAGE(env, ParseArgsDeleteAssets(env, info, asyncContext), "Failed to parse args");
1009 if (MediaLibraryNapiUtils::IsSystemApp()) {
1010 return MediaLibraryNapiUtils::NapiCreateAsyncWork(
1011 env, asyncContext, "ChangeRequestDeleteAssets", DeleteAssetsExecute, DeleteAssetsCompleteCallback);
1012 }
1013
1014 #ifdef HAS_ACE_ENGINE_PART
1015 // Deletion control by ui extension
1016 CHECK_COND(env, HasWritePermission(), OHOS_PERMISSION_DENIED_CODE);
1017 CHECK_COND_WITH_MESSAGE(
1018 env, asyncContext->uris.size() <= MAX_DELETE_NUMBER, "No more than 300 assets can be deleted at one time");
1019 auto context = OHOS::AbilityRuntime::GetStageModeContext(env, asyncContext->argv[PARAM0]);
1020 CHECK_COND_WITH_MESSAGE(env, context != nullptr, "Failed to get stage mode context");
1021 auto abilityContext = OHOS::AbilityRuntime::Context::ConvertTo<OHOS::AbilityRuntime::AbilityContext>(context);
1022 CHECK_COND(env, abilityContext != nullptr, JS_INNER_FAIL);
1023 auto abilityInfo = abilityContext->GetAbilityInfo();
1024 abilityContext->GetResourceManager()->GetStringById(abilityInfo->labelId, asyncContext->appName);
1025 auto uiContent = abilityContext->GetUIContent();
1026 CHECK_COND(env, uiContent != nullptr, JS_INNER_FAIL);
1027
1028 auto callback = std::make_shared<DeleteCallback>(env, uiContent);
1029 OHOS::Ace::ModalUIExtensionCallbacks extensionCallback = {
1030 ([callback](auto arg) { callback->OnRelease(arg); }),
1031 ([callback](auto arg1, auto arg2) { callback->OnResult(arg1, arg2); }),
1032 ([callback](auto arg) { callback->OnReceive(arg); }),
1033 ([callback](auto arg1, auto arg2, auto arg3) { callback->OnError(arg1, arg2, arg3); }),
1034 };
1035 OHOS::Ace::ModalUIExtensionConfig config;
1036 config.isProhibitBack = true;
1037 OHOS::AAFwk::Want request;
1038 CHECK_COND(env, initDeleteRequest(env, *asyncContext, request, callback), JS_INNER_FAIL);
1039
1040 int32_t sessionId = uiContent->CreateModalUIExtension(request, extensionCallback, config);
1041 CHECK_COND(env, sessionId != 0, JS_INNER_FAIL);
1042 callback->SetSessionId(sessionId);
1043 RETURN_NAPI_UNDEFINED(env);
1044 #else
1045 NapiError::ThrowError(env, JS_INNER_FAIL, "ace_engine is not support");
1046 return nullptr;
1047 #endif
1048 }
1049
JSSetEditData(napi_env env,napi_callback_info info)1050 napi_value MediaAssetChangeRequestNapi::JSSetEditData(napi_env env, napi_callback_info info)
1051 {
1052 if (!MediaLibraryNapiUtils::IsSystemApp()) {
1053 NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
1054 return nullptr;
1055 }
1056
1057 auto asyncContext = make_unique<MediaAssetChangeRequestAsyncContext>();
1058 CHECK_COND_WITH_MESSAGE(env,
1059 MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, asyncContext, ARGS_ONE, ARGS_ONE) == napi_ok,
1060 "Failed to get object info");
1061
1062 auto changeRequest = asyncContext->objectInfo;
1063 napi_value arg = asyncContext->argv[PARAM0];
1064 napi_valuetype valueType;
1065 MediaAssetEditDataNapi* editDataNapi;
1066 CHECK_ARGS(env, napi_typeof(env, arg, &valueType), JS_INNER_FAIL);
1067 CHECK_COND_WITH_MESSAGE(env, valueType == napi_object, "Invalid argument type");
1068 CHECK_ARGS(env, napi_unwrap(env, arg, reinterpret_cast<void**>(&editDataNapi)), JS_INNER_FAIL);
1069 CHECK_COND_WITH_MESSAGE(env, editDataNapi != nullptr, "Failed to get MediaAssetEditDataNapi object");
1070
1071 shared_ptr<MediaAssetEditData> editData = editDataNapi->GetMediaAssetEditData();
1072 CHECK_COND_WITH_MESSAGE(env, editData != nullptr, "editData is null");
1073 CHECK_COND_WITH_MESSAGE(env, !editData->GetCompatibleFormat().empty(), "Invalid compatibleFormat");
1074 CHECK_COND_WITH_MESSAGE(env, !editData->GetFormatVersion().empty(), "Invalid formatVersion");
1075 CHECK_COND_WITH_MESSAGE(env, !editData->GetData().empty(), "Invalid data");
1076 changeRequest->editData_ = editData;
1077 changeRequest->RecordChangeOperation(AssetChangeOperation::SET_EDIT_DATA);
1078 if (changeRequest->Contains(AssetChangeOperation::SAVE_CAMERA_PHOTO) &&
1079 !changeRequest->Contains(AssetChangeOperation::ADD_FILTERS)) {
1080 changeRequest->RecordChangeOperation(AssetChangeOperation::ADD_FILTERS);
1081 }
1082 RETURN_NAPI_UNDEFINED(env);
1083 }
1084
JSSetFavorite(napi_env env,napi_callback_info info)1085 napi_value MediaAssetChangeRequestNapi::JSSetFavorite(napi_env env, napi_callback_info info)
1086 {
1087 if (!MediaLibraryNapiUtils::IsSystemApp()) {
1088 NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
1089 return nullptr;
1090 }
1091
1092 auto asyncContext = make_unique<MediaAssetChangeRequestAsyncContext>();
1093 bool isFavorite;
1094 CHECK_COND_WITH_MESSAGE(env,
1095 MediaLibraryNapiUtils::ParseArgsBoolCallBack(env, info, asyncContext, isFavorite) == napi_ok,
1096 "Failed to parse args");
1097 CHECK_COND_WITH_MESSAGE(env, asyncContext->argc == ARGS_ONE, "Number of args is invalid");
1098
1099 auto changeRequest = asyncContext->objectInfo;
1100 CHECK_COND(env, changeRequest->GetFileAssetInstance() != nullptr, JS_INNER_FAIL);
1101 changeRequest->GetFileAssetInstance()->SetFavorite(isFavorite);
1102 changeRequest->RecordChangeOperation(AssetChangeOperation::SET_FAVORITE);
1103 RETURN_NAPI_UNDEFINED(env);
1104 }
1105
JSSetHidden(napi_env env,napi_callback_info info)1106 napi_value MediaAssetChangeRequestNapi::JSSetHidden(napi_env env, napi_callback_info info)
1107 {
1108 if (!MediaLibraryNapiUtils::IsSystemApp()) {
1109 NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
1110 return nullptr;
1111 }
1112
1113 auto asyncContext = make_unique<MediaAssetChangeRequestAsyncContext>();
1114 bool isHidden;
1115 CHECK_COND_WITH_MESSAGE(env,
1116 MediaLibraryNapiUtils::ParseArgsBoolCallBack(env, info, asyncContext, isHidden) == napi_ok,
1117 "Failed to parse args");
1118 CHECK_COND_WITH_MESSAGE(env, asyncContext->argc == ARGS_ONE, "Number of args is invalid");
1119
1120 auto changeRequest = asyncContext->objectInfo;
1121 CHECK_COND(env, changeRequest->GetFileAssetInstance() != nullptr, JS_INNER_FAIL);
1122 changeRequest->GetFileAssetInstance()->SetHidden(isHidden);
1123 changeRequest->RecordChangeOperation(AssetChangeOperation::SET_HIDDEN);
1124 RETURN_NAPI_UNDEFINED(env);
1125 }
1126
JSSetTitle(napi_env env,napi_callback_info info)1127 napi_value MediaAssetChangeRequestNapi::JSSetTitle(napi_env env, napi_callback_info info)
1128 {
1129 auto asyncContext = make_unique<MediaAssetChangeRequestAsyncContext>();
1130 string title;
1131 CHECK_COND_WITH_MESSAGE(env,
1132 MediaLibraryNapiUtils::ParseArgsStringCallback(env, info, asyncContext, title) == napi_ok,
1133 "Failed to parse args");
1134 CHECK_COND_WITH_MESSAGE(env, asyncContext->argc == ARGS_ONE, "Number of args is invalid");
1135
1136 auto changeRequest = asyncContext->objectInfo;
1137 auto fileAsset = changeRequest->GetFileAssetInstance();
1138 CHECK_COND(env, fileAsset != nullptr, JS_INNER_FAIL);
1139 string extension = MediaFileUtils::SplitByChar(fileAsset->GetDisplayName(), '.');
1140 string displayName = title + "." + extension;
1141 CHECK_COND_WITH_MESSAGE(env, MediaFileUtils::CheckDisplayName(displayName, true) == E_OK, "Invalid title");
1142
1143 fileAsset->SetTitle(title);
1144 fileAsset->SetDisplayName(displayName);
1145 if (!changeRequest->Contains(AssetChangeOperation::SET_DISPLAY_NAME) &&
1146 !changeRequest->Contains(AssetChangeOperation::SET_TITLE)) {
1147 changeRequest->RecordChangeOperation(AssetChangeOperation::SET_TITLE);
1148 changeRequest->SetOldDisplayName(displayName);
1149 }
1150
1151 // Merge the creation and SET_TITLE operations.
1152 if (changeRequest->Contains(AssetChangeOperation::CREATE_FROM_SCRATCH) ||
1153 changeRequest->Contains(AssetChangeOperation::CREATE_FROM_URI)) {
1154 changeRequest->creationValuesBucket_.valuesMap[MEDIA_DATA_DB_NAME] = displayName;
1155 changeRequest->creationValuesBucket_.valuesMap[PhotoColumn::MEDIA_TITLE] = title;
1156 }
1157 RETURN_NAPI_UNDEFINED(env);
1158 }
1159
SetDisplayNameInitParameters(unique_ptr<MediaAssetChangeRequestAsyncContext> & context,const std::string & oldExtension,const std::string & newExtension)1160 int32_t SetDisplayNameInitParameters(unique_ptr<MediaAssetChangeRequestAsyncContext>& context,
1161 const std::string &oldExtension, const std::string &newExtension)
1162 {
1163 auto changeRequest = context->objectInfo;
1164 CHECK_COND_RET(changeRequest != nullptr, JS_INNER_FAIL, "changeRequest is nullptr");
1165 auto fileAsset = changeRequest->GetFileAssetInstance();
1166 CHECK_COND_RET(fileAsset != nullptr, JS_INNER_FAIL, "fileAsset is nullptr");
1167
1168 if (newExtension == oldExtension) {
1169 changeRequest->RecordChangeOperation(AssetChangeOperation::SET_TITLE);
1170 if (changeRequest->Contains(AssetChangeOperation::CREATE_FROM_SCRATCH) ||
1171 changeRequest->Contains(AssetChangeOperation::CREATE_FROM_URI)) {
1172 changeRequest->PutStringToCreationValue(MediaColumn::MEDIA_NAME, fileAsset->GetDisplayName());
1173 changeRequest->PutStringToCreationValue(PhotoColumn::MEDIA_TITLE, fileAsset->GetTitle());
1174 }
1175 } else {
1176 changeRequest->SetIsEditDisplayName(true);
1177 std::string newPath = MediaFileUtils::UnSplitByChar(fileAsset->GetPath(), '.') + "." + newExtension;
1178 fileAsset->SetPath(newPath);
1179 std::string newMimeType = MediaFileUtils::GetMimeTypeFromDisplayName(fileAsset->GetDisplayName());
1180 fileAsset->SetMimeType(newMimeType);
1181 changeRequest->RecordChangeOperation(AssetChangeOperation::SET_DISPLAY_NAME);
1182 }
1183 return E_OK;
1184 }
1185
JSSetDisplayName(napi_env env,napi_callback_info info)1186 napi_value MediaAssetChangeRequestNapi::JSSetDisplayName(napi_env env, napi_callback_info info)
1187 {
1188 NAPI_INFO_LOG("Begin JSSetDisplayName.");
1189 if (!MediaLibraryNapiUtils::IsSystemApp()) {
1190 NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
1191 return nullptr;
1192 }
1193 auto asyncContext = make_unique<MediaAssetChangeRequestAsyncContext>();
1194 std::string newDisplayName;
1195 CHECK_COND_WITH_MESSAGE(env,
1196 MediaLibraryNapiUtils::ParseArgsStringCallback(env, info, asyncContext, newDisplayName) == napi_ok,
1197 "Failed to parse args");
1198 CHECK_COND_WITH_MESSAGE(env,
1199 MediaFileUtils::GetMimeTypeFromDisplayName(newDisplayName) == DEFAULT_MIME_TYPE,
1200 "Invalid newDisplayName, Extension is not support.");
1201 CHECK_COND_WITH_MESSAGE(env, asyncContext->argc == ARGS_ONE, "Number of args is invalid");
1202 CHECK_COND_WITH_MESSAGE(env, MediaFileUtils::CheckDisplayName(newDisplayName) == E_OK, "Invalid display name.");
1203 NAPI_INFO_LOG("wang do: newDisplayName: %{public}s", newDisplayName.c_str());
1204
1205 std::string newTitle = MediaFileUtils::GetTitleFromDisplayName(newDisplayName);
1206 std::string newExtension = MediaFileUtils::GetExtensionFromPath(newDisplayName);
1207 MediaType newMediaType = MediaFileUtils::GetMediaType(newDisplayName);
1208
1209 auto changeRequest = asyncContext->objectInfo;
1210 CHECK_COND(env, changeRequest != nullptr, JS_INNER_FAIL);
1211 auto fileAsset = changeRequest->GetFileAssetInstance();
1212 CHECK_COND(env,
1213 fileAsset != nullptr && fileAsset->GetPhotoSubType() != static_cast<int32_t>(PhotoSubType::BURST),
1214 JS_INNER_FAIL);
1215
1216 MediaType oldMediaType = fileAsset->GetMediaType();
1217 std::string oldExtension = MediaFileUtils::GetExtensionFromPath(fileAsset->GetDisplayName());
1218 CHECK_COND_WITH_MESSAGE(env, newMediaType == oldMediaType,
1219 "Invalid newDisplayName, newMediaType is not equal to oldMediaType.");
1220
1221 auto setTitleIndex = std::find(changeRequest->assetChangeOperations_.begin(),
1222 changeRequest->assetChangeOperations_.end(), AssetChangeOperation::SET_TITLE);
1223 if (setTitleIndex != changeRequest->assetChangeOperations_.end()) {
1224 changeRequest->assetChangeOperations_.erase(setTitleIndex);
1225 } else {
1226 // save old value
1227 changeRequest->SetOldDisplayName(fileAsset->GetDisplayName());
1228 }
1229 // set new value
1230 fileAsset->SetTitle(newTitle);
1231 fileAsset->SetDisplayName(newDisplayName);
1232 CHECK_COND_WITH_MESSAGE(env, SetDisplayNameInitParameters(asyncContext, oldExtension, newExtension) == E_OK,
1233 "failed to SetDisplayNameInitParameters");
1234 RETURN_NAPI_UNDEFINED(env);
1235 }
1236
JSSetOrientation(napi_env env,napi_callback_info info)1237 napi_value MediaAssetChangeRequestNapi::JSSetOrientation(napi_env env, napi_callback_info info)
1238 {
1239 auto asyncContext = make_unique<MediaAssetChangeRequestAsyncContext>();
1240 CHECK_COND_WITH_MESSAGE(env, asyncContext != nullptr, "asyncContext context is null");
1241
1242 int orientationValue;
1243 CHECK_COND_WITH_MESSAGE(env,
1244 MediaLibraryNapiUtils::ParseArgsNumberCallback(env, info, asyncContext, orientationValue) == napi_ok,
1245 "Failed to parse args for orientation");
1246 CHECK_COND_WITH_MESSAGE(env, asyncContext->argc == ARGS_ONE, "Number of args is invalid");
1247 if (std::find(ORIENTATION_ARRAY.begin(), ORIENTATION_ARRAY.end(), orientationValue) == ORIENTATION_ARRAY.end()) {
1248 napi_throw_range_error(env, nullptr, "orientationValue value is invalid.");
1249 return nullptr;
1250 }
1251
1252 auto changeRequest = asyncContext->objectInfo;
1253 CHECK_COND_WITH_MESSAGE(env, changeRequest != nullptr, "changeRequest is null");
1254 auto fileAsset = changeRequest->GetFileAssetInstance();
1255 CHECK_COND_WITH_MESSAGE(env, fileAsset != nullptr, "fileAsset is null");
1256 fileAsset->SetOrientation(orientationValue);
1257
1258 changeRequest->RecordChangeOperation(AssetChangeOperation::SET_ORIENTATION);
1259 RETURN_NAPI_UNDEFINED(env);
1260 }
1261
JSSetVideoEnhancementAttr(napi_env env,napi_callback_info info)1262 napi_value MediaAssetChangeRequestNapi::JSSetVideoEnhancementAttr(napi_env env, napi_callback_info info)
1263 {
1264 if (!MediaLibraryNapiUtils::IsSystemApp()) {
1265 NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
1266 return nullptr;
1267 }
1268
1269 auto asyncContext = make_unique<MediaAssetChangeRequestAsyncContext>();
1270 CHECK_COND_WITH_MESSAGE(env,
1271 MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, asyncContext, ARGS_TWO, ARGS_TWO) == napi_ok,
1272 "Failed to get object info");
1273
1274 int32_t videoEnhancementType;
1275 string photoId;
1276 MediaLibraryNapiUtils::GetInt32(env, asyncContext->argv[0], videoEnhancementType);
1277 MediaLibraryNapiUtils::GetParamStringWithLength(env, asyncContext->argv[1], MAX_PHOTO_ID_LEN, photoId);
1278
1279 auto changeRequest = asyncContext->objectInfo;
1280 changeRequest->fileAsset_->SetPhotoId(photoId);
1281 auto fileAsset = changeRequest->GetFileAssetInstance();
1282 CHECK_COND(env, fileAsset != nullptr, JS_INNER_FAIL);
1283 changeRequest->RecordChangeOperation(AssetChangeOperation::SET_VIDEO_ENHANCEMENT_ATTR);
1284 RETURN_NAPI_UNDEFINED(env);
1285 }
1286
JSSetLocation(napi_env env,napi_callback_info info)1287 napi_value MediaAssetChangeRequestNapi::JSSetLocation(napi_env env, napi_callback_info info)
1288 {
1289 if (!MediaLibraryNapiUtils::IsSystemApp()) {
1290 NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
1291 return nullptr;
1292 }
1293 NAPI_INFO_LOG("JSSetLocation begin.");
1294 auto asyncContext = make_unique<MediaAssetChangeRequestAsyncContext>();
1295 double latitude;
1296 double longitude;
1297 MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, asyncContext, ARGS_TWO, ARGS_TWO);
1298 MediaLibraryNapiUtils::GetDouble(env, asyncContext->argv[0], longitude);
1299 MediaLibraryNapiUtils::GetDouble(env, asyncContext->argv[1], latitude);
1300 asyncContext->objectInfo->fileAsset_->SetLongitude(longitude);
1301 asyncContext->objectInfo->fileAsset_->SetLatitude(latitude);
1302 asyncContext->objectInfo->assetChangeOperations_.push_back(AssetChangeOperation::SET_LOCATION);
1303 napi_value result = nullptr;
1304 CHECK_ARGS(env, napi_get_undefined(env, &result), JS_INNER_FAIL);
1305 return result;
1306 }
1307
SavePhotoProxyImage(const UniqueFd & destFd,sptr<PhotoProxy> photoProxyPtr)1308 static int SavePhotoProxyImage(const UniqueFd& destFd, sptr<PhotoProxy> photoProxyPtr)
1309 {
1310 void* imageAddr = photoProxyPtr->GetFileDataAddr();
1311 size_t imageSize = photoProxyPtr->GetFileSize();
1312 if (imageAddr == nullptr || imageSize == 0) {
1313 NAPI_ERR_LOG("imageAddr is nullptr or imageSize(%{public}zu)==0", imageSize);
1314 return E_ERR;
1315 }
1316
1317 NAPI_INFO_LOG("start pack PixelMap");
1318 Media::InitializationOptions opts;
1319 opts.pixelFormat = Media::PixelFormat::RGBA_8888;
1320 opts.size = {
1321 .width = photoProxyPtr->GetWidth(),
1322 .height = photoProxyPtr->GetHeight()
1323 };
1324 auto pixelMap = Media::PixelMap::Create(opts);
1325 if (pixelMap == nullptr) {
1326 NAPI_ERR_LOG("Create pixelMap failed.");
1327 return E_ERR;
1328 }
1329 pixelMap->SetPixelsAddr(imageAddr, nullptr, imageSize, Media::AllocatorType::SHARE_MEM_ALLOC, nullptr);
1330 auto pixelSize = static_cast<uint32_t>(pixelMap->GetByteCount());
1331
1332 // encode rgba to jpeg
1333 auto buffer = new (std::nothrow) uint8_t[pixelSize];
1334 int64_t packedSize = 0L;
1335 Media::ImagePacker imagePacker;
1336 Media::PackOption packOption;
1337 packOption.format = "image/jpeg";
1338 imagePacker.StartPacking(buffer, pixelSize, packOption);
1339 imagePacker.AddImage(*pixelMap);
1340 imagePacker.FinalizePacking(packedSize);
1341 if (buffer == nullptr) {
1342 NAPI_ERR_LOG("packet pixelMap failed");
1343 return E_ERR;
1344 }
1345 NAPI_INFO_LOG("pack pixelMap success, packedSize: %{public}" PRId64, packedSize);
1346
1347 int ret = write(destFd, buffer, packedSize);
1348 if (ret < 0) {
1349 NAPI_ERR_LOG("Failed to write photo proxy to cache file, return %{public}d", ret);
1350 delete[] buffer;
1351 return ret;
1352 }
1353 delete[] buffer;
1354 return ret;
1355 }
1356
JSSetUserComment(napi_env env,napi_callback_info info)1357 napi_value MediaAssetChangeRequestNapi::JSSetUserComment(napi_env env, napi_callback_info info)
1358 {
1359 if (!MediaLibraryNapiUtils::IsSystemApp()) {
1360 NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
1361 return nullptr;
1362 }
1363
1364 auto asyncContext = make_unique<MediaAssetChangeRequestAsyncContext>();
1365 string userComment;
1366 CHECK_COND_WITH_MESSAGE(env,
1367 MediaLibraryNapiUtils::ParseArgsStringCallback(env, info, asyncContext, userComment) == napi_ok,
1368 "Failed to parse args");
1369 CHECK_COND_WITH_MESSAGE(env, asyncContext->argc == ARGS_ONE, "Number of args is invalid");
1370 CHECK_COND_WITH_MESSAGE(env, userComment.length() <= USER_COMMENT_MAX_LEN, "user comment too long");
1371
1372 auto changeRequest = asyncContext->objectInfo;
1373 CHECK_COND(env, changeRequest->GetFileAssetInstance() != nullptr, JS_INNER_FAIL);
1374 changeRequest->GetFileAssetInstance()->SetUserComment(userComment);
1375 changeRequest->RecordChangeOperation(AssetChangeOperation::SET_USER_COMMENT);
1376 RETURN_NAPI_UNDEFINED(env);
1377 }
1378
JSSetEffectMode(napi_env env,napi_callback_info info)1379 napi_value MediaAssetChangeRequestNapi::JSSetEffectMode(napi_env env, napi_callback_info info)
1380 {
1381 if (!MediaLibraryNapiUtils::IsSystemApp()) {
1382 NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
1383 return nullptr;
1384 }
1385
1386 auto asyncContext = make_unique<MediaAssetChangeRequestAsyncContext>();
1387 int32_t effectMode;
1388 CHECK_COND_WITH_MESSAGE(env,
1389 MediaLibraryNapiUtils::ParseArgsNumberCallback(env, info, asyncContext, effectMode) == napi_ok,
1390 "Failed to parse effect mode");
1391 CHECK_COND_WITH_MESSAGE(env, asyncContext->argc == ARGS_ONE, "Number of args is invalid");
1392 CHECK_COND_WITH_MESSAGE(env, MediaFileUtils::CheckMovingPhotoEffectMode(effectMode), "Failed to check effect mode");
1393
1394 auto changeRequest = asyncContext->objectInfo;
1395 auto fileAsset = changeRequest->GetFileAssetInstance();
1396 CHECK_COND(env, fileAsset != nullptr, JS_INNER_FAIL);
1397 if (fileAsset->GetPhotoSubType() != static_cast<int32_t>(PhotoSubType::MOVING_PHOTO) &&
1398 (fileAsset->GetPhotoSubType() != static_cast<int32_t>(PhotoSubType::DEFAULT) ||
1399 fileAsset->GetMovingPhotoEffectMode() != static_cast<int32_t>(MovingPhotoEffectMode::IMAGE_ONLY))) {
1400 NapiError::ThrowError(env, JS_E_OPERATION_NOT_SUPPORT, "Operation not support: the asset is not moving photo");
1401 return nullptr;
1402 }
1403 if (fileAsset->GetPhotoSubType() == static_cast<int32_t>(PhotoSubType::DEFAULT) &&
1404 effectMode != static_cast<int32_t>(MovingPhotoEffectMode::IMAGE_ONLY)) {
1405 fileAsset->SetPhotoSubType(static_cast<int32_t>(PhotoSubType::MOVING_PHOTO));
1406 }
1407 fileAsset->SetMovingPhotoEffectMode(effectMode);
1408 changeRequest->RecordChangeOperation(AssetChangeOperation::SET_MOVING_PHOTO_EFFECT_MODE);
1409 RETURN_NAPI_UNDEFINED(env);
1410 }
1411
JSSetCameraShotKey(napi_env env,napi_callback_info info)1412 napi_value MediaAssetChangeRequestNapi::JSSetCameraShotKey(napi_env env, napi_callback_info info)
1413 {
1414 if (!MediaLibraryNapiUtils::IsSystemApp()) {
1415 NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
1416 return nullptr;
1417 }
1418
1419 auto asyncContext = make_unique<MediaAssetChangeRequestAsyncContext>();
1420 string cameraShotKey;
1421 CHECK_COND_WITH_MESSAGE(env,
1422 MediaLibraryNapiUtils::ParseArgsStringCallback(env, info, asyncContext, cameraShotKey) == napi_ok,
1423 "Failed to parse args");
1424 CHECK_COND_WITH_MESSAGE(env, asyncContext->argc == ARGS_ONE, "Number of args is invalid");
1425 CHECK_COND_WITH_MESSAGE(env, cameraShotKey.length() >= CAMERA_SHOT_KEY_SIZE, "Failed to check cameraShotKey");
1426
1427 auto changeRequest = asyncContext->objectInfo;
1428 CHECK_COND(env, changeRequest->GetFileAssetInstance() != nullptr, JS_INNER_FAIL);
1429 changeRequest->GetFileAssetInstance()->SetCameraShotKey(cameraShotKey);
1430 changeRequest->RecordChangeOperation(AssetChangeOperation::SET_CAMERA_SHOT_KEY);
1431 RETURN_NAPI_UNDEFINED(env);
1432 }
1433
JSSaveCameraPhoto(napi_env env,napi_callback_info info)1434 napi_value MediaAssetChangeRequestNapi::JSSaveCameraPhoto(napi_env env, napi_callback_info info)
1435 {
1436 NAPI_INFO_LOG("Begin MediaAssetChangeRequestNapi::JSSaveCameraPhoto");
1437 constexpr size_t minArgs = ARGS_ZERO;
1438 constexpr size_t maxArgs = ARGS_ONE;
1439 auto asyncContext = make_unique<MediaAssetChangeRequestAsyncContext>();
1440 CHECK_COND_WITH_MESSAGE(env,
1441 MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, asyncContext, minArgs, maxArgs) == napi_ok,
1442 "Failed to get object info");
1443 auto changeRequest = asyncContext->objectInfo;
1444 if (asyncContext->argc == ARGS_ONE) {
1445 int32_t fileType;
1446 MediaLibraryNapiUtils::GetInt32Arg(env, asyncContext->argv[PARAM0], fileType);
1447 NAPI_DEBUG_LOG("fileType: %{public}d", fileType);
1448 changeRequest->SetImageFileType(fileType);
1449 }
1450 auto fileAsset = changeRequest->GetFileAssetInstance();
1451 CHECK_COND(env, fileAsset != nullptr, JS_INNER_FAIL);
1452 if (changeRequest->Contains(AssetChangeOperation::SET_EDIT_DATA) &&
1453 !changeRequest->Contains(AssetChangeOperation::ADD_FILTERS)) {
1454 changeRequest->RecordChangeOperation(AssetChangeOperation::ADD_FILTERS);
1455 }
1456 changeRequest->RecordChangeOperation(AssetChangeOperation::SAVE_CAMERA_PHOTO);
1457 RETURN_NAPI_UNDEFINED(env);
1458 }
1459
JSDiscardCameraPhoto(napi_env env,napi_callback_info info)1460 napi_value MediaAssetChangeRequestNapi::JSDiscardCameraPhoto(napi_env env, napi_callback_info info)
1461 {
1462 auto asyncContext = make_unique<MediaAssetChangeRequestAsyncContext>();
1463 CHECK_COND_WITH_MESSAGE(env,
1464 MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, asyncContext, ARGS_ZERO, ARGS_ZERO) == napi_ok,
1465 "Failed to get object info");
1466
1467 auto changeRequest = asyncContext->objectInfo;
1468 auto fileAsset = changeRequest->GetFileAssetInstance();
1469 CHECK_COND(env, fileAsset != nullptr, JS_INNER_FAIL);
1470 changeRequest->RecordChangeOperation(AssetChangeOperation::DISCARD_CAMERA_PHOTO);
1471 RETURN_NAPI_UNDEFINED(env);
1472 }
1473
JSSetSupportedWatermarkType(napi_env env,napi_callback_info info)1474 napi_value MediaAssetChangeRequestNapi::JSSetSupportedWatermarkType(napi_env env, napi_callback_info info)
1475 {
1476 if (!MediaLibraryNapiUtils::IsSystemApp()) {
1477 NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
1478 return nullptr;
1479 }
1480
1481 auto asyncContext = make_unique<MediaAssetChangeRequestAsyncContext>();
1482 int32_t watermarkType;
1483 CHECK_COND_WITH_MESSAGE(env,
1484 MediaLibraryNapiUtils::ParseArgsNumberCallback(env, info, asyncContext, watermarkType) == napi_ok,
1485 "Failed to parse watermark type");
1486 CHECK_COND_WITH_MESSAGE(env, asyncContext->argc == ARGS_ONE, "Number of args is invalid");
1487 CHECK_COND_WITH_MESSAGE(env, MediaFileUtils::CheckSupportedWatermarkType(watermarkType),
1488 "Failed to check watermark type");
1489
1490 auto changeRequest = asyncContext->objectInfo;
1491 CHECK_COND(env, changeRequest->GetFileAssetInstance() != nullptr, JS_INNER_FAIL);
1492 changeRequest->GetFileAssetInstance()->SetSupportedWatermarkType(watermarkType);
1493 changeRequest->RecordChangeOperation(AssetChangeOperation::SET_SUPPORTED_WATERMARK_TYPE);
1494 RETURN_NAPI_UNDEFINED(env);
1495 }
1496
OpenWriteCacheHandler(MediaAssetChangeRequestAsyncContext & context,bool isMovingPhotoVideo=false)1497 static int32_t OpenWriteCacheHandler(MediaAssetChangeRequestAsyncContext& context, bool isMovingPhotoVideo = false)
1498 {
1499 auto changeRequest = context.objectInfo;
1500 auto fileAsset = changeRequest->GetFileAssetInstance();
1501 if (fileAsset == nullptr) {
1502 context.SaveError(E_FAIL);
1503 NAPI_ERR_LOG("fileAsset is null");
1504 return E_FAIL;
1505 }
1506
1507 // specify mp4 extension for cache file of moving photo video
1508 std::string extension = "";
1509 if (changeRequest->GetIsEditDisplayName()) {
1510 extension = isMovingPhotoVideo ? MOVING_PHOTO_VIDEO_EXTENSION
1511 : MediaFileUtils::GetExtensionFromPath(changeRequest->GetOldDisplayName());
1512 } else {
1513 extension = isMovingPhotoVideo ? MOVING_PHOTO_VIDEO_EXTENSION
1514 : MediaFileUtils::GetExtensionFromPath(fileAsset->GetDisplayName());
1515 }
1516 int64_t currentTimestamp = MediaFileUtils::UTCTimeNanoSeconds();
1517 uint32_t cacheFileId = changeRequest->FetchAddCacheFileId();
1518 string cacheFileName = to_string(currentTimestamp) + "_" + to_string(cacheFileId) + "." + extension;
1519 string uri = PhotoColumn::PHOTO_CACHE_URI_PREFIX + cacheFileName;
1520 MediaFileUtils::UriAppendKeyValue(uri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
1521 Uri openCacheUri(uri);
1522 int32_t ret = UserFileClient::OpenFile(openCacheUri, MEDIA_FILEMODE_WRITEONLY, fileAsset->GetUserId());
1523 if (ret == E_PERMISSION_DENIED) {
1524 context.error = OHOS_PERMISSION_DENIED_CODE;
1525 NAPI_ERR_LOG("Open cache file failed, permission denied");
1526 return ret;
1527 }
1528 if (ret < 0) {
1529 context.SaveError(ret);
1530 NAPI_ERR_LOG("Open cache file failed, ret: %{public}d", ret);
1531 }
1532
1533 if (isMovingPhotoVideo) {
1534 changeRequest->SetCacheMovingPhotoVideoName(cacheFileName);
1535 } else {
1536 changeRequest->SetCacheFileName(cacheFileName);
1537 }
1538 return ret;
1539 }
1540
GetWriteCacheHandlerExecute(napi_env env,void * data)1541 static void GetWriteCacheHandlerExecute(napi_env env, void* data)
1542 {
1543 MediaLibraryTracer tracer;
1544 tracer.Start("GetWriteCacheHandlerExecute");
1545
1546 auto* context = static_cast<MediaAssetChangeRequestAsyncContext*>(data);
1547 int32_t ret = OpenWriteCacheHandler(*context);
1548 if (ret < 0) {
1549 NAPI_ERR_LOG("Failed to open write cache handler, ret: %{public}d", ret);
1550 return;
1551 }
1552 context->fd = ret;
1553 context->objectInfo->RecordChangeOperation(AssetChangeOperation::GET_WRITE_CACHE_HANDLER);
1554 }
1555
GetWriteCacheHandlerCompleteCallback(napi_env env,napi_status status,void * data)1556 static void GetWriteCacheHandlerCompleteCallback(napi_env env, napi_status status, void* data)
1557 {
1558 auto* context = static_cast<MediaAssetChangeRequestAsyncContext*>(data);
1559 CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
1560 auto jsContext = make_unique<JSAsyncContextOutput>();
1561 if (context->error == ERR_DEFAULT) {
1562 napi_create_int32(env, context->fd, &jsContext->data);
1563 napi_get_undefined(env, &jsContext->error);
1564 jsContext->status = true;
1565 } else {
1566 context->HandleError(env, jsContext->error);
1567 napi_get_undefined(env, &jsContext->data);
1568 jsContext->status = false;
1569 }
1570
1571 if (context->work != nullptr) {
1572 MediaLibraryNapiUtils::InvokeJSAsyncMethod(
1573 env, context->deferred, context->callbackRef, context->work, *jsContext);
1574 }
1575 delete context;
1576 }
1577
GetResourceType(int32_t value)1578 static ResourceType GetResourceType(int32_t value)
1579 {
1580 ResourceType result = ResourceType::INVALID_RESOURCE;
1581 switch (value) {
1582 case static_cast<int32_t>(ResourceType::IMAGE_RESOURCE):
1583 case static_cast<int32_t>(ResourceType::VIDEO_RESOURCE):
1584 case static_cast<int32_t>(ResourceType::PHOTO_PROXY):
1585 result = static_cast<ResourceType>(value);
1586 break;
1587 default:
1588 break;
1589 }
1590 return result;
1591 }
1592
CheckWriteOperation(napi_env env,MediaAssetChangeRequestNapi * changeRequest,ResourceType resourceType=ResourceType::INVALID_RESOURCE)1593 static napi_value CheckWriteOperation(napi_env env, MediaAssetChangeRequestNapi* changeRequest,
1594 ResourceType resourceType = ResourceType::INVALID_RESOURCE)
1595 {
1596 if (changeRequest == nullptr) {
1597 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "changeRequest is null");
1598 return nullptr;
1599 }
1600
1601 if (changeRequest->IsMovingPhoto()) {
1602 if (!changeRequest->CheckMovingPhotoResource(resourceType)) {
1603 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "Failed to check resource to add for moving photo");
1604 return nullptr;
1605 }
1606 RETURN_NAPI_TRUE(env);
1607 }
1608
1609 if (changeRequest->Contains(AssetChangeOperation::CREATE_FROM_URI) ||
1610 changeRequest->Contains(AssetChangeOperation::GET_WRITE_CACHE_HANDLER) ||
1611 changeRequest->Contains(AssetChangeOperation::ADD_RESOURCE)) {
1612 NapiError::ThrowError(env, JS_E_OPERATION_NOT_SUPPORT,
1613 "The previous asset creation/modification request has not been applied");
1614 return nullptr;
1615 }
1616 RETURN_NAPI_TRUE(env);
1617 }
1618
JSGetWriteCacheHandler(napi_env env,napi_callback_info info)1619 napi_value MediaAssetChangeRequestNapi::JSGetWriteCacheHandler(napi_env env, napi_callback_info info)
1620 {
1621 auto asyncContext = make_unique<MediaAssetChangeRequestAsyncContext>();
1622 CHECK_COND_WITH_MESSAGE(env,
1623 MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, asyncContext, ARGS_ZERO, ARGS_ONE) == napi_ok,
1624 "Failed to get object info");
1625
1626 auto changeRequest = asyncContext->objectInfo;
1627 auto fileAsset = changeRequest->GetFileAssetInstance();
1628 CHECK_COND(env, fileAsset != nullptr, JS_INNER_FAIL);
1629 CHECK_COND(env, !changeRequest->IsMovingPhoto(), JS_E_OPERATION_NOT_SUPPORT);
1630 CHECK_COND(env, CheckWriteOperation(env, changeRequest), JS_E_OPERATION_NOT_SUPPORT);
1631 return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "ChangeRequestGetWriteCacheHandler",
1632 GetWriteCacheHandlerExecute, GetWriteCacheHandlerCompleteCallback);
1633 }
1634
CheckMovingPhotoVideo(void * dataBuffer,size_t size)1635 static bool CheckMovingPhotoVideo(void* dataBuffer, size_t size)
1636 {
1637 MediaLibraryTracer tracer;
1638 tracer.Start("CheckMovingPhotoVideo");
1639
1640 auto dataSource = make_shared<MediaDataSource>(dataBuffer, static_cast<int64_t>(size));
1641 auto avMetadataHelper = AVMetadataHelperFactory::CreateAVMetadataHelper();
1642 if (avMetadataHelper == nullptr) {
1643 NAPI_WARN_LOG("Failed to create AVMetadataHelper, ignore checking duration of moving photo video");
1644 return true;
1645 }
1646
1647 int32_t err = avMetadataHelper->SetSource(dataSource);
1648 if (err != E_OK) {
1649 NAPI_ERR_LOG("SetSource failed for dataSource, err = %{public}d", err);
1650 return false;
1651 }
1652
1653 unordered_map<int32_t, string> resultMap = avMetadataHelper->ResolveMetadata();
1654 if (resultMap.find(AV_KEY_DURATION) == resultMap.end()) {
1655 NAPI_ERR_LOG("AV_KEY_DURATION does not exist");
1656 return false;
1657 }
1658
1659 string durationStr = resultMap.at(AV_KEY_DURATION);
1660 int32_t duration = std::atoi(durationStr.c_str());
1661 if (!MediaFileUtils::CheckMovingPhotoVideoDuration(duration)) {
1662 NAPI_ERR_LOG("Failed to check duration of moving photo video: %{public}d ms", duration);
1663 return false;
1664 }
1665 return true;
1666 }
1667
AddMovingPhotoVideoResource(napi_env env,napi_callback_info info)1668 napi_value MediaAssetChangeRequestNapi::AddMovingPhotoVideoResource(napi_env env, napi_callback_info info)
1669 {
1670 MediaLibraryTracer tracer;
1671 tracer.Start("AddMovingPhotoVideoResource");
1672
1673 auto asyncContext = make_unique<MediaAssetChangeRequestAsyncContext>();
1674 CHECK_COND_WITH_MESSAGE(env,
1675 MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, asyncContext, ARGS_TWO, ARGS_TWO) == napi_ok,
1676 "Failed to get object info");
1677 auto changeRequest = asyncContext->objectInfo;
1678
1679 napi_valuetype valueType;
1680 napi_value value = asyncContext->argv[PARAM1];
1681 CHECK_COND_WITH_MESSAGE(env, napi_typeof(env, value, &valueType) == napi_ok, "Failed to get napi type");
1682 if (valueType == napi_string) { // addResource by file uri
1683 CHECK_COND(env, ParseFileUri(env, value, MediaType::MEDIA_TYPE_VIDEO, asyncContext), OHOS_INVALID_PARAM_CODE);
1684 if (!MediaFileUtils::CheckMovingPhotoVideo(asyncContext->realPath)) {
1685 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "Failed to check video resource of moving photo");
1686 return nullptr;
1687 }
1688 changeRequest->movingPhotoVideoRealPath_ = asyncContext->realPath;
1689 changeRequest->movingPhotoVideoResourceMode_ = AddResourceMode::FILE_URI;
1690 } else { // addResource by ArrayBuffer
1691 bool isArrayBuffer = false;
1692 CHECK_COND_WITH_MESSAGE(env, napi_is_arraybuffer(env, value, &isArrayBuffer) == napi_ok && isArrayBuffer,
1693 "Failed to check data type");
1694 CHECK_COND_WITH_MESSAGE(env,
1695 napi_get_arraybuffer_info(env, value, &(changeRequest->movingPhotoVideoDataBuffer_),
1696 &(changeRequest->movingPhotoVideoBufferSize_)) == napi_ok,
1697 "Failed to get data buffer");
1698 CHECK_COND_WITH_MESSAGE(env, changeRequest->movingPhotoVideoBufferSize_ > 0,
1699 "Failed to check size of data buffer");
1700 if (!CheckMovingPhotoVideo(changeRequest->movingPhotoVideoDataBuffer_,
1701 changeRequest->movingPhotoVideoBufferSize_)) {
1702 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "Failed to check video resource of moving photo");
1703 return nullptr;
1704 }
1705 changeRequest->movingPhotoVideoResourceMode_ = AddResourceMode::DATA_BUFFER;
1706 }
1707
1708 changeRequest->RecordChangeOperation(AssetChangeOperation::ADD_RESOURCE);
1709 changeRequest->addResourceTypes_.push_back(ResourceType::VIDEO_RESOURCE);
1710 RETURN_NAPI_UNDEFINED(env);
1711 }
1712
JSAddResource(napi_env env,napi_callback_info info)1713 napi_value MediaAssetChangeRequestNapi::JSAddResource(napi_env env, napi_callback_info info)
1714 {
1715 auto asyncContext = make_unique<MediaAssetChangeRequestAsyncContext>();
1716 CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, asyncContext,
1717 ARGS_TWO, ARGS_TWO) == napi_ok, "Failed to get object info");
1718 auto changeRequest = asyncContext->objectInfo;
1719 auto fileAsset = changeRequest->GetFileAssetInstance();
1720 CHECK_COND(env, fileAsset != nullptr, JS_INNER_FAIL);
1721
1722 int32_t resourceType = static_cast<int32_t>(ResourceType::INVALID_RESOURCE);
1723 CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::GetInt32(env, asyncContext->argv[PARAM0],
1724 resourceType) == napi_ok, "Failed to get resourceType");
1725 CHECK_COND(env, CheckWriteOperation(env, changeRequest, GetResourceType(resourceType)), JS_E_OPERATION_NOT_SUPPORT);
1726 if (changeRequest->IsMovingPhoto() && resourceType == static_cast<int32_t>(ResourceType::VIDEO_RESOURCE)) {
1727 return AddMovingPhotoVideoResource(env, info);
1728 }
1729 CHECK_COND_WITH_MESSAGE(env, resourceType == static_cast<int32_t>(fileAsset->GetMediaType()) ||
1730 resourceType == static_cast<int32_t>(ResourceType::PHOTO_PROXY), "Failed to check resourceType");
1731
1732 napi_valuetype valueType;
1733 napi_value value = asyncContext->argv[PARAM1];
1734 CHECK_COND_WITH_MESSAGE(env, napi_typeof(env, value, &valueType) == napi_ok, "Failed to get napi type");
1735 if (valueType == napi_string) {
1736 // addResource by file uri
1737 CHECK_COND(env, ParseFileUri(env, value, fileAsset->GetMediaType(), asyncContext), OHOS_INVALID_PARAM_CODE);
1738 changeRequest->realPath_ = asyncContext->realPath;
1739 changeRequest->addResourceMode_ = AddResourceMode::FILE_URI;
1740 } else {
1741 // addResource by data buffer
1742 bool isArrayBuffer = false;
1743 CHECK_COND_WITH_MESSAGE(env, napi_is_arraybuffer(env, value, &isArrayBuffer) == napi_ok,
1744 "Failed to check data type");
1745 if (isArrayBuffer) {
1746 CHECK_COND_WITH_MESSAGE(env, napi_get_arraybuffer_info(env, value, &(changeRequest->dataBuffer_),
1747 &(changeRequest->dataBufferSize_)) == napi_ok, "Failed to get data buffer");
1748 CHECK_COND_WITH_MESSAGE(env, changeRequest->dataBufferSize_ > 0, "Failed to check size of data buffer");
1749 changeRequest->addResourceMode_ = AddResourceMode::DATA_BUFFER;
1750 } else {
1751 // addResource by photoProxy
1752 if (!MediaLibraryNapiUtils::IsSystemApp()) {
1753 NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
1754 RETURN_NAPI_UNDEFINED(env);
1755 }
1756 PhotoProxyNapi* napiPhotoProxyPtr = nullptr;
1757 CHECK_ARGS(env, napi_unwrap(env, asyncContext->argv[PARAM1], reinterpret_cast<void**>(&napiPhotoProxyPtr)),
1758 JS_INNER_FAIL);
1759 changeRequest->photoProxy_ = napiPhotoProxyPtr->photoProxy_;
1760 changeRequest->addResourceMode_ = AddResourceMode::PHOTO_PROXY;
1761 }
1762 }
1763
1764 changeRequest->RecordChangeOperation(AssetChangeOperation::ADD_RESOURCE);
1765 changeRequest->addResourceTypes_.push_back(GetResourceType(resourceType));
1766 RETURN_NAPI_UNDEFINED(env);
1767 }
1768
SetNewFileAsset(int32_t id,const string & uri)1769 void MediaAssetChangeRequestNapi::SetNewFileAsset(int32_t id, const string& uri)
1770 {
1771 if (fileAsset_ == nullptr) {
1772 NAPI_ERR_LOG("fileAsset_ is nullptr");
1773 return;
1774 }
1775
1776 if (id <= 0 || uri.empty()) {
1777 NAPI_ERR_LOG("Failed to check file_id: %{public}d and uri: %{public}s", id, uri.c_str());
1778 return;
1779 }
1780 fileAsset_->SetId(id);
1781 fileAsset_->SetUri(uri);
1782 fileAsset_->SetTimePending(0);
1783 }
1784
IsCreation(MediaAssetChangeRequestAsyncContext & context)1785 static bool IsCreation(MediaAssetChangeRequestAsyncContext& context)
1786 {
1787 auto assetChangeOperations = context.assetChangeOperations;
1788 bool isCreateFromScratch = std::find(assetChangeOperations.begin(), assetChangeOperations.end(),
1789 AssetChangeOperation::CREATE_FROM_SCRATCH) != assetChangeOperations.end();
1790 bool isCreateFromUri = std::find(assetChangeOperations.begin(), assetChangeOperations.end(),
1791 AssetChangeOperation::CREATE_FROM_URI) != assetChangeOperations.end();
1792 return isCreateFromScratch || isCreateFromUri;
1793 }
1794
IsSetEffectMode(MediaAssetChangeRequestAsyncContext & context)1795 static bool IsSetEffectMode(MediaAssetChangeRequestAsyncContext& context)
1796 {
1797 auto assetChangeOperations = context.assetChangeOperations;
1798 return std::find(assetChangeOperations.begin(), assetChangeOperations.end(),
1799 AssetChangeOperation::SET_MOVING_PHOTO_EFFECT_MODE) != assetChangeOperations.end();
1800 }
1801
SendFile(const UniqueFd & srcFd,const UniqueFd & destFd)1802 static int32_t SendFile(const UniqueFd& srcFd, const UniqueFd& destFd)
1803 {
1804 if (srcFd.Get() < 0 || destFd.Get() < 0) {
1805 NAPI_ERR_LOG("Failed to check srcFd: %{public}d and destFd: %{public}d", srcFd.Get(), destFd.Get());
1806 return E_ERR;
1807 }
1808
1809 struct stat statSrc {};
1810 int32_t status = fstat(srcFd.Get(), &statSrc);
1811 if (status != 0) {
1812 NAPI_ERR_LOG("Failed to get file stat, errno=%{public}d", errno);
1813 return status;
1814 }
1815
1816 off_t offset = 0;
1817 off_t fileSize = statSrc.st_size;
1818 while (offset < fileSize) {
1819 ssize_t sent = sendfile(destFd.Get(), srcFd.Get(), &offset, fileSize - offset);
1820 if (sent < 0) {
1821 NAPI_ERR_LOG("Failed to sendfile with errno=%{public}d, srcFd=%{private}d, destFd=%{private}d", errno,
1822 srcFd.Get(), destFd.Get());
1823 return sent;
1824 }
1825 }
1826
1827 return E_OK;
1828 }
1829
CopyFileToMediaLibrary(const UniqueFd & destFd,bool isMovingPhotoVideo)1830 int32_t MediaAssetChangeRequestNapi::CopyFileToMediaLibrary(const UniqueFd& destFd, bool isMovingPhotoVideo)
1831 {
1832 string srcRealPath = isMovingPhotoVideo ? movingPhotoVideoRealPath_ : realPath_;
1833 CHECK_COND_RET(!srcRealPath.empty(), E_FAIL, "Failed to check real path of source");
1834
1835 string absFilePath;
1836 CHECK_COND_RET(PathToRealPath(srcRealPath, absFilePath), E_FAIL, "Not real path %{private}s", srcRealPath.c_str());
1837 UniqueFd srcFd(open(absFilePath.c_str(), O_RDONLY));
1838 if (srcFd.Get() < 0) {
1839 NAPI_ERR_LOG("Failed to open %{private}s, errno=%{public}d", absFilePath.c_str(), errno);
1840 return srcFd.Get();
1841 }
1842
1843 int32_t err = SendFile(srcFd, destFd);
1844 if (err != E_OK) {
1845 NAPI_ERR_LOG("Failed to send file from %{public}d to %{public}d", srcFd.Get(), destFd.Get());
1846 }
1847 return err;
1848 }
1849
CopyDataBufferToMediaLibrary(const UniqueFd & destFd,bool isMovingPhotoVideo)1850 int32_t MediaAssetChangeRequestNapi::CopyDataBufferToMediaLibrary(const UniqueFd& destFd, bool isMovingPhotoVideo)
1851 {
1852 size_t offset = 0;
1853 size_t length = isMovingPhotoVideo ? movingPhotoVideoBufferSize_ : dataBufferSize_;
1854 void* dataBuffer = isMovingPhotoVideo ? movingPhotoVideoDataBuffer_ : dataBuffer_;
1855 while (offset < length) {
1856 ssize_t written = write(destFd.Get(), (char*)dataBuffer + offset, length - offset);
1857 if (written < 0) {
1858 NAPI_ERR_LOG("Failed to copy data buffer, return %{public}d", static_cast<int>(written));
1859 return written;
1860 }
1861 offset += static_cast<size_t>(written);
1862 }
1863 return E_OK;
1864 }
1865
CopyMovingPhotoVideo(const string & assetUri)1866 int32_t MediaAssetChangeRequestNapi::CopyMovingPhotoVideo(const string& assetUri)
1867 {
1868 if (assetUri.empty()) {
1869 NAPI_ERR_LOG("Failed to check empty asset uri");
1870 return E_INVALID_URI;
1871 }
1872
1873 string videoUri = assetUri;
1874 MediaFileUtils::UriAppendKeyValue(videoUri, MEDIA_MOVING_PHOTO_OPRN_KEYWORD, OPEN_MOVING_PHOTO_VIDEO);
1875 Uri uri(videoUri);
1876 int videoFd = UserFileClient::OpenFile(uri, MEDIA_FILEMODE_WRITEONLY);
1877 if (videoFd < 0) {
1878 NAPI_ERR_LOG("Failed to open video of moving photo with write-only mode");
1879 return videoFd;
1880 }
1881
1882 int32_t ret = E_ERR;
1883 UniqueFd uniqueFd(videoFd);
1884 if (movingPhotoVideoResourceMode_ == AddResourceMode::FILE_URI) {
1885 ret = CopyFileToMediaLibrary(uniqueFd, true);
1886 } else if (movingPhotoVideoResourceMode_ == AddResourceMode::DATA_BUFFER) {
1887 ret = CopyDataBufferToMediaLibrary(uniqueFd, true);
1888 } else {
1889 NAPI_ERR_LOG("Invalid mode: %{public}d", movingPhotoVideoResourceMode_);
1890 return E_INVALID_VALUES;
1891 }
1892 return ret;
1893 }
1894
CreateAssetBySecurityComponent(string & assetUri)1895 int32_t MediaAssetChangeRequestNapi::CreateAssetBySecurityComponent(string& assetUri)
1896 {
1897 bool isValid = false;
1898 string title = creationValuesBucket_.Get(PhotoColumn::MEDIA_TITLE, isValid);
1899 CHECK_COND_RET(isValid, E_FAIL, "Failed to get title");
1900 string extension = creationValuesBucket_.Get(ASSET_EXTENTION, isValid);
1901 CHECK_COND_RET(isValid && MediaFileUtils::CheckDisplayName(title + "." + extension) == E_OK, E_FAIL,
1902 "Failed to check displayName");
1903 creationValuesBucket_.valuesMap.erase(MEDIA_DATA_DB_NAME);
1904
1905 string uri = PAH_CREATE_PHOTO_COMPONENT; // create asset by security component
1906 MediaLibraryNapiUtils::UriAppendKeyValue(uri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
1907 Uri createAssetUri(uri);
1908 return UserFileClient::InsertExt(createAssetUri, creationValuesBucket_, assetUri);
1909 }
1910
CopyToMediaLibrary(bool isCreation,AddResourceMode mode)1911 int32_t MediaAssetChangeRequestNapi::CopyToMediaLibrary(bool isCreation, AddResourceMode mode)
1912 {
1913 CHECK_COND_RET(fileAsset_ != nullptr, E_FAIL, "Failed to check fileAsset_");
1914 int32_t ret = E_ERR;
1915 int32_t id = 0;
1916 string assetUri;
1917 if (isCreation) {
1918 ret = CreateAssetBySecurityComponent(assetUri);
1919 CHECK_COND_RET(ret > 0, (ret == 0 ? E_ERR : ret), "Failed to create asset by security component");
1920 id = ret;
1921 } else {
1922 assetUri = fileAsset_->GetUri();
1923 }
1924 CHECK_COND_RET(!assetUri.empty(), E_ERR, "Failed to check empty asset uri");
1925
1926 if (IsMovingPhoto()) {
1927 ret = CopyMovingPhotoVideo(assetUri);
1928 if (ret != E_OK) {
1929 NAPI_ERR_LOG("Failed to copy data to moving photo video with error: %{public}d", ret);
1930 return ret;
1931 }
1932 }
1933
1934 Uri uri(assetUri);
1935 UniqueFd destFd(UserFileClient::OpenFile(uri, MEDIA_FILEMODE_WRITETRUNCATE));
1936 if (destFd.Get() < 0) {
1937 NAPI_ERR_LOG("Failed to open %{private}s with error: %{public}d", assetUri.c_str(), destFd.Get());
1938 return destFd.Get();
1939 }
1940
1941 if (mode == AddResourceMode::FILE_URI) {
1942 ret = CopyFileToMediaLibrary(destFd);
1943 } else if (mode == AddResourceMode::DATA_BUFFER) {
1944 ret = CopyDataBufferToMediaLibrary(destFd);
1945 } else {
1946 NAPI_ERR_LOG("Invalid mode: %{public}d", mode);
1947 return E_INVALID_VALUES;
1948 }
1949
1950 if (ret == E_OK && isCreation) {
1951 SetNewFileAsset(id, assetUri);
1952 }
1953 return ret;
1954 }
1955
WriteBySecurityComponent(MediaAssetChangeRequestAsyncContext & context)1956 static bool WriteBySecurityComponent(MediaAssetChangeRequestAsyncContext& context)
1957 {
1958 bool isCreation = IsCreation(context);
1959 int32_t ret = E_FAIL;
1960 auto assetChangeOperations = context.assetChangeOperations;
1961 bool isCreateFromUri = std::find(assetChangeOperations.begin(), assetChangeOperations.end(),
1962 AssetChangeOperation::CREATE_FROM_URI) != assetChangeOperations.end();
1963 auto changeRequest = context.objectInfo;
1964 if (isCreateFromUri) {
1965 ret = changeRequest->CopyToMediaLibrary(isCreation, AddResourceMode::FILE_URI);
1966 } else {
1967 ret = changeRequest->CopyToMediaLibrary(isCreation, changeRequest->GetAddResourceMode());
1968 }
1969
1970 if (ret < 0) {
1971 context.SaveError(ret);
1972 NAPI_ERR_LOG("Failed to write by security component, ret: %{public}d", ret);
1973 return false;
1974 }
1975 return true;
1976 }
1977
PutMediaAssetEditData(DataShare::DataShareValuesBucket & valuesBucket)1978 int32_t MediaAssetChangeRequestNapi::PutMediaAssetEditData(DataShare::DataShareValuesBucket& valuesBucket)
1979 {
1980 if (editData_ == nullptr) {
1981 return E_OK;
1982 }
1983
1984 string compatibleFormat = editData_->GetCompatibleFormat();
1985 CHECK_COND_RET(!compatibleFormat.empty(), E_FAIL, "Failed to check compatibleFormat");
1986 string formatVersion = editData_->GetFormatVersion();
1987 CHECK_COND_RET(!formatVersion.empty(), E_FAIL, "Failed to check formatVersion");
1988 string data = editData_->GetData();
1989 CHECK_COND_RET(!data.empty(), E_FAIL, "Failed to check data");
1990
1991 valuesBucket.Put(COMPATIBLE_FORMAT, compatibleFormat);
1992 valuesBucket.Put(FORMAT_VERSION, formatVersion);
1993 valuesBucket.Put(EDIT_DATA, data);
1994 return E_OK;
1995 }
1996
HandleValueBucketForSetLocation(std::shared_ptr<FileAsset> fileAsset,DataShare::DataShareValuesBucket & values,bool isWriteGpsAdvanced)1997 void HandleValueBucketForSetLocation(std::shared_ptr<FileAsset> fileAsset, DataShare::DataShareValuesBucket& values,
1998 bool isWriteGpsAdvanced)
1999 {
2000 if (fileAsset == nullptr) {
2001 NAPI_ERR_LOG("fileAsset is nullptr.");
2002 return;
2003 }
2004 if (isWriteGpsAdvanced) {
2005 NAPI_INFO_LOG("Need to setLocationAdvanced, check uri is correct.");
2006 values.Put(PhotoColumn::PHOTO_LATITUDE, fileAsset->GetLatitude());
2007 values.Put(PhotoColumn::PHOTO_LONGITUDE, fileAsset->GetLongitude());
2008 }
2009 }
2010
SubmitCacheWithCreation(std::string & uri,std::string & assetUri,bool isWriteGpsAdvanced,const int32_t userId)2011 int32_t MediaAssetChangeRequestNapi::SubmitCacheWithCreation(
2012 std::string &uri, std::string &assetUri, bool isWriteGpsAdvanced, const int32_t userId)
2013 {
2014 bool isValid = false;
2015 std::string displayName = creationValuesBucket_.Get(MEDIA_DATA_DB_NAME, isValid);
2016 CHECK_COND_RET(
2017 isValid && MediaFileUtils::CheckDisplayName(displayName) == E_OK, E_FAIL, "Failed to check displayName");
2018 if (GetIsEditDisplayName()) {
2019 MediaLibraryNapiUtils::UriAppendKeyValue(uri, SET_DISPLAY_NAME_KEY, displayName);
2020 MediaLibraryNapiUtils::UriAppendKeyValue(uri, CAN_FALLBACK, "1");
2021 MediaLibraryNapiUtils::UriAppendKeyValue(uri, OLD_DISPLAY_NAME, oldDisplayName_);
2022 SetIsEditDisplayName(false);
2023 }
2024 Uri submitCacheUri(uri);
2025 creationValuesBucket_.Put(CACHE_FILE_NAME, cacheFileName_);
2026 if (IsMovingPhoto()) {
2027 creationValuesBucket_.Put(CACHE_MOVING_PHOTO_VIDEO_NAME, cacheMovingPhotoVideoName_);
2028 }
2029 HandleValueBucketForSetLocation(fileAsset_, creationValuesBucket_, isWriteGpsAdvanced);
2030 return UserFileClient::InsertExt(submitCacheUri, creationValuesBucket_, assetUri, userId);
2031 }
2032
SubmitCacheWithoutCreation(std::string & uri,bool isSetEffectMode,bool isWriteGpsAdvanced,const int32_t userId)2033 int32_t MediaAssetChangeRequestNapi::SubmitCacheWithoutCreation(std::string &uri, bool isSetEffectMode,
2034 bool isWriteGpsAdvanced, const int32_t userId)
2035 {
2036 DataShare::DataShareValuesBucket valuesBucket;
2037 if (GetIsEditDisplayName()) {
2038 MediaLibraryNapiUtils::UriAppendKeyValue(uri, SET_DISPLAY_NAME_KEY, fileAsset_->GetDisplayName());
2039 MediaLibraryNapiUtils::UriAppendKeyValue(uri, CAN_FALLBACK, "1");
2040 MediaLibraryNapiUtils::UriAppendKeyValue(uri, OLD_DISPLAY_NAME, oldDisplayName_);
2041 SetIsEditDisplayName(false);
2042 }
2043 Uri submitCacheUri(uri);
2044 valuesBucket.Put(PhotoColumn::MEDIA_ID, fileAsset_->GetId());
2045 valuesBucket.Put(CACHE_FILE_NAME, cacheFileName_);
2046 int32_t ret = PutMediaAssetEditData(valuesBucket);
2047 CHECK_COND_RET(ret == E_OK, ret, "Failed to put editData");
2048 if (IsMovingPhoto()) {
2049 valuesBucket.Put(CACHE_MOVING_PHOTO_VIDEO_NAME, cacheMovingPhotoVideoName_);
2050 }
2051 if (isSetEffectMode) {
2052 valuesBucket.Put(PhotoColumn::MOVING_PHOTO_EFFECT_MODE, fileAsset_->GetMovingPhotoEffectMode());
2053 valuesBucket.Put(CACHE_MOVING_PHOTO_VIDEO_NAME, cacheMovingPhotoVideoName_);
2054 }
2055 HandleValueBucketForSetLocation(fileAsset_, valuesBucket, isWriteGpsAdvanced);
2056 return UserFileClient::Insert(submitCacheUri, valuesBucket, userId);
2057 }
2058
SubmitCache(bool isCreation,bool isSetEffectMode,bool isWriteGpsAdvanced,const int32_t userId)2059 int32_t MediaAssetChangeRequestNapi::SubmitCache(bool isCreation, bool isSetEffectMode,
2060 bool isWriteGpsAdvanced, const int32_t userId)
2061 {
2062 CHECK_COND_RET(fileAsset_ != nullptr, E_FAIL, "Failed to check fileAsset_");
2063 CHECK_COND_RET(!cacheFileName_.empty() || !cacheMovingPhotoVideoName_.empty(), E_FAIL,
2064 "Failed to check cache file");
2065
2066 string uri = PAH_SUBMIT_CACHE;
2067 MediaLibraryNapiUtils::UriAppendKeyValue(uri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
2068 if (isWriteGpsAdvanced) {
2069 MediaLibraryNapiUtils::UriAppendKeyValue(uri, SET_LOCATION_KEY, SET_LOCATION_VALUE);
2070 }
2071 NAPI_INFO_LOG("Check SubmitCache isWriteGpsAdvanced: %{public}d", isWriteGpsAdvanced);
2072
2073 string assetUri;
2074 int32_t ret;
2075 if (isCreation) {
2076 ret = SubmitCacheWithCreation(uri, assetUri, isWriteGpsAdvanced, userId);
2077 } else {
2078 ret = SubmitCacheWithoutCreation(uri, isSetEffectMode, isWriteGpsAdvanced, userId);
2079 }
2080
2081 if (ret == E_FAIL) {
2082 return ret;
2083 }
2084 if (ret > 0 && isCreation) {
2085 SetNewFileAsset(ret, assetUri);
2086 }
2087 cacheFileName_.clear();
2088 cacheMovingPhotoVideoName_.clear();
2089 oldDisplayName_.clear();
2090 return ret;
2091 }
2092
SubmitCacheExecute(MediaAssetChangeRequestAsyncContext & context)2093 static bool SubmitCacheExecute(MediaAssetChangeRequestAsyncContext& context)
2094 {
2095 MediaLibraryTracer tracer;
2096 tracer.Start("SubmitCacheExecute");
2097
2098 bool isCreation = IsCreation(context);
2099 bool isSetEffectMode = IsSetEffectMode(context);
2100 auto changeRequest = context.objectInfo;
2101 CHECK_COND_RET(changeRequest != nullptr, false, "Failed to get changeRequest");
2102
2103 auto fileAsset = changeRequest->GetFileAssetInstance();
2104 CHECK_COND_RET(fileAsset != nullptr, false, "Failed to get fileAsset");
2105
2106 bool isWriteGpsAdvanced = changeRequest->GetIsWriteGpsAdvanced();
2107 int32_t ret = changeRequest->SubmitCache(isCreation, isSetEffectMode, isWriteGpsAdvanced,
2108 changeRequest->GetFileAssetInstance()->GetUserId());
2109 if (ret < 0) {
2110 context.SaveError(ret);
2111 NAPI_ERR_LOG("Failed to write cache, ret: %{public}d", ret);
2112 return false;
2113 }
2114 return true;
2115 }
2116
WriteCacheByArrayBuffer(MediaAssetChangeRequestAsyncContext & context,const UniqueFd & destFd,bool isMovingPhotoVideo=false)2117 static bool WriteCacheByArrayBuffer(MediaAssetChangeRequestAsyncContext& context,
2118 const UniqueFd& destFd, bool isMovingPhotoVideo = false)
2119 {
2120 auto changeRequest = context.objectInfo;
2121 size_t offset = 0;
2122 size_t length = isMovingPhotoVideo ? changeRequest->GetMovingPhotoVideoSize() : changeRequest->GetDataBufferSize();
2123 void* dataBuffer = isMovingPhotoVideo ? changeRequest->GetMovingPhotoVideoBuffer() : changeRequest->GetDataBuffer();
2124 while (offset < length) {
2125 ssize_t written = write(destFd.Get(), (char*)dataBuffer + offset, length - offset);
2126 if (written < 0) {
2127 context.SaveError(written);
2128 NAPI_ERR_LOG("Failed to write data buffer to cache file, return %{public}d", static_cast<int>(written));
2129 return false;
2130 }
2131 offset += static_cast<size_t>(written);
2132 }
2133 return true;
2134 }
2135
SendToCacheFile(MediaAssetChangeRequestAsyncContext & context,const UniqueFd & destFd,bool isMovingPhotoVideo=false)2136 static bool SendToCacheFile(MediaAssetChangeRequestAsyncContext& context,
2137 const UniqueFd& destFd, bool isMovingPhotoVideo = false)
2138 {
2139 auto changeRequest = context.objectInfo;
2140 string realPath = isMovingPhotoVideo ? changeRequest->GetMovingPhotoVideoPath() : changeRequest->GetFileRealPath();
2141
2142 string absFilePath;
2143 if (!PathToRealPath(realPath, absFilePath)) {
2144 NAPI_ERR_LOG("Not real path %{private}s, errno=%{public}d", realPath.c_str(), errno);
2145 return false;
2146 }
2147
2148 UniqueFd srcFd(open(absFilePath.c_str(), O_RDONLY));
2149 if (srcFd.Get() < 0) {
2150 context.SaveError(srcFd.Get());
2151 NAPI_ERR_LOG("Failed to open file, errno=%{public}d", errno);
2152 return false;
2153 }
2154
2155 int32_t err = SendFile(srcFd, destFd);
2156 if (err != E_OK) {
2157 context.SaveError(err);
2158 NAPI_ERR_LOG("Failed to send file from %{public}d to %{public}d", srcFd.Get(), destFd.Get());
2159 return false;
2160 }
2161 return true;
2162 }
2163
CreateFromFileUriExecute(MediaAssetChangeRequestAsyncContext & context)2164 static bool CreateFromFileUriExecute(MediaAssetChangeRequestAsyncContext& context)
2165 {
2166 MediaLibraryTracer tracer;
2167 tracer.Start("CreateFromFileUriExecute");
2168
2169 if (!HasWritePermission()) {
2170 return WriteBySecurityComponent(context);
2171 }
2172
2173 int32_t cacheFd = OpenWriteCacheHandler(context);
2174 if (cacheFd < 0) {
2175 NAPI_ERR_LOG("Failed to open write cache handler, err: %{public}d", cacheFd);
2176 return false;
2177 }
2178
2179 UniqueFd uniqueFd(cacheFd);
2180 if (!SendToCacheFile(context, uniqueFd)) {
2181 NAPI_ERR_LOG("Faild to write cache file");
2182 return false;
2183 }
2184 return SubmitCacheExecute(context);
2185 }
2186
AddPhotoProxyResourceExecute(MediaAssetChangeRequestAsyncContext & context,const UniqueFd & destFd)2187 static bool AddPhotoProxyResourceExecute(MediaAssetChangeRequestAsyncContext& context, const UniqueFd& destFd)
2188 {
2189 string uri = PAH_ADD_IMAGE;
2190 MediaLibraryNapiUtils::UriAppendKeyValue(uri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
2191 Uri updateAssetUri(uri);
2192
2193 auto fileAsset = context.objectInfo->GetFileAssetInstance();
2194 DataShare::DataSharePredicates predicates;
2195 predicates.SetWhereClause(PhotoColumn::MEDIA_ID + " = ? ");
2196 predicates.SetWhereArgs({ to_string(fileAsset->GetId()) });
2197
2198 DataShare::DataShareValuesBucket valuesBucket;
2199 valuesBucket.Put(PhotoColumn::PHOTO_ID, context.objectInfo->GetPhotoProxyObj()->GetPhotoId());
2200 NAPI_INFO_LOG("photoId: %{public}s", context.objectInfo->GetPhotoProxyObj()->GetPhotoId().c_str());
2201 valuesBucket.Put(PhotoColumn::PHOTO_DEFERRED_PROC_TYPE,
2202 static_cast<int32_t>(context.objectInfo->GetPhotoProxyObj()->GetDeferredProcType()));
2203 valuesBucket.Put(MediaColumn::MEDIA_ID, fileAsset->GetId());
2204 int32_t changedRows = UserFileClient::Update(updateAssetUri, predicates, valuesBucket);
2205 if (changedRows < 0) {
2206 context.SaveError(changedRows);
2207 NAPI_ERR_LOG("Failed to set, err: %{public}d", changedRows);
2208 return false;
2209 }
2210
2211 int err = SavePhotoProxyImage(destFd, context.objectInfo->GetPhotoProxyObj());
2212 context.objectInfo->ReleasePhotoProxyObj();
2213 if (err < 0) {
2214 context.SaveError(err);
2215 NAPI_ERR_LOG("Failed to saveImage , err: %{public}d", err);
2216 return false;
2217 }
2218 return true;
2219 }
2220
AddResourceByMode(MediaAssetChangeRequestAsyncContext & context,const UniqueFd & uniqueFd,AddResourceMode mode,bool isMovingPhotoVideo=false)2221 static bool AddResourceByMode(MediaAssetChangeRequestAsyncContext& context,
2222 const UniqueFd& uniqueFd, AddResourceMode mode, bool isMovingPhotoVideo = false)
2223 {
2224 bool isWriteSuccess = false;
2225 if (mode == AddResourceMode::DATA_BUFFER) {
2226 isWriteSuccess = WriteCacheByArrayBuffer(context, uniqueFd, isMovingPhotoVideo);
2227 } else if (mode == AddResourceMode::FILE_URI) {
2228 isWriteSuccess = SendToCacheFile(context, uniqueFd, isMovingPhotoVideo);
2229 } else if (mode == AddResourceMode::PHOTO_PROXY) {
2230 isWriteSuccess = AddPhotoProxyResourceExecute(context, uniqueFd);
2231 } else {
2232 context.SaveError(E_FAIL);
2233 NAPI_ERR_LOG("Unsupported addResource mode");
2234 }
2235 return isWriteSuccess;
2236 }
2237
AddMovingPhotoVideoExecute(MediaAssetChangeRequestAsyncContext & context)2238 static bool AddMovingPhotoVideoExecute(MediaAssetChangeRequestAsyncContext& context)
2239 {
2240 MediaLibraryTracer tracer;
2241 tracer.Start("AddMovingPhotoVideoExecute");
2242
2243 int32_t cacheVideoFd = OpenWriteCacheHandler(context, true);
2244 if (cacheVideoFd < 0) {
2245 NAPI_ERR_LOG("Failed to open cache moving photo video, err: %{public}d", cacheVideoFd);
2246 return false;
2247 }
2248
2249 UniqueFd uniqueFd(cacheVideoFd);
2250 AddResourceMode mode = context.objectInfo->GetMovingPhotoVideoMode();
2251 if (!AddResourceByMode(context, uniqueFd, mode, true)) {
2252 NAPI_ERR_LOG("Faild to write cache file");
2253 return false;
2254 }
2255 return true;
2256 }
2257
HasAddResource(MediaAssetChangeRequestAsyncContext & context,ResourceType resourceType)2258 static bool HasAddResource(MediaAssetChangeRequestAsyncContext& context, ResourceType resourceType)
2259 {
2260 return std::find(context.addResourceTypes.begin(), context.addResourceTypes.end(), resourceType) !=
2261 context.addResourceTypes.end();
2262 }
2263
AddResourceExecute(MediaAssetChangeRequestAsyncContext & context)2264 static bool AddResourceExecute(MediaAssetChangeRequestAsyncContext& context)
2265 {
2266 MediaLibraryTracer tracer;
2267 tracer.Start("AddResourceExecute");
2268 NAPI_INFO_LOG("AddResourceExecute begin.");
2269 if (!HasWritePermission()) {
2270 return WriteBySecurityComponent(context);
2271 }
2272
2273 auto changeRequest = context.objectInfo;
2274 if (changeRequest->IsMovingPhoto() && HasAddResource(context, ResourceType::VIDEO_RESOURCE) &&
2275 !AddMovingPhotoVideoExecute(context)) {
2276 NAPI_ERR_LOG("Faild to write cache file for video of moving photo");
2277 return false;
2278 }
2279
2280 // image resource is not mandatory when setting effect mode of moving photo
2281 if (changeRequest->IsMovingPhoto() && !HasAddResource(context, ResourceType::IMAGE_RESOURCE)) {
2282 return SubmitCacheExecute(context);
2283 }
2284
2285 int32_t cacheFd = OpenWriteCacheHandler(context);
2286 if (cacheFd < 0) {
2287 NAPI_ERR_LOG("Failed to open write cache handler, err: %{public}d", cacheFd);
2288 return false;
2289 }
2290
2291 UniqueFd uniqueFd(cacheFd);
2292 AddResourceMode mode = changeRequest->GetAddResourceMode();
2293 if (!AddResourceByMode(context, uniqueFd, mode)) {
2294 NAPI_ERR_LOG("Faild to write cache file");
2295 return false;
2296 }
2297 return SubmitCacheExecute(context);
2298 }
2299
UpdateAssetProperty(MediaAssetChangeRequestAsyncContext & context,string uri,DataShare::DataSharePredicates & predicates,DataShare::DataShareValuesBucket & valuesBucket)2300 static bool UpdateAssetProperty(MediaAssetChangeRequestAsyncContext& context, string uri,
2301 DataShare::DataSharePredicates& predicates, DataShare::DataShareValuesBucket& valuesBucket)
2302 {
2303 MediaLibraryNapiUtils::UriAppendKeyValue(uri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
2304 Uri updateAssetUri(uri);
2305 int32_t userId = -1;
2306 if (context.objectInfo != nullptr && context.objectInfo->GetFileAssetInstance() != nullptr) {
2307 userId = context.objectInfo->GetFileAssetInstance()->GetUserId();
2308 }
2309 int32_t changedRows = UserFileClient::Update(updateAssetUri, predicates, valuesBucket, userId);
2310 if (changedRows < 0) {
2311 context.SaveError(changedRows);
2312 NAPI_ERR_LOG("Failed to update property of asset, err: %{public}d", changedRows);
2313 return false;
2314 }
2315 return true;
2316 }
2317
SetFavoriteExecute(MediaAssetChangeRequestAsyncContext & context)2318 static bool SetFavoriteExecute(MediaAssetChangeRequestAsyncContext& context)
2319 {
2320 MediaLibraryTracer tracer;
2321 tracer.Start("SetFavoriteExecute");
2322
2323 DataShare::DataSharePredicates predicates;
2324 DataShare::DataShareValuesBucket valuesBucket;
2325 auto fileAsset = context.objectInfo->GetFileAssetInstance();
2326 predicates.EqualTo(PhotoColumn::MEDIA_ID, to_string(fileAsset->GetId()));
2327 valuesBucket.Put(PhotoColumn::MEDIA_IS_FAV, fileAsset->IsFavorite() ? YES : NO);
2328 NAPI_INFO_LOG("update asset %{public}d favorite to %{public}d", fileAsset->GetId(),
2329 fileAsset->IsFavorite() ? YES : NO);
2330 return UpdateAssetProperty(context, PAH_UPDATE_PHOTO, predicates, valuesBucket);
2331 }
2332
SetHiddenExecute(MediaAssetChangeRequestAsyncContext & context)2333 static bool SetHiddenExecute(MediaAssetChangeRequestAsyncContext& context)
2334 {
2335 MediaLibraryTracer tracer;
2336 tracer.Start("SetHiddenExecute");
2337
2338 DataShare::DataSharePredicates predicates;
2339 DataShare::DataShareValuesBucket valuesBucket;
2340 auto fileAsset = context.objectInfo->GetFileAssetInstance();
2341 vector<string> assetUriArray(1, fileAsset->GetUri());
2342 predicates.In(PhotoColumn::MEDIA_ID, assetUriArray);
2343 valuesBucket.Put(PhotoColumn::MEDIA_HIDDEN, fileAsset->IsHidden() ? YES : NO);
2344 return UpdateAssetProperty(context, PAH_HIDE_PHOTOS, predicates, valuesBucket);
2345 }
2346
SetTitleExecute(MediaAssetChangeRequestAsyncContext & context)2347 static bool SetTitleExecute(MediaAssetChangeRequestAsyncContext& context)
2348 {
2349 MediaLibraryTracer tracer;
2350 tracer.Start("SetTitleExecute");
2351
2352 // In the scenario of creation, the new title will be applied when the asset is created.
2353 AssetChangeOperation firstOperation = context.assetChangeOperations.front();
2354 if (firstOperation == AssetChangeOperation::CREATE_FROM_SCRATCH ||
2355 firstOperation == AssetChangeOperation::CREATE_FROM_URI) {
2356 return true;
2357 }
2358
2359 DataShare::DataSharePredicates predicates;
2360 DataShare::DataShareValuesBucket valuesBucket;
2361 auto fileAsset = context.objectInfo->GetFileAssetInstance();
2362 predicates.EqualTo(PhotoColumn::MEDIA_ID, to_string(fileAsset->GetId()));
2363 valuesBucket.Put(PhotoColumn::MEDIA_TITLE, fileAsset->GetTitle());
2364 return UpdateAssetProperty(context, PAH_UPDATE_PHOTO, predicates, valuesBucket);
2365 }
2366
IsEditDisplayNameFussion(MediaAssetChangeRequestAsyncContext & context)2367 static bool IsEditDisplayNameFussion(MediaAssetChangeRequestAsyncContext& context)
2368 {
2369 auto opers = context.assetChangeOperations;
2370 bool isCreateFromUri =
2371 std::find(opers.begin(), opers.end(), AssetChangeOperation::CREATE_FROM_URI) != opers.end();
2372 bool isCreateFromScratch =
2373 std::find(opers.begin(), opers.end(), AssetChangeOperation::CREATE_FROM_SCRATCH) != opers.end();
2374 bool isSetEditData =
2375 std::find(opers.begin(), opers.end(), AssetChangeOperation::SET_EDIT_DATA) != opers.end();
2376 bool isAddResource =
2377 std::find(opers.begin(), opers.end(), AssetChangeOperation::ADD_RESOURCE) != opers.end();
2378 bool isAddFilters =
2379 std::find(opers.begin(), opers.end(), AssetChangeOperation::ADD_RESOURCE) != opers.end();
2380 bool isGetWriteCacheHandler =
2381 std::find(opers.begin(), opers.end(), AssetChangeOperation::GET_WRITE_CACHE_HANDLER) != opers.end();
2382 return isCreateFromUri || isCreateFromScratch || isSetEditData || isAddResource || isAddFilters ||
2383 isGetWriteCacheHandler;
2384 }
2385
SetDisplayNameExecute(MediaAssetChangeRequestAsyncContext & context)2386 static bool SetDisplayNameExecute(MediaAssetChangeRequestAsyncContext& context)
2387 {
2388 MediaLibraryTracer tracer;
2389 tracer.Start("SetDisplayNameExecute");
2390 NAPI_INFO_LOG("Begin SetDisplayNameExecute.");
2391
2392 // In the scenario of creation, the new title will be applied when the asset is created.
2393 if (IsEditDisplayNameFussion(context)) {
2394 return true;
2395 }
2396 auto changeRequest = context.objectInfo;
2397 CHECK_COND_RET(changeRequest != nullptr, JS_INNER_FAIL, "changeRequest is nullptr");
2398 auto fileAsset = changeRequest->GetFileAssetInstance();
2399 CHECK_COND_RET(fileAsset != nullptr, JS_INNER_FAIL, "fileAsset is nullptr");
2400
2401 std::string uri = PAH_UPDATE_PHOTO;
2402 MediaLibraryNapiUtils::UriAppendKeyValue(uri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
2403 MediaLibraryNapiUtils::UriAppendKeyValue(uri, SET_DISPLAY_NAME_KEY, fileAsset->GetDisplayName());
2404 MediaLibraryNapiUtils::UriAppendKeyValue(uri, CAN_FALLBACK, "0");
2405 MediaLibraryNapiUtils::UriAppendKeyValue(uri, OLD_DISPLAY_NAME, changeRequest->GetOldDisplayName());
2406 DataShare::DataSharePredicates predicates;
2407 predicates.EqualTo(PhotoColumn::MEDIA_ID, to_string(fileAsset->GetId()));
2408
2409 DataShare::DataShareValuesBucket valuesBucket;
2410 valuesBucket.Put(MediaColumn::MEDIA_NAME, fileAsset->GetDisplayName());
2411 changeRequest->SetIsEditDisplayName(false);
2412 return UpdateAssetProperty(context, uri, predicates, valuesBucket);
2413 }
2414
SetOrientationExecute(MediaAssetChangeRequestAsyncContext & context)2415 static bool SetOrientationExecute(MediaAssetChangeRequestAsyncContext& context)
2416 {
2417 MediaLibraryTracer tracer;
2418 tracer.Start("SetOrientationExecute");
2419
2420 DataShare::DataSharePredicates predicates;
2421 DataShare::DataShareValuesBucket valuesBucket;
2422 auto fileAsset = context.objectInfo->GetFileAssetInstance();
2423 if (fileAsset == nullptr) {
2424 NAPI_ERR_LOG("fileAsset is null");
2425 return false;
2426 }
2427 predicates.EqualTo(PhotoColumn::MEDIA_ID, to_string(fileAsset->GetId()));
2428 valuesBucket.Put(PhotoColumn::PHOTO_ORIENTATION, fileAsset->GetOrientation());
2429 return UpdateAssetProperty(context, PAH_UPDATE_PHOTO, predicates, valuesBucket);
2430 }
2431
SetUserCommentExecute(MediaAssetChangeRequestAsyncContext & context)2432 static bool SetUserCommentExecute(MediaAssetChangeRequestAsyncContext& context)
2433 {
2434 MediaLibraryTracer tracer;
2435 tracer.Start("SetUserCommentExecute");
2436
2437 DataShare::DataSharePredicates predicates;
2438 DataShare::DataShareValuesBucket valuesBucket;
2439 auto fileAsset = context.objectInfo->GetFileAssetInstance();
2440 predicates.EqualTo(PhotoColumn::MEDIA_ID, to_string(fileAsset->GetId()));
2441 valuesBucket.Put(PhotoColumn::PHOTO_USER_COMMENT, fileAsset->GetUserComment());
2442 return UpdateAssetProperty(context, PAH_EDIT_USER_COMMENT_PHOTO, predicates, valuesBucket);
2443 }
2444
SetEffectModeExecute(MediaAssetChangeRequestAsyncContext & context)2445 static bool SetEffectModeExecute(MediaAssetChangeRequestAsyncContext& context)
2446 {
2447 MediaLibraryTracer tracer;
2448 tracer.Start("SetEffectModeExecute");
2449
2450 // SET_MOVING_PHOTO_EFFECT_MODE will be applied together with ADD_RESOURCE
2451 auto changeRequest = context.objectInfo;
2452 if (std::find(context.assetChangeOperations.begin(), context.assetChangeOperations.end(),
2453 AssetChangeOperation::ADD_RESOURCE) != context.assetChangeOperations.end()) {
2454 return true;
2455 }
2456
2457 DataShare::DataSharePredicates predicates;
2458 DataShare::DataShareValuesBucket valuesBucket;
2459 auto fileAsset = changeRequest->GetFileAssetInstance();
2460 predicates.EqualTo(PhotoColumn::MEDIA_ID, to_string(fileAsset->GetId()));
2461 valuesBucket.Put(PhotoColumn::MOVING_PHOTO_EFFECT_MODE, fileAsset->GetMovingPhotoEffectMode());
2462 return UpdateAssetProperty(context, PAH_UPDATE_PHOTO, predicates, valuesBucket);
2463 }
2464
SetPhotoQualityExecute(MediaAssetChangeRequestAsyncContext & context)2465 static bool SetPhotoQualityExecute(MediaAssetChangeRequestAsyncContext& context)
2466 {
2467 MediaLibraryTracer tracer;
2468 tracer.Start("SetPhotoQualityExecute");
2469
2470 DataShare::DataSharePredicates predicates;
2471 DataShare::DataShareValuesBucket valuesBucket;
2472 auto fileAsset = context.objectInfo->GetFileAssetInstance();
2473 predicates.EqualTo(PhotoColumn::MEDIA_ID, to_string(fileAsset->GetId()));
2474 std::pair<std::string, int> photoQuality = fileAsset->GetPhotoIdAndQuality();
2475 valuesBucket.Put(PhotoColumn::PHOTO_ID, photoQuality.first);
2476 valuesBucket.Put(PhotoColumn::PHOTO_QUALITY, photoQuality.second);
2477 return UpdateAssetProperty(context, PAH_SET_PHOTO_QUALITY, predicates, valuesBucket);
2478 }
2479
SetLocationExecute(MediaAssetChangeRequestAsyncContext & context)2480 static bool SetLocationExecute(MediaAssetChangeRequestAsyncContext& context)
2481 {
2482 MediaLibraryTracer tracer;
2483 tracer.Start("SetLocationExecute");
2484 if (context.objectInfo->GetIsWriteGpsAdvanced()) {
2485 NAPI_INFO_LOG("SetLocation will execute by addResource.");
2486 return true;
2487 }
2488
2489 NAPI_INFO_LOG("SetLocation begin.");
2490 DataShare::DataSharePredicates predicates;
2491 DataShare::DataShareValuesBucket valuesBucket;
2492 auto changeRequest = context.objectInfo;
2493 auto fileAsset = changeRequest->GetFileAssetInstance();
2494 predicates.EqualTo(PhotoColumn::MEDIA_ID, to_string(fileAsset->GetId()));
2495 valuesBucket.Put(PhotoColumn::MEDIA_ID, fileAsset->GetId());
2496 if (changeRequest->GetIsEditDisplayName()) {
2497 std::string oldPath = MediaFileUtils::UnSplitByChar(fileAsset->GetPath(), '.') + "." +
2498 MediaFileUtils::GetExtensionFromPath(changeRequest->GetOldDisplayName());
2499 valuesBucket.Put(PhotoColumn::MEDIA_FILE_PATH, fileAsset->GetPath());
2500 } else {
2501 valuesBucket.Put(PhotoColumn::MEDIA_FILE_PATH, fileAsset->GetPath());
2502 }
2503 valuesBucket.Put(PhotoColumn::PHOTO_LATITUDE, fileAsset->GetLatitude());
2504 valuesBucket.Put(PhotoColumn::PHOTO_LONGITUDE, fileAsset->GetLongitude());
2505 return UpdateAssetProperty(context, PAH_SET_LOCATION, predicates, valuesBucket);
2506 }
2507
SetCameraShotKeyExecute(MediaAssetChangeRequestAsyncContext & context)2508 static bool SetCameraShotKeyExecute(MediaAssetChangeRequestAsyncContext& context)
2509 {
2510 MediaLibraryTracer tracer;
2511 tracer.Start("SetCameraShotKeyExecute");
2512
2513 DataShare::DataSharePredicates predicates;
2514 DataShare::DataShareValuesBucket valuesBucket;
2515 auto fileAsset = context.objectInfo->GetFileAssetInstance();
2516 predicates.EqualTo(PhotoColumn::MEDIA_ID, to_string(fileAsset->GetId()));
2517 valuesBucket.Put(PhotoColumn::CAMERA_SHOT_KEY, fileAsset->GetCameraShotKey());
2518 return UpdateAssetProperty(context, PAH_UPDATE_PHOTO, predicates, valuesBucket);
2519 }
2520
DiscardHighQualityPhoto(const shared_ptr<FileAsset> fileAsset)2521 static void DiscardHighQualityPhoto(const shared_ptr<FileAsset> fileAsset)
2522 {
2523 CHECK_NULL_PTR_RETURN_VOID(fileAsset, "fileAsset is nullptr");
2524 std::string uriStr = PAH_REMOVE_MSC_TASK;
2525 MediaLibraryNapiUtils::UriAppendKeyValue(uriStr, API_VERSION, to_string(MEDIA_API_VERSION_V10));
2526 Uri uri(uriStr);
2527 DataShare::DataSharePredicates predicates;
2528 int errCode = 0;
2529 std::vector<std::string> columns { to_string(fileAsset->GetId()) };
2530 UserFileClient::Query(uri, predicates, columns, errCode);
2531 }
2532
SaveCameraPhotoExecute(MediaAssetChangeRequestAsyncContext & context)2533 static bool SaveCameraPhotoExecute(MediaAssetChangeRequestAsyncContext& context)
2534 {
2535 MediaLibraryTracer tracer;
2536 tracer.Start("SaveCameraPhotoExecute");
2537 NAPI_INFO_LOG("Begin SaveCameraPhotoExecute");
2538
2539 auto objInfo = context.objectInfo;
2540 CHECK_COND_RET(objInfo != nullptr, false, "Failed to check objInfo");
2541
2542 auto fileAsset = objInfo->GetFileAssetInstance();
2543 CHECK_COND_RET(fileAsset != nullptr, false, "Failed to check fileAsset");
2544
2545 auto changeOpreations = context.assetChangeOperations;
2546 bool containsAddResource = std::find(changeOpreations.begin(), changeOpreations.end(),
2547 AssetChangeOperation::ADD_RESOURCE) != changeOpreations.end();
2548 std::string uriStr = PAH_SAVE_CAMERA_PHOTO;
2549 if (containsAddResource && !MediaLibraryNapiUtils::IsSystemApp()) {
2550 // remove high quality photo
2551 NAPI_INFO_LOG("discard high quality photo because add resource by third app");
2552 DiscardHighQualityPhoto(fileAsset);
2553
2554 // set dirty flag when third-party hap calling addResource to save camera photo
2555 MediaLibraryNapiUtils::UriAppendKeyValue(uriStr, PhotoColumn::PHOTO_DIRTY,
2556 to_string(static_cast<int32_t>(DirtyType::TYPE_NEW)));
2557 }
2558
2559 // The watermark will trigger the scan. If the watermark is turned on, there is no need to trigger the scan again.
2560 bool needScan = std::find(changeOpreations.begin(), changeOpreations.end(),
2561 AssetChangeOperation::ADD_FILTERS) == changeOpreations.end();
2562
2563 MediaLibraryNapiUtils::UriAppendKeyValue(uriStr, API_VERSION, to_string(MEDIA_API_VERSION_V10));
2564 MediaLibraryNapiUtils::UriAppendKeyValue(uriStr, MEDIA_OPERN_KEYWORD, to_string(needScan));
2565 MediaLibraryNapiUtils::UriAppendKeyValue(uriStr, PhotoColumn::MEDIA_FILE_PATH, fileAsset->GetUri());
2566 MediaLibraryNapiUtils::UriAppendKeyValue(uriStr, PhotoColumn::MEDIA_ID, to_string(fileAsset->GetId()));
2567 MediaLibraryNapiUtils::UriAppendKeyValue(uriStr, PhotoColumn::PHOTO_SUBTYPE,
2568 to_string(fileAsset->GetPhotoSubType()));
2569 MediaLibraryNapiUtils::UriAppendKeyValue(uriStr, IMAGE_FILE_TYPE,
2570 to_string(objInfo->GetImageFileType()));
2571 Uri uri(uriStr);
2572 DataShare::DataShareValuesBucket valuesBucket;
2573 valuesBucket.Put(PhotoColumn::PHOTO_IS_TEMP, false);
2574 DataShare::DataSharePredicates predicates;
2575 auto ret = UserFileClient::Update(uri, predicates, valuesBucket);
2576 if (ret < 0) {
2577 NAPI_ERR_LOG("save camera photo fail");
2578 }
2579 return true;
2580 }
2581
SetVideoEnhancementAttr(MediaAssetChangeRequestAsyncContext & context)2582 static bool SetVideoEnhancementAttr(MediaAssetChangeRequestAsyncContext& context)
2583 {
2584 MediaLibraryTracer tracer;
2585 tracer.Start("setVideoEnhancementAttr");
2586
2587 auto changeOpreations = context.assetChangeOperations;
2588 auto changeRequest = context.objectInfo;
2589 CHECK_COND_RET(changeRequest != nullptr, false, "Failed to get changeRequest");
2590 auto fileAsset = changeRequest->GetFileAssetInstance();
2591 CHECK_COND_RET(fileAsset != nullptr, false, "Failed to get fileAsset");
2592
2593 DataShare::DataSharePredicates predicates;
2594 predicates.EqualTo(PhotoColumn::MEDIA_ID, to_string(fileAsset->GetId()));
2595 DataShare::DataShareValuesBucket valuesBucket;
2596 valuesBucket.Put(PhotoColumn::PHOTO_IS_TEMP, false);
2597
2598 string uri = PAH_SET_VIDEO_ENHANCEMENT_ATTR;
2599 MediaLibraryNapiUtils::UriAppendKeyValue(uri, PhotoColumn::PHOTO_ID, fileAsset->GetPhotoId());
2600 MediaLibraryNapiUtils::UriAppendKeyValue(uri, MediaColumn::MEDIA_ID, to_string(fileAsset->GetId()));
2601 if (changeRequest->GetIsEditDisplayName()) {
2602 std::string oldPath = MediaFileUtils::UnSplitByChar(fileAsset->GetPath(), '.') +
2603 "." + MediaFileUtils::GetExtensionFromPath(changeRequest->GetOldDisplayName());
2604 MediaLibraryNapiUtils::UriAppendKeyValue(uri, MediaColumn::MEDIA_FILE_PATH, oldPath);
2605 } else {
2606 MediaLibraryNapiUtils::UriAppendKeyValue(uri, MediaColumn::MEDIA_FILE_PATH, fileAsset->GetPath());
2607 }
2608 MediaLibraryNapiUtils::UriAppendKeyValue(uri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
2609 Uri updateAssetUri(uri);
2610 int32_t changedRows = UserFileClient::Update(updateAssetUri, predicates, valuesBucket);
2611 if (changedRows < 0) {
2612 context.SaveError(changedRows);
2613 NAPI_ERR_LOG("Failed to update property of asset, err: %{public}d", changedRows);
2614 return false;
2615 }
2616 return true;
2617 }
2618
AddFiltersExecute(MediaAssetChangeRequestAsyncContext & context)2619 static bool AddFiltersExecute(MediaAssetChangeRequestAsyncContext& context)
2620 {
2621 MediaLibraryTracer tracer;
2622 tracer.Start("AddFiltersExecute");
2623
2624 auto changeRequest = context.objectInfo;
2625 CHECK_COND_RET(changeRequest != nullptr, false, "Failed to get changeRequest");
2626 auto fileAsset = changeRequest->GetFileAssetInstance();
2627 CHECK_COND_RET(fileAsset != nullptr, false, "Failed to get fileAsset");
2628
2629 std::string uri = PAH_ADD_FILTERS;
2630 DataShare::DataShareValuesBucket valuesBucket;
2631 if (changeRequest->GetIsEditDisplayName()) {
2632 MediaLibraryNapiUtils::UriAppendKeyValue(uri, SET_DISPLAY_NAME_KEY, fileAsset->GetDisplayName());
2633 MediaLibraryNapiUtils::UriAppendKeyValue(uri, CAN_FALLBACK, "1");
2634 MediaLibraryNapiUtils::UriAppendKeyValue(uri, OLD_DISPLAY_NAME, changeRequest->GetOldDisplayName());
2635 valuesBucket.Put(MediaColumn::MEDIA_NAME, fileAsset->GetDisplayName());
2636 valuesBucket.Put(MediaColumn::MEDIA_TITLE, fileAsset->GetTitle());
2637 valuesBucket.Put(MediaColumn::MEDIA_FILE_PATH, fileAsset->GetPath());
2638 valuesBucket.Put(MediaColumn::MEDIA_MIME_TYPE, fileAsset->GetMimeType());
2639 changeRequest->SetIsEditDisplayName(false);
2640 }
2641 MediaLibraryNapiUtils::UriAppendKeyValue(uri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
2642 Uri addFiltersUri(uri);
2643
2644 valuesBucket.Put(PhotoColumn::MEDIA_ID, fileAsset->GetId());
2645 int ret = context.objectInfo->PutMediaAssetEditData(valuesBucket);
2646 CHECK_COND_RET(ret == E_OK, false, "Failed to put editData");
2647 ret = UserFileClient::Insert(addFiltersUri, valuesBucket);
2648 if (ret < 0) {
2649 context.SaveError(ret);
2650 NAPI_ERR_LOG("Failed to add filters, ret: %{public}d", ret);
2651 return false;
2652 }
2653 return true;
2654 }
2655
DiscardCameraPhotoExecute(MediaAssetChangeRequestAsyncContext & context)2656 static bool DiscardCameraPhotoExecute(MediaAssetChangeRequestAsyncContext& context)
2657 {
2658 DataShare::DataSharePredicates predicates;
2659 DataShare::DataShareValuesBucket valuesBucket;
2660 valuesBucket.Put(PhotoColumn::PHOTO_IS_TEMP, true);
2661 auto fileAsset = context.objectInfo->GetFileAssetInstance();
2662 predicates.EqualTo(PhotoColumn::MEDIA_ID, to_string(fileAsset->GetId()));
2663 predicates.EqualTo(PhotoColumn::PHOTO_IS_TEMP, "1"); // only temp camera photo can be discarded
2664
2665 string uri = PAH_DISCARD_CAMERA_PHOTO;
2666 MediaLibraryNapiUtils::UriAppendKeyValue(uri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
2667 MediaLibraryNapiUtils::UriAppendKeyValue(uri, PhotoColumn::MEDIA_ID, to_string(fileAsset->GetId()));
2668 Uri updateAssetUri(uri);
2669 int32_t changedRows = UserFileClient::Update(updateAssetUri, predicates, valuesBucket);
2670 if (changedRows < 0) {
2671 context.SaveError(changedRows);
2672 NAPI_ERR_LOG("Failed to update property of asset, err: %{public}d", changedRows);
2673 return false;
2674 }
2675 return true;
2676 }
2677
SetSupportedWatermarkTypeExecute(MediaAssetChangeRequestAsyncContext & context)2678 static bool SetSupportedWatermarkTypeExecute(MediaAssetChangeRequestAsyncContext& context)
2679 {
2680 MediaLibraryTracer tracer;
2681 tracer.Start("SetSupportedWatermarkTypeExecute");
2682
2683 DataShare::DataSharePredicates predicates;
2684 DataShare::DataShareValuesBucket valuesBucket;
2685 auto fileAsset = context.objectInfo->GetFileAssetInstance();
2686 if (fileAsset == nullptr) {
2687 NAPI_ERR_LOG("Fail to get fileAsset");
2688 return false;
2689 }
2690 NAPI_INFO_LOG("enter SetSupportedWatermarkTypeExecute: %{public}d", fileAsset->GetSupportedWatermarkType());
2691 predicates.EqualTo(PhotoColumn::MEDIA_ID, to_string(fileAsset->GetId()));
2692 valuesBucket.Put(PhotoColumn::SUPPORTED_WATERMARK_TYPE, fileAsset->GetSupportedWatermarkType());
2693
2694 string uri = PAH_UPDATE_PHOTO_SUPPORTED_WATERMARK_TYPE;
2695 MediaLibraryNapiUtils::UriAppendKeyValue(uri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
2696 Uri updateAssetUri(uri);
2697 int32_t changedRows = UserFileClient::Update(updateAssetUri, predicates, valuesBucket);
2698 if (changedRows < 0) {
2699 context.SaveError(changedRows);
2700 NAPI_ERR_LOG("Failed to update supported_watermark_type of asset, err: %{public}d", changedRows);
2701 return false;
2702 }
2703 return true;
2704 }
2705
2706 static const unordered_map<AssetChangeOperation, bool (*)(MediaAssetChangeRequestAsyncContext&)> EXECUTE_MAP = {
2707 { AssetChangeOperation::CREATE_FROM_URI, CreateFromFileUriExecute },
2708 { AssetChangeOperation::GET_WRITE_CACHE_HANDLER, SubmitCacheExecute },
2709 { AssetChangeOperation::ADD_RESOURCE, AddResourceExecute },
2710 { AssetChangeOperation::SET_FAVORITE, SetFavoriteExecute },
2711 { AssetChangeOperation::SET_HIDDEN, SetHiddenExecute },
2712 { AssetChangeOperation::SET_TITLE, SetTitleExecute },
2713 { AssetChangeOperation::SET_DISPLAY_NAME, SetDisplayNameExecute },
2714 { AssetChangeOperation::SET_ORIENTATION, SetOrientationExecute },
2715 { AssetChangeOperation::SET_USER_COMMENT, SetUserCommentExecute },
2716 { AssetChangeOperation::SET_MOVING_PHOTO_EFFECT_MODE, SetEffectModeExecute },
2717 { AssetChangeOperation::SET_PHOTO_QUALITY_AND_PHOTOID, SetPhotoQualityExecute },
2718 { AssetChangeOperation::SET_LOCATION, SetLocationExecute },
2719 { AssetChangeOperation::SET_CAMERA_SHOT_KEY, SetCameraShotKeyExecute },
2720 { AssetChangeOperation::SAVE_CAMERA_PHOTO, SaveCameraPhotoExecute },
2721 { AssetChangeOperation::ADD_FILTERS, AddFiltersExecute },
2722 { AssetChangeOperation::DISCARD_CAMERA_PHOTO, DiscardCameraPhotoExecute },
2723 { AssetChangeOperation::SET_SUPPORTED_WATERMARK_TYPE, SetSupportedWatermarkTypeExecute },
2724 { AssetChangeOperation::SET_VIDEO_ENHANCEMENT_ATTR, SetVideoEnhancementAttr },
2725 };
2726
RecordAddResourceAndSetLocation(MediaAssetChangeRequestAsyncContext & context)2727 static void RecordAddResourceAndSetLocation(MediaAssetChangeRequestAsyncContext& context)
2728 {
2729 std::vector<AssetChangeOperation> operations = context.assetChangeOperations;
2730 bool isAddResource =
2731 std::find(operations.begin(), operations.end(), AssetChangeOperation::ADD_RESOURCE) != operations.end();
2732 bool isSetLocation =
2733 std::find(operations.begin(), operations.end(), AssetChangeOperation::SET_LOCATION) != operations.end();
2734 if (isAddResource && isSetLocation) {
2735 context.objectInfo->SetIsWriteGpsAdvanced(true);
2736 }
2737 NAPI_INFO_LOG("Check addResource and setLocation, isAddResource: %{public}d, isSetLocation: %{public}d",
2738 isAddResource, isSetLocation);
2739 }
2740
ApplyAssetChangeRequestExecute(napi_env env,void * data)2741 static void ApplyAssetChangeRequestExecute(napi_env env, void* data)
2742 {
2743 MediaLibraryTracer tracer;
2744 tracer.Start("ApplyAssetChangeRequestExecute");
2745
2746 auto* context = static_cast<MediaAssetChangeRequestAsyncContext*>(data);
2747 if (context == nullptr) {
2748 NAPI_ERR_LOG("Failed to check because context is nullptr");
2749 return;
2750 }
2751 if (context->objectInfo == nullptr || context->objectInfo->GetFileAssetInstance() == nullptr) {
2752 context->SaveError(E_FAIL);
2753 NAPI_ERR_LOG("Failed to check async context of MediaAssetChangeRequest object");
2754 return;
2755 }
2756 RecordAddResourceAndSetLocation(*context);
2757 unordered_set<AssetChangeOperation> appliedOperations;
2758 for (const auto& changeOperation : context->assetChangeOperations) {
2759 // Keep the final result of each operation, and commit it only once.
2760 if (appliedOperations.find(changeOperation) != appliedOperations.end()) {
2761 continue;
2762 }
2763
2764 bool valid = false;
2765 auto iter = EXECUTE_MAP.find(changeOperation);
2766 if (iter != EXECUTE_MAP.end()) {
2767 tracer.Start("ApplyAssetChangeRequestExecute " + to_string(static_cast<int32_t>(changeOperation)));
2768 valid = iter->second(*context);
2769 tracer.Finish();
2770 } else if (changeOperation == AssetChangeOperation::CREATE_FROM_SCRATCH ||
2771 changeOperation == AssetChangeOperation::SET_EDIT_DATA) {
2772 // Perform CREATE_FROM_SCRATCH and SET_EDIT_DATA during GET_WRITE_CACHE_HANDLER or ADD_RESOURCE.
2773 valid = true;
2774 } else {
2775 NAPI_ERR_LOG("Invalid asset change operation: %{public}d", changeOperation);
2776 context->error = OHOS_INVALID_PARAM_CODE;
2777 return;
2778 }
2779
2780 if (!valid) {
2781 NAPI_ERR_LOG("Failed to apply asset change request, operation: %{public}d", changeOperation);
2782 return;
2783 }
2784 appliedOperations.insert(changeOperation);
2785 }
2786 }
2787
ApplyAssetChangeRequestCompleteCallback(napi_env env,napi_status status,void * data)2788 static void ApplyAssetChangeRequestCompleteCallback(napi_env env, napi_status status, void* data)
2789 {
2790 MediaLibraryTracer tracer;
2791 tracer.Start("ApplyAssetChangeRequestCompleteCallback");
2792
2793 auto* context = static_cast<MediaAssetChangeRequestAsyncContext*>(data);
2794 CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
2795 auto jsContext = make_unique<JSAsyncContextOutput>();
2796 jsContext->status = false;
2797 napi_get_undefined(env, &jsContext->data);
2798 napi_get_undefined(env, &jsContext->error);
2799 if (context->error == ERR_DEFAULT) {
2800 jsContext->status = true;
2801 } else {
2802 context->HandleError(env, jsContext->error);
2803 }
2804
2805 if (context->work != nullptr) {
2806 MediaLibraryNapiUtils::InvokeJSAsyncMethod(
2807 env, context->deferred, context->callbackRef, context->work, *jsContext);
2808 }
2809 delete context;
2810 }
2811
ApplyChanges(napi_env env,napi_callback_info info)2812 napi_value MediaAssetChangeRequestNapi::ApplyChanges(napi_env env, napi_callback_info info)
2813 {
2814 NAPI_INFO_LOG("Begin MediaAssetChangeRequestNapi::ApplyChanges");
2815 constexpr size_t minArgs = ARGS_ONE;
2816 constexpr size_t maxArgs = ARGS_TWO;
2817 auto asyncContext = make_unique<MediaAssetChangeRequestAsyncContext>();
2818 CHECK_COND_WITH_MESSAGE(env,
2819 MediaLibraryNapiUtils::AsyncContextGetArgs(env, info, asyncContext, minArgs, maxArgs) == napi_ok,
2820 "Failed to get args");
2821 asyncContext->objectInfo = this;
2822
2823 CHECK_COND_WITH_MESSAGE(env, CheckChangeOperations(env), "Failed to check asset change request operations");
2824 asyncContext->assetChangeOperations = assetChangeOperations_;
2825 asyncContext->addResourceTypes = addResourceTypes_;
2826 assetChangeOperations_.clear();
2827 addResourceTypes_.clear();
2828 return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "ApplyMediaAssetChangeRequest",
2829 ApplyAssetChangeRequestExecute, ApplyAssetChangeRequestCompleteCallback);
2830 }
2831
ParseArgsDeleteLocalAssetsPermanently(napi_env env,napi_callback_info info,unique_ptr<MediaAssetChangeRequestAsyncContext> & context)2832 static napi_value ParseArgsDeleteLocalAssetsPermanently(
2833 napi_env env, napi_callback_info info, unique_ptr<MediaAssetChangeRequestAsyncContext>& context)
2834 {
2835 NAPI_DEBUG_LOG("enter ParseArgsDeleteLocalAssetsPermanently.");
2836 if (!MediaLibraryNapiUtils::IsSystemApp()) {
2837 NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
2838 return nullptr;
2839 }
2840 constexpr size_t minArgs = ARGS_TWO;
2841 constexpr size_t maxArgs = ARGS_THREE;
2842 CHECK_COND_WITH_MESSAGE(env,
2843 MediaLibraryNapiUtils::AsyncContextGetArgs(env, info, context, minArgs, maxArgs) == napi_ok,
2844 "Failed to get args");
2845 CHECK_COND(env, MediaAssetChangeRequestNapi::InitUserFileClient(env, info), JS_INNER_FAIL);
2846
2847 vector<napi_value> napiValues;
2848 napi_valuetype valueType = napi_undefined;
2849 CHECK_NULLPTR_RET(MediaLibraryNapiUtils::GetNapiValueArray(env, context->argv[PARAM1], napiValues));
2850 CHECK_COND_WITH_MESSAGE(env, !napiValues.empty(), "array is empty");
2851 CHECK_ARGS(env, napi_typeof(env, napiValues.front(), &valueType), JS_INNER_FAIL);
2852 CHECK_COND_WITH_MESSAGE(env, valueType == napi_object, "Invalid argument type");
2853
2854 if (napiValues.size() > BATCH_DELETE_MAX_NUMBER) {
2855 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE,
2856 "Exceeded the maximum batch output quantity, cannot be deleted.");
2857 return nullptr;
2858 }
2859 vector<string> deleteIds;
2860 for (const auto& napiValue : napiValues) {
2861 FileAssetNapi* obj = nullptr;
2862 CHECK_ARGS(env, napi_unwrap(env, napiValue, reinterpret_cast<void**>(&obj)), JS_INNER_FAIL);
2863 CHECK_COND_WITH_MESSAGE(env, obj != nullptr, "Failed to get photo napi object");
2864 deleteIds.push_back(to_string(obj->GetFileId()));
2865 }
2866 context->predicates.In(PhotoColumn::MEDIA_ID, deleteIds);
2867 RETURN_NAPI_TRUE(env);
2868 }
2869
DeleteLocalAssetsPermanentlydExecute(napi_env env,void * data)2870 static void DeleteLocalAssetsPermanentlydExecute(napi_env env, void* data)
2871 {
2872 NAPI_DEBUG_LOG("enter DeleteLocalAssetsPermanentlydExecute.");
2873 MediaLibraryTracer tracer;
2874 tracer.Start("DeleteLocalAssetsPermanentlydExecute");
2875
2876 auto* context = static_cast<MediaAssetChangeRequestAsyncContext*>(data);
2877 DataShare::DataShareValuesBucket valuesBucket;
2878 valuesBucket.Put(PhotoColumn::MEDIA_DATE_TRASHED, 0);
2879 Uri deleteLocalAssetsCompletedUri(URI_DELETE_PHOTOS_COMPLETED);
2880 int ret = UserFileClient::Update(deleteLocalAssetsCompletedUri, context->predicates, valuesBucket);
2881 if (ret < 0) {
2882 context->SaveError(ret);
2883 NAPI_ERR_LOG("Failed to delete assets from local album permanently, err: %{public}d", ret);
2884 return;
2885 }
2886 }
2887
DeleteLocalAssetsPermanentlyCallback(napi_env env,napi_status status,void * data)2888 static void DeleteLocalAssetsPermanentlyCallback(napi_env env, napi_status status, void* data)
2889 {
2890 NAPI_DEBUG_LOG("enter DeleteLocalAssetsPermanentlyCallback.");
2891 auto* context = static_cast<MediaAssetChangeRequestAsyncContext*>(data);
2892 CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
2893 auto jsContext = make_unique<JSAsyncContextOutput>();
2894 jsContext->status = false;
2895 napi_get_undefined(env, &jsContext->data);
2896 napi_get_undefined(env, &jsContext->error);
2897 if (context->error == ERR_DEFAULT) {
2898 jsContext->status = true;
2899 } else {
2900 context->HandleError(env, jsContext->error);
2901 }
2902
2903 if (context->work != nullptr) {
2904 MediaLibraryNapiUtils::InvokeJSAsyncMethod(
2905 env, context->deferred, context->callbackRef, context->work, *jsContext);
2906 }
2907 delete context;
2908 }
2909
JSDeleteLocalAssetsPermanently(napi_env env,napi_callback_info info)2910 napi_value MediaAssetChangeRequestNapi::JSDeleteLocalAssetsPermanently(napi_env env, napi_callback_info info)
2911 {
2912 NAPI_DEBUG_LOG("enter JSDeleteLocalAssetsPermanently.");
2913 auto asyncContext = make_unique<MediaAssetChangeRequestAsyncContext>();
2914 CHECK_COND_WITH_MESSAGE(env, ParseArgsDeleteLocalAssetsPermanently(env, info, asyncContext),
2915 "Failed to parse args");
2916 return MediaLibraryNapiUtils::NapiCreateAsyncWork(
2917 env, asyncContext, "ChangeRequestDeleteLocalAssetsPermanently",
2918 DeleteLocalAssetsPermanentlydExecute, DeleteLocalAssetsPermanentlyCallback);
2919 }
2920 } // namespace OHOS::Media