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