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