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