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