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 <fcntl.h>
17 #include <unistd.h>
18 #include <cstring>
19 #include <securec.h>
20
21 #include "moving_photo_impl.h"
22 #include "media_file_utils.h"
23 #include "media_log.h"
24 #include "unique_fd.h"
25 #include "userfilemgr_uri.h"
26 #include "file_uri.h"
27 #include "medialibrary_db_const.h"
28 #include "medialibrary_errno.h"
29 #include "media_userfile_client.h"
30
31 using namespace OHOS::Media;
32 using UniqueFd = OHOS::UniqueFd;
33 using Uri = OHOS::Uri;
34
CreateMovingPhoto(const std::string & uri)35 std::shared_ptr<MovingPhoto> MovingPhotoFactory::CreateMovingPhoto(const std::string& uri)
36 {
37 std::shared_ptr<MovingPhotoImpl> impl = std::make_shared<MovingPhotoImpl>(uri);
38 CHECK_AND_PRINT_LOG(impl != nullptr, "Failed to create MovingPhotoImpl instance.");
39
40 return impl;
41 }
42
MovingPhotoImpl(const std::string & imageUri)43 MovingPhotoImpl::MovingPhotoImpl(const std::string& imageUri) : imageUri_(imageUri)
44 {
45 }
46
~MovingPhotoImpl()47 MovingPhotoImpl::~MovingPhotoImpl()
48 {
49 if (arrayBufferData_ != nullptr) {
50 free(arrayBufferData_);
51 arrayBufferData_ = nullptr;
52 }
53 }
54
GetUri(const char ** uri)55 MediaLibrary_ErrorCode MovingPhotoImpl::GetUri(const char** uri)
56 {
57 *uri = imageUri_.c_str();
58 MEDIA_INFO_LOG("Moving photo uri = %{public}s", imageUri_.c_str());
59 return MEDIA_LIBRARY_OK;
60 }
61
RequestContentWithUris(char * imageUri,char * videoUri)62 MediaLibrary_ErrorCode MovingPhotoImpl::RequestContentWithUris(char* imageUri, char* videoUri)
63 {
64 destImageUri_ = imageUri;
65 destVideoUri_ = videoUri;
66 MEDIA_DEBUG_LOG("Request content imageUri = %{public}s, video = %{public}s", imageUri, videoUri);
67 int32_t ret = RequestContentToSandbox();
68 CHECK_AND_RETURN_RET_LOG(ret == E_OK, MEDIA_LIBRARY_OPERATION_NOT_SUPPORTED, "RequestContentToSandbox failed");
69
70 return MEDIA_LIBRARY_OK;
71 }
72
RequestContentWithUri(MediaLibrary_ResourceType resourceType,char * uri)73 MediaLibrary_ErrorCode MovingPhotoImpl::RequestContentWithUri(MediaLibrary_ResourceType resourceType, char* uri)
74 {
75 resourceType_ = resourceType;
76 if (resourceType == MEDIA_LIBRARY_IMAGE_RESOURCE) {
77 destImageUri_ = uri;
78 destVideoUri_ = nullptr;
79 MEDIA_DEBUG_LOG("Request content with uri destImageUri_ = %{public}s", destImageUri_);
80 } else if (resourceType == MEDIA_LIBRARY_VIDEO_RESOURCE) {
81 destImageUri_ = nullptr;
82 destVideoUri_ = uri;
83 MEDIA_DEBUG_LOG("Request content with uri destVideoUri_ = %{public}s", destVideoUri_);
84 } else {
85 MEDIA_ERR_LOG("Request content with uri, invalid resourceType");
86 destImageUri_ = nullptr;
87 destVideoUri_ = nullptr;
88 return MEDIA_LIBRARY_PARAMETER_ERROR;
89 }
90 int32_t ret = RequestContentToSandbox();
91 CHECK_AND_RETURN_RET_LOG(ret == E_OK, MEDIA_LIBRARY_OPERATION_NOT_SUPPORTED, "RequestContentToSandbox failed");
92
93 return MEDIA_LIBRARY_OK;
94 }
95
RequestContentWithBuffer(MediaLibrary_ResourceType resourceType,const uint8_t ** buffer,uint32_t * size)96 MediaLibrary_ErrorCode MovingPhotoImpl::RequestContentWithBuffer(MediaLibrary_ResourceType resourceType,
97 const uint8_t** buffer, uint32_t* size)
98 {
99 resourceType_ = resourceType;
100 int32_t ret = RequestContentToArrayBuffer();
101 CHECK_AND_RETURN_RET_LOG(ret == E_OK, MEDIA_LIBRARY_OPERATION_NOT_SUPPORTED, "RequestContentToArrayBuffer failed");
102
103 if (arrayBufferLength_ <= 0) {
104 MEDIA_ERR_LOG("arrayBufferLength equals 0,ivalid buffer length");
105 return MEDIA_LIBRARY_OPERATION_NOT_SUPPORTED;
106 }
107
108 *buffer = reinterpret_cast<const uint8_t*>(arrayBufferData_);
109 *size = arrayBufferLength_;
110 MEDIA_INFO_LOG("Request content buffer size = %{public}zu", arrayBufferLength_);
111 return MEDIA_LIBRARY_OK;
112 }
113
RequestContentToSandbox()114 int32_t MovingPhotoImpl::RequestContentToSandbox()
115 {
116 std::string movingPhotoUri = imageUri_;
117 if (sourceMode_ == SourceMode::ORIGINAL_MODE) {
118 MediaFileUtils::UriAppendKeyValue(movingPhotoUri, MEDIA_OPERN_KEYWORD, SOURCE_REQUEST);
119 }
120
121 if (destImageUri_ && strlen(destImageUri_) > 0) {
122 MEDIA_DEBUG_LOG("Sandbox image movingPhotoUri = %{public}s, destImageUri_ = %{public}s",
123 movingPhotoUri.c_str(), destImageUri_);
124 int32_t imageFd = OpenReadOnlyFile(movingPhotoUri, true);
125 CHECK_AND_RETURN_RET_LOG(HandleFd(imageFd), imageFd, "Open source image file failed");
126 std::string imageUri(destImageUri_);
127 int32_t ret = WriteToSandboxUri(imageFd, imageUri);
128 CHECK_AND_RETURN_RET_LOG(ret == E_OK, ret, "Write image to sandbox failed");
129 }
130 if (destVideoUri_ && strlen(destVideoUri_) > 0) {
131 MEDIA_DEBUG_LOG("Sandbox video movingPhotoUri = %{public}s, destVideoUri_ = %{public}s",
132 movingPhotoUri.c_str(), destVideoUri_);
133 int32_t videoFd = OpenReadOnlyFile(movingPhotoUri, false);
134 CHECK_AND_RETURN_RET_LOG(HandleFd(videoFd), videoFd, "Open source video file failed");
135 std::string videoUri(destVideoUri_);
136 int32_t ret = WriteToSandboxUri(videoFd, videoUri);
137 CHECK_AND_RETURN_RET_LOG(ret == E_OK, ret, "Write video to sandbox failed");
138 }
139 MEDIA_INFO_LOG("Request content to sandbox done");
140 return E_OK;
141 }
142
WriteToSandboxUri(int32_t srcFd,std::string & sandboxUri)143 int32_t MovingPhotoImpl::WriteToSandboxUri(int32_t srcFd, std::string& sandboxUri)
144 {
145 UniqueFd srcUniqueFd(srcFd);
146 OHOS::AppFileService::ModuleFileUri::FileUri fileUri(sandboxUri);
147 std::string destPath = fileUri.GetRealPath();
148 MEDIA_INFO_LOG("Dest real path = %{public}s", destPath.c_str());
149 if (!MediaFileUtils::IsFileExists(destPath) && !MediaFileUtils::CreateFile(destPath)) {
150 MEDIA_ERR_LOG("Create empty dest file in sandbox failed, path:%{public}s", destPath.c_str());
151 return E_HAS_FS_ERROR;
152 }
153
154 int32_t destFd = MediaFileUtils::OpenFile(destPath, MEDIA_FILEMODE_READWRITE);
155 if (destFd < 0) {
156 MEDIA_ERR_LOG("Open dest file failed, error: %{public}d", errno);
157 return E_HAS_FS_ERROR;
158 }
159 UniqueFd destUniqueFd(destFd);
160
161 if (ftruncate(destUniqueFd.Get(), 0) == -1) {
162 MEDIA_ERR_LOG("Truncate old file in sandbox failed, error:%{public}d", errno);
163 return E_HAS_FS_ERROR;
164 }
165 return CopyFileFromMediaLibrary(srcUniqueFd.Get(), destUniqueFd.Get());
166 }
167
CopyFileFromMediaLibrary(int32_t srcFd,int32_t destFd)168 int32_t MovingPhotoImpl::CopyFileFromMediaLibrary(int32_t srcFd, int32_t destFd)
169 {
170 constexpr size_t bufferSize = 4096;
171 char buffer[bufferSize];
172 ssize_t bytesRead;
173 ssize_t bytesWritten;
174 while ((bytesRead = read(srcFd, buffer, bufferSize)) > 0) {
175 bytesWritten = write(destFd, buffer, bytesRead);
176 if (bytesWritten != bytesRead) {
177 MEDIA_ERR_LOG("Failed to copy file from srcFd=%{public}d to destFd=%{public}d, errno=%{public}d",
178 srcFd, destFd, errno);
179 return E_HAS_FS_ERROR;
180 }
181 }
182
183 if (bytesRead < 0) {
184 MEDIA_ERR_LOG("Failed to read from srcFd=%{public}d, errno=%{public}d", srcFd, errno);
185 return E_HAS_FS_ERROR;
186 }
187 MEDIA_INFO_LOG("Copy file from media library done");
188 return E_OK;
189 }
190
OpenReadOnlyFile(const std::string & uri,bool isReadImage)191 int32_t MovingPhotoImpl::OpenReadOnlyFile(const std::string& uri, bool isReadImage)
192 {
193 CHECK_AND_RETURN_RET_LOG(!uri.empty(), E_ERR, "Failed to open read only file, uri is empty");
194
195 std::string curUri = uri;
196 bool isMediaLibUri = MediaFileUtils::IsMediaLibraryUri(uri);
197 MEDIA_DEBUG_LOG("isMediaLibUri = %{public}d, isReadImage = %{public}d", isMediaLibUri, isReadImage);
198 if (!isMediaLibUri) {
199 std::vector<std::string> uris;
200 if (!MediaFileUtils::SplitMovingPhotoUri(uri, uris)) {
201 MEDIA_ERR_LOG("Failed to open read only file, split moving photo failed");
202 return -1;
203 }
204 curUri = uris[isReadImage ? MOVING_PHOTO_IMAGE_POS : MOVING_PHOTO_VIDEO_POS];
205 }
206 return isReadImage ? OpenReadOnlyImage(curUri, isMediaLibUri) : OpenReadOnlyVideo(curUri, isMediaLibUri);
207 }
208
OpenReadOnlyImage(const std::string & imageUri,bool isMediaLibUri)209 int32_t MovingPhotoImpl::OpenReadOnlyImage(const std::string& imageUri, bool isMediaLibUri)
210 {
211 if (isMediaLibUri) {
212 Uri uri(imageUri);
213 return UserFileClient::OpenFile(uri, MEDIA_FILEMODE_READONLY);
214 }
215 OHOS::AppFileService::ModuleFileUri::FileUri fileUri(imageUri);
216 std::string realPath = fileUri.GetRealPath();
217 int32_t fd = open(realPath.c_str(), O_RDONLY);
218 CHECK_AND_RETURN_RET_LOG(fd >= 0, E_ERR, "Failed to open read only image file");
219
220 return fd;
221 }
222
HandleFd(int32_t & fd)223 bool MovingPhotoImpl::HandleFd(int32_t& fd)
224 {
225 if (fd == E_ERR) {
226 fd = E_HAS_FS_ERROR;
227 return false;
228 } else if (fd < 0) {
229 MEDIA_ERR_LOG("Open failed due to OpenFile failure, error: %{public}d", fd);
230 return false;
231 }
232 return true;
233 }
234
OpenReadOnlyVideo(const std::string & videoUri,bool isMediaLibUri)235 int32_t MovingPhotoImpl::OpenReadOnlyVideo(const std::string& videoUri, bool isMediaLibUri)
236 {
237 if (isMediaLibUri) {
238 std::string openVideoUri = videoUri;
239 MediaFileUtils::UriAppendKeyValue(openVideoUri, MEDIA_MOVING_PHOTO_OPRN_KEYWORD,
240 OPEN_MOVING_PHOTO_VIDEO);
241 Uri uri(openVideoUri);
242 return UserFileClient::OpenFile(uri, MEDIA_FILEMODE_READONLY);
243 }
244 OHOS::AppFileService::ModuleFileUri::FileUri fileUri(videoUri);
245 std::string realPath = fileUri.GetRealPath();
246 int32_t fd = open(realPath.c_str(), O_RDONLY);
247 if (fd < 0) {
248 MEDIA_ERR_LOG("Failed to open read only video file, errno: %{public}d", errno);
249 return -1;
250 }
251 return fd;
252 }
253
AcquireFdForArrayBuffer()254 int32_t MovingPhotoImpl::AcquireFdForArrayBuffer()
255 {
256 int32_t fd = 0;
257 std::string movingPhotoUri = imageUri_;
258 switch (resourceType_) {
259 case MediaLibrary_ResourceType::MEDIA_LIBRARY_IMAGE_RESOURCE: {
260 fd = OpenReadOnlyFile(movingPhotoUri, true);
261 CHECK_AND_RETURN_RET_LOG(HandleFd(fd), fd, "Open source image file failed");
262 return fd;
263 }
264 case MediaLibrary_ResourceType::MEDIA_LIBRARY_VIDEO_RESOURCE: {
265 fd = OpenReadOnlyFile(movingPhotoUri, false);
266 CHECK_AND_RETURN_RET_LOG(HandleFd(fd), fd, "Open source video file failed");
267 return fd;
268 }
269 default:
270 MEDIA_ERR_LOG("Invalid resource type: %{public}d", static_cast<int32_t>(resourceType_));
271 return -EINVAL;
272 }
273 }
274
RequestContentToArrayBuffer()275 int32_t MovingPhotoImpl::RequestContentToArrayBuffer()
276 {
277 int32_t fd = AcquireFdForArrayBuffer();
278 CHECK_AND_RETURN_RET_LOG(fd >= 0, fd, "Acquire fd for arraybuffer failed");
279
280 UniqueFd uniqueFd(fd);
281 off_t fileLen = lseek(uniqueFd.Get(), 0, SEEK_END);
282 if (fileLen < 0) {
283 MEDIA_ERR_LOG("Failed to get file length, error: %{public}d", errno);
284 return E_HAS_FS_ERROR;
285 }
286
287 off_t ret = lseek(uniqueFd.Get(), 0, SEEK_SET);
288 if (ret < 0) {
289 MEDIA_ERR_LOG("Failed to reset file offset, error: %{public}d", errno);
290 return E_HAS_FS_ERROR;
291 }
292
293 if (static_cast<uint64_t>(fileLen) > static_cast<uint64_t>(SIZE_MAX)) {
294 MEDIA_ERR_LOG("File length is too large to fit in a size_t, length: %{public}zu",
295 static_cast<size_t>(fileLen));
296 return E_HAS_FS_ERROR;
297 }
298
299 size_t fileSize = static_cast<size_t>(fileLen);
300 arrayBufferData_ = malloc(fileSize);
301 if (!arrayBufferData_) {
302 MEDIA_ERR_LOG("Failed to malloc array buffer, moving photo uri is %{public}s, resource type is %{public}d",
303 imageUri_.c_str(), static_cast<int32_t>(resourceType_));
304 return E_HAS_FS_ERROR;
305 }
306 memset_s(arrayBufferData_, fileSize, 0, fileSize);
307
308 ssize_t readBytes = read(uniqueFd.Get(), arrayBufferData_, fileSize);
309 if (readBytes != fileSize) {
310 MEDIA_ERR_LOG("read file failed, read bytes is %{public}zu, actual length is %{public}zu, error: %{public}d",
311 readBytes, fileSize, errno);
312 free(arrayBufferData_);
313 arrayBufferData_ = nullptr;
314 return E_HAS_FS_ERROR;
315 }
316 arrayBufferLength_ = fileSize;
317 return E_OK;
318 }
319