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