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