• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2025 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 #include <array>
17 #include <fcntl.h>
18 #include <sys/sendfile.h>
19 #include <sys/stat.h>
20 #include "access_token.h"
21 #include "accesstoken_kit.h"
22 #include "directory_ex.h"
23 #include "file_uri.h"
24 #include "ani_class_name.h"
25 #include "image_packer.h"
26 #include "ipc_skeleton.h"
27 #include "media_asset_change_request_ani.h"
28 #include "media_column.h"
29 #include "media_file_utils.h"
30 #include "medialibrary_ani_utils.h"
31 #include "medialibrary_client_errno.h"
32 #include "medialibrary_db_const.h"
33 #include "medialibrary_tracer.h"
34 #include "permission_utils.h"
35 #include "photo_proxy_ani.h"
36 #ifdef HAS_ACE_ENGINE_PART
37 #include "ui_content.h"
38 #endif
39 #include "userfile_client.h"
40 #include "userfile_manager_types.h"
41 #include "want.h"
42 
43 using namespace std;
44 using namespace OHOS::Security::AccessToken;
45 using UniqueFd = OHOS::UniqueFd;
46 
47 namespace OHOS::Media {
48 std::atomic<uint32_t> MediaAssetChangeRequestAni::cacheFileId_ = 0;
49 constexpr int64_t CREATE_ASSET_REQUEST_PENDING = -4;
50 
51 const std::string PAH_SUBTYPE = "subtype";
52 const std::string CAMERA_SHOT_KEY = "cameraShotKey";
53 const std::map<std::string, std::string> PHOTO_CREATE_OPTIONS_PARAM = {
54     { PAH_SUBTYPE, PhotoColumn::PHOTO_SUBTYPE },
55     { CAMERA_SHOT_KEY, PhotoColumn::CAMERA_SHOT_KEY },
56 };
57 
58 const std::string TITLE = "title";
59 const std::map<std::string, std::string> CREATE_OPTIONS_PARAM = {
60     { TITLE, PhotoColumn::MEDIA_TITLE },
61     { PAH_SUBTYPE, PhotoColumn::PHOTO_SUBTYPE },
62 };
63 
64 const std::string DEFAULT_TITLE_TIME_FORMAT = "%Y%m%d_%H%M%S";
65 const std::string DEFAULT_TITLE_IMG_PREFIX = "IMG_";
66 const std::string DEFAULT_TITLE_VIDEO_PREFIX = "VID_";
67 const std::string MOVING_PHOTO_VIDEO_EXTENSION = "mp4";
68 
ReadData(const shared_ptr<AVSharedMemory> & mem,uint32_t length)69 int32_t MediaDataSource::ReadData(const shared_ptr<AVSharedMemory>& mem, uint32_t length)
70 {
71     if (readPos_ >= size_) {
72         ANI_ERR_LOG("Failed to check read position");
73         return SOURCE_ERROR_EOF;
74     }
75 
76     if (memcpy_s(mem->GetBase(), mem->GetSize(), (char*)buffer_ + readPos_, length) != E_OK) {
77         ANI_ERR_LOG("Failed to copy buffer to mem");
78         return SOURCE_ERROR_IO;
79     }
80     readPos_ += static_cast<int64_t>(length);
81     return static_cast<int32_t>(length);
82 }
83 
ReadAt(const std::shared_ptr<AVSharedMemory> & mem,uint32_t length,int64_t pos)84 int32_t MediaDataSource::ReadAt(const std::shared_ptr<AVSharedMemory>& mem, uint32_t length, int64_t pos)
85 {
86     readPos_ = pos;
87     return ReadData(mem, length);
88 }
89 
ReadAt(int64_t pos,uint32_t length,const std::shared_ptr<AVSharedMemory> & mem)90 int32_t MediaDataSource::ReadAt(int64_t pos, uint32_t length, const std::shared_ptr<AVSharedMemory>& mem)
91 {
92     readPos_ = pos;
93     return ReadData(mem, length);
94 }
95 
ReadAt(uint32_t length,const std::shared_ptr<AVSharedMemory> & mem)96 int32_t MediaDataSource::ReadAt(uint32_t length, const std::shared_ptr<AVSharedMemory>& mem)
97 {
98     return ReadData(mem, length);
99 }
100 
GetSize(int64_t & size)101 int32_t MediaDataSource::GetSize(int64_t& size)
102 {
103     size = size_;
104     return E_OK;
105 }
106 
MediaAssetChangeRequestAniInit(ani_env * env)107 ani_status MediaAssetChangeRequestAni::MediaAssetChangeRequestAniInit(ani_env *env)
108 {
109     static const char *className = ANI_CLASS_MEDIA_ASSET_CHANGE_REQUEST.c_str();
110     ani_class cls;
111     ani_status status = env->FindClass(className, &cls);
112     if (status != ANI_OK) {
113         ANI_ERR_LOG("Failed to find class: %{public}s", className);
114         return status;
115     }
116 
117     std::array methods = {
118         ani_native_function {"create", nullptr, reinterpret_cast<void *>(Constructor)},
119         ani_native_function {"addResource", nullptr, reinterpret_cast<void *>(addResourceByFileUri)},
120         ani_native_function {"addResource", nullptr, reinterpret_cast<void *>(addResourceByArrayBuffer)},
121         ani_native_function {"addResource", nullptr, reinterpret_cast<void *>(addResourceByPhotoProxy)},
122         ani_native_function {"createAssetRequest", nullptr, reinterpret_cast<void *>(createAssetRequestSystem)},
123         ani_native_function {"createAssetRequest", nullptr, reinterpret_cast<void *>(createAssetRequest)},
124         ani_native_function {"createImageAssetRequest", nullptr, reinterpret_cast<void *>(createImageAssetRequest)},
125         ani_native_function {"createVideoAssetRequest", nullptr, reinterpret_cast<void *>(createVideoAssetRequest)},
126         ani_native_function {"getAsset", nullptr, reinterpret_cast<void *>(getAsset)},
127         ani_native_function {"deleteAssetsByPhotoAssetSync", nullptr,
128             reinterpret_cast<void *>(deleteAssetsByPhotoAsset)},
129         ani_native_function {"deleteAssetsByUriListSync", nullptr, reinterpret_cast<void *>(deleteAssetsByUriList)},
130     };
131 
132     status = env->Class_BindNativeMethods(cls, methods.data(), methods.size());
133     if (status != ANI_OK) {
134         ANI_ERR_LOG("Failed to bind native methods to: %{public}s", className);
135         return status;
136     }
137     return ANI_OK;
138 }
139 
Constructor(ani_env * env,ani_class clazz,ani_object fileAssetAni)140 ani_object MediaAssetChangeRequestAni::Constructor(ani_env *env, [[maybe_unused]] ani_class clazz,
141     ani_object fileAssetAni)
142 {
143     FileAssetAni* fileAssetAniPtr = FileAssetAni::Unwrap(env, fileAssetAni);
144     CHECK_COND_WITH_RET_MESSAGE(env, fileAssetAniPtr != nullptr, nullptr, "fileAssetAniPtr is null");
145     auto fileAssetPtr = fileAssetAniPtr->GetFileAssetInstance();
146     CHECK_COND_WITH_RET_MESSAGE(env, fileAssetPtr != nullptr, nullptr, "fileAssetPtr is null");
147     CHECK_COND_WITH_RET_MESSAGE(env,
148         fileAssetPtr->GetResultNapiType() == ResultNapiType::TYPE_PHOTOACCESS_HELPER &&
149             (fileAssetPtr->GetMediaType() == MEDIA_TYPE_IMAGE || fileAssetPtr->GetMediaType() == MEDIA_TYPE_VIDEO),
150         nullptr, "Unsupported type of fileAsset");
151 
152     auto nativeHandle = std::make_unique<MediaAssetChangeRequestAni>(fileAssetAniPtr);
153     nativeHandle->fileAsset_ = fileAssetPtr;
154 
155     static const char *className = ANI_CLASS_MEDIA_ASSET_CHANGE_REQUEST.c_str();
156     ani_class cls;
157     if (ANI_OK != env->FindClass(className, &cls)) {
158         ANI_ERR_LOG("Failed to find class: %{public}s", className);
159         return nullptr;
160     }
161 
162     ani_method ctor;
163     if (ANI_OK != env->Class_FindMethod(cls, "<ctor>", "J:V", &ctor)) {
164         ANI_ERR_LOG("Failed to find method: %{public}s", "ctor");
165         return nullptr;
166     }
167 
168     // wrap nativeHandle to aniObject
169     ani_object aniObject;
170     if (ANI_OK !=env->Object_New(cls, ctor, &aniObject, reinterpret_cast<ani_long>(nativeHandle.release()))) {
171         ANI_ERR_LOG("New MediaAssetChangeRequest Fail");
172         return nullptr;
173     }
174 
175     return aniObject;
176 }
177 
Wrap(ani_env * env,MediaAssetChangeRequestAni * changeRequest)178 ani_object MediaAssetChangeRequestAni::Wrap(ani_env *env, MediaAssetChangeRequestAni* changeRequest)
179 {
180     static const char *className = ANI_CLASS_MEDIA_ASSET_CHANGE_REQUEST.c_str();
181     ani_class cls;
182     if (ANI_OK != env->FindClass(className, &cls)) {
183         ANI_ERR_LOG("Failed to find class: %{public}s", className);
184         return nullptr;
185     }
186 
187     ani_method ctor;
188     if (ANI_OK != env->Class_FindMethod(cls, "<ctor>", "J:V", &ctor)) {
189         ANI_ERR_LOG("Failed to find method: %{public}s", "ctor");
190         return nullptr;
191     }
192 
193     // wrap nativeHandle to aniObject
194     ani_object aniObject;
195     if (ANI_OK !=env->Object_New(cls, ctor, &aniObject, reinterpret_cast<ani_long>(changeRequest))) {
196         ANI_ERR_LOG("New MediaAssetChangeRequest Fail");
197         return nullptr;
198     }
199     return aniObject;
200 }
201 
Unwrap(ani_env * env,ani_object aniObject)202 MediaAssetChangeRequestAni* MediaAssetChangeRequestAni::Unwrap(ani_env *env, ani_object aniObject)
203 {
204     ani_long context;
205     if (ANI_OK != env->Object_GetFieldByName_Long(aniObject, "nativeHandle", &context)) {
206         return nullptr;
207     }
208     return reinterpret_cast<MediaAssetChangeRequestAni*>(context);
209 }
210 
MediaAssetChangeRequestAni(FileAssetAni * fileAssetAni)211 MediaAssetChangeRequestAni::MediaAssetChangeRequestAni(FileAssetAni* fileAssetAni)
212 {
213     if (fileAssetAni == nullptr) {
214         ANI_ERR_LOG("fileAssetAni is nullptr");
215         return;
216     }
217     auto fileAsset = fileAssetAni->GetFileAssetInstance();
218     if (fileAsset == nullptr) {
219         ANI_ERR_LOG("fileAsset is nullptr");
220         return;
221     }
222     if (fileAsset->GetResultNapiType() != ResultNapiType::TYPE_PHOTOACCESS_HELPER ||
223         (fileAsset->GetMediaType() != MEDIA_TYPE_IMAGE && fileAsset->GetMediaType() != MEDIA_TYPE_VIDEO)) {
224         ANI_ERR_LOG("Unsupported type of fileAsset");
225         return;
226     }
227     fileAsset_ = fileAsset;
228 }
229 
DeleteCache(const std::string & cacheFileName)230 static void DeleteCache(const std::string& cacheFileName)
231 {
232     if (cacheFileName.empty()) {
233         return;
234     }
235 
236     std::string uri = PhotoColumn::PHOTO_CACHE_URI_PREFIX + cacheFileName;
237     MediaFileUtils::UriAppendKeyValue(uri, API_VERSION, std::to_string(MEDIA_API_VERSION_V10));
238     Uri deleteCacheUri(uri);
239     DataShare::DataSharePredicates predicates;
240     int32_t ret = UserFileClient::Delete(deleteCacheUri, predicates);
241     if (ret < 0) {
242         ANI_ERR_LOG("Failed to delete cache: %{private}s, error: %{public}d", cacheFileName.c_str(), ret);
243     }
244 }
245 
~MediaAssetChangeRequestAni()246 MediaAssetChangeRequestAni::~MediaAssetChangeRequestAni()
247 {
248     DeleteCache(cacheFileName_);
249     DeleteCache(cacheMovingPhotoVideoName_);
250 }
251 
ParseCreateOptions(std::unique_ptr<MediaAssetChangeRequestAniContext> & context,const std::unordered_map<std::string,std::variant<int32_t,bool,std::string>> & optionsMap,const std::map<std::string,std::string> & createOptionsMap)252 static bool ParseCreateOptions(std::unique_ptr<MediaAssetChangeRequestAniContext>& context,
253     const std::unordered_map<std::string, std::variant<int32_t, bool, std::string>>& optionsMap,
254     const std::map<std::string, std::string>& createOptionsMap)
255 {
256     if (optionsMap.empty()) {
257         ANI_INFO_LOG("optionsMap is empty. There is no need to parse create options.");
258         return true;
259     }
260     for (const auto& [key, value] : optionsMap) {
261         auto iter = createOptionsMap.find(key);
262         if (iter != createOptionsMap.end()) {
263             std::string column = iter->second;
264             if (std::holds_alternative<int32_t>(value)) {
265                 context->valuesBucket.Put(column, std::get<int32_t>(value));
266             } else if (std::holds_alternative<bool>(value)) {
267                 context->valuesBucket.Put(column, std::get<bool>(value));
268             } else if (std::holds_alternative<std::string>(value)) {
269                 context->valuesBucket.Put(column, std::get<std::string>(value));
270             } else {
271                 ANI_ERR_LOG("value type of key: %{public}s not supported", key.c_str());
272                 continue;
273             }
274         } else {
275             ANI_ERR_LOG("key %{public}s not found in createOptionsMap", key.c_str());
276             continue;
277         }
278     }
279 
280     return true;
281 }
282 
GetResourceType(int32_t value)283 static ResourceType GetResourceType(int32_t value)
284 {
285     ResourceType result = ResourceType::INVALID_RESOURCE;
286     switch (value) {
287         case static_cast<int32_t>(ResourceType::IMAGE_RESOURCE):
288         case static_cast<int32_t>(ResourceType::VIDEO_RESOURCE):
289         case static_cast<int32_t>(ResourceType::PHOTO_PROXY):
290             result = static_cast<ResourceType>(value);
291             break;
292         default:
293             break;
294     }
295     return result;
296 }
297 
CheckWriteOperation(ani_env * env,MediaAssetChangeRequestAni * changeRequest,ResourceType resourceType=ResourceType::INVALID_RESOURCE)298 static bool CheckWriteOperation(ani_env *env, MediaAssetChangeRequestAni* changeRequest,
299     ResourceType resourceType = ResourceType::INVALID_RESOURCE)
300 {
301     if (changeRequest == nullptr) {
302         AniError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "changeRequest is null");
303         return false;
304     }
305 
306     if (changeRequest->IsMovingPhoto()) {
307         if (!changeRequest->CheckMovingPhotoResource(resourceType)) {
308             AniError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "Failed to check resource to add for moving photo");
309             return false;
310         }
311         return true;
312     }
313 
314     if (changeRequest->Contains(AssetChangeOperation::CREATE_FROM_URI) ||
315         changeRequest->Contains(AssetChangeOperation::GET_WRITE_CACHE_HANDLER) ||
316         changeRequest->Contains(AssetChangeOperation::ADD_RESOURCE)) {
317         AniError::ThrowError(env, JS_E_OPERATION_NOT_SUPPORT,
318             "The previous asset creation/modification request has not been applied");
319         return false;
320     }
321     return true;
322 }
323 
HasWritePermission()324 static bool HasWritePermission()
325 {
326     AccessTokenID tokenCaller = IPCSkeleton::GetSelfTokenID();
327     int result = AccessTokenKit::VerifyAccessToken(tokenCaller, PERM_WRITE_IMAGEVIDEO);
328     return result == PermissionState::PERMISSION_GRANTED;
329 }
330 
CheckMovingPhotoCreationArgs(std::unique_ptr<MediaAssetChangeRequestAniContext> & context)331 static bool CheckMovingPhotoCreationArgs(std::unique_ptr<MediaAssetChangeRequestAniContext>& context)
332 {
333     ANI_CHECK_RETURN_RET_LOG(context != nullptr, false, "context is null");
334     bool isValid = false;
335     int32_t mediaType = context->valuesBucket.Get(MEDIA_DATA_DB_MEDIA_TYPE, isValid);
336     if (!isValid) {
337         ANI_ERR_LOG("Failed to get media type");
338         return false;
339     }
340 
341     if (mediaType != static_cast<int32_t>(MEDIA_TYPE_IMAGE)) {
342         ANI_ERR_LOG("Failed to check media type (%{public}d) for moving photo", mediaType);
343         return false;
344     }
345 
346     std::string extension = context->valuesBucket.Get(ASSET_EXTENTION, isValid);
347     if (isValid) {
348         return MediaFileUtils::CheckMovingPhotoExtension(extension);
349     }
350 
351     std::string displayName = context->valuesBucket.Get(MEDIA_DATA_DB_NAME, isValid);
352     return isValid && MediaFileUtils::CheckMovingPhotoExtension(MediaFileUtils::GetExtensionFromPath(displayName));
353 }
354 
CheckCreateOption(std::unique_ptr<MediaAssetChangeRequestAniContext> & context,bool isSystemApi)355 static bool CheckCreateOption(std::unique_ptr<MediaAssetChangeRequestAniContext>& context, bool isSystemApi)
356 {
357     ANI_CHECK_RETURN_RET_LOG(context != nullptr, false, "context is null");
358     bool isValid = false;
359     int32_t subtype = context->valuesBucket.Get(PhotoColumn::PHOTO_SUBTYPE, isValid);
360     if (isValid) {
361         if (subtype < static_cast<int32_t>(PhotoSubType::DEFAULT) ||
362             subtype >= static_cast<int32_t>(PhotoSubType::SUBTYPE_END)) {
363             ANI_ERR_LOG("Failed to check subtype: %{public}d", subtype);
364             return false;
365         }
366 
367         // check media type and extension for moving photo
368         if (subtype == static_cast<int32_t>(PhotoSubType::MOVING_PHOTO) &&
369             !CheckMovingPhotoCreationArgs(context)) {
370             ANI_ERR_LOG("Failed to check creation args for moving photo");
371             return false;
372         }
373 
374         // check subtype for public api
375         if (!isSystemApi && subtype != static_cast<int32_t>(PhotoSubType::DEFAULT) &&
376             subtype != static_cast<int32_t>(PhotoSubType::MOVING_PHOTO)) {
377             ANI_ERR_LOG("Failed to check subtype: %{public}d", subtype);
378             return false;
379         }
380     }
381 
382     std::string cameraShotKey = context->valuesBucket.Get(PhotoColumn::CAMERA_SHOT_KEY, isValid);
383     if (isValid) {
384         if (cameraShotKey.size() < CAMERA_SHOT_KEY_SIZE) {
385             ANI_ERR_LOG("cameraShotKey is not null but is less than CAMERA_SHOT_KEY_SIZE");
386             return false;
387         }
388         if (subtype == static_cast<int32_t>(PhotoSubType::SCREENSHOT)) {
389             ANI_ERR_LOG("cameraShotKey is not null with subtype is SCREENSHOT");
390             return false;
391         } else {
392             context->valuesBucket.Put(PhotoColumn::PHOTO_SUBTYPE, static_cast<int32_t>(PhotoSubType::CAMERA));
393         }
394     }
395     return true;
396 }
397 
398 static const unordered_map<MovingPhotoEffectMode, unordered_map<ResourceType, bool>> EFFECT_MODE_RESOURCE_CHECK = {
399     { MovingPhotoEffectMode::DEFAULT,
400         { { ResourceType::IMAGE_RESOURCE, false }, { ResourceType::VIDEO_RESOURCE, false } } },
401     { MovingPhotoEffectMode::BOUNCE_PLAY,
402         { { ResourceType::IMAGE_RESOURCE, false }, { ResourceType::VIDEO_RESOURCE, true } } },
403     { MovingPhotoEffectMode::LOOP_PLAY,
404         { { ResourceType::IMAGE_RESOURCE, false }, { ResourceType::VIDEO_RESOURCE, true } } },
405     { MovingPhotoEffectMode::CINEMA_GRAPH,
406         { { ResourceType::IMAGE_RESOURCE, false }, { ResourceType::VIDEO_RESOURCE, true } } },
407     { MovingPhotoEffectMode::LONG_EXPOSURE,
408         { { ResourceType::IMAGE_RESOURCE, true }, { ResourceType::VIDEO_RESOURCE, false } } },
409     { MovingPhotoEffectMode::MULTI_EXPOSURE,
410         { { ResourceType::IMAGE_RESOURCE, true }, { ResourceType::VIDEO_RESOURCE, false } } },
411     { MovingPhotoEffectMode::IMAGE_ONLY,
412         { { ResourceType::IMAGE_RESOURCE, false }, { ResourceType::VIDEO_RESOURCE, false } } },
413 };
414 
CheckEffectModeWriteOperation()415 bool MediaAssetChangeRequestAni::CheckEffectModeWriteOperation()
416 {
417     if (fileAsset_ == nullptr) {
418         ANI_ERR_LOG("fileAsset is nullptr");
419         return false;
420     }
421 
422     if (fileAsset_->GetTimePending() != 0) {
423         ANI_ERR_LOG("Failed to check pending of fileAsset: %{public}" PRId64, fileAsset_->GetTimePending());
424         return false;
425     }
426 
427     MovingPhotoEffectMode effectMode = static_cast<MovingPhotoEffectMode>(fileAsset_->GetMovingPhotoEffectMode());
428     auto iter = EFFECT_MODE_RESOURCE_CHECK.find(effectMode);
429     if (iter == EFFECT_MODE_RESOURCE_CHECK.end()) {
430         ANI_ERR_LOG("Failed to check effect mode: %{public}d", static_cast<int32_t>(effectMode));
431         return false;
432     }
433 
434     bool isImageExist = ContainsResource(ResourceType::IMAGE_RESOURCE);
435     bool isVideoExist = ContainsResource(ResourceType::VIDEO_RESOURCE);
436     if (iter->second.at(ResourceType::IMAGE_RESOURCE) && !isImageExist) {
437         ANI_ERR_LOG("Failed to check image resource for effect mode: %{public}d", static_cast<int32_t>(effectMode));
438         return false;
439     }
440     if (iter->second.at(ResourceType::VIDEO_RESOURCE) && !isVideoExist) {
441         ANI_ERR_LOG("Failed to check video resource for effect mode: %{public}d", static_cast<int32_t>(effectMode));
442         return false;
443     }
444     return true;
445 }
446 
CheckMovingPhotoWriteOperation()447 bool MediaAssetChangeRequestAni::CheckMovingPhotoWriteOperation()
448 {
449     if (Contains(AssetChangeOperation::SET_MOVING_PHOTO_EFFECT_MODE)) {
450         return CheckEffectModeWriteOperation();
451     }
452 
453     bool containsAddResource = Contains(AssetChangeOperation::ADD_RESOURCE);
454     if (!containsAddResource) {
455         return true;
456     }
457 
458     bool isCreation = Contains(AssetChangeOperation::CREATE_FROM_SCRATCH);
459     if (!isCreation) {
460         return true;
461     }
462 
463     int addResourceTimes =
464         std::count(assetChangeOperations_.begin(), assetChangeOperations_.end(), AssetChangeOperation::ADD_RESOURCE);
465     bool isImageExist = ContainsResource(ResourceType::IMAGE_RESOURCE);
466     bool isVideoExist = ContainsResource(ResourceType::VIDEO_RESOURCE);
467     return addResourceTimes == 2 && isImageExist && isVideoExist; // must add resource 2 times with image and video
468 }
469 
CheckChangeOperations(ani_env * env)470 bool MediaAssetChangeRequestAni::CheckChangeOperations(ani_env *env)
471 {
472     if (assetChangeOperations_.empty()) {
473         AniError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "None request to apply");
474         return false;
475     }
476 
477     bool isCreateFromScratch = Contains(AssetChangeOperation::CREATE_FROM_SCRATCH);
478     bool isCreateFromUri = Contains(AssetChangeOperation::CREATE_FROM_URI);
479     bool containsEdit = Contains(AssetChangeOperation::SET_EDIT_DATA);
480     bool containsGetHandler = Contains(AssetChangeOperation::GET_WRITE_CACHE_HANDLER);
481     bool containsAddResource = Contains(AssetChangeOperation::ADD_RESOURCE);
482     bool isSaveCameraPhoto = Contains(AssetChangeOperation::SAVE_CAMERA_PHOTO);
483     if ((isCreateFromScratch || containsEdit) && !containsGetHandler && !containsAddResource && !isSaveCameraPhoto) {
484         AniError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "Cannot create or edit asset without data to write");
485         return false;
486     }
487 
488     if (containsEdit && (isCreateFromScratch || isCreateFromUri)) {
489         AniError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "Cannot create together with edit");
490         return false;
491     }
492 
493     auto fileAsset = GetFileAssetInstance();
494     if (fileAsset == nullptr) {
495         AniError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "fileAsset is null");
496         return false;
497     }
498 
499     AssetChangeOperation firstOperation = assetChangeOperations_.front();
500     if (fileAsset->GetId() <= 0 && firstOperation != AssetChangeOperation::CREATE_FROM_SCRATCH &&
501         firstOperation != AssetChangeOperation::CREATE_FROM_URI) {
502         AniError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "Invalid asset change request");
503         return false;
504     }
505 
506     bool isMovingPhoto = IsMovingPhoto();
507     if (isMovingPhoto && !CheckMovingPhotoWriteOperation()) {
508         AniError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "Invalid write operation for moving photo");
509         return false;
510     }
511 
512     return true;
513 }
514 
GetFileAssetInstance() const515 std::shared_ptr<FileAsset> MediaAssetChangeRequestAni::GetFileAssetInstance() const
516 {
517     return fileAsset_;
518 }
519 
GetPhotoProxyObj()520 sptr<PhotoProxy> MediaAssetChangeRequestAni::GetPhotoProxyObj()
521 {
522     return photoProxy_;
523 }
524 
ReleasePhotoProxyObj()525 void MediaAssetChangeRequestAni::ReleasePhotoProxyObj()
526 {
527     photoProxy_->Release();
528     photoProxy_ = nullptr;
529 }
530 
FetchAddCacheFileId()531 uint32_t MediaAssetChangeRequestAni::FetchAddCacheFileId()
532 {
533     uint32_t id = cacheFileId_.fetch_add(1);
534     return id;
535 }
536 
SetCacheFileName(string & fileName)537 void MediaAssetChangeRequestAni::SetCacheFileName(string& fileName)
538 {
539     cacheFileName_ = fileName;
540 }
541 
SetCacheMovingPhotoVideoName(string & fileName)542 void MediaAssetChangeRequestAni::SetCacheMovingPhotoVideoName(string& fileName)
543 {
544     cacheMovingPhotoVideoName_ = fileName;
545 }
546 
GetFileRealPath() const547 string MediaAssetChangeRequestAni::GetFileRealPath() const
548 {
549     return realPath_;
550 }
GetAddResourceMode() const551 AddResourceMode MediaAssetChangeRequestAni::GetAddResourceMode() const
552 {
553     return addResourceMode_;
554 }
555 
GetDataBuffer() const556 void* MediaAssetChangeRequestAni::GetDataBuffer() const
557 {
558     return dataBuffer_;
559 }
560 
GetDataBufferSize() const561 size_t MediaAssetChangeRequestAni::GetDataBufferSize() const
562 {
563     return dataBufferSize_;
564 }
565 
GetMovingPhotoVideoPath() const566 string MediaAssetChangeRequestAni::GetMovingPhotoVideoPath() const
567 {
568     return movingPhotoVideoRealPath_;
569 }
570 
GetMovingPhotoVideoMode() const571 AddResourceMode MediaAssetChangeRequestAni::GetMovingPhotoVideoMode() const
572 {
573     return movingPhotoVideoResourceMode_;
574 }
575 
GetMovingPhotoVideoBuffer() const576 void* MediaAssetChangeRequestAni::GetMovingPhotoVideoBuffer() const
577 {
578     return movingPhotoVideoDataBuffer_;
579 }
580 
GetMovingPhotoVideoSize() const581 size_t MediaAssetChangeRequestAni::GetMovingPhotoVideoSize() const
582 {
583     return movingPhotoVideoBufferSize_;
584 }
585 
RecordChangeOperation(AssetChangeOperation changeOperation)586 void MediaAssetChangeRequestAni::RecordChangeOperation(AssetChangeOperation changeOperation)
587 {
588     if ((changeOperation == AssetChangeOperation::GET_WRITE_CACHE_HANDLER ||
589             changeOperation == AssetChangeOperation::ADD_RESOURCE ||
590             changeOperation == AssetChangeOperation::ADD_FILTERS) &&
591         Contains(AssetChangeOperation::CREATE_FROM_SCRATCH)) {
592         assetChangeOperations_.insert(assetChangeOperations_.begin() + 1, changeOperation);
593         return;
594     }
595     if (changeOperation == AssetChangeOperation::ADD_RESOURCE &&
596         Contains(AssetChangeOperation::SET_MOVING_PHOTO_EFFECT_MODE)) {
597         assetChangeOperations_.insert(assetChangeOperations_.begin(), changeOperation);
598         return;
599     }
600     assetChangeOperations_.push_back(changeOperation);
601 }
602 
Contains(AssetChangeOperation changeOperation) const603 bool MediaAssetChangeRequestAni::Contains(AssetChangeOperation changeOperation) const
604 {
605     return std::find(assetChangeOperations_.begin(), assetChangeOperations_.end(), changeOperation) !=
606         assetChangeOperations_.end();
607 }
608 
ContainsResource(ResourceType resourceType) const609 bool MediaAssetChangeRequestAni::ContainsResource(ResourceType resourceType) const
610 {
611     return std::find(addResourceTypes_.begin(), addResourceTypes_.end(), resourceType) != addResourceTypes_.end();
612 }
613 
IsMovingPhoto() const614 bool MediaAssetChangeRequestAni::IsMovingPhoto() const
615 {
616     return fileAsset_ != nullptr &&
617         (fileAsset_->GetPhotoSubType() == static_cast<int32_t>(PhotoSubType::MOVING_PHOTO) ||
618         (fileAsset_->GetPhotoSubType() == static_cast<int32_t>(PhotoSubType::DEFAULT) &&
619         fileAsset_->GetMovingPhotoEffectMode() == static_cast<int32_t>(MovingPhotoEffectMode::IMAGE_ONLY)));
620 }
621 
CheckMovingPhotoResource(ResourceType resourceType) const622 bool MediaAssetChangeRequestAni::CheckMovingPhotoResource(ResourceType resourceType) const
623 {
624     if (resourceType == ResourceType::INVALID_RESOURCE) {
625         ANI_ERR_LOG("Invalid resource type");
626         return false;
627     }
628 
629     bool isResourceTypeVaild = !ContainsResource(resourceType);
630     int addResourceTimes =
631         std::count(assetChangeOperations_.begin(), assetChangeOperations_.end(), AssetChangeOperation::ADD_RESOURCE);
632     return isResourceTypeVaild && addResourceTimes <= 1; // currently, add resource no more than once
633 }
634 
ParseArgsCreateAssetSystem(ani_env * env,ani_string displayName,ani_object photoCreateOptions,std::unique_ptr<MediaAssetChangeRequestAniContext> & context)635 static bool ParseArgsCreateAssetSystem(ani_env *env, ani_string displayName,
636     ani_object photoCreateOptions, std::unique_ptr<MediaAssetChangeRequestAniContext>& context)
637 {
638     std::string displayNameStr;
639     CHECK_COND_WITH_RET_MESSAGE(env,
640         MediaLibraryAniUtils::GetParamStringPathMax(env, displayName, displayNameStr) == ANI_OK, false,
641         "Failed to get displayName");
642     CHECK_COND_WITH_RET_MESSAGE(env, MediaFileUtils::CheckDisplayName(displayNameStr) == E_OK, false,
643         "Failed to check displayName");
644     MediaType mediaType = MediaFileUtils::GetMediaType(displayNameStr);
645     CHECK_COND_WITH_RET_MESSAGE(env, mediaType == MEDIA_TYPE_IMAGE || mediaType == MEDIA_TYPE_VIDEO, false,
646         "Invalid file type");
647     context->valuesBucket.Put(MEDIA_DATA_DB_NAME, displayNameStr);
648     context->valuesBucket.Put(MEDIA_DATA_DB_MEDIA_TYPE, static_cast<int32_t>(mediaType));
649 
650     ani_boolean isUndefined;
651     env->Reference_IsUndefined(photoCreateOptions, &isUndefined);
652     if (isUndefined) {
653         ANI_INFO_LOG("PhotoCreateOptions is undefined. There is no need to parse create options");
654         return true;
655     }
656     auto optionsMap = MediaLibraryAniUtils::GetPhotoCreateOptions(env, photoCreateOptions);
657     // parse photo create options
658     if (ParseCreateOptions(context, optionsMap, PHOTO_CREATE_OPTIONS_PARAM)) {
659         CheckCreateOption(context, false);
660     }
661     return true;
662 }
663 
ParseArgsCreateAssetCommon(ani_env * env,ani_enum_item photoType,ani_string extension,ani_object createOptions,std::unique_ptr<MediaAssetChangeRequestAniContext> & context)664 static bool ParseArgsCreateAssetCommon(ani_env *env, ani_enum_item photoType, ani_string extension,
665     ani_object createOptions, std::unique_ptr<MediaAssetChangeRequestAniContext>& context)
666 {
667     // Parse photoType.
668     MediaType mediaType;
669     int32_t mediaTypeInt;
670     ANI_CHECK_RETURN_RET_LOG(MediaLibraryEnumAni::EnumGetValueInt32(
671         env, photoType, mediaTypeInt) == ANI_OK, false, "Failed to get photoType");
672     mediaType = static_cast<MediaType>(mediaTypeInt);
673     ANI_CHECK_RETURN_RET_LOG(mediaType == MEDIA_TYPE_IMAGE || mediaType == MEDIA_TYPE_VIDEO,
674         false, "Invalid photoType");
675 
676     // Parse extension.
677     std::string extensionStr;
678     ANI_CHECK_RETURN_RET_LOG(
679         MediaLibraryAniUtils::GetParamStringPathMax(env, extension, extensionStr) == ANI_OK, false,
680         "Failed to get extension");
681     ANI_CHECK_RETURN_RET_LOG(mediaType == MediaFileUtils::GetMediaType("." + extensionStr), false,
682         "Failed to check extension");
683     context->valuesBucket.Put(ASSET_EXTENTION, extensionStr);
684     context->valuesBucket.Put(MEDIA_DATA_DB_MEDIA_TYPE, static_cast<int32_t>(mediaType));
685 
686     // Parse options if exists.
687     ani_boolean isUndefined;
688     env->Reference_IsUndefined(createOptions, &isUndefined);
689     if (!isUndefined) {
690         auto optionsMap = MediaLibraryAniUtils::GetCreateOptions(env, createOptions);
691         ParseCreateOptions(context, optionsMap, CREATE_OPTIONS_PARAM);
692         CheckCreateOption(context, true);
693     }
694 
695     bool isValid = false;
696     std::string title = context->valuesBucket.Get(PhotoColumn::MEDIA_TITLE, isValid);
697     if (!isValid) {
698         title = mediaType == MEDIA_TYPE_IMAGE ? DEFAULT_TITLE_IMG_PREFIX : DEFAULT_TITLE_VIDEO_PREFIX;
699         title += MediaFileUtils::StrCreateTime(DEFAULT_TITLE_TIME_FORMAT, MediaFileUtils::UTCTimeSeconds());
700         context->valuesBucket.Put(PhotoColumn::MEDIA_TITLE, title);
701     }
702 
703     std::string displayName = title + "." + extensionStr;
704     CHECK_COND_WITH_RET_MESSAGE(env, MediaFileUtils::CheckDisplayName(displayName) == E_OK, false,
705         "Failed to check displayName");
706     context->valuesBucket.Put(MEDIA_DATA_DB_NAME, displayName);
707     return true;
708 }
709 
CreateAssetRequestCommon(ani_env * env,std::unique_ptr<MediaAssetChangeRequestAniContext> & context)710 ani_object MediaAssetChangeRequestAni::CreateAssetRequestCommon(
711     ani_env *env, std::unique_ptr<MediaAssetChangeRequestAniContext>& context)
712 {
713     bool isValid = false;
714     std::string displayName = context->valuesBucket.Get(MEDIA_DATA_DB_NAME, isValid);
715     int32_t subtype = context->valuesBucket.Get(PhotoColumn::PHOTO_SUBTYPE, isValid); // default is 0
716     auto emptyFileAsset = std::make_unique<FileAsset>();
717     emptyFileAsset->SetDisplayName(displayName);
718     emptyFileAsset->SetTitle(MediaFileUtils::GetTitleFromDisplayName(displayName));
719     emptyFileAsset->SetMediaType(MediaFileUtils::GetMediaType(displayName));
720     emptyFileAsset->SetPhotoSubType(subtype);
721     emptyFileAsset->SetTimePending(CREATE_ASSET_REQUEST_PENDING);
722     emptyFileAsset->SetResultNapiType(ResultNapiType::TYPE_PHOTOACCESS_HELPER);
723     FileAssetAni* fileAssetAni = FileAssetAni::CreateFileAsset(env, emptyFileAsset);
724     CHECK_COND_WITH_RET_MESSAGE(env, fileAssetAni != nullptr, nullptr, "Failed to create file asset");
725 
726     auto changeRequest = std::make_unique<MediaAssetChangeRequestAni>(fileAssetAni);
727     changeRequest->creationValuesBucket_ = std::move(context->valuesBucket);
728     changeRequest->RecordChangeOperation(AssetChangeOperation::CREATE_FROM_SCRATCH);
729     return Wrap(env, changeRequest.release());
730 }
731 
createAssetRequestSystem(ani_env * env,ani_object context,ani_string displayName,ani_object photoCreateOptions)732 ani_object MediaAssetChangeRequestAni::createAssetRequestSystem(ani_env *env, ani_object context,
733     ani_string displayName, ani_object photoCreateOptions)
734 {
735     CHECK_COND_WITH_RET_MESSAGE(env, MediaLibraryAniUtils::IsSystemApp(), nullptr,
736         "This interface can be called only by system apps");
737     auto aniContext = std::make_unique<MediaAssetChangeRequestAniContext>();
738     CHECK_COND_WITH_RET_MESSAGE(env, ParseArgsCreateAssetSystem(
739         env, displayName, photoCreateOptions, aniContext), nullptr, "Failed to parse create options");
740     return CreateAssetRequestCommon(env, aniContext);
741 }
742 
createAssetRequest(ani_env * env,ani_object context,ani_enum_item photoType,ani_string extension,ani_object createOptions)743 ani_object MediaAssetChangeRequestAni::createAssetRequest(ani_env *env, ani_object context,
744     ani_enum_item photoType, ani_string extension, ani_object createOptions)
745 {
746     CHECK_COND_WITH_RET_MESSAGE(env, MediaLibraryAniUtils::IsSystemApp(), nullptr,
747         "This interface can be called only by system apps");
748     auto aniContext = std::make_unique<MediaAssetChangeRequestAniContext>();
749     CHECK_COND_WITH_RET_MESSAGE(env, ParseArgsCreateAssetCommon(
750         env, photoType, extension, createOptions, aniContext), nullptr, "Failed to parse create options");
751     return CreateAssetRequestCommon(env, aniContext);
752 }
753 
ParseFileUri(ani_env * env,ani_object fileUriAni,MediaType mediaType,std::unique_ptr<MediaAssetChangeRequestAniContext> & context)754 static ani_object ParseFileUri(ani_env *env, ani_object fileUriAni, MediaType mediaType,
755     std::unique_ptr<MediaAssetChangeRequestAniContext>& context)
756 {
757     std::string fileUriStr;
758     CHECK_COND_WITH_MESSAGE(env, MediaLibraryAniUtils::GetParamStringPathMax(env, fileUriAni, fileUriStr) == ANI_OK,
759         "Failed to get fileUri");
760     OHOS::AppFileService::ModuleFileUri::FileUri fileUri(fileUriStr);
761     std::string path = fileUri.GetRealPath();
762     CHECK_COND(env, OHOS::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 reinterpret_cast<ani_object>(true);
766 }
767 
ParseArgsCreateAssetFromFileUri(ani_env * env,ani_object fileUriAni,MediaType mediaType,std::unique_ptr<MediaAssetChangeRequestAniContext> & context)768 static ani_object ParseArgsCreateAssetFromFileUri(ani_env *env, ani_object fileUriAni, MediaType mediaType,
769     std::unique_ptr<MediaAssetChangeRequestAniContext>& context)
770 {
771     return ParseFileUri(env, fileUriAni, mediaType, context);
772 }
773 
CreateAssetRequestFromRealPath(ani_env * env,const std::string & realPath)774 ani_object MediaAssetChangeRequestAni::CreateAssetRequestFromRealPath(ani_env *env, const std::string &realPath)
775 {
776     std::string displayName = MediaFileUtils::GetFileName(realPath);
777     CHECK_COND_WITH_MESSAGE(env, MediaFileUtils::CheckDisplayName(displayName) == E_OK, "Invalid fileName");
778     std::string title = MediaFileUtils::GetTitleFromDisplayName(displayName);
779     MediaType mediaType = MediaFileUtils::GetMediaType(displayName);
780     auto emptyFileAsset = std::make_unique<FileAsset>();
781     emptyFileAsset->SetDisplayName(displayName);
782     emptyFileAsset->SetTitle(title);
783     emptyFileAsset->SetMediaType(mediaType);
784     emptyFileAsset->SetPhotoSubType(static_cast<int32_t>(PhotoSubType::DEFAULT));
785     emptyFileAsset->SetTimePending(CREATE_ASSET_REQUEST_PENDING);
786     emptyFileAsset->SetResultNapiType(ResultNapiType::TYPE_PHOTOACCESS_HELPER);
787     FileAssetAni* fileAssetAni = FileAssetAni::CreateFileAsset(env, emptyFileAsset);
788     ANI_CHECK_RETURN_RET_LOG(fileAssetAni != nullptr, nullptr, "context is null");
789     auto changeRequest = std::make_unique<MediaAssetChangeRequestAni>(fileAssetAni);
790     changeRequest->realPath_ = realPath;
791     changeRequest->creationValuesBucket_.Put(MEDIA_DATA_DB_NAME, displayName);
792     changeRequest->creationValuesBucket_.Put(ASSET_EXTENTION, MediaFileUtils::GetExtensionFromPath(displayName));
793     changeRequest->creationValuesBucket_.Put(MEDIA_DATA_DB_MEDIA_TYPE, static_cast<int32_t>(mediaType));
794     changeRequest->creationValuesBucket_.Put(PhotoColumn::MEDIA_TITLE, title);
795     changeRequest->RecordChangeOperation(AssetChangeOperation::CREATE_FROM_URI);
796     return Wrap(env, changeRequest.release());
797 }
798 
createImageAssetRequest(ani_env * env,ani_object context,ani_string fileUri)799 ani_object MediaAssetChangeRequestAni::createImageAssetRequest(ani_env *env, ani_object context, ani_string fileUri)
800 {
801     auto aniContext = std::make_unique<MediaAssetChangeRequestAniContext>();
802     CHECK_COND_WITH_MESSAGE(env, ParseArgsCreateAssetFromFileUri(env,
803         fileUri, MediaType::MEDIA_TYPE_IMAGE, aniContext), "Failed to parse args");
804     return CreateAssetRequestFromRealPath(env, aniContext->realPath);
805 }
806 
createVideoAssetRequest(ani_env * env,ani_object context,ani_string fileUri)807 ani_object MediaAssetChangeRequestAni::createVideoAssetRequest(ani_env *env, ani_object context, ani_string fileUri)
808 {
809     auto aniContext = std::make_unique<MediaAssetChangeRequestAniContext>();
810     CHECK_COND_WITH_MESSAGE(env, ParseArgsCreateAssetFromFileUri(env,
811         fileUri, MediaType::MEDIA_TYPE_VIDEO, aniContext), "Failed to parse args");
812     return CreateAssetRequestFromRealPath(env, aniContext->realPath);
813 }
814 
getAsset(ani_env * env,ani_object aniObject)815 ani_object MediaAssetChangeRequestAni::getAsset(ani_env *env, ani_object aniObject)
816 {
817     auto aniContext = std::make_unique<MediaAssetChangeRequestAniContext>();
818     aniContext->objectInfo = Unwrap(env, aniObject);
819     auto changeRequest = aniContext->objectInfo;
820     CHECK_COND_WITH_RET_MESSAGE(env, changeRequest != nullptr, nullptr, "changeRequest is null");
821     auto fileAsset = changeRequest->GetFileAssetInstance();
822     CHECK_COND(env, fileAsset != nullptr, JS_INNER_FAIL);
823 
824     if (fileAsset->GetId() > 0) {
825         return FileAssetAni::Wrap(env, FileAssetAni::CreatePhotoAsset(env, fileAsset));
826     }
827     return nullptr;
828 }
829 
830 #ifdef HAS_ACE_ENGINE_PART
initDeleteRequest(napi_env env,MediaAssetChangeRequestAniContext & context,OHOS::AAFwk::Want & request,shared_ptr<DeleteCallback> & callback)831 static napi_value initDeleteRequest(napi_env env, MediaAssetChangeRequestAniContext& context,
832     OHOS::AAFwk::Want& request, shared_ptr<DeleteCallback>& callback)
833 {
834     request.SetElementName(DELETE_UI_PACKAGE_NAME, DELETE_UI_EXT_ABILITY_NAME);
835     request.SetParam(DELETE_UI_EXTENSION_TYPE, DELETE_UI_REQUEST_TYPE);
836 
837     CHECK_COND(env, !context.appName.empty(), JS_INNER_FAIL);
838     request.SetParam(DELETE_UI_APPNAME, context.appName);
839 
840     request.SetParam(DELETE_UI_URIS, context.uris);
841     callback->SetUris(context.uris);
842 
843     napi_valuetype valueType = napi_undefined;
844     CHECK_COND_WITH_MESSAGE(env, context.argc >= ARGS_THREE && context.argc <= ARGS_FOUR, "Failed to check args");
845     napi_value func = context.argv[PARAM1];
846     if (ANI_OK != napi_typeof(env, func, &valueType)) {
847         AniError::ThrowError(env, JS_INNER_FAIL);
848         return nullptr;
849     }
850     CHECK_COND_WITH_MESSAGE(env, valueType == napi_function, "Failed to check args");
851     callback->SetFunc(func);
852     RETURN_NAPI_TRUE(env);
853 }
854 #endif
855 
DeleteAssetsExecute(ani_env * env,std::unique_ptr<MediaAssetChangeRequestAniContext> & context)856 static void DeleteAssetsExecute(ani_env *env, std::unique_ptr<MediaAssetChangeRequestAniContext>& context)
857 {
858     MediaLibraryTracer tracer;
859     tracer.Start("DeleteAssetsExecute");
860 
861     string trashUri = PAH_TRASH_PHOTO;
862     MediaLibraryAniUtils::UriAppendKeyValue(trashUri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
863     Uri updateAssetUri(trashUri);
864     int32_t changedRows = UserFileClient::Update(updateAssetUri, context->predicates, context->valuesBucket);
865     if (changedRows < 0) {
866         context->SaveError(changedRows);
867         ANI_ERR_LOG("Failed to delete assets, err: %{public}d", changedRows);
868     }
869 }
870 
DeleteAssetsCommon(ani_env * env,std::unique_ptr<MediaAssetChangeRequestAniContext> & aniContext,std::vector<std::string> & uris)871 static ani_object DeleteAssetsCommon(ani_env *env, std::unique_ptr<MediaAssetChangeRequestAniContext>& aniContext,
872     std::vector<std::string>& uris)
873 {
874     CHECK_COND_WITH_MESSAGE(env, !uris.empty(), "Failed to check empty array");
875     for (const auto& uri : uris) {
876         CHECK_COND(env, uri.find(PhotoColumn::PHOTO_URI_PREFIX) != string::npos, JS_E_URI);
877     }
878 
879     ANI_INFO_LOG("DeleteAssetsExecute size:%{public}zu", uris.size());
880     aniContext->predicates.In(PhotoColumn::MEDIA_ID, uris);
881     aniContext->valuesBucket.Put(PhotoColumn::MEDIA_DATE_TRASHED, MediaFileUtils::UTCTimeSeconds());
882     aniContext->uris.assign(uris.begin(), uris.end());
883 
884     // Delete assets
885     if (MediaLibraryAniUtils::IsSystemApp()) {
886         DeleteAssetsExecute(env, aniContext);
887         return nullptr;
888     }
889 #ifdef HAS_ACE_ENGINE_PART
890     // Deletion control by ui extension
891     CHECK_COND(env, HasWritePermission(), OHOS_PERMISSION_DENIED_CODE);
892     CHECK_COND_WITH_MESSAGE(
893         env, aniContext->uris.size() <= MAX_DELETE_NUMBER, "No more than 300 assets can be deleted at one time");
894     auto context = OHOS::AbilityRuntime::GetStageModeContext(env, aniContext->argv[PARAM0]);
895     CHECK_COND_WITH_MESSAGE(env, context != nullptr, "Failed to get stage mode context");
896     auto abilityContext = OHOS::AbilityRuntime::Context::ConvertTo<OHOS::AbilityRuntime::AbilityContext>(context);
897     CHECK_COND(env, abilityContext != nullptr, JS_INNER_FAIL);
898     auto abilityInfo = abilityContext->GetAbilityInfo();
899     abilityContext->GetResourceManager()->GetStringById(abilityInfo->labelId, aniContext->appName);
900     auto uiContent = abilityContext->GetUIContent();
901     CHECK_COND(env, uiContent != nullptr, JS_INNER_FAIL);
902 
903     auto callback = std::make_shared<DeleteCallback>(env, uiContent);
904     OHOS::Ace::ModalUIExtensionCallbacks extensionCallback = {
905         ([callback](auto arg) { callback->OnRelease(arg); }),
906         ([callback](auto arg1, auto arg2) { callback->OnResult(arg1, arg2); }),
907         ([callback](auto arg) { callback->OnReceive(arg); }),
908         ([callback](auto arg1, auto arg2, auto arg3) { callback->OnError(arg1, arg2, arg3); }),
909     };
910     OHOS::Ace::ModalUIExtensionConfig config;
911     config.isProhibitBack = true;
912     OHOS::AAFwk::Want request;
913     CHECK_COND(env, initDeleteRequest(env, *aniContext, request, callback), JS_INNER_FAIL);
914 
915     int32_t sessionId = uiContent->CreateModalUIExtension(request, extensionCallback, config);
916     CHECK_COND(env, sessionId != 0, JS_INNER_FAIL);
917     callback->SetSessionId(sessionId);
918     RETURN_NAPI_UNDEFINED(env);
919 #else
920     AniError::ThrowError(env, JS_INNER_FAIL, "ace_engine is not support");
921     return nullptr;
922 #endif
923 }
924 
deleteAssetsByPhotoAsset(ani_env * env,ani_object context,ani_object assets)925 ani_object MediaAssetChangeRequestAni::deleteAssetsByPhotoAsset(ani_env *env, ani_object context, ani_object assets)
926 {
927     MediaLibraryTracer tracer;
928     tracer.Start("JSDeleteAssets");
929 
930     auto aniContext = make_unique<MediaAssetChangeRequestAniContext>();
931     std::vector<std::string> uris;
932     CHECK_COND(env, MediaAssetChangeRequestAni::InitUserFileClient(env, context), JS_INNER_FAIL);
933     CHECK_COND_WITH_MESSAGE(env, MediaLibraryAniUtils::GetUriArrayFromAssets(env, assets, uris) == ANI_OK,
934         "Failed to get uri array from assets");
935     return DeleteAssetsCommon(env, aniContext, uris);
936 }
937 
deleteAssetsByUriList(ani_env * env,ani_object context,ani_object uriList)938 ani_object MediaAssetChangeRequestAni::deleteAssetsByUriList(ani_env *env, ani_object context, ani_object uriList)
939 {
940     MediaLibraryTracer tracer;
941     tracer.Start("JSDeleteAssets");
942 
943     auto aniContext = make_unique<MediaAssetChangeRequestAniContext>();
944     std::vector<std::string> uris;
945     CHECK_COND(env, MediaAssetChangeRequestAni::InitUserFileClient(env, context), JS_INNER_FAIL);
946     CHECK_COND_WITH_MESSAGE(env, MediaLibraryAniUtils::GetStringArray(env, uriList, uris) == ANI_OK,
947         "Failed to get uri array from uriList");
948     return DeleteAssetsCommon(env, aniContext, uris);
949 }
950 
CheckMovingPhotoVideo(void * dataBuffer,size_t size)951 static bool CheckMovingPhotoVideo(void* dataBuffer, size_t size)
952 {
953     MediaLibraryTracer tracer;
954     tracer.Start("CheckMovingPhotoVideo");
955 
956     auto dataSource = make_shared<MediaDataSource>(dataBuffer, static_cast<int64_t>(size));
957     auto avMetadataHelper = AVMetadataHelperFactory::CreateAVMetadataHelper();
958     if (avMetadataHelper == nullptr) {
959         ANI_WARN_LOG("Failed to create AVMetadataHelper, ignore checking duration of moving photo video");
960         return true;
961     }
962 
963     int32_t err = avMetadataHelper->SetSource(dataSource);
964     if (err != E_OK) {
965         ANI_ERR_LOG("SetSource failed for dataSource, err = %{public}d", err);
966         return false;
967     }
968 
969     unordered_map<int32_t, string> resultMap = avMetadataHelper->ResolveMetadata();
970     if (resultMap.find(AV_KEY_DURATION) == resultMap.end()) {
971         ANI_ERR_LOG("AV_KEY_DURATION does not exist");
972         return false;
973     }
974 
975     string durationStr = resultMap.at(AV_KEY_DURATION);
976     int32_t duration = std::atoi(durationStr.c_str());
977     if (!MediaFileUtils::CheckMovingPhotoVideoDuration(duration)) {
978         ANI_ERR_LOG("Failed to check duration of moving photo video: %{public}d ms", duration);
979         return false;
980     }
981     return true;
982 }
983 
AddMovingPhotoVideoResourceByFileUri(ani_env * env,ani_object aniObject,ani_string fileUri)984 ani_object MediaAssetChangeRequestAni::AddMovingPhotoVideoResourceByFileUri(ani_env *env, ani_object aniObject,
985     ani_string fileUri)
986 {
987     MediaLibraryTracer tracer;
988     tracer.Start("AddMovingPhotoVideoResource");
989 
990     auto aniContext = make_unique<MediaAssetChangeRequestAniContext>();
991     aniContext->objectInfo = MediaAssetChangeRequestAni::Unwrap(env, aniObject);
992     auto changeRequest = aniContext->objectInfo;
993     CHECK_COND_WITH_RET_MESSAGE(env, changeRequest != nullptr, nullptr, "changeRequest is null");
994 
995     CHECK_COND(env, ParseFileUri(env, fileUri, MediaType::MEDIA_TYPE_VIDEO, aniContext), OHOS_INVALID_PARAM_CODE);
996     if (!MediaFileUtils::CheckMovingPhotoVideo(aniContext->realPath)) {
997         AniError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "Failed to check video resource of moving photo");
998         return nullptr;
999     }
1000     changeRequest->movingPhotoVideoRealPath_ = aniContext->realPath;
1001     changeRequest->movingPhotoVideoResourceMode_ = AddResourceMode::FILE_URI;
1002     changeRequest->RecordChangeOperation(AssetChangeOperation::ADD_RESOURCE);
1003     changeRequest->addResourceTypes_.push_back(ResourceType::VIDEO_RESOURCE);
1004     return nullptr;
1005 }
1006 
AddMovingPhotoVideoResourceByArrayBuffer(ani_env * env,ani_object aniObject,ani_object arrayBuffer)1007 ani_object MediaAssetChangeRequestAni::AddMovingPhotoVideoResourceByArrayBuffer(ani_env *env, ani_object aniObject,
1008     ani_object arrayBuffer)
1009 {
1010     MediaLibraryTracer tracer;
1011     tracer.Start("AddMovingPhotoVideoResource");
1012 
1013     auto aniContext = make_unique<MediaAssetChangeRequestAniContext>();
1014     aniContext->objectInfo = MediaAssetChangeRequestAni::Unwrap(env, aniObject);
1015     auto changeRequest = aniContext->objectInfo;
1016     CHECK_COND_WITH_RET_MESSAGE(env, changeRequest != nullptr, nullptr, "changeRequest is null");
1017 
1018     std::unique_ptr<uint8_t[]> buffer;
1019     CHECK_COND_WITH_MESSAGE(env, MediaLibraryAniUtils::GetArrayBuffer(
1020         env, arrayBuffer, buffer, changeRequest->movingPhotoVideoBufferSize_) == ANI_OK, "Failed to get data buffer");
1021     changeRequest->movingPhotoVideoDataBuffer_ = buffer.release();
1022     CHECK_COND_WITH_MESSAGE(env, changeRequest->movingPhotoVideoBufferSize_ > 0,
1023         "Failed to check size of data buffer");
1024     if (!CheckMovingPhotoVideo(changeRequest->movingPhotoVideoDataBuffer_,
1025         changeRequest->movingPhotoVideoBufferSize_)) {
1026         AniError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "Failed to check video resource of moving photo");
1027         return nullptr;
1028     }
1029     changeRequest->movingPhotoVideoResourceMode_ = AddResourceMode::DATA_BUFFER;
1030 
1031     changeRequest->RecordChangeOperation(AssetChangeOperation::ADD_RESOURCE);
1032     changeRequest->addResourceTypes_.push_back(ResourceType::VIDEO_RESOURCE);
1033     return nullptr;
1034 }
1035 
addResourceByFileUri(ani_env * env,ani_object aniObject,ani_enum_item resourceTypeItem,ani_string fileUri)1036 ani_object MediaAssetChangeRequestAni::addResourceByFileUri(ani_env *env, ani_object aniObject,
1037     ani_enum_item resourceTypeItem, ani_string fileUri)
1038 {
1039     auto aniContext = make_unique<MediaAssetChangeRequestAniContext>();
1040     aniContext->objectInfo = MediaAssetChangeRequestAni::Unwrap(env, aniObject);
1041     auto changeRequest = aniContext->objectInfo;
1042     CHECK_COND_WITH_RET_MESSAGE(env, changeRequest != nullptr, nullptr, "changeRequest is null");
1043     auto fileAsset = changeRequest->GetFileAssetInstance();
1044     CHECK_COND(env, fileAsset != nullptr, JS_INNER_FAIL);
1045 
1046     int32_t resourceType = static_cast<int32_t>(ResourceType::INVALID_RESOURCE);
1047     CHECK_COND_WITH_MESSAGE(env, MediaLibraryEnumAni::EnumGetValueInt32(
1048         env, resourceTypeItem, resourceType) == ANI_OK, "Failed to get resourceType");
1049     CHECK_COND(env,
1050         CheckWriteOperation(env, changeRequest, GetResourceType(resourceType)), JS_E_OPERATION_NOT_SUPPORT);
1051     if (changeRequest->IsMovingPhoto() && resourceType == static_cast<int32_t>(ResourceType::VIDEO_RESOURCE)) {
1052         return AddMovingPhotoVideoResourceByFileUri(env, aniObject, fileUri);
1053     }
1054     CHECK_COND_WITH_MESSAGE(env, resourceType == static_cast<int32_t>(fileAsset->GetMediaType()),
1055         "Failed to check resourceType");
1056 
1057     CHECK_COND(env, ParseFileUri(env, fileUri, fileAsset->GetMediaType(), aniContext), OHOS_INVALID_PARAM_CODE);
1058     changeRequest->realPath_ = aniContext->realPath;
1059     changeRequest->addResourceMode_ = AddResourceMode::FILE_URI;
1060 
1061     changeRequest->RecordChangeOperation(AssetChangeOperation::ADD_RESOURCE);
1062     changeRequest->addResourceTypes_.push_back(GetResourceType(resourceType));
1063     return nullptr;
1064 }
1065 
addResourceByArrayBuffer(ani_env * env,ani_object aniObject,ani_enum_item resourceTypeItem,ani_object arrayBuffer)1066 ani_object MediaAssetChangeRequestAni::addResourceByArrayBuffer(ani_env *env, ani_object aniObject,
1067     ani_enum_item resourceTypeItem, ani_object arrayBuffer)
1068 {
1069     auto aniContext = make_unique<MediaAssetChangeRequestAniContext>();
1070     aniContext->objectInfo = MediaAssetChangeRequestAni::Unwrap(env, aniObject);
1071     auto changeRequest = aniContext->objectInfo;
1072     CHECK_COND_WITH_RET_MESSAGE(env, changeRequest != nullptr, nullptr, "changeRequest is null");
1073     auto fileAsset = changeRequest->GetFileAssetInstance();
1074     CHECK_COND(env, fileAsset != nullptr, JS_INNER_FAIL);
1075 
1076     int32_t resourceType = static_cast<int32_t>(ResourceType::INVALID_RESOURCE);
1077     CHECK_COND_WITH_MESSAGE(env, MediaLibraryEnumAni::EnumGetValueInt32(
1078         env, resourceTypeItem, resourceType) == ANI_OK, "Failed to get resourceType");
1079     CHECK_COND(env,
1080         CheckWriteOperation(env, changeRequest, GetResourceType(resourceType)), JS_E_OPERATION_NOT_SUPPORT);
1081     if (changeRequest->IsMovingPhoto() && resourceType == static_cast<int32_t>(ResourceType::VIDEO_RESOURCE)) {
1082         return AddMovingPhotoVideoResourceByArrayBuffer(env, aniObject, arrayBuffer);
1083     }
1084     CHECK_COND_WITH_MESSAGE(env, resourceType == static_cast<int32_t>(fileAsset->GetMediaType()),
1085         "Failed to check resourceType");
1086 
1087     std::unique_ptr<uint8_t[]> buffer;
1088     CHECK_COND_WITH_MESSAGE(env, MediaLibraryAniUtils::GetArrayBuffer(
1089         env, arrayBuffer, buffer, changeRequest->dataBufferSize_) == ANI_OK, "Failed to get data buffer");
1090     changeRequest->dataBuffer_ = buffer.release();
1091     CHECK_COND_WITH_MESSAGE(env, changeRequest->dataBufferSize_ > 0, "Failed to check size of data buffer");
1092     changeRequest->addResourceMode_ = AddResourceMode::DATA_BUFFER;
1093 
1094     changeRequest->RecordChangeOperation(AssetChangeOperation::ADD_RESOURCE);
1095     changeRequest->addResourceTypes_.push_back(GetResourceType(resourceType));
1096     return nullptr;
1097 }
1098 
addResourceByPhotoProxy(ani_env * env,ani_object aniObject,ani_enum_item resourceTypeItem,ani_object proxy)1099 ani_object MediaAssetChangeRequestAni::addResourceByPhotoProxy(ani_env *env, ani_object aniObject,
1100     ani_enum_item resourceTypeItem, ani_object proxy)
1101 {
1102     auto aniContext = make_unique<MediaAssetChangeRequestAniContext>();
1103     aniContext->objectInfo = MediaAssetChangeRequestAni::Unwrap(env, aniObject);
1104     auto changeRequest = aniContext->objectInfo;
1105     CHECK_COND_WITH_RET_MESSAGE(env, changeRequest != nullptr, nullptr, "changeRequest is null");
1106     auto fileAsset = changeRequest->GetFileAssetInstance();
1107     CHECK_COND(env, fileAsset != nullptr, JS_INNER_FAIL);
1108 
1109     int32_t resourceType = static_cast<int32_t>(ResourceType::INVALID_RESOURCE);
1110     CHECK_COND_WITH_MESSAGE(env, MediaLibraryEnumAni::EnumGetValueInt32(
1111         env, resourceTypeItem, resourceType) == ANI_OK, "Failed to get resourceType");
1112     CHECK_COND(env,
1113         CheckWriteOperation(env, changeRequest, GetResourceType(resourceType)), JS_E_OPERATION_NOT_SUPPORT);
1114     CHECK_COND_WITH_MESSAGE(env, resourceType == static_cast<int32_t>(ResourceType::PHOTO_PROXY),
1115         "Failed to check resourceType");
1116     if (!MediaLibraryAniUtils::IsSystemApp()) {
1117         AniError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
1118         return nullptr;
1119     }
1120 
1121     auto photoProxyAni = std::make_unique<PhotoProxyAni>();
1122     changeRequest->photoProxy_ = photoProxyAni->photoProxy_;
1123     changeRequest->addResourceMode_ = AddResourceMode::PHOTO_PROXY;
1124 
1125     changeRequest->RecordChangeOperation(AssetChangeOperation::ADD_RESOURCE);
1126     changeRequest->addResourceTypes_.push_back(GetResourceType(resourceType));
1127     return nullptr;
1128 }
1129 
SetNewFileAsset(int32_t id,const string & uri)1130 void MediaAssetChangeRequestAni::SetNewFileAsset(int32_t id, const string& uri)
1131 {
1132     if (fileAsset_ == nullptr) {
1133         ANI_ERR_LOG("fileAsset_ is nullptr");
1134         return;
1135     }
1136 
1137     if (id <= 0 || uri.empty()) {
1138         ANI_ERR_LOG("Failed to check file_id: %{public}d and uri: %{public}s", id, uri.c_str());
1139         return;
1140     }
1141     fileAsset_->SetId(id);
1142     fileAsset_->SetUri(uri);
1143     fileAsset_->SetTimePending(0);
1144 }
1145 
IsCreation(MediaAssetChangeRequestAniContext & context)1146 static bool IsCreation(MediaAssetChangeRequestAniContext& context)
1147 {
1148     auto assetChangeOperations = context.assetChangeOperations;
1149     bool isCreateFromScratch = std::find(assetChangeOperations.begin(), assetChangeOperations.end(),
1150                                          AssetChangeOperation::CREATE_FROM_SCRATCH) != assetChangeOperations.end();
1151     bool isCreateFromUri = std::find(assetChangeOperations.begin(), assetChangeOperations.end(),
1152                                      AssetChangeOperation::CREATE_FROM_URI) != assetChangeOperations.end();
1153     return isCreateFromScratch || isCreateFromUri;
1154 }
1155 
IsSetEffectMode(MediaAssetChangeRequestAniContext & context)1156 static bool IsSetEffectMode(MediaAssetChangeRequestAniContext& context)
1157 {
1158     auto assetChangeOperations = context.assetChangeOperations;
1159     return std::find(assetChangeOperations.begin(), assetChangeOperations.end(),
1160         AssetChangeOperation::SET_MOVING_PHOTO_EFFECT_MODE) != assetChangeOperations.end();
1161 }
1162 
SendFile(const UniqueFd & srcFd,const UniqueFd & destFd)1163 static int32_t SendFile(const UniqueFd& srcFd, const UniqueFd& destFd)
1164 {
1165     if (srcFd.Get() < 0 || destFd.Get() < 0) {
1166         ANI_ERR_LOG("Failed to check srcFd: %{public}d and destFd: %{public}d", srcFd.Get(), destFd.Get());
1167         return E_ERR;
1168     }
1169 
1170     struct stat statSrc {};
1171     int32_t status = fstat(srcFd.Get(), &statSrc);
1172     if (status != 0) {
1173         ANI_ERR_LOG("Failed to get file stat, errno=%{public}d", errno);
1174         return status;
1175     }
1176 
1177     off_t offset = 0;
1178     off_t fileSize = statSrc.st_size;
1179     while (offset < fileSize) {
1180         ssize_t sent = sendfile(destFd.Get(), srcFd.Get(), &offset, fileSize - offset);
1181         if (sent < 0) {
1182             ANI_ERR_LOG("Failed to sendfile with errno=%{public}d, srcFd=%{private}d, destFd=%{private}d", errno,
1183                 srcFd.Get(), destFd.Get());
1184             return sent;
1185         }
1186     }
1187 
1188     return E_OK;
1189 }
1190 
CopyFileToMediaLibrary(const UniqueFd & destFd,bool isMovingPhotoVideo)1191 int32_t MediaAssetChangeRequestAni::CopyFileToMediaLibrary(const UniqueFd& destFd, bool isMovingPhotoVideo)
1192 {
1193     string srcRealPath = isMovingPhotoVideo ? movingPhotoVideoRealPath_ : realPath_;
1194     CHECK_COND_RET(!srcRealPath.empty(), E_FAIL, "Failed to check real path of source");
1195 
1196     string absFilePath;
1197     CHECK_COND_RET(OHOS::PathToRealPath(srcRealPath, absFilePath), E_FAIL,
1198         "Not real path %{private}s", srcRealPath.c_str());
1199     UniqueFd srcFd(open(absFilePath.c_str(), O_RDONLY));
1200     if (srcFd.Get() < 0) {
1201         ANI_ERR_LOG("Failed to open %{private}s, errno=%{public}d", absFilePath.c_str(), errno);
1202         return srcFd.Get();
1203     }
1204 
1205     int32_t err = SendFile(srcFd, destFd);
1206     if (err != E_OK) {
1207         ANI_ERR_LOG("Failed to send file from %{public}d to %{public}d", srcFd.Get(), destFd.Get());
1208     }
1209     return err;
1210 }
1211 
CopyDataBufferToMediaLibrary(const UniqueFd & destFd,bool isMovingPhotoVideo)1212 int32_t MediaAssetChangeRequestAni::CopyDataBufferToMediaLibrary(const UniqueFd& destFd, bool isMovingPhotoVideo)
1213 {
1214     size_t offset = 0;
1215     size_t length = isMovingPhotoVideo ? movingPhotoVideoBufferSize_ : dataBufferSize_;
1216     void* dataBuffer = isMovingPhotoVideo ? movingPhotoVideoDataBuffer_ : dataBuffer_;
1217     while (offset < length) {
1218         ssize_t written = write(destFd.Get(), (char*)dataBuffer + offset, length - offset);
1219         if (written < 0) {
1220             ANI_ERR_LOG("Failed to copy data buffer, return %{public}d", static_cast<int>(written));
1221             return written;
1222         }
1223         offset += static_cast<size_t>(written);
1224     }
1225     return E_OK;
1226 }
1227 
CopyMovingPhotoVideo(const string & assetUri)1228 int32_t MediaAssetChangeRequestAni::CopyMovingPhotoVideo(const string& assetUri)
1229 {
1230     if (assetUri.empty()) {
1231         ANI_ERR_LOG("Failed to check empty asset uri");
1232         return E_INVALID_URI;
1233     }
1234 
1235     string videoUri = assetUri;
1236     MediaFileUtils::UriAppendKeyValue(videoUri, MEDIA_MOVING_PHOTO_OPRN_KEYWORD, OPEN_MOVING_PHOTO_VIDEO);
1237     Uri uri(videoUri);
1238     int videoFd = UserFileClient::OpenFile(uri, MEDIA_FILEMODE_WRITEONLY);
1239     if (videoFd < 0) {
1240         ANI_ERR_LOG("Failed to open video of moving photo with write-only mode");
1241         return videoFd;
1242     }
1243 
1244     int32_t ret = E_ERR;
1245     UniqueFd uniqueFd(videoFd);
1246     if (movingPhotoVideoResourceMode_ == AddResourceMode::FILE_URI) {
1247         ret = CopyFileToMediaLibrary(uniqueFd, true);
1248     } else if (movingPhotoVideoResourceMode_ == AddResourceMode::DATA_BUFFER) {
1249         ret = CopyDataBufferToMediaLibrary(uniqueFd, true);
1250     } else {
1251         ANI_ERR_LOG("Invalid mode: %{public}d", movingPhotoVideoResourceMode_);
1252         return E_INVALID_VALUES;
1253     }
1254     return ret;
1255 }
1256 
CreateAssetBySecurityComponent(string & assetUri)1257 int32_t MediaAssetChangeRequestAni::CreateAssetBySecurityComponent(string& assetUri)
1258 {
1259     bool isValid = false;
1260     string title = creationValuesBucket_.Get(PhotoColumn::MEDIA_TITLE, isValid);
1261     CHECK_COND_RET(isValid, E_FAIL, "Failed to get title");
1262     string extension = creationValuesBucket_.Get(ASSET_EXTENTION, isValid);
1263     CHECK_COND_RET(isValid && MediaFileUtils::CheckDisplayName(title + "." + extension) == E_OK, E_FAIL,
1264         "Failed to check displayName");
1265     creationValuesBucket_.valuesMap.erase(MEDIA_DATA_DB_NAME);
1266 
1267     std::string uri = PAH_CREATE_PHOTO_COMPONENT; // create asset by security component
1268     MediaLibraryAniUtils::UriAppendKeyValue(uri, API_VERSION, std::to_string(MEDIA_API_VERSION_V10));
1269     Uri createAssetUri(uri);
1270     return UserFileClient::InsertExt(createAssetUri, creationValuesBucket_, assetUri);
1271 }
1272 
CopyToMediaLibrary(bool isCreation,AddResourceMode mode)1273 int32_t MediaAssetChangeRequestAni::CopyToMediaLibrary(bool isCreation, AddResourceMode mode)
1274 {
1275     CHECK_COND_RET(fileAsset_ != nullptr, E_FAIL, "Failed to check fileAsset_");
1276     int32_t ret = E_ERR;
1277     int32_t id = 0;
1278     string assetUri;
1279     if (isCreation) {
1280         ret = CreateAssetBySecurityComponent(assetUri);
1281         CHECK_COND_RET(ret > 0, (ret == 0 ? E_ERR : ret), "Failed to create asset by security component");
1282         id = ret;
1283     } else {
1284         assetUri = fileAsset_->GetUri();
1285     }
1286     CHECK_COND_RET(!assetUri.empty(), E_ERR, "Failed to check empty asset uri");
1287 
1288     if (IsMovingPhoto()) {
1289         ret = CopyMovingPhotoVideo(assetUri);
1290         if (ret != E_OK) {
1291             ANI_ERR_LOG("Failed to copy data to moving photo video with error: %{public}d", ret);
1292             return ret;
1293         }
1294     }
1295 
1296     Uri uri(assetUri);
1297     UniqueFd destFd(UserFileClient::OpenFile(uri, MEDIA_FILEMODE_WRITEONLY));
1298     if (destFd.Get() < 0) {
1299         ANI_ERR_LOG("Failed to open %{private}s with error: %{public}d", assetUri.c_str(), destFd.Get());
1300         return destFd.Get();
1301     }
1302 
1303     if (mode == AddResourceMode::FILE_URI) {
1304         ret = CopyFileToMediaLibrary(destFd);
1305     } else if (mode == AddResourceMode::DATA_BUFFER) {
1306         ret = CopyDataBufferToMediaLibrary(destFd);
1307     } else {
1308         ANI_ERR_LOG("Invalid mode: %{public}d", mode);
1309         return E_INVALID_VALUES;
1310     }
1311 
1312     if (ret == E_OK && isCreation) {
1313         SetNewFileAsset(id, assetUri);
1314     }
1315     return ret;
1316 }
1317 
WriteBySecurityComponent(MediaAssetChangeRequestAniContext & context)1318 static bool WriteBySecurityComponent(MediaAssetChangeRequestAniContext& context)
1319 {
1320     bool isCreation = IsCreation(context);
1321     int32_t ret = E_FAIL;
1322     auto assetChangeOperations = context.assetChangeOperations;
1323     bool isCreateFromUri = std::find(assetChangeOperations.begin(), assetChangeOperations.end(),
1324                                      AssetChangeOperation::CREATE_FROM_URI) != assetChangeOperations.end();
1325     auto changeRequest = context.objectInfo;
1326     if (isCreateFromUri) {
1327         ret = changeRequest->CopyToMediaLibrary(isCreation, AddResourceMode::FILE_URI);
1328     } else {
1329         ret = changeRequest->CopyToMediaLibrary(isCreation, changeRequest->GetAddResourceMode());
1330     }
1331 
1332     if (ret < 0) {
1333         context.SaveError(ret);
1334         ANI_ERR_LOG("Failed to write by security component, ret: %{public}d", ret);
1335         return false;
1336     }
1337     return true;
1338 }
1339 
PutMediaAssetEditData(DataShare::DataShareValuesBucket & valuesBucket)1340 int32_t MediaAssetChangeRequestAni::PutMediaAssetEditData(DataShare::DataShareValuesBucket& valuesBucket)
1341 {
1342     if (editData_ == nullptr) {
1343         return E_OK;
1344     }
1345 
1346     string compatibleFormat = editData_->GetCompatibleFormat();
1347     CHECK_COND_RET(!compatibleFormat.empty(), E_FAIL, "Failed to check compatibleFormat");
1348     string formatVersion = editData_->GetFormatVersion();
1349     CHECK_COND_RET(!formatVersion.empty(), E_FAIL, "Failed to check formatVersion");
1350     string data = editData_->GetData();
1351     CHECK_COND_RET(!data.empty(), E_FAIL, "Failed to check data");
1352 
1353     valuesBucket.Put(COMPATIBLE_FORMAT, compatibleFormat);
1354     valuesBucket.Put(FORMAT_VERSION, formatVersion);
1355     valuesBucket.Put(EDIT_DATA, data);
1356     return E_OK;
1357 }
1358 
SubmitCache(bool isCreation,bool isSetEffectMode)1359 int32_t MediaAssetChangeRequestAni::SubmitCache(bool isCreation, bool isSetEffectMode)
1360 {
1361     CHECK_COND_RET(fileAsset_ != nullptr, E_FAIL, "Failed to check fileAsset_");
1362     CHECK_COND_RET(!cacheFileName_.empty() || !cacheMovingPhotoVideoName_.empty(), E_FAIL,
1363         "Failed to check cache file");
1364 
1365     string uri = PAH_SUBMIT_CACHE;
1366     MediaLibraryAniUtils::UriAppendKeyValue(uri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
1367     Uri submitCacheUri(uri);
1368 
1369     string assetUri;
1370     int32_t ret;
1371     if (isCreation) {
1372         bool isValid = false;
1373         string displayName = creationValuesBucket_.Get(MEDIA_DATA_DB_NAME, isValid);
1374         CHECK_COND_RET(
1375             isValid && MediaFileUtils::CheckDisplayName(displayName) == E_OK, E_FAIL, "Failed to check displayName");
1376         creationValuesBucket_.Put(CACHE_FILE_NAME, cacheFileName_);
1377         if (IsMovingPhoto()) {
1378             creationValuesBucket_.Put(CACHE_MOVING_PHOTO_VIDEO_NAME, cacheMovingPhotoVideoName_);
1379         }
1380         ret = UserFileClient::InsertExt(submitCacheUri, creationValuesBucket_, assetUri);
1381     } else {
1382         DataShare::DataShareValuesBucket valuesBucket;
1383         valuesBucket.Put(PhotoColumn::MEDIA_ID, fileAsset_->GetId());
1384         valuesBucket.Put(CACHE_FILE_NAME, cacheFileName_);
1385         ret = PutMediaAssetEditData(valuesBucket);
1386         CHECK_COND_RET(ret == E_OK, ret, "Failed to put editData");
1387         if (IsMovingPhoto()) {
1388             valuesBucket.Put(CACHE_MOVING_PHOTO_VIDEO_NAME, cacheMovingPhotoVideoName_);
1389         }
1390         if (isSetEffectMode) {
1391             valuesBucket.Put(PhotoColumn::MOVING_PHOTO_EFFECT_MODE, fileAsset_->GetMovingPhotoEffectMode());
1392             valuesBucket.Put(CACHE_MOVING_PHOTO_VIDEO_NAME, cacheMovingPhotoVideoName_);
1393         }
1394         ret = UserFileClient::Insert(submitCacheUri, valuesBucket);
1395     }
1396 
1397     if (ret > 0 && isCreation) {
1398         SetNewFileAsset(ret, assetUri);
1399     }
1400     cacheFileName_.clear();
1401     cacheMovingPhotoVideoName_.clear();
1402     return ret;
1403 }
1404 
SubmitCacheExecute(MediaAssetChangeRequestAniContext & context)1405 static bool SubmitCacheExecute(MediaAssetChangeRequestAniContext& context)
1406 {
1407     MediaLibraryTracer tracer;
1408     tracer.Start("SubmitCacheExecute");
1409 
1410     bool isCreation = IsCreation(context);
1411     bool isSetEffectMode = IsSetEffectMode(context);
1412     auto changeRequest = context.objectInfo;
1413     int32_t ret = changeRequest->SubmitCache(isCreation, isSetEffectMode);
1414     if (ret < 0) {
1415         context.SaveError(ret);
1416         ANI_ERR_LOG("Failed to write cache, ret: %{public}d", ret);
1417         return false;
1418     }
1419     return true;
1420 }
1421 
SavePhotoProxyImage(const UniqueFd & destFd,sptr<PhotoProxy> photoProxyPtr)1422 static int SavePhotoProxyImage(const UniqueFd& destFd, sptr<PhotoProxy> photoProxyPtr)
1423 {
1424     void* imageAddr = photoProxyPtr->GetFileDataAddr();
1425     size_t imageSize = photoProxyPtr->GetFileSize();
1426     if (imageAddr == nullptr || imageSize == 0) {
1427         ANI_ERR_LOG("imageAddr is nullptr or imageSize(%{public}zu)==0", imageSize);
1428         return E_ERR;
1429     }
1430 
1431     ANI_INFO_LOG("start pack PixelMap");
1432     Media::InitializationOptions opts;
1433     opts.pixelFormat = Media::PixelFormat::RGBA_8888;
1434     opts.size = {
1435         .width = photoProxyPtr->GetWidth(),
1436         .height = photoProxyPtr->GetHeight()
1437     };
1438     auto pixelMap = Media::PixelMap::Create(opts);
1439     if (pixelMap == nullptr) {
1440         ANI_ERR_LOG("Create pixelMap failed.");
1441         return E_ERR;
1442     }
1443     pixelMap->SetPixelsAddr(imageAddr, nullptr, imageSize, Media::AllocatorType::SHARE_MEM_ALLOC, nullptr);
1444     auto pixelSize = static_cast<uint32_t>(pixelMap->GetByteCount());
1445 
1446     // encode rgba to jpeg
1447     auto buffer = new (std::nothrow) uint8_t[pixelSize];
1448     int64_t packedSize = 0L;
1449     Media::ImagePacker imagePacker;
1450     Media::PackOption packOption;
1451     packOption.format = "image/jpeg";
1452     imagePacker.StartPacking(buffer, pixelSize, packOption);
1453     imagePacker.AddImage(*pixelMap);
1454     imagePacker.FinalizePacking(packedSize);
1455     if (buffer == nullptr) {
1456         ANI_ERR_LOG("packet pixelMap failed");
1457         return E_ERR;
1458     }
1459     ANI_INFO_LOG("pack pixelMap success, packedSize: %{public}" PRId64, packedSize);
1460 
1461     int ret = write(destFd, buffer, packedSize);
1462     if (ret < 0) {
1463         ANI_ERR_LOG("Failed to write photo proxy to cache file, return %{public}d", ret);
1464         return ret;
1465     }
1466     delete[] buffer;
1467     return ret;
1468 }
1469 
OpenWriteCacheHandler(MediaAssetChangeRequestAniContext & context,bool isMovingPhotoVideo=false)1470 static int32_t OpenWriteCacheHandler(MediaAssetChangeRequestAniContext& context, bool isMovingPhotoVideo = false)
1471 {
1472     auto changeRequest = context.objectInfo;
1473     auto fileAsset = changeRequest->GetFileAssetInstance();
1474     if (fileAsset == nullptr) {
1475         context.SaveError(E_FAIL);
1476         ANI_ERR_LOG("fileAsset is null");
1477         return E_FAIL;
1478     }
1479 
1480     // specify mp4 extension for cache file of moving photo video
1481     string extension = isMovingPhotoVideo ? MOVING_PHOTO_VIDEO_EXTENSION
1482                                           : MediaFileUtils::GetExtensionFromPath(fileAsset->GetDisplayName());
1483     int64_t currentTimestamp = MediaFileUtils::UTCTimeNanoSeconds();
1484     uint32_t cacheFileId = changeRequest->FetchAddCacheFileId();
1485     string cacheFileName = to_string(currentTimestamp) + "_" + to_string(cacheFileId) + "." + extension;
1486     string uri = PhotoColumn::PHOTO_CACHE_URI_PREFIX + cacheFileName;
1487     MediaFileUtils::UriAppendKeyValue(uri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
1488     Uri openCacheUri(uri);
1489     int32_t ret = UserFileClient::OpenFile(openCacheUri, MEDIA_FILEMODE_WRITEONLY);
1490     if (ret == E_PERMISSION_DENIED) {
1491         context.error = OHOS_PERMISSION_DENIED_CODE;
1492         ANI_ERR_LOG("Open cache file failed, permission denied");
1493         return ret;
1494     }
1495     if (ret < 0) {
1496         context.SaveError(ret);
1497         ANI_ERR_LOG("Open cache file failed, ret: %{public}d", ret);
1498     }
1499 
1500     if (isMovingPhotoVideo) {
1501         changeRequest->SetCacheMovingPhotoVideoName(cacheFileName);
1502     } else {
1503         changeRequest->SetCacheFileName(cacheFileName);
1504     }
1505     return ret;
1506 }
1507 
WriteCacheByArrayBuffer(MediaAssetChangeRequestAniContext & context,const UniqueFd & destFd,bool isMovingPhotoVideo=false)1508 static bool WriteCacheByArrayBuffer(MediaAssetChangeRequestAniContext& context,
1509     const UniqueFd& destFd, bool isMovingPhotoVideo = false)
1510 {
1511     auto changeRequest = context.objectInfo;
1512     size_t offset = 0;
1513     size_t length = isMovingPhotoVideo ? changeRequest->GetMovingPhotoVideoSize() : changeRequest->GetDataBufferSize();
1514     void* dataBuffer = isMovingPhotoVideo ? changeRequest->GetMovingPhotoVideoBuffer() : changeRequest->GetDataBuffer();
1515     while (offset < length) {
1516         ssize_t written = write(destFd.Get(), (char*)dataBuffer + offset, length - offset);
1517         if (written < 0) {
1518             context.SaveError(written);
1519             ANI_ERR_LOG("Failed to write data buffer to cache file, return %{public}d", static_cast<int>(written));
1520             return false;
1521         }
1522         offset += static_cast<size_t>(written);
1523     }
1524     return true;
1525 }
1526 
SendToCacheFile(MediaAssetChangeRequestAniContext & context,const UniqueFd & destFd,bool isMovingPhotoVideo=false)1527 static bool SendToCacheFile(MediaAssetChangeRequestAniContext& context,
1528     const UniqueFd& destFd, bool isMovingPhotoVideo = false)
1529 {
1530     auto changeRequest = context.objectInfo;
1531     string realPath = isMovingPhotoVideo ? changeRequest->GetMovingPhotoVideoPath() : changeRequest->GetFileRealPath();
1532 
1533     string absFilePath;
1534     if (!OHOS::PathToRealPath(realPath, absFilePath)) {
1535         ANI_ERR_LOG("Not real path %{private}s, errno=%{public}d", realPath.c_str(), errno);
1536         return false;
1537     }
1538 
1539     UniqueFd srcFd(open(absFilePath.c_str(), O_RDONLY));
1540     if (srcFd.Get() < 0) {
1541         context.SaveError(srcFd.Get());
1542         ANI_ERR_LOG("Failed to open file, errno=%{public}d", errno);
1543         return false;
1544     }
1545 
1546     int32_t err = SendFile(srcFd, destFd);
1547     if (err != E_OK) {
1548         context.SaveError(err);
1549         ANI_ERR_LOG("Failed to send file from %{public}d to %{public}d", srcFd.Get(), destFd.Get());
1550         return false;
1551     }
1552     return true;
1553 }
1554 
AddPhotoProxyResourceExecute(MediaAssetChangeRequestAniContext & context,const UniqueFd & destFd)1555 static bool AddPhotoProxyResourceExecute(MediaAssetChangeRequestAniContext& context, const UniqueFd& destFd)
1556 {
1557     string uri = PAH_ADD_IMAGE;
1558     MediaLibraryAniUtils::UriAppendKeyValue(uri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
1559     Uri updateAssetUri(uri);
1560 
1561     auto fileAsset = context.objectInfo->GetFileAssetInstance();
1562     DataShare::DataSharePredicates predicates;
1563     predicates.SetWhereClause(PhotoColumn::MEDIA_ID + " = ? ");
1564     predicates.SetWhereArgs({ to_string(fileAsset->GetId()) });
1565 
1566     DataShare::DataShareValuesBucket valuesBucket;
1567     valuesBucket.Put(PhotoColumn::PHOTO_ID, context.objectInfo->GetPhotoProxyObj()->GetPhotoId());
1568     ANI_INFO_LOG("photoId: %{public}s", context.objectInfo->GetPhotoProxyObj()->GetPhotoId().c_str());
1569     valuesBucket.Put(PhotoColumn::PHOTO_DEFERRED_PROC_TYPE,
1570         static_cast<int32_t>(context.objectInfo->GetPhotoProxyObj()->GetDeferredProcType()));
1571     valuesBucket.Put(MediaColumn::MEDIA_ID, fileAsset->GetId());
1572     int32_t changedRows = UserFileClient::Update(updateAssetUri, predicates, valuesBucket);
1573     if (changedRows < 0) {
1574         context.SaveError(changedRows);
1575         ANI_ERR_LOG("Failed to set, err: %{public}d", changedRows);
1576         return false;
1577     }
1578 
1579     int err = SavePhotoProxyImage(destFd, context.objectInfo->GetPhotoProxyObj());
1580     context.objectInfo->ReleasePhotoProxyObj();
1581     if (err < 0) {
1582         context.SaveError(err);
1583         ANI_ERR_LOG("Failed to saveImage , err: %{public}d", err);
1584         return false;
1585     }
1586     return true;
1587 }
1588 
AddResourceByMode(MediaAssetChangeRequestAniContext & context,const UniqueFd & uniqueFd,AddResourceMode mode,bool isMovingPhotoVideo=false)1589 static bool AddResourceByMode(MediaAssetChangeRequestAniContext& context,
1590     const UniqueFd& uniqueFd, AddResourceMode mode, bool isMovingPhotoVideo = false)
1591 {
1592     bool isWriteSuccess = false;
1593     if (mode == AddResourceMode::DATA_BUFFER) {
1594         isWriteSuccess = WriteCacheByArrayBuffer(context, uniqueFd, isMovingPhotoVideo);
1595     } else if (mode == AddResourceMode::FILE_URI) {
1596         isWriteSuccess = SendToCacheFile(context, uniqueFd, isMovingPhotoVideo);
1597     } else if (mode == AddResourceMode::PHOTO_PROXY) {
1598         isWriteSuccess = AddPhotoProxyResourceExecute(context, uniqueFd);
1599     } else {
1600         context.SaveError(E_FAIL);
1601         ANI_ERR_LOG("Unsupported addResource mode");
1602     }
1603     return isWriteSuccess;
1604 }
1605 
AddMovingPhotoVideoExecute(MediaAssetChangeRequestAniContext & context)1606 static bool AddMovingPhotoVideoExecute(MediaAssetChangeRequestAniContext& context)
1607 {
1608     MediaLibraryTracer tracer;
1609     tracer.Start("AddMovingPhotoVideoExecute");
1610 
1611     int32_t cacheVideoFd = OpenWriteCacheHandler(context, true);
1612     if (cacheVideoFd < 0) {
1613         ANI_ERR_LOG("Failed to open cache moving photo video, err: %{public}d", cacheVideoFd);
1614         return false;
1615     }
1616 
1617     UniqueFd uniqueFd(cacheVideoFd);
1618     AddResourceMode mode = context.objectInfo->GetMovingPhotoVideoMode();
1619     if (!AddResourceByMode(context, uniqueFd, mode, true)) {
1620         ANI_ERR_LOG("Faild to write cache file");
1621         return false;
1622     }
1623     return true;
1624 }
1625 
HasAddResource(MediaAssetChangeRequestAniContext & context,ResourceType resourceType)1626 static bool HasAddResource(MediaAssetChangeRequestAniContext& context, ResourceType resourceType)
1627 {
1628     return std::find(context.addResourceTypes.begin(), context.addResourceTypes.end(), resourceType) !=
1629         context.addResourceTypes.end();
1630 }
1631 
AddResourceExecute(MediaAssetChangeRequestAniContext & context)1632 static bool AddResourceExecute(MediaAssetChangeRequestAniContext& context)
1633 {
1634     MediaLibraryTracer tracer;
1635     tracer.Start("AddResourceExecute");
1636 
1637     if (!HasWritePermission()) {
1638         return WriteBySecurityComponent(context);
1639     }
1640 
1641     auto changeRequest = context.objectInfo;
1642     if (changeRequest->IsMovingPhoto() && HasAddResource(context, ResourceType::VIDEO_RESOURCE) &&
1643         !AddMovingPhotoVideoExecute(context)) {
1644         ANI_ERR_LOG("Faild to write cache file for video of moving photo");
1645         return false;
1646     }
1647 
1648     // image resource is not mandatory when setting effect mode of moving photo
1649     if (changeRequest->IsMovingPhoto() && !HasAddResource(context, ResourceType::IMAGE_RESOURCE)) {
1650         return SubmitCacheExecute(context);
1651     }
1652 
1653     int32_t cacheFd = OpenWriteCacheHandler(context);
1654     if (cacheFd < 0) {
1655         ANI_ERR_LOG("Failed to open write cache handler, err: %{public}d", cacheFd);
1656         return false;
1657     }
1658 
1659     UniqueFd uniqueFd(cacheFd);
1660     AddResourceMode mode = changeRequest->GetAddResourceMode();
1661     if (!AddResourceByMode(context, uniqueFd, mode)) {
1662         ANI_ERR_LOG("Faild to write cache file");
1663         return false;
1664     }
1665     return SubmitCacheExecute(context);
1666 }
1667 
1668 static const unordered_map<AssetChangeOperation, bool (*)(MediaAssetChangeRequestAniContext&)> EXECUTE_MAP = {
1669     { AssetChangeOperation::ADD_RESOURCE, AddResourceExecute },
1670 };
1671 
ApplyAssetChangeRequestExecute(ani_env * env,std::unique_ptr<MediaAssetChangeRequestAniContext> & context)1672 static ani_status ApplyAssetChangeRequestExecute(ani_env *env,
1673     std::unique_ptr<MediaAssetChangeRequestAniContext>& context)
1674 {
1675     MediaLibraryTracer tracer;
1676     tracer.Start("ApplyAssetChangeRequestExecute");
1677 
1678     if (context == nullptr || context->objectInfo == nullptr ||
1679         context->objectInfo->GetFileAssetInstance() == nullptr) {
1680         context->SaveError(E_FAIL);
1681         ANI_ERR_LOG("Failed to check async context of MediaAssetChangeRequest object");
1682         return ANI_INVALID_ARGS;
1683     }
1684 
1685     unordered_set<AssetChangeOperation> appliedOperations;
1686     for (const auto& changeOperation : context->assetChangeOperations) {
1687         // Keep the final result of each operation, and commit it only once.
1688         if (appliedOperations.find(changeOperation) != appliedOperations.end()) {
1689             continue;
1690         }
1691 
1692         bool valid = false;
1693         auto iter = EXECUTE_MAP.find(changeOperation);
1694         if (iter != EXECUTE_MAP.end()) {
1695             tracer.Start("ApplyAssetChangeRequestExecute " + to_string(static_cast<int32_t>(changeOperation)));
1696             valid = iter->second(*context);
1697             tracer.Finish();
1698         } else if (changeOperation == AssetChangeOperation::CREATE_FROM_SCRATCH ||
1699                    changeOperation == AssetChangeOperation::SET_EDIT_DATA) {
1700             // Perform CREATE_FROM_SCRATCH and SET_EDIT_DATA during GET_WRITE_CACHE_HANDLER or ADD_RESOURCE.
1701             valid = true;
1702         } else {
1703             ANI_ERR_LOG("Invalid asset change operation: %{public}d", changeOperation);
1704             context->error = OHOS_INVALID_PARAM_CODE;
1705             return ANI_INVALID_ARGS;
1706         }
1707 
1708         if (!valid) {
1709             ANI_ERR_LOG("Failed to apply asset change request, operation: %{public}d", changeOperation);
1710             return ANI_ERROR;
1711         }
1712         appliedOperations.insert(changeOperation);
1713     }
1714     return ANI_OK;
1715 }
1716 
ApplyChanges(ani_env * env,ani_object aniObject)1717 ani_status MediaAssetChangeRequestAni::ApplyChanges(ani_env *env, ani_object aniObject)
1718 {
1719     auto aniContext = make_unique<MediaAssetChangeRequestAniContext>();
1720     aniContext->objectInfo = this;
1721 
1722     ANI_CHECK_RETURN_RET_LOG(CheckChangeOperations(env), ANI_INVALID_ARGS,
1723         "Failed to check asset change request operations");
1724     aniContext->assetChangeOperations = assetChangeOperations_;
1725     aniContext->addResourceTypes = addResourceTypes_;
1726     assetChangeOperations_.clear();
1727     addResourceTypes_.clear();
1728     ANI_CHECK_RETURN_RET_LOG(ApplyAssetChangeRequestExecute(env, aniContext), ANI_ERROR,
1729         "Failed to apply asset change request");
1730     return ANI_OK;
1731 }
1732 } // namespace Media