1 /*
2  * Copyright (C) 2024 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 "media_asset_change_request_impl.h"
17 
18 #include <fcntl.h>
19 #include <sys/sendfile.h>
20 #include "securec.h"
21 
22 #include "oh_media_asset.h"
23 #include "media_log.h"
24 #include "medialibrary_errno.h"
25 #include "media_file_utils.h"
26 #include "media_column.h"
27 #include "file_uri.h"
28 #include "directory_ex.h"
29 #include "medialibrary_db_const.h"
30 #include "access_token.h"
31 #include "accesstoken_kit.h"
32 #include "ipc_skeleton.h"
33 #include "image_packer.h"
34 #include "permission_utils.h"
35 #include "media_userfile_client.h"
36 #include "userfilemgr_uri.h"
37 
38 using namespace std;
39 using namespace OHOS::Media;
40 using namespace OHOS::Security::AccessToken;
41 
42 atomic<uint32_t> MediaAssetChangeRequestImpl::cacheFileId_(0);
43 const string MOVING_PHOTO_VIDEO_EXTENSION = "mp4";
44 const string API_VERSION = "api_version";
45 
CreateMediaAssetChangeRequest(std::shared_ptr<MediaAsset> mediaAsset)46 std::shared_ptr<MediaAssetChangeRequest> MediaAssetChangeRequestFactory::CreateMediaAssetChangeRequest(
47     std::shared_ptr<MediaAsset> mediaAsset)
48 {
49     std::shared_ptr<MediaAssetChangeRequestImpl> impl = std::make_shared<MediaAssetChangeRequestImpl>(mediaAsset);
50     CHECK_AND_PRINT_LOG(impl != nullptr, "Failed to create MediaAssetChangeRequestImpl instance.");
51 
52     return impl;
53 }
54 
MediaAssetChangeRequestImpl(std::shared_ptr<MediaAsset> mediaAsset)55 MediaAssetChangeRequestImpl::MediaAssetChangeRequestImpl(std::shared_ptr<MediaAsset> mediaAsset)
56 {
57     mediaAsset_ = mediaAsset;
58     movingPhotoVideoDataBuffer_ = nullptr;
59     dataBuffer_ = nullptr;
60     movingPhotoVideoResourceMode_ = AddResourceMode::DEFAULT;
61     addResourceMode_ = AddResourceMode::DEFAULT;
62     movingPhotoVideoBufferSize_ = 0;
63     dataBufferSize_ = 0;
64 }
65 
~MediaAssetChangeRequestImpl()66 MediaAssetChangeRequestImpl::~MediaAssetChangeRequestImpl()
67 {
68     mediaAsset_ = nullptr;
69     if (movingPhotoVideoDataBuffer_ != nullptr) {
70         delete[] movingPhotoVideoDataBuffer_;
71         movingPhotoVideoDataBuffer_ = nullptr;
72     }
73 
74     if (dataBuffer_ != nullptr) {
75         delete[] dataBuffer_;
76         dataBuffer_ = nullptr;
77     }
78 
79     if (editData_ != nullptr) {
80         editData_ = nullptr;
81     }
82 
83     addResourceTypes_.clear();
84     assetChangeOperations_.clear();
85 }
86 
GetWriteCacheHandler(int32_t * fd)87 MediaLibrary_ErrorCode MediaAssetChangeRequestImpl::GetWriteCacheHandler(int32_t* fd)
88 {
89     unique_lock<mutex> ulock(mutex_);
90     auto fileAsset = mediaAsset_->GetFileAssetInstance();
91     CHECK_AND_RETURN_RET_LOG(fileAsset != nullptr, MEDIA_LIBRARY_INTERNAL_SYSTEM_ERROR, "fileAsset get failed!");
92     CHECK_AND_RETURN_RET_LOG(!IsMovingPhoto(), MEDIA_LIBRARY_OPERATION_NOT_SUPPORTED, "cann't be moving photo!");
93     CHECK_AND_RETURN_RET_LOG(CheckWriteOperation(MediaLibrary_ResourceType::MEDIA_LIBRARY_VIDEO_RESOURCE),
94         MEDIA_LIBRARY_OPERATION_NOT_SUPPORTED, "Not supported!");
95 
96     int32_t ret = OpenWriteCacheHandler();
97     if (ret < 0) {
98         MEDIA_ERR_LOG("Failed to open write cache handler,ret: %{public}d", ret);
99         return MEDIA_LIBRARY_OPERATION_NOT_SUPPORTED;
100     }
101     *fd = ret;
102     RecordChangeOperation(AssetChangeOperation::GET_WRITE_CACHE_HANDLER);
103     return MEDIA_LIBRARY_OK;
104 }
105 
SaveCameraPhoto(MediaLibrary_ImageFileType imageFileType)106 MediaLibrary_ErrorCode MediaAssetChangeRequestImpl::SaveCameraPhoto(MediaLibrary_ImageFileType imageFileType)
107 {
108     CHECK_AND_RETURN_RET_LOG(imageFileType == MEDIA_LIBRARY_IMAGE_JPEG, MEDIA_LIBRARY_OPERATION_NOT_SUPPORTED,
109         "imageFileType not support");
110 
111     unique_lock<mutex> ulock(mutex_);
112     auto fileAsset = mediaAsset_->GetFileAssetInstance();
113     CHECK_AND_RETURN_RET_LOG(fileAsset != nullptr, MEDIA_LIBRARY_OPERATION_NOT_SUPPORTED, "fileAsset get failed!");
114 
115     RecordChangeOperation(AssetChangeOperation::SAVE_CAMERA_PHOTO);
116     return MEDIA_LIBRARY_OK;
117 }
118 
DiscardCameraPhoto()119 MediaLibrary_ErrorCode MediaAssetChangeRequestImpl::DiscardCameraPhoto()
120 {
121     unique_lock<mutex> ulock(mutex_);
122     auto fileAsset = mediaAsset_->GetFileAssetInstance();
123     CHECK_AND_RETURN_RET_LOG(fileAsset != nullptr, MEDIA_LIBRARY_OPERATION_NOT_SUPPORTED, "fileAsset get failed!");
124 
125     RecordChangeOperation(AssetChangeOperation::DISCARD_CAMERA_PHOTO);
126     return MEDIA_LIBRARY_OK;
127 }
128 
AddResourceWithUri(MediaLibrary_ResourceType resourceType,char * fileUri)129 MediaLibrary_ErrorCode MediaAssetChangeRequestImpl::AddResourceWithUri(MediaLibrary_ResourceType resourceType,
130     char* fileUri)
131 {
132     unique_lock<mutex> ulock(mutex_);
133     CHECK_AND_RETURN_RET_LOG(CheckWriteOperation(resourceType), MEDIA_LIBRARY_OPERATION_NOT_SUPPORTED,
134         "operation not support");
135 
136     string realPath;
137     OHOS::AppFileService::ModuleFileUri::FileUri fileUriStr(fileUri);
138     string path = fileUriStr.GetRealPath();
139     bool result = OHOS::PathToRealPath(path, realPath);
140     CHECK_AND_RETURN_RET_LOG(result, MEDIA_LIBRARY_NO_SUCH_FILE, "File real path isn't existed");
141 
142     auto fileAsset = mediaAsset_->GetFileAssetInstance();
143     CHECK_AND_RETURN_RET_LOG(fileAsset != nullptr, MEDIA_LIBRARY_OPERATION_NOT_SUPPORTED, "fileAsset get failed!");
144 
145     if ((fileAsset->GetPhotoSubType() == static_cast<int32_t>(PhotoSubType::MOVING_PHOTO)) &&
146         resourceType == static_cast<int32_t>(MediaLibrary_ResourceType::MEDIA_LIBRARY_VIDEO_RESOURCE)) {
147         if ((MediaType::MEDIA_TYPE_VIDEO) != MediaFileUtils::GetMediaType(realPath)) {
148             MEDIA_ERR_LOG("Invalid file type");
149             return MEDIA_LIBRARY_PARAMETER_ERROR;
150         }
151         if (!(MediaFileUtils::CheckMovingPhotoVideo(realPath))) {
152             MEDIA_ERR_LOG("invalid param code");
153             return MEDIA_LIBRARY_NO_SUCH_FILE;
154         }
155 
156         movingPhotoVideoRealPath_ = realPath;
157         movingPhotoVideoResourceMode_ = AddResourceMode::FILE_URI;
158         RecordChangeOperation(AssetChangeOperation::ADD_RESOURCE);
159         addResourceTypes_.push_back(MediaLibrary_ResourceType::MEDIA_LIBRARY_VIDEO_RESOURCE);
160         return MEDIA_LIBRARY_OK;
161     }
162 
163     if (fileAsset->GetMediaType() != MediaFileUtils::GetMediaType(realPath)) {
164         MEDIA_ERR_LOG("Invalid file type");
165         return MEDIA_LIBRARY_PARAMETER_ERROR;
166     }
167     realPath_ = realPath;
168     addResourceMode_ = AddResourceMode::FILE_URI;
169     RecordChangeOperation(AssetChangeOperation::ADD_RESOURCE);
170     addResourceTypes_.push_back(resourceType);
171     return MEDIA_LIBRARY_OK;
172 }
173 
AddResourceWithBuffer(MediaLibrary_ResourceType resourceType,uint8_t * buffer,uint32_t length)174 MediaLibrary_ErrorCode MediaAssetChangeRequestImpl::AddResourceWithBuffer(MediaLibrary_ResourceType resourceType,
175     uint8_t* buffer, uint32_t length)
176 {
177     unique_lock<mutex> ulock(mutex_);
178     CHECK_AND_RETURN_RET_LOG(CheckWriteOperation(resourceType), MEDIA_LIBRARY_OPERATION_NOT_SUPPORTED,
179         "operation not support");
180 
181     auto fileAsset = mediaAsset_->GetFileAssetInstance();
182     CHECK_AND_RETURN_RET_LOG(fileAsset != nullptr, MEDIA_LIBRARY_OPERATION_NOT_SUPPORTED, "fileAsset get failed!");
183     CHECK_AND_RETURN_RET_LOG(!IsMovingPhoto(), MEDIA_LIBRARY_OPERATION_NOT_SUPPORTED,
184         "not support edit moving photo with buffer");
185 
186     if (dataBuffer_ != nullptr) {
187         delete[] dataBuffer_;
188     }
189     dataBuffer_ = new uint8_t[length + 1];
190     CHECK_AND_RETURN_RET_LOG(dataBuffer_ != nullptr, MEDIA_LIBRARY_INTERNAL_SYSTEM_ERROR,
191         "create dataBuffer_ failed!");
192 
193     dataBufferSize_ = length;
194     if (length > 0) {
195         int ret = memcpy_s(dataBuffer_, length + 1, buffer, length);
196         CHECK_AND_RETURN_RET_LOG(ret == E_OK, MEDIA_LIBRARY_INTERNAL_SYSTEM_ERROR,
197             "memcpy buffer failed!");
198     }
199     addResourceMode_ = AddResourceMode::DATA_BUFFER;
200     RecordChangeOperation(AssetChangeOperation::ADD_RESOURCE);
201     addResourceTypes_.push_back(resourceType);
202     return MEDIA_LIBRARY_OK;
203 }
204 
ApplyChanges()205 MediaLibrary_ErrorCode MediaAssetChangeRequestImpl::ApplyChanges()
206 {
207     unique_lock<mutex> ulock(mutex_);
208     bool result = CheckChangeOperations();
209     CHECK_AND_RETURN_RET_LOG(result, MEDIA_LIBRARY_PARAMETER_ERROR, "Failed to check asset change request operations");
210 
211     auto fileAsset = mediaAsset_->GetFileAssetInstance();
212     CHECK_AND_RETURN_RET_LOG(fileAsset != nullptr, MEDIA_LIBRARY_INTERNAL_SYSTEM_ERROR, "fileAsset is nullptr");
213 
214     unordered_set<AssetChangeOperation> appliedOperations;
215     for (const auto& changeOperation : assetChangeOperations_) {
216         if (appliedOperations.find(changeOperation) != appliedOperations.end()) {
217             continue;
218         }
219 
220         bool valid = ChangeOperationExecute(changeOperation);
221         if (!valid) {
222             MEDIA_ERR_LOG("Failed to apply asset change request, operation: %{public}d", changeOperation);
223             return MEDIA_LIBRARY_OPERATION_NOT_SUPPORTED;
224         }
225         appliedOperations.insert(changeOperation);
226     }
227     assetChangeOperations_.clear();
228     addResourceTypes_.clear();
229     movingPhotoVideoResourceMode_ = AddResourceMode::DEFAULT;
230     addResourceMode_ = AddResourceMode::DEFAULT;
231     return MEDIA_LIBRARY_OK;
232 }
233 
IsMovingPhoto()234 bool MediaAssetChangeRequestImpl::IsMovingPhoto()
235 {
236     auto fileAsset = mediaAsset_->GetFileAssetInstance();
237     CHECK_AND_RETURN_RET_LOG(fileAsset != nullptr, false, "fileAsset is nullptr");
238 
239     return fileAsset->GetPhotoSubType() == static_cast<int32_t>(PhotoSubType::MOVING_PHOTO);
240 }
241 
CheckWriteOperation(MediaLibrary_ResourceType resourceType)242 bool MediaAssetChangeRequestImpl::CheckWriteOperation(MediaLibrary_ResourceType resourceType)
243 {
244     if (IsMovingPhoto()) {
245         CHECK_AND_RETURN_RET_LOG(CheckMovingPhotoResource(resourceType), false,
246             "Failed to check resource to add for moving photo");
247         return true;
248     }
249 
250     if (Contains(AssetChangeOperation::CREATE_FROM_URI) ||
251         Contains(AssetChangeOperation::GET_WRITE_CACHE_HANDLER) ||
252         Contains(AssetChangeOperation::ADD_RESOURCE)) {
253         MEDIA_ERR_LOG("The previous asset creation/modification request has not been applied");
254         return false;
255     }
256     return true;
257 }
258 
CheckMovingPhotoResource(MediaLibrary_ResourceType resourceType)259 bool MediaAssetChangeRequestImpl::CheckMovingPhotoResource(MediaLibrary_ResourceType resourceType)
260 {
261     bool isResourceTypeVaild = !ContainsResource(resourceType);
262     int addResourceTimes =
263         count(assetChangeOperations_.begin(), assetChangeOperations_.end(), AssetChangeOperation::ADD_RESOURCE);
264     return isResourceTypeVaild && addResourceTimes <= 1;
265 }
266 
ContainsResource(MediaLibrary_ResourceType resourceType)267 bool MediaAssetChangeRequestImpl::ContainsResource(MediaLibrary_ResourceType resourceType)
268 {
269     return find(addResourceTypes_.begin(), addResourceTypes_.end(), resourceType) != addResourceTypes_.end();
270 }
271 
Contains(AssetChangeOperation changeOperation)272 bool MediaAssetChangeRequestImpl::Contains(AssetChangeOperation changeOperation)
273 {
274     return find(assetChangeOperations_.begin(), assetChangeOperations_.end(), changeOperation) !=
275            assetChangeOperations_.end();
276 }
277 
OpenWriteCacheHandler(bool isMovingPhotoVideo)278 int32_t MediaAssetChangeRequestImpl::OpenWriteCacheHandler(bool isMovingPhotoVideo)
279 {
280     auto fileAsset = mediaAsset_->GetFileAssetInstance();
281     CHECK_AND_RETURN_RET_LOG(fileAsset != nullptr, E_FAIL, "fileAsset is null");
282 
283     // specify mp4 extension for cache file of moving photo video
284     string extension = isMovingPhotoVideo ? MOVING_PHOTO_VIDEO_EXTENSION
285         : MediaFileUtils::GetExtensionFromPath(fileAsset->GetDisplayName());
286     int64_t currentTimestamp = MediaFileUtils::UTCTimeNanoSeconds();
287     uint32_t cacheFileId = FetchAddCacheFileId();
288     string cacheFileName = to_string(currentTimestamp) + "_" + to_string(cacheFileId) + "." + extension;
289     string uri = PhotoColumn::PHOTO_CACHE_URI_PREFIX + cacheFileName;
290     MediaFileUtils::UriAppendKeyValue(uri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
291     Uri openCacheUri(uri);
292     int32_t ret = UserFileClient::OpenFile(openCacheUri, MEDIA_FILEMODE_WRITEONLY);
293     CHECK_AND_RETURN_RET_LOG(ret != E_PERMISSION_DENIED, ret, "Open cache file failed, permission denied");
294 
295     if (ret < 0) {
296         MEDIA_ERR_LOG("Open cache file failed, ret: %{public}d", ret);
297     }
298 
299     if (isMovingPhotoVideo) {
300         cacheMovingPhotoVideoName_ = cacheFileName;
301     } else {
302         cacheFileName_ = cacheFileName;
303     }
304     return ret;
305 }
306 
FetchAddCacheFileId()307 uint32_t MediaAssetChangeRequestImpl::FetchAddCacheFileId()
308 {
309     return cacheFileId_.fetch_add(1);
310 }
311 
RecordChangeOperation(AssetChangeOperation changeOperation)312 void MediaAssetChangeRequestImpl::RecordChangeOperation(AssetChangeOperation changeOperation)
313 {
314     if ((changeOperation == AssetChangeOperation::GET_WRITE_CACHE_HANDLER ||
315         changeOperation == AssetChangeOperation::ADD_RESOURCE ||
316         changeOperation == AssetChangeOperation::ADD_FILTERS) &&
317         Contains(AssetChangeOperation::CREATE_FROM_SCRATCH)) {
318         assetChangeOperations_.insert(assetChangeOperations_.begin() + 1, changeOperation);
319         return;
320     }
321     assetChangeOperations_.push_back(changeOperation);
322 }
323 
CheckChangeOperations()324 bool MediaAssetChangeRequestImpl::CheckChangeOperations()
325 {
326     CHECK_AND_RETURN_RET_LOG(assetChangeOperations_.size() != 0, false, "None request to apply");
327 
328     bool isCreateFromScratch = Contains(AssetChangeOperation::CREATE_FROM_SCRATCH);
329     bool isCreateFromUri = Contains(AssetChangeOperation::CREATE_FROM_URI);
330     bool containsEdit = Contains(AssetChangeOperation::SET_EDIT_DATA);
331     bool containsGetHandler = Contains(AssetChangeOperation::GET_WRITE_CACHE_HANDLER);
332     bool containsAddResource = Contains(AssetChangeOperation::ADD_RESOURCE);
333     bool isSaveCameraPhoto = Contains(AssetChangeOperation::SAVE_CAMERA_PHOTO);
334     if ((isCreateFromScratch || containsEdit) && !containsGetHandler && !containsAddResource && !isSaveCameraPhoto) {
335         MEDIA_ERR_LOG("Cannot create or edit asset without data to write");
336         return false;
337     }
338 
339     if (containsEdit && (isCreateFromScratch || isCreateFromUri)) {
340         MEDIA_ERR_LOG("Cannot create together with edit");
341         return false;
342     }
343 
344     auto fileAsset = mediaAsset_->GetFileAssetInstance();
345     CHECK_AND_RETURN_RET_LOG(fileAsset != nullptr, false, "fileAsset is null");
346 
347     AssetChangeOperation firstOperation = assetChangeOperations_.front();
348     if (fileAsset->GetId() <= 0 && firstOperation != AssetChangeOperation::CREATE_FROM_SCRATCH &&
349         firstOperation != AssetChangeOperation::CREATE_FROM_URI) {
350         MEDIA_ERR_LOG("Invalid asset change request");
351         return false;
352     }
353 
354     bool isMovingPhoto = IsMovingPhoto();
355     if (isMovingPhoto && !CheckMovingPhotoWriteOperation()) {
356         MEDIA_ERR_LOG("Invalid write operation for moving photo");
357         return false;
358     }
359     return true;
360 }
361 
CheckMovingPhotoWriteOperation()362 bool MediaAssetChangeRequestImpl::CheckMovingPhotoWriteOperation()
363 {
364     if (!Contains(AssetChangeOperation::ADD_RESOURCE)) {
365         return true;
366     }
367 
368     if (!Contains(AssetChangeOperation::CREATE_FROM_SCRATCH)) {
369         MEDIA_ERR_LOG("Moving photo is not supported to edit now");
370         return false;
371     }
372 
373     int addResourceTimes =
374         count(assetChangeOperations_.begin(), assetChangeOperations_.end(), AssetChangeOperation::ADD_RESOURCE);
375     bool isImageExist = ContainsResource(MediaLibrary_ResourceType::MEDIA_LIBRARY_IMAGE_RESOURCE);
376     bool isVideoExist = ContainsResource(MediaLibrary_ResourceType::MEDIA_LIBRARY_VIDEO_RESOURCE);
377     return addResourceTimes == 2 && isImageExist && isVideoExist; // must add resource 2 times with image and video
378 }
379 
ChangeOperationExecute(AssetChangeOperation option)380 bool MediaAssetChangeRequestImpl::ChangeOperationExecute(AssetChangeOperation option)
381 {
382     bool ret = false;
383     switch (option) {
384         case AssetChangeOperation::GET_WRITE_CACHE_HANDLER:
385             ret = SubmitCacheExecute();
386             break;
387         case AssetChangeOperation::ADD_RESOURCE:
388             ret = AddResourceExecute();
389             break;
390         case AssetChangeOperation::SAVE_CAMERA_PHOTO:
391             ret = SaveCameraPhotoExecute();
392             break;
393         case AssetChangeOperation::DISCARD_CAMERA_PHOTO:
394             ret = DiscardCameraPhotoExecute();
395             break;
396         default:
397             break;
398     }
399     return ret;
400 }
401 
SubmitCacheExecute()402 bool MediaAssetChangeRequestImpl::SubmitCacheExecute()
403 {
404     bool isCreation = IsCreation();
405     bool isSetEffectMode = IsSetEffectMode();
406     int32_t ret = SubmitCache(isCreation, isSetEffectMode);
407     if (ret < 0) {
408         MEDIA_ERR_LOG("Failed to write cache, ret: %{public}d", ret);
409         return false;
410     }
411     return true;
412 }
413 
AddResourceExecute()414 bool MediaAssetChangeRequestImpl::AddResourceExecute()
415 {
416     if (IsMovingPhoto() && movingPhotoVideoResourceMode_ != AddResourceMode::FILE_URI) {
417         MEDIA_ERR_LOG("not support edit moving photo with buffer");
418         return false;
419     }
420     if (!HasWritePermission()) {
421         return WriteBySecurityComponent();
422     }
423 
424     if (IsMovingPhoto() && HasAddResource(MediaLibrary_ResourceType::MEDIA_LIBRARY_VIDEO_RESOURCE) &&
425         !AddMovingPhotoVideoExecute()) {
426         MEDIA_ERR_LOG("Faild to write cache file for video of moving photo");
427         return false;
428     }
429 
430     if (IsMovingPhoto() && !HasAddResource(MediaLibrary_ResourceType::MEDIA_LIBRARY_IMAGE_RESOURCE)) {
431         return SubmitCacheExecute();
432     }
433     int32_t cacheFd = OpenWriteCacheHandler();
434     if (cacheFd < 0) {
435         MEDIA_ERR_LOG("Failed to open write cache handler, err: %{public}d", cacheFd);
436         return false;
437     }
438     OHOS::UniqueFd uniqueFd(cacheFd);
439     AddResourceMode mode = addResourceMode_;
440     if (!AddResourceByMode(uniqueFd, mode)) {
441         MEDIA_ERR_LOG("Faild to write cache file");
442         return false;
443     }
444     return SubmitCacheExecute();
445 }
446 
SaveCameraPhotoExecute()447 bool MediaAssetChangeRequestImpl::SaveCameraPhotoExecute()
448 {
449     bool containsAddResource = find(assetChangeOperations_.begin(), assetChangeOperations_.end(),
450         AssetChangeOperation::ADD_RESOURCE) != assetChangeOperations_.end();
451     std::string uriStr = PAH_SAVE_CAMERA_PHOTO;
452     if (containsAddResource && !PermissionUtils::IsSystemApp()) {
453         // remove high quality photo
454         MEDIA_INFO_LOG("discard high quality photo because add resource by third app");
455         DiscardHighQualityPhoto();
456 
457         // set dirty flag when third-party hap calling addResource to save camera photo
458         MediaFileUtils::UriAppendKeyValue(uriStr, PhotoColumn::PHOTO_DIRTY,
459             to_string(static_cast<int32_t>(DirtyType::TYPE_NEW)));
460     }
461 
462     // The watermark will trigger the scan. If the watermark is turned on, there is no need to trigger the scan again.
463     bool needScan = std::find(assetChangeOperations_.begin(), assetChangeOperations_.end(),
464         AssetChangeOperation::ADD_FILTERS) == assetChangeOperations_.end();
465 
466     auto fileAsset = mediaAsset_->GetFileAssetInstance();
467     CHECK_AND_RETURN_RET_LOG(fileAsset != nullptr, false, "fileAsset is nullptr");
468 
469     MediaFileUtils::UriAppendKeyValue(uriStr, API_VERSION, to_string(MEDIA_API_VERSION_V10));
470     MediaFileUtils::UriAppendKeyValue(uriStr, MEDIA_OPERN_KEYWORD, to_string(needScan));
471     MediaFileUtils::UriAppendKeyValue(uriStr, PhotoColumn::MEDIA_FILE_PATH, fileAsset->GetUri());
472     MediaFileUtils::UriAppendKeyValue(uriStr, PhotoColumn::MEDIA_ID, to_string(fileAsset->GetId()));
473     MediaFileUtils::UriAppendKeyValue(uriStr, PhotoColumn::PHOTO_SUBTYPE,
474         to_string(fileAsset->GetPhotoSubType()));
475     Uri uri(uriStr);
476     OHOS::DataShare::DataShareValuesBucket valuesBucket;
477     valuesBucket.Put(PhotoColumn::PHOTO_IS_TEMP, false);
478     OHOS::DataShare::DataSharePredicates predicates;
479     bool ret = UserFileClient::Update(uri, predicates, valuesBucket);
480     CHECK_AND_RETURN_RET_LOG(ret, false, "save camera photo fail");
481 
482     return true;
483 }
484 
DiscardCameraPhotoExecute()485 bool MediaAssetChangeRequestImpl::DiscardCameraPhotoExecute()
486 {
487     OHOS::DataShare::DataSharePredicates predicates;
488     OHOS::DataShare::DataShareValuesBucket valuesBucket;
489     valuesBucket.Put(PhotoColumn::PHOTO_IS_TEMP, true);
490 
491     auto fileAsset = mediaAsset_->GetFileAssetInstance();
492     predicates.EqualTo(PhotoColumn::MEDIA_ID, to_string(fileAsset->GetId()));
493 
494     string uri = PAH_DISCARD_CAMERA_PHOTO;
495     MediaFileUtils::UriAppendKeyValue(uri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
496     Uri updateAssetUri(uri);
497     int32_t changedRows = UserFileClient::Update(updateAssetUri, predicates, valuesBucket);
498     if (changedRows < 0) {
499         MEDIA_ERR_LOG("Failed to update property of asset, err: %{public}d", changedRows);
500         return false;
501     }
502     return true;
503 }
504 
HasWritePermission()505 bool MediaAssetChangeRequestImpl::HasWritePermission()
506 {
507     AccessTokenID tokenCaller = OHOS::IPCSkeleton::GetSelfTokenID();
508     int result = AccessTokenKit::VerifyAccessToken(tokenCaller, PERM_WRITE_IMAGEVIDEO);
509     return result == PermissionState::PERMISSION_GRANTED;
510 }
511 
WriteBySecurityComponent()512 bool MediaAssetChangeRequestImpl::WriteBySecurityComponent()
513 {
514     bool isCreation = IsCreation();
515     int32_t ret = E_FAIL;
516     bool isCreateFromUri = find(assetChangeOperations_.begin(), assetChangeOperations_.end(),
517         AssetChangeOperation::CREATE_FROM_URI) != assetChangeOperations_.end();
518     if (isCreateFromUri) {
519         ret = CopyToMediaLibrary(isCreation, AddResourceMode::FILE_URI);
520     } else {
521         ret = CopyToMediaLibrary(isCreation, addResourceMode_);
522     }
523     if (ret < 0) {
524         MEDIA_ERR_LOG("Failed to write by security component, ret: %{public}d", ret);
525         return false;
526     }
527     return true;
528 }
529 
IsCreation()530 bool MediaAssetChangeRequestImpl::IsCreation()
531 {
532     bool isCreateFromScratch = find(assetChangeOperations_.begin(), assetChangeOperations_.end(),
533         AssetChangeOperation::CREATE_FROM_SCRATCH) != assetChangeOperations_.end();
534     bool isCreateFromUri = find(assetChangeOperations_.begin(), assetChangeOperations_.end(),
535         AssetChangeOperation::CREATE_FROM_URI) != assetChangeOperations_.end();
536     return isCreateFromScratch || isCreateFromUri;
537 }
538 
CopyToMediaLibrary(bool isCreation,AddResourceMode mode)539 int32_t MediaAssetChangeRequestImpl::CopyToMediaLibrary(bool isCreation, AddResourceMode mode)
540 {
541     auto fileAsset = mediaAsset_->GetFileAssetInstance();
542     CHECK_AND_RETURN_RET_LOG(fileAsset != nullptr, E_FAIL, "fileAsset is null");
543 
544     int32_t ret = E_ERR;
545     int32_t id = 0;
546     string assetUri;
547     if (isCreation) {
548         ret = CreateAssetBySecurityComponent(assetUri);
549         CHECK_AND_RETURN_RET_LOG(ret > 0, (ret == 0 ? E_ERR : ret), "Failed to create asset by security component");
550         id = ret;
551     } else {
552         assetUri = fileAsset->GetUri();
553     }
554     CHECK_AND_RETURN_RET_LOG(!assetUri.empty(), E_ERR, "Failed to check empty asset uri");
555 
556     if (IsMovingPhoto()) {
557         ret = CopyMovingPhotoVideo(assetUri);
558         if (ret != E_OK) {
559             MEDIA_ERR_LOG("Failed to copy data to moving photo video with error: %{public}d", ret);
560             return ret;
561         }
562     }
563 
564     Uri uri(assetUri);
565     OHOS::UniqueFd destFd(UserFileClient::OpenFile(uri, MEDIA_FILEMODE_WRITEONLY));
566     if (destFd.Get() < 0) {
567         MEDIA_ERR_LOG("Failed to open %{public}s with error: %{public}d", assetUri.c_str(), destFd.Get());
568         return destFd.Get();
569     }
570 
571     if (mode == AddResourceMode::FILE_URI) {
572         ret = CopyFileToMediaLibrary(destFd);
573     } else if (mode == AddResourceMode::DATA_BUFFER) {
574         ret = CopyDataBufferToMediaLibrary(destFd);
575     } else {
576         MEDIA_ERR_LOG("Invalid mode: %{public}d", mode);
577         return E_INVALID_VALUES;
578     }
579 
580     if (ret == E_OK && isCreation) {
581         SetNewFileAsset(id, assetUri);
582     }
583     return ret;
584 }
585 
CreateAssetBySecurityComponent(string & assetUri)586 int32_t MediaAssetChangeRequestImpl::CreateAssetBySecurityComponent(string& assetUri)
587 {
588     bool isValid = false;
589     string title = creationValuesBucket_.Get(PhotoColumn::MEDIA_TITLE, isValid);
590     CHECK_AND_RETURN_RET_LOG(isValid, E_FAIL, "Failed to get title");
591 
592     string extension = creationValuesBucket_.Get(ASSET_EXTENTION, isValid);
593     CHECK_AND_RETURN_RET_LOG(isValid && MediaFileUtils::CheckDisplayName(title + "." + extension) == E_OK, E_FAIL,
594         "Failed to check displayName");
595 
596     creationValuesBucket_.valuesMap.erase(MEDIA_DATA_DB_NAME);
597     string uri = PAH_CREATE_PHOTO_COMPONENT;
598     MediaFileUtils::UriAppendKeyValue(uri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
599     Uri createAssetUri(uri);
600     return UserFileClient::InsertExt(createAssetUri, creationValuesBucket_, assetUri);
601 }
602 
CopyMovingPhotoVideo(const string & assetUri)603 int32_t MediaAssetChangeRequestImpl::CopyMovingPhotoVideo(const string& assetUri)
604 {
605     CHECK_AND_RETURN_RET_LOG(!assetUri.empty(), E_INVALID_URI, "Failed to check empty asset uri");
606 
607     string videoUri = assetUri;
608     MediaFileUtils::UriAppendKeyValue(videoUri, MEDIA_MOVING_PHOTO_OPRN_KEYWORD, OPEN_MOVING_PHOTO_VIDEO);
609     Uri uri(videoUri);
610     int videoFd = UserFileClient::OpenFile(uri, MEDIA_FILEMODE_WRITEONLY);
611     CHECK_AND_RETURN_RET_LOG(videoFd >= 0, videoFd, "Failed to open video of moving photo with write-only mode");
612 
613     int32_t ret = E_ERR;
614     OHOS::UniqueFd uniqueFd(videoFd);
615     if (movingPhotoVideoResourceMode_ == AddResourceMode::FILE_URI) {
616         ret = CopyFileToMediaLibrary(uniqueFd, true);
617     } else if (movingPhotoVideoResourceMode_ == AddResourceMode::DATA_BUFFER) {
618         ret = CopyDataBufferToMediaLibrary(uniqueFd, true);
619     } else {
620         MEDIA_ERR_LOG("Invalid mode: %{public}d", movingPhotoVideoResourceMode_);
621         return E_INVALID_VALUES;
622     }
623     return ret;
624 }
625 
CopyFileToMediaLibrary(const OHOS::UniqueFd & destFd,bool isMovingPhotoVideo)626 int32_t MediaAssetChangeRequestImpl::CopyFileToMediaLibrary(const OHOS::UniqueFd& destFd, bool isMovingPhotoVideo)
627 {
628     string srcRealPath = isMovingPhotoVideo ? movingPhotoVideoRealPath_ : realPath_;
629     CHECK_AND_RETURN_RET_LOG(!srcRealPath.empty(), E_FAIL, "Failed to check real path of source");
630 
631     string absFilePath;
632     CHECK_AND_RETURN_RET_LOG(OHOS::PathToRealPath(srcRealPath, absFilePath), E_FAIL, "Not real path %{public}s",
633         srcRealPath.c_str());
634 
635     OHOS::UniqueFd srcFd(open(absFilePath.c_str(), O_RDONLY));
636     if (srcFd.Get() < 0) {
637         MEDIA_ERR_LOG("Failed to open %{public}s, errno=%{public}d", absFilePath.c_str(), errno);
638         return srcFd.Get();
639     }
640 
641     int32_t err = SendFile(srcFd, destFd);
642     if (err != E_OK) {
643         MEDIA_ERR_LOG("Failed to send file from %{public}d to %{public}d", srcFd.Get(), destFd.Get());
644     }
645     return err;
646 }
647 
CopyDataBufferToMediaLibrary(const OHOS::UniqueFd & destFd,bool isMovingPhotoVideo)648 int32_t MediaAssetChangeRequestImpl::CopyDataBufferToMediaLibrary(const OHOS::UniqueFd& destFd,
649     bool isMovingPhotoVideo)
650 {
651     size_t offset = 0;
652     size_t length = isMovingPhotoVideo ? movingPhotoVideoBufferSize_ : dataBufferSize_;
653     void* dataBuffer = isMovingPhotoVideo ? movingPhotoVideoDataBuffer_ : dataBuffer_;
654     while (offset < length) {
655         ssize_t written = write(destFd.Get(), (char*)dataBuffer + offset, length - offset);
656         if (written < 0) {
657             MEDIA_ERR_LOG("Failed to copy data buffer, return %{public}d", static_cast<int>(written));
658             return written;
659         }
660         offset += static_cast<size_t>(written);
661     }
662     return E_OK;
663 }
664 
SetNewFileAsset(int32_t id,const string & uri)665 void MediaAssetChangeRequestImpl::SetNewFileAsset(int32_t id, const string& uri)
666 {
667     auto fileAsset = mediaAsset_->GetFileAssetInstance();
668     if (fileAsset == nullptr) {
669         MEDIA_ERR_LOG("fileAsset is nullptr");
670         return;
671     }
672 
673     if (id <= 0 || uri.empty()) {
674         MEDIA_ERR_LOG("Failed to check file_id: %{public}d and uri: %{public}s", id, uri.c_str());
675         return;
676     }
677     fileAsset->SetId(id);
678     fileAsset->SetUri(uri);
679     fileAsset->SetTimePending(0);
680 }
681 
SendToCacheFile(const OHOS::UniqueFd & destFd,bool isMovingPhotoVideo)682 bool MediaAssetChangeRequestImpl::SendToCacheFile(const OHOS::UniqueFd& destFd, bool isMovingPhotoVideo)
683 {
684     string realPath = isMovingPhotoVideo ? movingPhotoVideoRealPath_ : realPath_;
685     string absFilePath;
686     if (!OHOS::PathToRealPath(realPath, absFilePath)) {
687         MEDIA_ERR_LOG("Not real path %{public}s, errno=%{public}d", realPath.c_str(), errno);
688         return false;
689     }
690 
691     OHOS::UniqueFd srcFd(open(absFilePath.c_str(), O_RDONLY));
692     if (srcFd.Get() < 0) {
693         MEDIA_ERR_LOG("Failed to open file, errno=%{public}d", errno);
694         return false;
695     }
696 
697     int32_t err = SendFile(srcFd, destFd);
698     if (err != E_OK) {
699         MEDIA_ERR_LOG("Failed to send file from %{public}d to %{public}d", srcFd.Get(), destFd.Get());
700         return false;
701     }
702     return true;
703 }
704 
IsSetEffectMode()705 bool MediaAssetChangeRequestImpl::IsSetEffectMode()
706 {
707     return find(assetChangeOperations_.begin(), assetChangeOperations_.end(),
708         AssetChangeOperation::SET_MOVING_PHOTO_EFFECT_MODE) != assetChangeOperations_.end();
709 }
710 
SubmitCache(bool isCreation,bool isSetEffectMode)711 int32_t MediaAssetChangeRequestImpl::SubmitCache(bool isCreation, bool isSetEffectMode)
712 {
713     auto fileAsset = mediaAsset_->GetFileAssetInstance();
714     CHECK_AND_RETURN_RET_LOG(fileAsset != nullptr, E_FAIL, "fileAsset is null");
715     CHECK_AND_RETURN_RET_LOG(!cacheFileName_.empty() || !cacheMovingPhotoVideoName_.empty(), E_FAIL,
716         "Failed to check cache file");
717 
718     string uri = PAH_SUBMIT_CACHE;
719     MediaFileUtils::UriAppendKeyValue(uri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
720     Uri submitCacheUri(uri);
721     string assetUri;
722     int32_t ret;
723     if (isCreation) {
724         bool isValid = false;
725         string displayName = creationValuesBucket_.Get(MEDIA_DATA_DB_NAME, isValid);
726         CHECK_AND_RETURN_RET_LOG(
727             isValid && MediaFileUtils::CheckDisplayName(displayName) == E_OK, E_FAIL, "Failed to check displayName");
728 
729         creationValuesBucket_.Put(CACHE_FILE_NAME, cacheFileName_);
730         if (IsMovingPhoto()) {
731             creationValuesBucket_.Put(CACHE_MOVING_PHOTO_VIDEO_NAME, cacheMovingPhotoVideoName_);
732         }
733         ret = UserFileClient::InsertExt(submitCacheUri, creationValuesBucket_, assetUri);
734     } else {
735         OHOS::DataShare::DataShareValuesBucket valuesBucket;
736         valuesBucket.Put(PhotoColumn::MEDIA_ID, fileAsset->GetId());
737         valuesBucket.Put(CACHE_FILE_NAME, cacheFileName_);
738         ret = PutMediaAssetEditData(valuesBucket);
739         CHECK_AND_RETURN_RET_LOG(ret == E_OK, ret, "Failed to put editData");
740 
741         if (isSetEffectMode) {
742             valuesBucket.Put(PhotoColumn::MOVING_PHOTO_EFFECT_MODE, fileAsset->GetMovingPhotoEffectMode());
743             valuesBucket.Put(CACHE_MOVING_PHOTO_VIDEO_NAME, cacheMovingPhotoVideoName_);
744         }
745         ret = UserFileClient::Insert(submitCacheUri, valuesBucket);
746     }
747 
748     if (ret > 0 && isCreation) {
749         SetNewFileAsset(ret, assetUri);
750     }
751     cacheFileName_.clear();
752     cacheMovingPhotoVideoName_.clear();
753     return ret;
754 }
755 
SendFile(const OHOS::UniqueFd & srcFd,const OHOS::UniqueFd & destFd)756 int32_t MediaAssetChangeRequestImpl::SendFile(const OHOS::UniqueFd& srcFd, const OHOS::UniqueFd& destFd)
757 {
758     if (srcFd.Get() < 0 || destFd.Get() < 0) {
759         MEDIA_ERR_LOG("Failed to check srcFd: %{public}d and destFd: %{public}d", srcFd.Get(), destFd.Get());
760         return E_ERR;
761     }
762 
763     struct stat statSrc {};
764     int32_t status = fstat(srcFd.Get(), &statSrc);
765     if (status != 0) {
766         MEDIA_ERR_LOG("Failed to get file stat, errno=%{public}d", errno);
767         return status;
768     }
769 
770     off_t offset = 0;
771     off_t fileSize = statSrc.st_size;
772     while (offset < fileSize) {
773         ssize_t sent = sendfile(destFd.Get(), srcFd.Get(), &offset, fileSize - offset);
774         if (sent < 0) {
775             MEDIA_ERR_LOG("Failed to sendfile with errno=%{public}d, srcFd=%{public}d, destFd=%{public}d", errno,
776                 srcFd.Get(), destFd.Get());
777             return sent;
778         }
779     }
780 
781     return E_OK;
782 }
783 
PutMediaAssetEditData(OHOS::DataShare::DataShareValuesBucket & valuesBucket)784 int32_t MediaAssetChangeRequestImpl::PutMediaAssetEditData(OHOS::DataShare::DataShareValuesBucket& valuesBucket)
785 {
786     if (editData_ == nullptr) {
787         return E_OK;
788     }
789 
790     string compatibleFormat = editData_->GetCompatibleFormat();
791     CHECK_AND_RETURN_RET_LOG(!compatibleFormat.empty(), E_FAIL, "Failed to check compatibleFormat");
792 
793     string formatVersion = editData_->GetFormatVersion();
794     CHECK_AND_RETURN_RET_LOG(!formatVersion.empty(), E_FAIL, "Failed to check formatVersion");
795 
796     string data = editData_->GetData();
797     CHECK_AND_RETURN_RET_LOG(!data.empty(), E_FAIL, "Failed to check data");
798 
799     valuesBucket.Put(COMPATIBLE_FORMAT, compatibleFormat);
800     valuesBucket.Put(FORMAT_VERSION, formatVersion);
801     valuesBucket.Put(EDIT_DATA, data);
802     return E_OK;
803 }
804 
HasAddResource(MediaLibrary_ResourceType resourceType)805 bool MediaAssetChangeRequestImpl::HasAddResource(MediaLibrary_ResourceType resourceType)
806 {
807     return find(addResourceTypes_.begin(), addResourceTypes_.end(), resourceType) !=
808         addResourceTypes_.end();
809 }
810 
AddMovingPhotoVideoExecute()811 bool MediaAssetChangeRequestImpl::AddMovingPhotoVideoExecute()
812 {
813     CHECK_AND_RETURN_RET_LOG(movingPhotoVideoResourceMode_ == AddResourceMode::FILE_URI, false,
814         "not support edit moving photo with buffer");
815     int32_t cacheVideoFd = OpenWriteCacheHandler(true);
816     if (cacheVideoFd < 0) {
817         MEDIA_ERR_LOG("Failed to open cache moving photo video, err: %{public}d", cacheVideoFd);
818         return false;
819     }
820     OHOS::UniqueFd uniqueFd(cacheVideoFd);
821     AddResourceMode mode = movingPhotoVideoResourceMode_;
822     if (!AddResourceByMode(uniqueFd, mode, true)) {
823         MEDIA_ERR_LOG("Faild to write cache file");
824         return false;
825     }
826     return true;
827 }
828 
AddResourceByMode(const OHOS::UniqueFd & uniqueFd,AddResourceMode mode,bool isMovingPhotoVideo)829 bool MediaAssetChangeRequestImpl::AddResourceByMode(const OHOS::UniqueFd& uniqueFd,
830     AddResourceMode mode, bool isMovingPhotoVideo)
831 {
832     bool isWriteSuccess = false;
833     if (mode == AddResourceMode::DATA_BUFFER) {
834         isWriteSuccess = WriteCacheByArrayBuffer(uniqueFd, isMovingPhotoVideo);
835     } else if (mode == AddResourceMode::FILE_URI) {
836         isWriteSuccess = SendToCacheFile(uniqueFd, isMovingPhotoVideo);
837     } else {
838         MEDIA_ERR_LOG("Unsupported addResource mode");
839     }
840     return isWriteSuccess;
841 }
842 
WriteCacheByArrayBuffer(const OHOS::UniqueFd & destFd,bool isMovingPhotoVideo)843 bool MediaAssetChangeRequestImpl::WriteCacheByArrayBuffer(const OHOS::UniqueFd& destFd, bool isMovingPhotoVideo)
844 {
845     size_t offset = 0;
846     size_t length = isMovingPhotoVideo ? movingPhotoVideoBufferSize_ : dataBufferSize_;
847     void* dataBuffer = isMovingPhotoVideo ? movingPhotoVideoDataBuffer_ : dataBuffer_;
848     while (offset < length) {
849         ssize_t written = write(destFd.Get(), (char*)dataBuffer + offset, length - offset);
850         if (written < 0) {
851             MEDIA_ERR_LOG("Failed to write data buffer to cache file, return %{public}d", static_cast<int>(written));
852             return false;
853         }
854         offset += static_cast<size_t>(written);
855     }
856     return true;
857 }
858 
DiscardHighQualityPhoto()859 void MediaAssetChangeRequestImpl::DiscardHighQualityPhoto()
860 {
861     string uriStr = PAH_REMOVE_MSC_TASK;
862     MediaFileUtils::UriAppendKeyValue(uriStr, API_VERSION, to_string(MEDIA_API_VERSION_V10));
863     Uri uri(uriStr);
864     OHOS::DataShare::DataSharePredicates predicates;
865     int errCode = 0;
866 
867     auto fileAsset = mediaAsset_->GetFileAssetInstance();
868     vector<string> columns { to_string(fileAsset->GetId()) };
869     UserFileClient::Query(uri, predicates, columns, errCode);
870 }
871