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