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