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