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