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 "moving_photo_impl.h"
17
18 #include <fcntl.h>
19 #include <unistd.h>
20
21 #include "directory_ex.h"
22 #include "file_uri.h"
23 #include "media_file_utils.h"
24 #include "medialibrary_errno.h"
25 #include "userfile_client.h"
26 #include "userfile_manager_types.h"
27
28 using namespace std;
29
30 namespace OHOS {
31 namespace Media {
FfiMovingPhotoImpl(const string & photoUri,SourceMode sourceMode)32 FfiMovingPhotoImpl::FfiMovingPhotoImpl(const string& photoUri, SourceMode sourceMode)
33 {
34 this->photoUri_ = photoUri;
35 this->sourceMode_ = sourceMode;
36 }
37
GetUri()38 string FfiMovingPhotoImpl::GetUri()
39 {
40 return photoUri_;
41 }
42
GetSourceMode()43 SourceMode FfiMovingPhotoImpl::GetSourceMode()
44 {
45 return sourceMode_;
46 }
47
SetSourceMode(SourceMode sourceMode)48 void FfiMovingPhotoImpl::SetSourceMode(SourceMode sourceMode)
49 {
50 this->sourceMode_ = sourceMode;
51 }
52
OpenReadOnlyImage(const std::string & imageUri,bool isMediaLibUri)53 static int32_t OpenReadOnlyImage(const std::string& imageUri, bool isMediaLibUri)
54 {
55 if (isMediaLibUri) {
56 Uri uri(imageUri);
57 return UserFileClient::OpenFile(uri, MEDIA_FILEMODE_READONLY);
58 }
59 AppFileService::ModuleFileUri::FileUri fileUri(imageUri);
60 std::string realPath = fileUri.GetRealPath();
61 int32_t fd = open(realPath.c_str(), O_RDONLY);
62 if (fd < 0) {
63 LOGE("Failed to open read only image file");
64 return E_ERR;
65 }
66 return fd;
67 }
68
OpenReadOnlyVideo(const std::string & videoUri,bool isMediaLibUri)69 static int32_t OpenReadOnlyVideo(const std::string& videoUri, bool isMediaLibUri)
70 {
71 if (isMediaLibUri) {
72 std::string openVideoUri = videoUri;
73 MediaFileUtils::UriAppendKeyValue(openVideoUri, MEDIA_MOVING_PHOTO_OPRN_KEYWORD,
74 OPEN_MOVING_PHOTO_VIDEO);
75 Uri uri(openVideoUri);
76 return UserFileClient::OpenFile(uri, MEDIA_FILEMODE_READONLY);
77 }
78 AppFileService::ModuleFileUri::FileUri fileUri(videoUri);
79 std::string realPath = fileUri.GetRealPath();
80 int32_t fd = open(realPath.c_str(), O_RDONLY);
81 if (fd < 0) {
82 LOGE("Failed to open read only video file");
83 return E_ERR;
84 }
85 return fd;
86 }
87
HandleFd(int32_t & fd)88 static bool HandleFd(int32_t& fd)
89 {
90 if (fd == E_ERR) {
91 fd = E_HAS_FS_ERROR;
92 return false;
93 } else if (fd < 0) {
94 LOGE("Open failed due to OpenFile failure, error: %{public}d", fd);
95 return false;
96 }
97 return true;
98 }
99
OpenReadOnlyFile(const string & uri,bool isReadImage)100 int32_t FfiMovingPhotoImpl::OpenReadOnlyFile(const string& uri, bool isReadImage)
101 {
102 if (uri.empty()) {
103 LOGE("Failed to open read only file, uri is empty");
104 return E_ERR;
105 }
106 std::string curUri = uri;
107 bool isMediaLibUri = MediaFileUtils::IsMediaLibraryUri(uri);
108 if (!isMediaLibUri) {
109 std::vector<std::string> uris;
110 if (!MediaFileUtils::SplitMovingPhotoUri(uri, uris)) {
111 LOGE("Failed to open read only file, split moving photo failed");
112 return E_ERR;
113 }
114 curUri = uris[isReadImage ? MOVING_PHOTO_IMAGE_POS : MOVING_PHOTO_VIDEO_POS];
115 }
116 return isReadImage ? OpenReadOnlyImage(curUri, isMediaLibUri) : OpenReadOnlyVideo(curUri, isMediaLibUri);
117 }
118
OpenReadOnlyLivePhoto(const string & destLivePhotoUri)119 int32_t FfiMovingPhotoImpl::OpenReadOnlyLivePhoto(const string& destLivePhotoUri)
120 {
121 if (destLivePhotoUri.empty()) {
122 LOGE("Failed to open read only file, uri is empty");
123 return E_ERR;
124 }
125 if (MediaFileUtils::IsMediaLibraryUri(destLivePhotoUri)) {
126 string livePhotoUri = destLivePhotoUri;
127 MediaFileUtils::UriAppendKeyValue(livePhotoUri, MEDIA_MOVING_PHOTO_OPRN_KEYWORD,
128 OPEN_PRIVATE_LIVE_PHOTO);
129 Uri uri(livePhotoUri);
130 return UserFileClient::OpenFile(uri, MEDIA_FILEMODE_READONLY);
131 }
132 return E_ERR;
133 }
134
CopyFileFromMediaLibrary(int32_t srcFd,int32_t destFd)135 static int32_t CopyFileFromMediaLibrary(int32_t srcFd, int32_t destFd)
136 {
137 size_t bufferSize = 4096;
138 char buffer[bufferSize];
139 ssize_t bytesRead;
140 ssize_t bytesWritten;
141 while ((bytesRead = read(srcFd, buffer, bufferSize)) > 0) {
142 bytesWritten = write(destFd, buffer, bytesRead);
143 if (bytesWritten != bytesRead) {
144 LOGE("Failed to copy file from srcFd=%{public}d to destFd=%{public}d, errno=%{public}d",
145 srcFd, destFd, errno);
146 return E_HAS_FS_ERROR;
147 }
148 }
149
150 if (bytesRead < 0) {
151 LOGE("Failed to read from srcFd=%{public}d, errno=%{public}d", srcFd, errno);
152 return E_HAS_FS_ERROR;
153 }
154 return E_OK;
155 }
156
WriteToSandboxUri(int32_t srcFd,const string & sandboxUri)157 static int32_t WriteToSandboxUri(int32_t srcFd, const string& sandboxUri)
158 {
159 UniqueFd srcUniqueFd(srcFd);
160 AppFileService::ModuleFileUri::FileUri fileUri(sandboxUri);
161 string destPath = fileUri.GetRealPath();
162 if (!MediaFileUtils::IsFileExists(destPath) && !MediaFileUtils::CreateFile(destPath)) {
163 LOGE("Create empty dest file in sandbox failed, path:%{private}s", destPath.c_str());
164 return E_HAS_FS_ERROR;
165 }
166 int32_t destFd = MediaFileUtils::OpenFile(destPath, MEDIA_FILEMODE_READWRITE);
167 if (destFd < 0) {
168 LOGE("Open dest file failed, error: %{public}d", errno);
169 return E_HAS_FS_ERROR;
170 }
171 UniqueFd destUniqueFd(destFd);
172 if (ftruncate(destUniqueFd.Get(), 0) == -1) {
173 LOGE("Truncate old file in sandbox failed, error:%{public}d", errno);
174 return E_HAS_FS_ERROR;
175 }
176 return CopyFileFromMediaLibrary(srcUniqueFd.Get(), destUniqueFd.Get());
177 }
178
RequestContentToSandbox(const string & destImageUri,const string & destVideoUri,string movingPhotoUri,SourceMode sourceMode)179 static int32_t RequestContentToSandbox(const string &destImageUri, const string &destVideoUri,
180 string movingPhotoUri, SourceMode sourceMode)
181 {
182 if (sourceMode == SourceMode::ORIGINAL_MODE) {
183 MediaFileUtils::UriAppendKeyValue(movingPhotoUri, MEDIA_OPERN_KEYWORD, SOURCE_REQUEST);
184 }
185 if (!destImageUri.empty()) {
186 int32_t imageFd = FfiMovingPhotoImpl::OpenReadOnlyFile(movingPhotoUri, true);
187 if (!HandleFd(imageFd)) {
188 LOGE("Open source image file failed");
189 return imageFd;
190 }
191 int32_t ret = WriteToSandboxUri(imageFd, destImageUri);
192 if (ret != E_OK) {
193 LOGE("Write image to sandbox failed");
194 return ret;
195 }
196 }
197 if (!destVideoUri.empty()) {
198 int32_t videoFd = FfiMovingPhotoImpl::OpenReadOnlyFile(movingPhotoUri, false);
199 if (!HandleFd(videoFd)) {
200 LOGE("Open source video file failed");
201 return videoFd;
202 }
203 int32_t ret = WriteToSandboxUri(videoFd, destVideoUri);
204 if (ret != E_OK) {
205 LOGE("Write video to sandbox failed");
206 return ret;
207 }
208 }
209 return E_OK;
210 }
211
IsValidResourceType(int32_t resourceType)212 static bool IsValidResourceType(int32_t resourceType)
213 {
214 // public API only support IMAGE_RESOURCE/VIDEO_RESOURCE
215 return (resourceType == static_cast<int>(ResourceType::IMAGE_RESOURCE) ||
216 resourceType == static_cast<int>(ResourceType::VIDEO_RESOURCE));
217 }
218
RequestContent(char * imageFileUri,char * videoFileUri,int32_t & errCode)219 void FfiMovingPhotoImpl::RequestContent(char* imageFileUri, char* videoFileUri, int32_t &errCode)
220 {
221 // write both image and video to sandbox
222 string destImageUri(imageFileUri);
223 string destVideoUri(videoFileUri);
224 int32_t ret = RequestContentToSandbox(destImageUri, destVideoUri, photoUri_, sourceMode_);
225 if (ret != E_OK) {
226 errCode = static_cast<int32_t>(MediaLibraryNapiUtils::TransErrorCode("RequestContent", ret));
227 }
228 }
229
RequestContent(int32_t resourceType,char * fileUri,int32_t & errCode)230 void FfiMovingPhotoImpl::RequestContent(int32_t resourceType, char* fileUri, int32_t &errCode)
231 {
232 string destImageUri = "";
233 string destVideoUri = "";
234 if (!IsValidResourceType(resourceType)) {
235 LOGE("Invalid resource type");
236 errCode = OHOS_INVALID_PARAM_CODE;
237 return;
238 }
239 if (resourceType == static_cast<int>(ResourceType::IMAGE_RESOURCE)) {
240 destImageUri = string(fileUri);
241 } else {
242 destVideoUri = string(fileUri);
243 }
244 int32_t ret = RequestContentToSandbox(destImageUri, destVideoUri, photoUri_, sourceMode_);
245 if (ret != E_OK) {
246 errCode = static_cast<int32_t>(MediaLibraryNapiUtils::TransErrorCode("RequestContent", ret));
247 }
248 }
249
AcquireFdForArrayBuffer(string movingPhotoUri,SourceMode sourceMode,ResourceType resourceType)250 static int32_t AcquireFdForArrayBuffer(string movingPhotoUri, SourceMode sourceMode, ResourceType resourceType)
251 {
252 int32_t fd = 0;
253 if (sourceMode == SourceMode::ORIGINAL_MODE) {
254 MediaFileUtils::UriAppendKeyValue(movingPhotoUri, MEDIA_OPERN_KEYWORD, SOURCE_REQUEST);
255 }
256 switch (resourceType) {
257 case ResourceType::IMAGE_RESOURCE:
258 fd = FfiMovingPhotoImpl::OpenReadOnlyFile(movingPhotoUri, true);
259 if (!HandleFd(fd)) {
260 LOGE("Open source image file failed");
261 }
262 return fd;
263 case ResourceType::VIDEO_RESOURCE:
264 fd = FfiMovingPhotoImpl::OpenReadOnlyFile(movingPhotoUri, false);
265 if (!HandleFd(fd)) {
266 LOGE("Open source video file failed");
267 }
268 return fd;
269 default:
270 LOGE("Invalid resource type: %{public}d", static_cast<int32_t>(resourceType));
271 return -EINVAL;
272 }
273 }
274
RequestContentToArrayBuffer(int32_t resourceType,string movingPhotoUri,SourceMode sourceMode,CArrUI8 & result)275 static int32_t RequestContentToArrayBuffer(int32_t resourceType, string movingPhotoUri,
276 SourceMode sourceMode, CArrUI8 &result)
277 {
278 int32_t fd = AcquireFdForArrayBuffer(movingPhotoUri, sourceMode, static_cast<ResourceType>(resourceType));
279 if (fd < 0) {
280 return fd;
281 }
282 UniqueFd uniqueFd(fd);
283 off_t fileLen = lseek(uniqueFd.Get(), 0, SEEK_END);
284 if (fileLen < 0) {
285 LOGE("Failed to get file length, error: %{public}d", errno);
286 return E_HAS_FS_ERROR;
287 }
288 off_t ret = lseek(uniqueFd.Get(), 0, SEEK_SET);
289 if (ret < 0) {
290 LOGE("Failed to reset file offset, error: %{public}d", errno);
291 return E_HAS_FS_ERROR;
292 }
293 size_t fileSize = static_cast<size_t>(fileLen);
294 void* arrayBufferData = malloc(fileSize);
295 if (!arrayBufferData) {
296 LOGE("Failed to malloc array buffer data, moving photo uri is %{public}s, resource type is %{public}d",
297 movingPhotoUri.c_str(), resourceType);
298 return E_HAS_FS_ERROR;
299 }
300 size_t readBytes = static_cast<size_t>(read(uniqueFd.Get(), arrayBufferData, fileSize));
301 if (readBytes != fileSize) {
302 LOGE("read file failed, read bytes is %{public}zu, actual length is %{public}zu, "
303 "error: %{public}d", readBytes, fileSize, errno);
304 free(arrayBufferData);
305 return E_HAS_FS_ERROR;
306 }
307 if (fileSize > 0) {
308 if (arrayBufferData == nullptr) {
309 LOGE("get arrayBuffer failed.");
310 return E_HAS_FS_ERROR;
311 }
312 result.head = static_cast<uint8_t*>(arrayBufferData);
313 result.size = static_cast<int64_t>(fileSize);
314 }
315 return E_OK;
316 }
317
RequestContent(int32_t resourceType,int32_t & errCode)318 CArrUI8 FfiMovingPhotoImpl::RequestContent(int32_t resourceType, int32_t &errCode)
319 {
320 CArrUI8 result = {
321 .head = nullptr,
322 .size = 0
323 };
324 if (!IsValidResourceType(resourceType)) {
325 LOGE("Invalid resource type");
326 errCode = OHOS_INVALID_PARAM_CODE;
327 return result;
328 }
329 int32_t ret = RequestContentToArrayBuffer(resourceType, photoUri_, sourceMode_, result);
330 if (ret != E_OK) {
331 errCode = static_cast<int32_t>(MediaLibraryNapiUtils::TransErrorCode("RequestContent", ret));
332 }
333 return result;
334 }
335 }
336 }