• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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