1 /*
2 * Copyright (C) 2023 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 #define MLOG_TAG "MediaAssetManagerNapi"
17
18 #include "media_asset_manager_napi.h"
19
20 #include <fcntl.h>
21 #include <string>
22 #include <sys/sendfile.h>
23 #include <unordered_map>
24 #include <uuid/uuid.h>
25
26 #include "access_token.h"
27 #include "accesstoken_kit.h"
28 #include "dataobs_mgr_client.h"
29 #include "directory_ex.h"
30 #include "file_asset_napi.h"
31 #include "file_uri.h"
32 #include "image_source.h"
33 #include "image_source_napi.h"
34 #include "ipc_skeleton.h"
35 #include "media_column.h"
36 #include "media_file_utils.h"
37 #include "media_file_uri.h"
38 #include "medialibrary_client_errno.h"
39 #include "media_library_napi.h"
40 #include "medialibrary_errno.h"
41 #include "medialibrary_napi_log.h"
42 #include "medialibrary_napi_utils.h"
43 #include "medialibrary_napi_utils_ext.h"
44 #include "medialibrary_tracer.h"
45 #include "moving_photo_napi.h"
46 #include "permission_utils.h"
47 #include "picture_handle_client.h"
48 #include "ui_extension_context.h"
49 #include "userfile_client.h"
50
51 using namespace OHOS::Security::AccessToken;
52
53 namespace OHOS {
54 namespace Media {
55 static const std::string MEDIA_ASSET_MANAGER_CLASS = "MediaAssetManager";
56 static std::mutex multiStagesCaptureLock;
57 static std::mutex registerTaskLock;
58
59 const int32_t LOW_QUALITY_IMAGE = 1;
60 const int32_t HIGH_QUALITY_IMAGE = 0;
61
62 const int32_t UUID_STR_LENGTH = 37;
63 const int32_t REQUEST_ID_MAX_LEN = 64;
64 const int32_t MAX_URI_SIZE = 384; // 256 for display name and 128 for relative path
65
66 thread_local unique_ptr<ChangeListenerNapi> g_multiStagesRequestListObj = nullptr;
67 thread_local napi_ref constructor_ = nullptr;
68
69 static std::map<std::string, std::shared_ptr<MultiStagesTaskObserver>> multiStagesObserverMap;
70 static std::map<std::string, std::map<std::string, AssetHandler*>> inProcessUriMap;
71 static SafeMap<std::string, AssetHandler*> inProcessFastRequests;
72
Init(napi_env env,napi_value exports)73 napi_value MediaAssetManagerNapi::Init(napi_env env, napi_value exports)
74 {
75 NapiClassInfo info = {.name = MEDIA_ASSET_MANAGER_CLASS,
76 .ref = &constructor_,
77 .constructor = Constructor,
78 .props = {
79 DECLARE_NAPI_STATIC_FUNCTION("requestImage", JSRequestImage),
80 DECLARE_NAPI_STATIC_FUNCTION("requestImageData", JSRequestImageData),
81 DECLARE_NAPI_STATIC_FUNCTION("requestMovingPhoto", JSRequestMovingPhoto),
82 DECLARE_NAPI_STATIC_FUNCTION("cancelRequest", JSCancelRequest),
83 DECLARE_NAPI_STATIC_FUNCTION("requestVideoFile", JSRequestVideoFile),
84 DECLARE_NAPI_STATIC_FUNCTION("loadMovingPhoto", JSLoadMovingPhoto),
85 DECLARE_NAPI_STATIC_FUNCTION("quickRequestImage", JSRequestEfficientIImage)
86 }};
87 MediaLibraryNapiUtils::NapiDefineClass(env, exports, info);
88 return exports;
89 }
90
Constructor(napi_env env,napi_callback_info info)91 napi_value MediaAssetManagerNapi::Constructor(napi_env env, napi_callback_info info)
92 {
93 napi_value newTarget = nullptr;
94 CHECK_ARGS(env, napi_get_new_target(env, info, &newTarget), JS_INNER_FAIL);
95 bool isConstructor = newTarget != nullptr;
96 if (isConstructor) {
97 napi_value thisVar = nullptr;
98 unique_ptr<MediaAssetManagerNapi> obj = make_unique<MediaAssetManagerNapi>();
99 CHECK_COND_WITH_MESSAGE(env, obj != nullptr, "Create MediaAssetManagerNapi failed");
100 CHECK_ARGS(env,
101 napi_wrap(env, thisVar, reinterpret_cast<void*>(obj.get()), MediaAssetManagerNapi::Destructor,
102 nullptr, nullptr),
103 JS_INNER_FAIL);
104 obj.release();
105 return thisVar;
106 }
107 napi_value constructor = nullptr;
108 napi_value result = nullptr;
109 NAPI_CALL(env, napi_get_reference_value(env, constructor_, &constructor));
110 NAPI_CALL(env, napi_new_instance(env, constructor, 0, nullptr, &result));
111 return result;
112 }
113
Destructor(napi_env env,void * nativeObject,void * finalizeHint)114 void MediaAssetManagerNapi::Destructor(napi_env env, void *nativeObject, void *finalizeHint)
115 {
116 auto* mediaAssetManager = reinterpret_cast<MediaAssetManagerNapi*>(nativeObject);
117 if (mediaAssetManager != nullptr) {
118 delete mediaAssetManager;
119 mediaAssetManager = nullptr;
120 }
121 }
122
CreateAssetHandler(const std::string & photoId,const std::string & requestId,const std::string & uri,const MediaAssetDataHandlerPtr & handler,napi_threadsafe_function func)123 static AssetHandler* CreateAssetHandler(const std::string &photoId, const std::string &requestId,
124 const std::string &uri, const MediaAssetDataHandlerPtr &handler, napi_threadsafe_function func)
125 {
126 AssetHandler *assetHandler = new AssetHandler(photoId, requestId, uri, handler, func);
127 NAPI_DEBUG_LOG("[AssetHandler create] photoId: %{public}s, requestId: %{public}s, uri: %{public}s, %{public}p.",
128 photoId.c_str(), requestId.c_str(), uri.c_str(), assetHandler);
129 return assetHandler;
130 }
131
DeleteAssetHandlerSafe(AssetHandler * handler,napi_env env)132 static void DeleteAssetHandlerSafe(AssetHandler *handler, napi_env env)
133 {
134 if (handler != nullptr) {
135 NAPI_DEBUG_LOG("[AssetHandler delete] %{public}p.", handler);
136 handler->dataHandler->DeleteNapiReference(env);
137 delete handler;
138 handler = nullptr;
139 }
140 }
141
HasReadPermission()142 static bool HasReadPermission()
143 {
144 AccessTokenID tokenCaller = IPCSkeleton::GetSelfTokenID();
145 int result = AccessTokenKit::VerifyAccessToken(tokenCaller, PERM_READ_IMAGEVIDEO);
146 return result == PermissionState::PERMISSION_GRANTED;
147 }
148
InsertInProcessMapRecord(const std::string & requestUri,const std::string & requestId,AssetHandler * handler)149 static void InsertInProcessMapRecord(const std::string &requestUri, const std::string &requestId,
150 AssetHandler *handler)
151 {
152 std::lock_guard<std::mutex> lock(multiStagesCaptureLock);
153 std::map<std::string, AssetHandler*> assetHandler;
154 auto uriLocal = MediaFileUtils::GetUriWithoutDisplayname(requestUri);
155 if (inProcessUriMap.find(uriLocal) != inProcessUriMap.end()) {
156 assetHandler = inProcessUriMap[uriLocal];
157 assetHandler[requestId] = handler;
158 inProcessUriMap[uriLocal] = assetHandler;
159 } else {
160 assetHandler[requestId] = handler;
161 inProcessUriMap[uriLocal] = assetHandler;
162 }
163 }
164
DeleteInProcessMapRecord(const std::string & requestUri,const std::string & requestId)165 static void DeleteInProcessMapRecord(const std::string &requestUri, const std::string &requestId)
166 {
167 std::lock_guard<std::mutex> lock(multiStagesCaptureLock);
168 auto uriLocal = MediaFileUtils::GetUriWithoutDisplayname(requestUri);
169 if (inProcessUriMap.find(uriLocal) == inProcessUriMap.end()) {
170 return;
171 }
172
173 std::map<std::string, AssetHandler*> assetHandlers = inProcessUriMap[uriLocal];
174 if (assetHandlers.find(requestId) == assetHandlers.end()) {
175 return;
176 }
177
178 assetHandlers.erase(requestId);
179 if (!assetHandlers.empty()) {
180 inProcessUriMap[uriLocal] = assetHandlers;
181 return;
182 }
183
184 inProcessUriMap.erase(uriLocal);
185
186 if (multiStagesObserverMap.find(uriLocal) != multiStagesObserverMap.end()) {
187 UserFileClient::UnregisterObserverExt(Uri(uriLocal),
188 static_cast<std::shared_ptr<DataShare::DataShareObserver>>(multiStagesObserverMap[uriLocal]));
189 }
190 multiStagesObserverMap.erase(uriLocal);
191 }
192
IsInProcessInMapRecord(const std::string & requestId,AssetHandler * & handler)193 static int32_t IsInProcessInMapRecord(const std::string &requestId, AssetHandler* &handler)
194 {
195 std::lock_guard<std::mutex> lock(multiStagesCaptureLock);
196 for (auto record : inProcessUriMap) {
197 if (record.second.find(requestId) != record.second.end()) {
198 handler = record.second[requestId];
199 return true;
200 }
201 }
202
203 return false;
204 }
205
InsertDataHandler(NotifyMode notifyMode,napi_env env,MediaAssetManagerAsyncContext * asyncContext)206 static AssetHandler* InsertDataHandler(NotifyMode notifyMode, napi_env env,
207 MediaAssetManagerAsyncContext *asyncContext)
208 {
209 napi_ref dataHandlerRef;
210 napi_threadsafe_function threadSafeFunc;
211 if (notifyMode == NotifyMode::FAST_NOTIFY) {
212 dataHandlerRef = asyncContext->dataHandlerRef;
213 asyncContext->dataHandlerRef = nullptr;
214 threadSafeFunc = asyncContext->onDataPreparedPtr;
215 } else {
216 dataHandlerRef = asyncContext->dataHandlerRef2;
217 asyncContext->dataHandlerRef2 = nullptr;
218 threadSafeFunc = asyncContext->onDataPreparedPtr2;
219 }
220 std::shared_ptr<NapiMediaAssetDataHandler> mediaAssetDataHandler = make_shared<NapiMediaAssetDataHandler>(
221 env, dataHandlerRef, asyncContext->returnDataType, asyncContext->photoUri, asyncContext->destUri,
222 asyncContext->sourceMode);
223 mediaAssetDataHandler->SetNotifyMode(notifyMode);
224
225 AssetHandler *assetHandler = CreateAssetHandler(asyncContext->photoId, asyncContext->requestId,
226 asyncContext->photoUri, mediaAssetDataHandler, threadSafeFunc);
227 assetHandler->photoQuality = asyncContext->photoQuality;
228 assetHandler->needsExtraInfo = asyncContext->needsExtraInfo;
229 NAPI_INFO_LOG("Add %{public}d, %{public}s, %{public}s", notifyMode, asyncContext->photoUri.c_str(),
230 asyncContext->requestId.c_str());
231
232 switch (notifyMode) {
233 case NotifyMode::FAST_NOTIFY: {
234 inProcessFastRequests.EnsureInsert(asyncContext->requestId, assetHandler);
235 break;
236 }
237 case NotifyMode::WAIT_FOR_HIGH_QUALITY: {
238 InsertInProcessMapRecord(asyncContext->photoUri, asyncContext->requestId, assetHandler);
239 break;
240 }
241 default:
242 break;
243 }
244
245 return assetHandler;
246 }
247
DeleteDataHandler(NotifyMode notifyMode,const std::string & requestUri,const std::string & requestId)248 static void DeleteDataHandler(NotifyMode notifyMode, const std::string &requestUri, const std::string &requestId)
249 {
250 auto uriLocal = MediaFileUtils::GetUriWithoutDisplayname(requestUri);
251 NAPI_INFO_LOG("Rmv %{public}d, %{public}s, %{public}s", notifyMode, requestUri.c_str(), requestId.c_str());
252 if (notifyMode == NotifyMode::WAIT_FOR_HIGH_QUALITY) {
253 DeleteInProcessMapRecord(uriLocal, requestId);
254 }
255 inProcessFastRequests.Erase(requestId);
256 }
257
QueryPhotoStatus(int fileId,const string & photoUri,std::string & photoId,bool hasReadPermission)258 MultiStagesCapturePhotoStatus MediaAssetManagerNapi::QueryPhotoStatus(int fileId,
259 const string& photoUri, std::string &photoId, bool hasReadPermission)
260 {
261 photoId = "";
262 DataShare::DataSharePredicates predicates;
263 predicates.EqualTo(MediaColumn::MEDIA_ID, fileId);
264 std::vector<std::string> fetchColumn { PhotoColumn::PHOTO_QUALITY, PhotoColumn::PHOTO_ID};
265 string queryUri;
266 if (hasReadPermission) {
267 queryUri = PAH_QUERY_PHOTO;
268 } else {
269 queryUri = photoUri;
270 MediaFileUri::RemoveAllFragment(queryUri);
271 }
272 Uri uri(queryUri);
273 int errCode = 0;
274 auto resultSet = UserFileClient::Query(uri, predicates, fetchColumn, errCode);
275 if (resultSet == nullptr || resultSet->GoToFirstRow() != E_OK) {
276 NAPI_ERR_LOG("query resultSet is nullptr");
277 return MultiStagesCapturePhotoStatus::HIGH_QUALITY_STATUS;
278 }
279 int indexOfPhotoId = -1;
280 resultSet->GetColumnIndex(PhotoColumn::PHOTO_ID, indexOfPhotoId);
281 resultSet->GetString(indexOfPhotoId, photoId);
282
283 int columnIndexQuality = -1;
284 resultSet->GetColumnIndex(PhotoColumn::PHOTO_QUALITY, columnIndexQuality);
285 int currentPhotoQuality = HIGH_QUALITY_IMAGE;
286 resultSet->GetInt(columnIndexQuality, currentPhotoQuality);
287 if (currentPhotoQuality == LOW_QUALITY_IMAGE) {
288 NAPI_DEBUG_LOG("query photo status : lowQuality");
289 return MultiStagesCapturePhotoStatus::LOW_QUALITY_STATUS;
290 }
291 NAPI_DEBUG_LOG("query photo status quality: %{public}d", currentPhotoQuality);
292 return MultiStagesCapturePhotoStatus::HIGH_QUALITY_STATUS;
293 }
294
ProcessImage(const int fileId,const int deliveryMode)295 void MediaAssetManagerNapi::ProcessImage(const int fileId, const int deliveryMode)
296 {
297 std::string uriStr = PAH_PROCESS_IMAGE;
298 MediaLibraryNapiUtils::UriAppendKeyValue(uriStr, API_VERSION, to_string(MEDIA_API_VERSION_V10));
299 Uri uri(uriStr);
300 DataShare::DataSharePredicates predicates;
301 int errCode = 0;
302 std::vector<std::string> columns { std::to_string(fileId), std::to_string(deliveryMode) };
303 UserFileClient::Query(uri, predicates, columns, errCode);
304 }
305
CancelProcessImage(const std::string & photoId)306 void MediaAssetManagerNapi::CancelProcessImage(const std::string &photoId)
307 {
308 std::string uriStr = PAH_CANCEL_PROCESS_IMAGE;
309 MediaLibraryNapiUtils::UriAppendKeyValue(uriStr, API_VERSION, to_string(MEDIA_API_VERSION_V10));
310 Uri uri(uriStr);
311 DataShare::DataSharePredicates predicates;
312 int errCode = 0;
313 std::vector<std::string> columns { photoId };
314 UserFileClient::Query(uri, predicates, columns, errCode);
315 }
316
AddImage(const int fileId,DeliveryMode deliveryMode)317 void MediaAssetManagerNapi::AddImage(const int fileId, DeliveryMode deliveryMode)
318 {
319 Uri updateAssetUri(PAH_ADD_IMAGE);
320 DataShare::DataSharePredicates predicates;
321 DataShare::DataShareValuesBucket valuesBucket;
322 valuesBucket.Put(MediaColumn::MEDIA_ID, fileId);
323 valuesBucket.Put("deliveryMode", static_cast<int>(deliveryMode));
324 UserFileClient::Update(updateAssetUri, predicates, valuesBucket);
325 }
326
GetDeliveryMode(napi_env env,const napi_value arg,const string & propName,DeliveryMode & deliveryMode)327 napi_status GetDeliveryMode(napi_env env, const napi_value arg, const string &propName,
328 DeliveryMode& deliveryMode)
329 {
330 bool present = false;
331 napi_value property = nullptr;
332 int mode = -1;
333 CHECK_STATUS_RET(napi_has_named_property(env, arg, propName.c_str(), &present),
334 "Failed to check property name");
335 if (!present) {
336 NAPI_ERR_LOG("No delivery mode specified");
337 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "No delivery mode specified");
338 return napi_invalid_arg;
339 }
340 CHECK_STATUS_RET(napi_get_named_property(env, arg, propName.c_str(), &property), "Failed to get property");
341 CHECK_STATUS_RET(napi_get_value_int32(env, property, &mode), "Failed to parse deliveryMode argument value");
342
343 // delivery mode's valid range is 0 - 2
344 if (mode < 0 || mode > 2) {
345 NAPI_ERR_LOG("delivery mode invalid argument ");
346 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "invalid delivery mode value");
347 return napi_invalid_arg;
348 }
349 deliveryMode = static_cast<DeliveryMode>(mode);
350 return napi_ok;
351 }
352
GetSourceMode(napi_env env,const napi_value arg,const string & propName,SourceMode & sourceMode)353 napi_status GetSourceMode(napi_env env, const napi_value arg, const string &propName,
354 SourceMode& sourceMode)
355 {
356 bool present = false;
357 napi_value property = nullptr;
358 int mode = -1;
359 CHECK_STATUS_RET(napi_has_named_property(env, arg, propName.c_str(), &present), "Failed to check property name");
360 if (!present) {
361 // use default source mode
362 sourceMode = SourceMode::EDITED_MODE;
363 return napi_ok;
364 } else if (!MediaLibraryNapiUtils::IsSystemApp()) {
365 NAPI_ERR_LOG("Source mode is only available to system apps");
366 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "Source mode is only available to system apps");
367 return napi_invalid_arg;
368 }
369 CHECK_STATUS_RET(napi_get_named_property(env, arg, propName.c_str(), &property), "Failed to get property");
370 CHECK_STATUS_RET(napi_get_value_int32(env, property, &mode), "Failed to parse sourceMode argument value");
371
372 // source mode's valid range is 0 - 1
373 if (mode < 0 || mode > 1) {
374 NAPI_ERR_LOG("source mode invalid");
375 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "invalid source mode value");
376 return napi_invalid_arg;
377 }
378 sourceMode = static_cast<SourceMode>(mode);
379 return napi_ok;
380 }
381
ParseArgGetRequestOption(napi_env env,napi_value arg,DeliveryMode & deliveryMode,SourceMode & sourceMode)382 napi_status ParseArgGetRequestOption(napi_env env, napi_value arg, DeliveryMode &deliveryMode, SourceMode &sourceMode)
383 {
384 CHECK_STATUS_RET(GetDeliveryMode(env, arg, "deliveryMode", deliveryMode), "Failed to parse deliveryMode");
385 CHECK_STATUS_RET(GetSourceMode(env, arg, "sourceMode", sourceMode), "Failed to parse sourceMode");
386 return napi_ok;
387 }
388
ParseArgGetPhotoAsset(napi_env env,napi_value arg,int & fileId,std::string & uri,std::string & displayName)389 napi_status ParseArgGetPhotoAsset(napi_env env, napi_value arg, int &fileId, std::string &uri,
390 std::string &displayName)
391 {
392 if (arg == nullptr) {
393 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "ParseArgGetPhotoAsset failed to get photoAsset");
394 return napi_invalid_arg;
395 }
396 FileAssetNapi *obj = nullptr;
397 napi_unwrap(env, arg, reinterpret_cast<void**>(&obj));
398 if (obj == nullptr) {
399 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "failed to get asset napi object");
400 return napi_invalid_arg;
401 }
402 fileId = obj->GetFileId();
403 uri = obj->GetFileUri();
404 displayName = obj->GetFileDisplayName();
405 return napi_ok;
406 }
407
ParseArgGetDestPath(napi_env env,napi_value arg,std::string & destPath)408 napi_status ParseArgGetDestPath(napi_env env, napi_value arg, std::string &destPath)
409 {
410 if (arg == nullptr) {
411 NAPI_ERR_LOG("destPath arg is invalid");
412 return napi_invalid_arg;
413 }
414 napi_get_print_string(env, arg, destPath);
415 if (destPath.empty()) {
416 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "failed to get destPath napi object");
417 return napi_invalid_arg;
418 }
419 return napi_ok;
420 }
421
ParseArgGetEfficientImageDataHandler(napi_env env,napi_value arg,napi_value & dataHandler,bool & needsExtraInfo)422 napi_status ParseArgGetEfficientImageDataHandler(napi_env env, napi_value arg, napi_value& dataHandler,
423 bool& needsExtraInfo)
424 {
425 CHECK_COND_LOG_THROW_RETURN_RET(env, arg != nullptr, OHOS_INVALID_PARAM_CODE, "efficient handler invalid argument",
426 napi_invalid_arg, "efficient data handler is nullptr");
427
428 napi_valuetype valueType;
429 napi_status status = napi_typeof(env, arg, &valueType);
430 CHECK_COND_LOG_THROW_RETURN_RET(env, status == napi_ok, OHOS_INVALID_PARAM_CODE, "invalid efficient data handler",
431 napi_invalid_arg, "failed to get type of efficient data handler, napi status: %{public}d",
432 static_cast<int>(status));
433 CHECK_COND_LOG_THROW_RETURN_RET(env, valueType == napi_object, OHOS_INVALID_PARAM_CODE,
434 "efficient data handler not a object", napi_invalid_arg, "efficient data handler not a object");
435
436 dataHandler = arg;
437
438 napi_value onDataPrepared;
439 status = napi_get_named_property(env, arg, ON_DATA_PREPARED_FUNC, &onDataPrepared);
440 CHECK_COND_LOG_THROW_RETURN_RET(env, status == napi_ok, OHOS_INVALID_PARAM_CODE,
441 "unable to get onDataPrepared function", napi_invalid_arg,
442 "failed to get type of efficient data handler, napi status: %{public}d", static_cast<int>(status));
443 status = napi_typeof(env, onDataPrepared, &valueType);
444 CHECK_COND_LOG_THROW_RETURN_RET(env, status == napi_ok, OHOS_INVALID_PARAM_CODE, "invalid onDataPrepared",
445 napi_invalid_arg, "failed to get type of onDataPrepared, napi status: %{public}d", static_cast<int>(status));
446 CHECK_COND_LOG_THROW_RETURN_RET(env, valueType == napi_function, OHOS_INVALID_PARAM_CODE,
447 "onDataPrepared not a function", napi_invalid_arg, "onDataPrepared not a function");
448
449 napi_value paramCountNapi;
450 status = napi_get_named_property(env, onDataPrepared, "length", ¶mCountNapi);
451 CHECK_COND_LOG_THROW_RETURN_RET(env, status == napi_ok, OHOS_INVALID_PARAM_CODE, "invalid onDataPrepared",
452 napi_invalid_arg, "get onDataPrepared arg count fail, napi status: %{public}d", static_cast<int>(status));
453 int32_t paramCount = -1;
454 constexpr int paramCountMin = 2;
455 constexpr int paramCountMax = 3;
456 status = napi_get_value_int32(env, paramCountNapi, ¶mCount);
457 CHECK_COND_LOG_THROW_RETURN_RET(env, status == napi_ok, OHOS_INVALID_PARAM_CODE, "invalid onDataPrepared",
458 napi_invalid_arg, "get onDataPrepared arg count value fail, napi status: %{public}d", static_cast<int>(status));
459 CHECK_COND_LOG_THROW_RETURN_RET(env, (paramCount >= paramCountMin && paramCount <= paramCountMax),
460 OHOS_INVALID_PARAM_CODE, "onDataPrepared has wrong number of parameters",
461 napi_invalid_arg, "onDataPrepared has wrong number of parameters");
462
463 if (paramCount == ARGS_THREE) {
464 needsExtraInfo = true;
465 }
466 return napi_ok;
467 }
468
ParseArgGetDataHandler(napi_env env,napi_value arg,napi_value & dataHandler,bool & needsExtraInfo)469 napi_status ParseArgGetDataHandler(napi_env env, napi_value arg, napi_value& dataHandler, bool& needsExtraInfo)
470 {
471 CHECK_COND_LOG_THROW_RETURN_RET(env, arg != nullptr, OHOS_INVALID_PARAM_CODE, "data handler invalid argument",
472 napi_invalid_arg, "data handler is nullptr");
473
474 napi_valuetype valueType;
475 napi_status status = napi_typeof(env, arg, &valueType);
476 CHECK_COND_LOG_THROW_RETURN_RET(env, status == napi_ok, OHOS_INVALID_PARAM_CODE, "invalid data handler",
477 napi_invalid_arg, "failed to get type of data handler, napi status: %{public}d", static_cast<int>(status));
478 CHECK_COND_LOG_THROW_RETURN_RET(env, valueType == napi_object, OHOS_INVALID_PARAM_CODE,
479 "data handler not a object", napi_invalid_arg, "data handler not a object");
480
481 dataHandler = arg;
482
483 napi_value onDataPrepared;
484 status = napi_get_named_property(env, arg, ON_DATA_PREPARED_FUNC, &onDataPrepared);
485 CHECK_COND_LOG_THROW_RETURN_RET(env, status == napi_ok, OHOS_INVALID_PARAM_CODE,
486 "unable to get onDataPrepared function", napi_invalid_arg,
487 "failed to get type of data handler, napi status: %{public}d", static_cast<int>(status));
488 status = napi_typeof(env, onDataPrepared, &valueType);
489 CHECK_COND_LOG_THROW_RETURN_RET(env, status == napi_ok, OHOS_INVALID_PARAM_CODE, "invalid onDataPrepared",
490 napi_invalid_arg, "failed to get type of onDataPrepared, napi status: %{public}d", static_cast<int>(status));
491 CHECK_COND_LOG_THROW_RETURN_RET(env, valueType == napi_function, OHOS_INVALID_PARAM_CODE,
492 "onDataPrepared not a function", napi_invalid_arg, "onDataPrepared not a function");
493
494 napi_value paramCountNapi;
495 status = napi_get_named_property(env, onDataPrepared, "length", ¶mCountNapi);
496 CHECK_COND_LOG_THROW_RETURN_RET(env, status == napi_ok, OHOS_INVALID_PARAM_CODE, "invalid onDataPrepared",
497 napi_invalid_arg, "get onDataPrepared arg count fail, napi status: %{public}d", static_cast<int>(status));
498 int32_t paramCount = -1;
499 constexpr int paramCountMin = 1;
500 constexpr int paramCountMax = 2;
501 status = napi_get_value_int32(env, paramCountNapi, ¶mCount);
502 CHECK_COND_LOG_THROW_RETURN_RET(env, status == napi_ok, OHOS_INVALID_PARAM_CODE, "invalid onDataPrepared",
503 napi_invalid_arg, "get onDataPrepared arg count value fail, napi status: %{public}d", static_cast<int>(status));
504 CHECK_COND_LOG_THROW_RETURN_RET(env, (paramCount >= paramCountMin && paramCount <= paramCountMax),
505 OHOS_INVALID_PARAM_CODE, "onDataPrepared has wrong number of parameters",
506 napi_invalid_arg, "onDataPrepared has wrong number of parameters");
507
508 if (paramCount == ARGS_TWO) {
509 needsExtraInfo = true;
510 }
511 return napi_ok;
512 }
513
GenerateRequestId()514 static std::string GenerateRequestId()
515 {
516 uuid_t uuid;
517 uuid_generate(uuid);
518 char str[UUID_STR_LENGTH] = {};
519 uuid_unparse(uuid, str);
520 return str;
521 }
522
RegisterTaskObserver(napi_env env,MediaAssetManagerAsyncContext * asyncContext)523 void MediaAssetManagerNapi::RegisterTaskObserver(napi_env env, MediaAssetManagerAsyncContext *asyncContext)
524 {
525 auto dataObserver = std::make_shared<MultiStagesTaskObserver>(asyncContext->fileId);
526 auto uriLocal = MediaFileUtils::GetUriWithoutDisplayname(asyncContext->photoUri);
527 NAPI_INFO_LOG("uri: %{public}s, %{public}s", asyncContext->photoUri.c_str(), uriLocal.c_str());
528 Uri uri(asyncContext->photoUri);
529 std::unique_lock<std::mutex> registerLock(registerTaskLock);
530 if (multiStagesObserverMap.find(uriLocal) == multiStagesObserverMap.end()) {
531 UserFileClient::RegisterObserverExt(Uri(uriLocal),
532 static_cast<std::shared_ptr<DataShare::DataShareObserver>>(dataObserver), false);
533 multiStagesObserverMap.insert(std::make_pair(uriLocal, dataObserver));
534 }
535 registerLock.unlock();
536
537 InsertDataHandler(NotifyMode::WAIT_FOR_HIGH_QUALITY, env, asyncContext);
538
539 MediaAssetManagerNapi::ProcessImage(asyncContext->fileId, static_cast<int32_t>(asyncContext->deliveryMode));
540 }
541
ParseRequestMediaArgs(napi_env env,napi_callback_info info,unique_ptr<MediaAssetManagerAsyncContext> & asyncContext)542 napi_status MediaAssetManagerNapi::ParseRequestMediaArgs(napi_env env, napi_callback_info info,
543 unique_ptr<MediaAssetManagerAsyncContext> &asyncContext)
544 {
545 napi_value thisVar = nullptr;
546 GET_JS_ARGS(env, info, asyncContext->argc, asyncContext->argv, thisVar);
547 if (asyncContext->argc != ARGS_FOUR && asyncContext->argc != ARGS_FIVE) {
548 NAPI_ERR_LOG("requestMedia argc error");
549 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "requestMedia argc invalid");
550 return napi_invalid_arg;
551 }
552 if (ParseArgGetPhotoAsset(env, asyncContext->argv[PARAM1], asyncContext->fileId, asyncContext->photoUri,
553 asyncContext->displayName) != napi_ok) {
554 NAPI_ERR_LOG("requestMedia ParseArgGetPhotoAsset error");
555 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "requestMedia ParseArgGetPhotoAsset error");
556 return napi_invalid_arg;
557 }
558 if (ParseArgGetRequestOption(env, asyncContext->argv[PARAM2], asyncContext->deliveryMode,
559 asyncContext->sourceMode) != napi_ok) {
560 NAPI_ERR_LOG("requestMedia ParseArgGetRequestOption error");
561 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "requestMedia ParseArgGetRequestOption error");
562 return napi_invalid_arg;
563 }
564 if (asyncContext->argc == ARGS_FOUR) {
565 if (ParseArgGetDataHandler(env, asyncContext->argv[PARAM3], asyncContext->dataHandler,
566 asyncContext->needsExtraInfo) != napi_ok) {
567 NAPI_ERR_LOG("requestMedia ParseArgGetDataHandler error");
568 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "requestMedia ParseArgGetDataHandler error");
569 return napi_invalid_arg;
570 }
571 } else if (asyncContext->argc == ARGS_FIVE) {
572 if (ParseArgGetDestPath(env, asyncContext->argv[PARAM3], asyncContext->destUri) != napi_ok) {
573 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "requestMedia ParseArgGetDestPath error");
574 return napi_invalid_arg;
575 }
576 if (ParseArgGetDataHandler(env, asyncContext->argv[PARAM4], asyncContext->dataHandler,
577 asyncContext->needsExtraInfo) != napi_ok) {
578 NAPI_ERR_LOG("requestMedia ParseArgGetDataHandler error");
579 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "requestMedia ParseArgGetDataHandler error");
580 return napi_invalid_arg;
581 }
582 }
583 asyncContext->hasReadPermission = HasReadPermission();
584 return napi_ok;
585 }
586
ParseEfficentRequestMediaArgs(napi_env env,napi_callback_info info,unique_ptr<MediaAssetManagerAsyncContext> & asyncContext)587 napi_status MediaAssetManagerNapi::ParseEfficentRequestMediaArgs(napi_env env, napi_callback_info info,
588 unique_ptr<MediaAssetManagerAsyncContext> &asyncContext)
589 {
590 napi_value thisVar = nullptr;
591 GET_JS_ARGS(env, info, asyncContext->argc, asyncContext->argv, thisVar);
592 if (asyncContext->argc != ARGS_FOUR && asyncContext->argc != ARGS_FIVE) {
593 NAPI_ERR_LOG("requestMedia argc error");
594 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "requestMedia argc invalid");
595 return napi_invalid_arg;
596 }
597
598 if (ParseArgGetPhotoAsset(env, asyncContext->argv[PARAM1], asyncContext->fileId, asyncContext->photoUri,
599 asyncContext->displayName) != napi_ok) {
600 NAPI_ERR_LOG("requestMedia ParseArgGetPhotoAsset error");
601 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "requestMedia ParseArgGetPhotoAsset error");
602 return napi_invalid_arg;
603 }
604 if (ParseArgGetRequestOption(env, asyncContext->argv[PARAM2], asyncContext->deliveryMode,
605 asyncContext->sourceMode) != napi_ok) {
606 NAPI_ERR_LOG("requestMedia ParseArgGetRequestOption error");
607 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "requestMedia ParseArgGetRequestOption error");
608 return napi_invalid_arg;
609 }
610 if (asyncContext->argc == ARGS_FOUR) {
611 if (ParseArgGetEfficientImageDataHandler(env, asyncContext->argv[PARAM3], asyncContext->dataHandler,
612 asyncContext->needsExtraInfo) != napi_ok) {
613 NAPI_ERR_LOG("requestMedia ParseArgGetEfficientImageDataHandler error");
614 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE,
615 "requestMedia ParseArgGetEfficientImageDataHandler error");
616 return napi_invalid_arg;
617 }
618 } else if (asyncContext->argc == ARGS_FIVE) {
619 if (ParseArgGetDestPath(env, asyncContext->argv[PARAM3], asyncContext->destUri) != napi_ok) {
620 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "requestMedia ParseArgGetDestPath error");
621 return napi_invalid_arg;
622 }
623 if (ParseArgGetEfficientImageDataHandler(env, asyncContext->argv[PARAM4], asyncContext->dataHandler,
624 asyncContext->needsExtraInfo) != napi_ok) {
625 NAPI_ERR_LOG("requestMedia ParseArgGetEfficientImageDataHandler error");
626 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE,
627 "requestMedia ParseArgGetEfficientImageDataHandler error");
628 return napi_invalid_arg;
629 }
630 }
631 asyncContext->hasReadPermission = HasReadPermission();
632 return napi_ok;
633 }
634
InitUserFileClient(napi_env env,napi_callback_info info)635 bool MediaAssetManagerNapi::InitUserFileClient(napi_env env, napi_callback_info info)
636 {
637 if (UserFileClient::IsValid()) {
638 return true;
639 }
640
641 std::unique_lock<std::mutex> helperLock(MediaLibraryNapi::sUserFileClientMutex_);
642 if (!UserFileClient::IsValid()) {
643 UserFileClient::Init(env, info);
644 }
645 helperLock.unlock();
646 return UserFileClient::IsValid();
647 }
648
GetPhotoSubtype(napi_env env,napi_value photoAssetArg)649 static int32_t GetPhotoSubtype(napi_env env, napi_value photoAssetArg)
650 {
651 if (photoAssetArg == nullptr) {
652 NAPI_ERR_LOG(
653 "Dfx adaptation to moving photo collector error: failed to get photo subtype, photo asset is null");
654 return -1;
655 }
656 FileAssetNapi *obj = nullptr;
657 napi_unwrap(env, photoAssetArg, reinterpret_cast<void**>(&obj));
658 if (obj == nullptr) {
659 NAPI_ERR_LOG("Dfx adaptation to moving photo collector error: failed to unwrap file asset");
660 return -1;
661 }
662 return obj->GetFileAssetInstance()->GetPhotoSubType();
663 }
664
JSRequestImageData(napi_env env,napi_callback_info info)665 napi_value MediaAssetManagerNapi::JSRequestImageData(napi_env env, napi_callback_info info)
666 {
667 if (env == nullptr || info == nullptr) {
668 NAPI_ERR_LOG("JSRequestImageData js arg invalid");
669 NapiError::ThrowError(env, JS_INNER_FAIL, "JSRequestImageData js arg invalid");
670 return nullptr;
671 }
672 if (!InitUserFileClient(env, info)) {
673 NAPI_ERR_LOG("JSRequestImageData init user file client failed");
674 NapiError::ThrowError(env, JS_INNER_FAIL, "handler is invalid");
675 return nullptr;
676 }
677
678 MediaLibraryTracer tracer;
679 tracer.Start("JSRequestImageData");
680 unique_ptr<MediaAssetManagerAsyncContext> asyncContext = make_unique<MediaAssetManagerAsyncContext>();
681 asyncContext->returnDataType = ReturnDataType::TYPE_ARRAY_BUFFER;
682 if (ParseRequestMediaArgs(env, info, asyncContext) != napi_ok) {
683 NAPI_ERR_LOG("failed to parse requestImagedata args");
684 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "failed to parse requestImagedata args");
685 return nullptr;
686 }
687 if (CreateDataHandlerRef(env, asyncContext, asyncContext->dataHandlerRef) != napi_ok
688 || CreateOnDataPreparedThreadSafeFunc(env, asyncContext, asyncContext->onDataPreparedPtr) != napi_ok) {
689 NAPI_ERR_LOG("CreateDataHandlerRef or CreateOnDataPreparedThreadSafeFunc failed");
690 return nullptr;
691 }
692 if (CreateDataHandlerRef(env, asyncContext, asyncContext->dataHandlerRef2) != napi_ok
693 || CreateOnDataPreparedThreadSafeFunc(env, asyncContext, asyncContext->onDataPreparedPtr2) != napi_ok) {
694 NAPI_ERR_LOG("CreateDataHandlerRef or CreateOnDataPreparedThreadSafeFunc failed");
695 return nullptr;
696 }
697
698 asyncContext->requestId = GenerateRequestId();
699 asyncContext->subType = static_cast<PhotoSubType>(GetPhotoSubtype(env, asyncContext->argv[PARAM1]));
700
701 return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSRequestImageData", JSRequestExecute,
702 JSRequestComplete);
703 }
704
JSRequestImage(napi_env env,napi_callback_info info)705 napi_value MediaAssetManagerNapi::JSRequestImage(napi_env env, napi_callback_info info)
706 {
707 NAPI_DEBUG_LOG("JSRequestImage");
708 if (env == nullptr || info == nullptr) {
709 NAPI_ERR_LOG("JSRequestImage js arg invalid");
710 NapiError::ThrowError(env, JS_INNER_FAIL, "JSRequestImage js arg invalid");
711 return nullptr;
712 }
713 if (!InitUserFileClient(env, info)) {
714 NAPI_ERR_LOG("JSRequestImage init user file client failed");
715 NapiError::ThrowError(env, JS_INNER_FAIL, "handler is invalid");
716 return nullptr;
717 }
718
719 MediaLibraryTracer tracer;
720 tracer.Start("JSRequestImage");
721
722 unique_ptr<MediaAssetManagerAsyncContext> asyncContext = make_unique<MediaAssetManagerAsyncContext>();
723 asyncContext->returnDataType = ReturnDataType::TYPE_IMAGE_SOURCE;
724 if (ParseRequestMediaArgs(env, info, asyncContext) != napi_ok) {
725 NAPI_ERR_LOG("failed to parse requestImage args");
726 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "failed to parse requestImage args");
727 return nullptr;
728 }
729 if (CreateDataHandlerRef(env, asyncContext, asyncContext->dataHandlerRef) != napi_ok
730 || CreateOnDataPreparedThreadSafeFunc(env, asyncContext, asyncContext->onDataPreparedPtr) != napi_ok) {
731 NAPI_ERR_LOG("CreateDataHandlerRef or CreateOnDataPreparedThreadSafeFunc failed");
732 return nullptr;
733 }
734 if (CreateDataHandlerRef(env, asyncContext, asyncContext->dataHandlerRef2) != napi_ok
735 || CreateOnDataPreparedThreadSafeFunc(env, asyncContext, asyncContext->onDataPreparedPtr2) != napi_ok) {
736 NAPI_ERR_LOG("CreateDataHandlerRef or CreateOnDataPreparedThreadSafeFunc failed");
737 return nullptr;
738 }
739
740 asyncContext->requestId = GenerateRequestId();
741 asyncContext->subType = static_cast<PhotoSubType>(GetPhotoSubtype(env, asyncContext->argv[PARAM1]));
742
743 return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSRequestImage", JSRequestExecute,
744 JSRequestComplete);
745 }
746
JSRequestEfficientIImage(napi_env env,napi_callback_info info)747 napi_value MediaAssetManagerNapi::JSRequestEfficientIImage(napi_env env, napi_callback_info info)
748 {
749 NAPI_DEBUG_LOG("JSRequestEfficientIImage");
750 if (env == nullptr || info == nullptr) {
751 NAPI_ERR_LOG("JSRequestEfficientIImage js arg invalid");
752 NapiError::ThrowError(env, JS_INNER_FAIL, "JSRequestEfficientIImage js arg invalid");
753 return nullptr;
754 }
755 if (!InitUserFileClient(env, info)) {
756 NAPI_ERR_LOG("JSRequestEfficientIImage init user file client failed");
757 NapiError::ThrowError(env, JS_INNER_FAIL, "handler is invalid");
758 return nullptr;
759 }
760
761 MediaLibraryTracer tracer;
762 tracer.Start("JSRequestEfficientIImage");
763
764 unique_ptr<MediaAssetManagerAsyncContext> asyncContext = make_unique<MediaAssetManagerAsyncContext>();
765 asyncContext->returnDataType = ReturnDataType::TYPE_PICTURE;
766 if (ParseEfficentRequestMediaArgs(env, info, asyncContext) != napi_ok) {
767 NAPI_ERR_LOG("failed to parse JSRequestEfficientIImage args");
768 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "failed to parse JSRequestEfficientIImage args");
769 return nullptr;
770 }
771 if (CreateDataHandlerRef(env, asyncContext, asyncContext->dataHandlerRef) != napi_ok
772 || CreateOnDataPreparedThreadSafeFunc(env, asyncContext, asyncContext->onDataPreparedPtr) != napi_ok) {
773 NAPI_ERR_LOG("CreateDataHandlerRef or CreateOnDataPreparedThreadSafeFunc failed");
774 return nullptr;
775 }
776 if (CreateDataHandlerRef(env, asyncContext, asyncContext->dataHandlerRef2) != napi_ok
777 || CreateOnDataPreparedThreadSafeFunc(env, asyncContext, asyncContext->onDataPreparedPtr2) != napi_ok) {
778 NAPI_ERR_LOG("CreateDataHandlerRef or CreateOnDataPreparedThreadSafeFunc failed");
779 return nullptr;
780 }
781
782 asyncContext->requestId = GenerateRequestId();
783 asyncContext->subType = static_cast<PhotoSubType>(GetPhotoSubtype(env, asyncContext->argv[PARAM1]));
784
785 return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSRequestEfficientIImage", JSRequestExecute,
786 JSRequestComplete);
787 }
788
JSRequestVideoFile(napi_env env,napi_callback_info info)789 napi_value MediaAssetManagerNapi::JSRequestVideoFile(napi_env env, napi_callback_info info)
790 {
791 if (env == nullptr || info == nullptr) {
792 NAPI_ERR_LOG("JSRequestVideoFile js arg invalid");
793 NapiError::ThrowError(env, JS_INNER_FAIL, "JSRequestVideoFile js arg invalid");
794 return nullptr;
795 }
796 if (!InitUserFileClient(env, info)) {
797 NAPI_ERR_LOG("JSRequestVideoFile init user file client failed");
798 NapiError::ThrowError(env, JS_INNER_FAIL, "handler is invalid");
799 return nullptr;
800 }
801
802 MediaLibraryTracer tracer;
803 tracer.Start("JSRequestVideoFile");
804
805 unique_ptr<MediaAssetManagerAsyncContext> asyncContext = make_unique<MediaAssetManagerAsyncContext>();
806 asyncContext->returnDataType = ReturnDataType::TYPE_TARGET_PATH;
807 if (ParseRequestMediaArgs(env, info, asyncContext) != napi_ok) {
808 NAPI_ERR_LOG("failed to parse requestVideo args");
809 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "failed to parse requestVideo args");
810 return nullptr;
811 }
812 if (asyncContext->photoUri.length() > MAX_URI_SIZE || asyncContext->destUri.length() > MAX_URI_SIZE) {
813 NAPI_ERR_LOG("request video file uri lens out of limit photoUri lens: %{public}zu, destUri lens: %{public}zu",
814 asyncContext->photoUri.length(), asyncContext->destUri.length());
815 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "request video file uri lens out of limit");
816 return nullptr;
817 }
818 if (MediaFileUtils::GetMediaType(asyncContext->displayName) != MEDIA_TYPE_VIDEO ||
819 MediaFileUtils::GetMediaType(MediaFileUtils::GetFileName(asyncContext->destUri)) != MEDIA_TYPE_VIDEO) {
820 NAPI_ERR_LOG("request video file type invalid");
821 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "request video file type invalid");
822 return nullptr;
823 }
824 if (CreateDataHandlerRef(env, asyncContext, asyncContext->dataHandlerRef) != napi_ok
825 || CreateOnDataPreparedThreadSafeFunc(env, asyncContext, asyncContext->onDataPreparedPtr) != napi_ok) {
826 NAPI_ERR_LOG("CreateDataHandlerRef or CreateOnDataPreparedThreadSafeFunc failed");
827 return nullptr;
828 }
829
830 asyncContext->requestId = GenerateRequestId();
831 return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSRequestVideoFile",
832 JSRequestVideoFileExecute, JSRequestComplete);
833 }
834
OnHandleRequestImage(napi_env env,MediaAssetManagerAsyncContext * asyncContext)835 void MediaAssetManagerNapi::OnHandleRequestImage(napi_env env, MediaAssetManagerAsyncContext *asyncContext)
836 {
837 MultiStagesCapturePhotoStatus status = MultiStagesCapturePhotoStatus::HIGH_QUALITY_STATUS;
838 switch (asyncContext->deliveryMode) {
839 case DeliveryMode::FAST:
840 if (asyncContext->needsExtraInfo) {
841 asyncContext->photoQuality = MediaAssetManagerNapi::QueryPhotoStatus(asyncContext->fileId,
842 asyncContext->photoUri, asyncContext->photoId, asyncContext->hasReadPermission);
843 }
844 MediaAssetManagerNapi::NotifyDataPreparedWithoutRegister(env, asyncContext);
845 break;
846 case DeliveryMode::HIGH_QUALITY:
847 status = MediaAssetManagerNapi::QueryPhotoStatus(asyncContext->fileId,
848 asyncContext->photoUri, asyncContext->photoId, asyncContext->hasReadPermission);
849 asyncContext->photoQuality = status;
850 if (status == MultiStagesCapturePhotoStatus::HIGH_QUALITY_STATUS) {
851 MediaAssetManagerNapi::NotifyDataPreparedWithoutRegister(env, asyncContext);
852 } else {
853 RegisterTaskObserver(env, asyncContext);
854 }
855 break;
856 case DeliveryMode::BALANCED_MODE:
857 status = MediaAssetManagerNapi::QueryPhotoStatus(asyncContext->fileId,
858 asyncContext->photoUri, asyncContext->photoId, asyncContext->hasReadPermission);
859 asyncContext->photoQuality = status;
860 MediaAssetManagerNapi::NotifyDataPreparedWithoutRegister(env, asyncContext);
861 if (status == MultiStagesCapturePhotoStatus::LOW_QUALITY_STATUS) {
862 RegisterTaskObserver(env, asyncContext);
863 }
864 break;
865 default: {
866 NAPI_ERR_LOG("invalid delivery mode");
867 return;
868 }
869 }
870 }
871
OnHandleRequestVideo(napi_env env,MediaAssetManagerAsyncContext * asyncContext)872 void MediaAssetManagerNapi::OnHandleRequestVideo(napi_env env, MediaAssetManagerAsyncContext *asyncContext)
873 {
874 switch (asyncContext->deliveryMode) {
875 case DeliveryMode::FAST:
876 MediaAssetManagerNapi::NotifyDataPreparedWithoutRegister(env, asyncContext);
877 break;
878 case DeliveryMode::HIGH_QUALITY:
879 MediaAssetManagerNapi::NotifyDataPreparedWithoutRegister(env, asyncContext);
880 break;
881 case DeliveryMode::BALANCED_MODE:
882 MediaAssetManagerNapi::NotifyDataPreparedWithoutRegister(env, asyncContext);
883 break;
884 default: {
885 NAPI_ERR_LOG("invalid delivery mode");
886 return;
887 }
888 }
889 }
890
NotifyDataPreparedWithoutRegister(napi_env env,MediaAssetManagerAsyncContext * asyncContext)891 void MediaAssetManagerNapi::NotifyDataPreparedWithoutRegister(napi_env env,
892 MediaAssetManagerAsyncContext *asyncContext)
893 {
894 AssetHandler *assetHandler = InsertDataHandler(NotifyMode::FAST_NOTIFY, env, asyncContext);
895 if (assetHandler == nullptr) {
896 NAPI_ERR_LOG("assetHandler is nullptr");
897 return;
898 }
899 asyncContext->assetHandler = assetHandler;
900 }
901
PhotoQualityToString(MultiStagesCapturePhotoStatus photoQuality)902 static string PhotoQualityToString(MultiStagesCapturePhotoStatus photoQuality)
903 {
904 static const string HIGH_QUALITY_STRING = "high";
905 static const string LOW_QUALITY_STRING = "low";
906 if (photoQuality != MultiStagesCapturePhotoStatus::HIGH_QUALITY_STATUS &&
907 photoQuality != MultiStagesCapturePhotoStatus::LOW_QUALITY_STATUS) {
908 NAPI_ERR_LOG("Invalid photo quality: %{public}d", static_cast<int>(photoQuality));
909 return HIGH_QUALITY_STRING;
910 }
911
912 return (photoQuality == MultiStagesCapturePhotoStatus::HIGH_QUALITY_STATUS) ? HIGH_QUALITY_STRING :
913 LOW_QUALITY_STRING;
914 }
915
GetInfoMapNapiValue(napi_env env,AssetHandler * assetHandler)916 static napi_value GetInfoMapNapiValue(napi_env env, AssetHandler* assetHandler)
917 {
918 napi_status status;
919 napi_value mapNapiValue {nullptr};
920 status = napi_create_map(env, &mapNapiValue);
921 CHECK_COND_RET(status == napi_ok && mapNapiValue != nullptr, nullptr,
922 "Failed to create map napi value, napi status: %{public}d", static_cast<int>(status));
923
924 napi_value qualityInfo {nullptr};
925 status = napi_create_string_utf8(env, PhotoQualityToString(assetHandler->photoQuality).c_str(),
926 NAPI_AUTO_LENGTH, &qualityInfo);
927 CHECK_COND_RET(status == napi_ok && qualityInfo != nullptr, nullptr,
928 "Failed to create quality string, napi status: %{public}d", static_cast<int>(status));
929
930 status = napi_set_named_property(env, mapNapiValue, "quality", qualityInfo);
931 CHECK_COND_RET(status == napi_ok, nullptr, "Failed to set quality property, napi status: %{public}d",
932 static_cast<int>(status));
933
934 status = napi_map_set_named_property(env, mapNapiValue, "quality", qualityInfo);
935 CHECK_COND_RET(status == napi_ok, nullptr, "Failed to set quality map key-value, napi status: %{public}d",
936 static_cast<int>(status));
937
938 return mapNapiValue;
939 }
940
GetNapiValueOfMedia(napi_env env,const std::shared_ptr<NapiMediaAssetDataHandler> & dataHandler,bool & isPicture)941 static napi_value GetNapiValueOfMedia(napi_env env, const std::shared_ptr<NapiMediaAssetDataHandler>& dataHandler,
942 bool& isPicture)
943 {
944 NAPI_DEBUG_LOG("GetNapiValueOfMedia");
945 napi_value napiValueOfMedia = nullptr;
946 if (dataHandler->GetReturnDataType() == ReturnDataType::TYPE_ARRAY_BUFFER) {
947 MediaAssetManagerNapi::GetByteArrayNapiObject(dataHandler->GetRequestUri(), napiValueOfMedia,
948 dataHandler->GetSourceMode() == SourceMode::ORIGINAL_MODE, env);
949 } else if (dataHandler->GetReturnDataType() == ReturnDataType::TYPE_IMAGE_SOURCE) {
950 MediaAssetManagerNapi::GetImageSourceNapiObject(dataHandler->GetRequestUri(), napiValueOfMedia,
951 dataHandler->GetSourceMode() == SourceMode::ORIGINAL_MODE, env);
952 } else if (dataHandler->GetReturnDataType() == ReturnDataType::TYPE_TARGET_PATH) {
953 MediaAssetManagerNapi::WriteDataToDestPath(dataHandler->GetRequestUri(), dataHandler->GetDestUri(),
954 napiValueOfMedia, dataHandler->GetSourceMode() == SourceMode::ORIGINAL_MODE, env);
955 } else if (dataHandler->GetReturnDataType() == ReturnDataType::TYPE_MOVING_PHOTO) {
956 napiValueOfMedia = MovingPhotoNapi::NewMovingPhotoNapi(
957 env, dataHandler->GetRequestUri(), dataHandler->GetSourceMode());
958 } else if (dataHandler->GetReturnDataType() == ReturnDataType::TYPE_PICTURE) {
959 MediaAssetManagerNapi::GetPictureNapiObject(dataHandler->GetRequestUri(), napiValueOfMedia,
960 dataHandler->GetSourceMode() == SourceMode::ORIGINAL_MODE, env, isPicture);
961 } else {
962 NAPI_ERR_LOG("source mode type invalid");
963 }
964 return napiValueOfMedia;
965 }
966
SavePicture(const std::string & fileUri)967 static void SavePicture(const std::string &fileUri)
968 {
969 std::string uriStr = PATH_SAVE_PICTURE;
970 std::string tempStr = fileUri.substr(PhotoColumn::PHOTO_URI_PREFIX.length());
971 std::size_t index = tempStr.find("/");
972 std::string fileId = tempStr.substr(0, index);
973 MediaLibraryNapiUtils::UriAppendKeyValue(uriStr, API_VERSION, to_string(MEDIA_API_VERSION_V10));
974 MediaLibraryNapiUtils::UriAppendKeyValue(uriStr, PhotoColumn::MEDIA_ID, fileId);
975 MediaLibraryNapiUtils::UriAppendKeyValue(uriStr, IMAGE_FILE_TYPE, "1");
976 MediaLibraryNapiUtils::UriAppendKeyValue(uriStr, "uri", fileUri);
977 Uri uri(uriStr);
978 DataShare::DataShareValuesBucket valuesBucket;
979 valuesBucket.Put(PhotoColumn::PHOTO_IS_TEMP, false);
980 DataShare::DataSharePredicates predicate;
981 UserFileClient::Update(uri, predicate, valuesBucket);
982 }
983
HandleValueOfMedia(napi_env env,MediaAssetDataHandlerPtr dataHandler,AssetHandler * assetHandler,napi_value napiValueOfInfoMap)984 int32_t MediaAssetManagerNapi::HandleValueOfMedia(napi_env env, MediaAssetDataHandlerPtr dataHandler,
985 AssetHandler *assetHandler, napi_value napiValueOfInfoMap)
986 {
987 bool isPicture = true;
988 if (dataHandler->GetReturnDataType() != ReturnDataType::TYPE_ARRAY_BUFFER &&
989 dataHandler->GetReturnDataType() != ReturnDataType::TYPE_IMAGE_SOURCE) {
990 string uri = dataHandler->GetRequestUri();
991 SavePicture(uri);
992 }
993 napi_value napiValueOfMedia = GetNapiValueOfMedia(env, dataHandler, isPicture);
994 if (dataHandler->GetReturnDataType() == ReturnDataType::TYPE_PICTURE) {
995 if (isPicture) {
996 dataHandler->JsOnDataPrepared(env, napiValueOfMedia, nullptr, napiValueOfInfoMap);
997 } else {
998 if (napiValueOfMedia == nullptr) {
999 napi_get_undefined(env, &napiValueOfMedia);
1000 }
1001 dataHandler->JsOnDataPrepared(env, nullptr, napiValueOfMedia, napiValueOfInfoMap);
1002 }
1003 } else {
1004 if (napiValueOfMedia == nullptr) {
1005 napi_get_undefined(env, &napiValueOfMedia);
1006 }
1007 dataHandler->JsOnDataPrepared(env, napiValueOfMedia, napiValueOfInfoMap);
1008 }
1009 return E_OK;
1010 }
1011
OnDataPrepared(napi_env env,napi_value cb,void * context,void * data)1012 void MediaAssetManagerNapi::OnDataPrepared(napi_env env, napi_value cb, void *context, void *data)
1013 {
1014 NAPI_DEBUG_LOG("OnDataPrepared");
1015 AssetHandler *assetHandler = reinterpret_cast<AssetHandler *>(data);
1016 CHECK_NULL_PTR_RETURN_VOID(assetHandler, "assetHandler is nullptr");
1017 auto dataHandler = assetHandler->dataHandler;
1018 if (dataHandler == nullptr) {
1019 NAPI_ERR_LOG("data handler is nullptr");
1020 DeleteAssetHandlerSafe(assetHandler, env);
1021 return;
1022 }
1023
1024 NotifyMode notifyMode = dataHandler->GetNotifyMode();
1025 if (notifyMode == NotifyMode::FAST_NOTIFY) {
1026 AssetHandler *tmp;
1027 if (!inProcessFastRequests.Find(assetHandler->requestId, tmp)) {
1028 NAPI_ERR_LOG("The request has been canceled");
1029 DeleteAssetHandlerSafe(assetHandler, env);
1030 return;
1031 }
1032 }
1033
1034 napi_value napiValueOfInfoMap = nullptr;
1035 if (assetHandler->needsExtraInfo) {
1036 napiValueOfInfoMap = GetInfoMapNapiValue(env, assetHandler);
1037 if (napiValueOfInfoMap == nullptr) {
1038 NAPI_ERR_LOG("Failed to get info map");
1039 napi_get_undefined(env, &napiValueOfInfoMap);
1040 }
1041 }
1042 int32_t ret = HandleValueOfMedia(env, dataHandler, assetHandler, napiValueOfInfoMap);
1043 if (ret != E_OK) {
1044 return;
1045 }
1046 DeleteDataHandler(notifyMode, assetHandler->requestUri, assetHandler->requestId);
1047 NAPI_DEBUG_LOG("delete assetHandler");
1048 DeleteAssetHandlerSafe(assetHandler, env);
1049 }
1050
NotifyMediaDataPrepared(AssetHandler * assetHandler)1051 void MediaAssetManagerNapi::NotifyMediaDataPrepared(AssetHandler *assetHandler)
1052 {
1053 napi_status status = napi_call_threadsafe_function(assetHandler->threadSafeFunc, (void *)assetHandler,
1054 napi_tsfn_blocking);
1055 if (status != napi_ok) {
1056 NAPI_ERR_LOG("napi_call_threadsafe_function fail, %{public}d", static_cast<int32_t>(status));
1057 napi_release_threadsafe_function(assetHandler->threadSafeFunc, napi_tsfn_release);
1058 DeleteAssetHandlerSafe(assetHandler, nullptr);
1059 }
1060 }
1061
OnChange(const ChangeInfo & changeInfo)1062 void MultiStagesTaskObserver::OnChange(const ChangeInfo &changeInfo)
1063 {
1064 if (changeInfo.changeType_ != static_cast<int32_t>(NotifyType::NOTIFY_UPDATE)) {
1065 NAPI_DEBUG_LOG("ignore notify change, type: %{public}d", changeInfo.changeType_);
1066 return;
1067 }
1068 for (auto &uri : changeInfo.uris_) {
1069 string uriString = uri.ToString();
1070 NAPI_DEBUG_LOG("%{public}s", uriString.c_str());
1071 std::string photoId = "";
1072 if (MediaAssetManagerNapi::QueryPhotoStatus(fileId_, uriString, photoId, true) !=
1073 MultiStagesCapturePhotoStatus::HIGH_QUALITY_STATUS) {
1074 NAPI_ERR_LOG("requested data not prepared");
1075 continue;
1076 }
1077
1078 std::lock_guard<std::mutex> lock(multiStagesCaptureLock);
1079 if (inProcessUriMap.find(uriString) == inProcessUriMap.end()) {
1080 NAPI_INFO_LOG("current uri does not in process, uri: %{public}s", uriString.c_str());
1081 return;
1082 }
1083 std::map<std::string, AssetHandler *> assetHandlers = inProcessUriMap[uriString];
1084 for (auto handler : assetHandlers) {
1085 auto assetHandler = handler.second;
1086 assetHandler->photoQuality = MultiStagesCapturePhotoStatus::HIGH_QUALITY_STATUS;
1087 MediaAssetManagerNapi::NotifyMediaDataPrepared(assetHandler);
1088 }
1089 }
1090 }
1091
GetImageSourceNapiObject(const std::string & fileUri,napi_value & imageSourceNapiObj,bool isSource,napi_env env)1092 void MediaAssetManagerNapi::GetImageSourceNapiObject(const std::string &fileUri, napi_value &imageSourceNapiObj,
1093 bool isSource, napi_env env)
1094 {
1095 if (env == nullptr) {
1096 NAPI_ERR_LOG(" create image source object failed, need to initialize js env");
1097 return;
1098 }
1099 napi_value tempImageSourceNapi;
1100 ImageSourceNapi::CreateImageSourceNapi(env, &tempImageSourceNapi);
1101 ImageSourceNapi* imageSourceNapi = nullptr;
1102 napi_unwrap(env, tempImageSourceNapi, reinterpret_cast<void**>(&imageSourceNapi));
1103 if (imageSourceNapi == nullptr) {
1104 NAPI_ERR_LOG("unwrap image napi object failed");
1105 return;
1106 }
1107 std::string tmpUri = fileUri;
1108 if (isSource) {
1109 MediaFileUtils::UriAppendKeyValue(tmpUri, MEDIA_OPERN_KEYWORD, SOURCE_REQUEST);
1110 NAPI_INFO_LOG("request source image's imageSource");
1111 }
1112 Uri uri(tmpUri);
1113 int fd = UserFileClient::OpenFile(uri, "r");
1114 if (fd < 0) {
1115 NAPI_ERR_LOG("get image fd failed, errno: %{public}d", errno);
1116 return;
1117 }
1118
1119 SourceOptions opts;
1120 uint32_t errCode = 0;
1121 auto nativeImageSourcePtr = ImageSource::CreateImageSource(fd, opts, errCode);
1122 close(fd);
1123 if (nativeImageSourcePtr == nullptr) {
1124 NAPI_ERR_LOG("get ImageSource::CreateImageSource failed nullptr");
1125 return;
1126 }
1127 imageSourceNapi->SetNativeImageSource(std::move(nativeImageSourcePtr));
1128 imageSourceNapiObj = tempImageSourceNapi;
1129 }
1130
GetPictureNapiObject(const std::string & fileUri,napi_value & pictureNapiObj,bool isSource,napi_env env,bool & isPicture)1131 void MediaAssetManagerNapi::GetPictureNapiObject(const std::string &fileUri, napi_value &pictureNapiObj,
1132 bool isSource, napi_env env, bool& isPicture)
1133 {
1134 if (env == nullptr) {
1135 NAPI_ERR_LOG(" create image source object failed, need to initialize js env");
1136 return;
1137 }
1138 NAPI_DEBUG_LOG("GetPictureNapiObject");
1139
1140 std::string tempStr = fileUri.substr(PhotoColumn::PHOTO_URI_PREFIX.length());
1141 std::size_t index = tempStr.find("/");
1142 std::string fileId = tempStr.substr(0, index);
1143 std::string uri = fileUri;
1144 int32_t errCode = E_OK;
1145 auto pic = PictureHandlerClient::RequestPicture(std::atoi(fileId.c_str()), errCode);
1146 if (pic == nullptr) {
1147 NAPI_ERR_LOG("picture is null");
1148 isPicture = false;
1149 if (errCode == E_ERR) {
1150 SavePicture(uri);
1151 }
1152 GetImageSourceNapiObject(fileUri, pictureNapiObj, isSource, env);
1153 return;
1154 }
1155 NAPI_ERR_LOG("picture is not null");
1156 napi_value tempPictureNapi;
1157 PictureNapi::CreatePictureNapi(env, &tempPictureNapi);
1158 PictureNapi* pictureNapi = nullptr;
1159 napi_unwrap(env, tempPictureNapi, reinterpret_cast<void**>(&pictureNapi));
1160 if (pictureNapi == nullptr) {
1161 NAPI_ERR_LOG("GetPictureNapiObject unwrap image napi object failed");
1162 return;
1163 }
1164 pictureNapi->SetNativePicture(pic);
1165 pictureNapiObj = tempPictureNapi;
1166 }
1167
1168
GetByteArrayNapiObject(const std::string & requestUri,napi_value & arrayBuffer,bool isSource,napi_env env)1169 void MediaAssetManagerNapi::GetByteArrayNapiObject(const std::string &requestUri, napi_value &arrayBuffer,
1170 bool isSource, napi_env env)
1171 {
1172 if (env == nullptr) {
1173 NAPI_ERR_LOG("create byte array object failed, need to initialize js env");
1174 return;
1175 }
1176
1177 std::string tmpUri = requestUri;
1178 if (isSource) {
1179 MediaFileUtils::UriAppendKeyValue(tmpUri, MEDIA_OPERN_KEYWORD, SOURCE_REQUEST);
1180 }
1181 Uri uri(tmpUri);
1182 int imageFd = UserFileClient::OpenFile(uri, MEDIA_FILEMODE_READONLY);
1183 if (imageFd < 0) {
1184 NAPI_ERR_LOG("get image fd failed, %{public}d", errno);
1185 return;
1186 }
1187 ssize_t imgLen = lseek(imageFd, 0, SEEK_END);
1188 void* buffer = nullptr;
1189 napi_create_arraybuffer(env, imgLen, &buffer, &arrayBuffer);
1190 lseek(imageFd, 0, SEEK_SET);
1191 ssize_t readRet = read(imageFd, buffer, imgLen);
1192 close(imageFd);
1193 if (readRet != imgLen) {
1194 NAPI_ERR_LOG("read image failed");
1195 return;
1196 }
1197 }
1198
IsMovingPhoto(int32_t photoSubType,int32_t effectMode,int32_t sourceMode)1199 bool IsMovingPhoto(int32_t photoSubType, int32_t effectMode, int32_t sourceMode)
1200 {
1201 return photoSubType == static_cast<int32_t>(PhotoSubType::MOVING_PHOTO) ||
1202 (MediaLibraryNapiUtils::IsSystemApp() && sourceMode == static_cast<int32_t>(SourceMode::ORIGINAL_MODE) &&
1203 effectMode == static_cast<int32_t>(MovingPhotoEffectMode::IMAGE_ONLY));
1204 }
1205
ParseArgsForRequestMovingPhoto(napi_env env,size_t argc,const napi_value argv[],unique_ptr<MediaAssetManagerAsyncContext> & context)1206 static napi_value ParseArgsForRequestMovingPhoto(napi_env env, size_t argc, const napi_value argv[],
1207 unique_ptr<MediaAssetManagerAsyncContext> &context)
1208 {
1209 CHECK_COND_WITH_MESSAGE(env, (argc == ARGS_FOUR), "Invalid number of arguments");
1210
1211 FileAssetNapi *fileAssetNapi = nullptr;
1212 CHECK_COND_WITH_MESSAGE(env,
1213 (napi_unwrap(env, argv[PARAM1], reinterpret_cast<void**>(&fileAssetNapi)) == napi_ok),
1214 "Failed to parse photo asset");
1215 CHECK_COND_WITH_MESSAGE(env, fileAssetNapi != nullptr, "Failed to parse photo asset");
1216 auto fileAssetPtr = fileAssetNapi->GetFileAssetInstance();
1217 CHECK_COND_WITH_MESSAGE(env, fileAssetPtr != nullptr, "fileAsset is null");
1218 context->photoUri = fileAssetPtr->GetUri();
1219 context->fileId = fileAssetPtr->GetId();
1220 context->returnDataType = ReturnDataType::TYPE_MOVING_PHOTO;
1221 context->hasReadPermission = HasReadPermission();
1222 context->subType = PhotoSubType::MOVING_PHOTO;
1223
1224 CHECK_COND_WITH_MESSAGE(env,
1225 ParseArgGetRequestOption(env, argv[PARAM2], context->deliveryMode, context->sourceMode) == napi_ok,
1226 "Failed to parse request option");
1227 CHECK_COND_WITH_MESSAGE(env, IsMovingPhoto(fileAssetPtr->GetPhotoSubType(),
1228 fileAssetPtr->GetMovingPhotoEffectMode(), static_cast<int32_t>(context->sourceMode)),
1229 "Asset is not a moving photo");
1230 if (ParseArgGetDataHandler(env, argv[PARAM3], context->dataHandler, context->needsExtraInfo) != napi_ok) {
1231 NAPI_ERR_LOG("requestMovingPhoto ParseArgGetDataHandler error");
1232 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "requestMovingPhoto ParseArgGetDataHandler error");
1233 return nullptr;
1234 }
1235
1236 RETURN_NAPI_TRUE(env);
1237 }
1238
JSRequestMovingPhoto(napi_env env,napi_callback_info info)1239 napi_value MediaAssetManagerNapi::JSRequestMovingPhoto(napi_env env, napi_callback_info info)
1240 {
1241 MediaLibraryTracer tracer;
1242 tracer.Start("JSRequestMovingPhoto");
1243
1244 unique_ptr<MediaAssetManagerAsyncContext> asyncContext = make_unique<MediaAssetManagerAsyncContext>();
1245 CHECK_ARGS(env, napi_get_cb_info(env, info, &(asyncContext->argc), asyncContext->argv, nullptr, nullptr),
1246 JS_INNER_FAIL);
1247 CHECK_NULLPTR_RET(ParseArgsForRequestMovingPhoto(env, asyncContext->argc, asyncContext->argv, asyncContext));
1248 CHECK_COND(env, InitUserFileClient(env, info), JS_INNER_FAIL);
1249 if (CreateDataHandlerRef(env, asyncContext, asyncContext->dataHandlerRef) != napi_ok
1250 || CreateOnDataPreparedThreadSafeFunc(env, asyncContext, asyncContext->onDataPreparedPtr) != napi_ok) {
1251 NAPI_ERR_LOG("CreateDataHandlerRef or CreateOnDataPreparedThreadSafeFunc failed");
1252 return nullptr;
1253 }
1254 if (CreateDataHandlerRef(env, asyncContext, asyncContext->dataHandlerRef2) != napi_ok
1255 || CreateOnDataPreparedThreadSafeFunc(env, asyncContext, asyncContext->onDataPreparedPtr2) != napi_ok) {
1256 NAPI_ERR_LOG("CreateDataHandlerRef or CreateOnDataPreparedThreadSafeFunc failed");
1257 return nullptr;
1258 }
1259 asyncContext->requestId = GenerateRequestId();
1260
1261 return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSRequestMovingPhoto", JSRequestExecute,
1262 JSRequestComplete);
1263 }
1264
WriteDataToDestPath(std::string requestUri,std::string responseUri,napi_value & result,bool isSource,napi_env env)1265 void MediaAssetManagerNapi::WriteDataToDestPath(std::string requestUri, std::string responseUri,
1266 napi_value &result, bool isSource, napi_env env)
1267 {
1268 if (env == nullptr) {
1269 NAPI_ERR_LOG("create byte array object failed, need to initialize js env");
1270 return;
1271 }
1272 if (requestUri.empty() || responseUri.empty()) {
1273 napi_get_boolean(env, false, &result);
1274 NAPI_ERR_LOG("requestUri or responseUri is nullptr");
1275 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "requestUri or responseUri is nullptr");
1276 return;
1277 }
1278 std::string tmpUri = requestUri;
1279 if (isSource) {
1280 MediaFileUtils::UriAppendKeyValue(tmpUri, MEDIA_OPERN_KEYWORD, SOURCE_REQUEST);
1281 }
1282 Uri srcUri(tmpUri);
1283 int srcFd = UserFileClient::OpenFile(srcUri, MEDIA_FILEMODE_READONLY);
1284 if (srcFd < 0) {
1285 napi_get_boolean(env, false, &result);
1286 NAPI_ERR_LOG("get source file fd failed %{public}d", srcFd);
1287 NapiError::ThrowError(env, OHOS_PERMISSION_DENIED_CODE, "open source file error");
1288 return;
1289 }
1290 struct stat statSrc;
1291 if (fstat(srcFd, &statSrc) == -1) {
1292 close(srcFd);
1293 napi_get_boolean(env, false, &result);
1294 NAPI_DEBUG_LOG("File get stat failed, %{public}d", errno);
1295 NapiError::ThrowError(env, OHOS_PERMISSION_DENIED_CODE, "open source file error");
1296 return;
1297 }
1298 int destFd = GetFdFromSandBoxUri(responseUri);
1299 if (destFd < 0) {
1300 close(srcFd);
1301 napi_get_boolean(env, false, &result);
1302 NAPI_ERR_LOG("get dest fd failed %{public}d", destFd);
1303 NapiError::ThrowError(env, OHOS_PERMISSION_DENIED_CODE, "open dest file error");
1304 return;
1305 }
1306 SendFile(env, srcFd, destFd, result, statSrc.st_size);
1307 close(srcFd);
1308 close(destFd);
1309 return;
1310 }
1311
SendFile(napi_env env,int srcFd,int destFd,napi_value & result,off_t fileSize)1312 void MediaAssetManagerNapi::SendFile(napi_env env, int srcFd, int destFd, napi_value &result, off_t fileSize)
1313 {
1314 if (srcFd < 0 || destFd < 0) {
1315 NAPI_ERR_LOG("srcFd or destFd is invalid");
1316 napi_get_boolean(env, false, &result);
1317 return;
1318 }
1319 if (sendfile(destFd, srcFd, nullptr, fileSize) == -1) {
1320 close(srcFd);
1321 close(destFd);
1322 napi_get_boolean(env, false, &result);
1323 NAPI_ERR_LOG("send file failed, %{public}d", errno);
1324 NapiError::ThrowError(env, OHOS_PERMISSION_DENIED_CODE, "send file failed");
1325 return;
1326 }
1327 napi_get_boolean(env, true, &result);
1328 }
1329
IsFastRequestCanceled(const std::string & requestId,std::string & photoId)1330 static bool IsFastRequestCanceled(const std::string &requestId, std::string &photoId)
1331 {
1332 AssetHandler *assetHandler = nullptr;
1333 if (!inProcessFastRequests.Find(requestId, assetHandler)) {
1334 NAPI_ERR_LOG("requestId(%{public}s) not in progress.", requestId.c_str());
1335 return false;
1336 }
1337
1338 if (assetHandler == nullptr) {
1339 NAPI_ERR_LOG("assetHandler is nullptr.");
1340 return false;
1341 }
1342 photoId = assetHandler->photoId;
1343 inProcessFastRequests.Erase(requestId);
1344 return true;
1345 }
1346
IsMapRecordCanceled(const std::string & requestId,std::string & photoId,napi_env env)1347 static bool IsMapRecordCanceled(const std::string &requestId, std::string &photoId, napi_env env)
1348 {
1349 AssetHandler *assetHandler = nullptr;
1350 if (!IsInProcessInMapRecord(requestId, assetHandler)) {
1351 NAPI_ERR_LOG("requestId(%{public}s) not in progress.", requestId.c_str());
1352 return false;
1353 }
1354
1355 if (assetHandler == nullptr) {
1356 NAPI_ERR_LOG("assetHandler is nullptr.");
1357 return false;
1358 }
1359 photoId = assetHandler->photoId;
1360 DeleteInProcessMapRecord(assetHandler->requestUri, assetHandler->requestId);
1361 DeleteAssetHandlerSafe(assetHandler, env);
1362 return true;
1363 }
1364
JSCancelRequest(napi_env env,napi_callback_info info)1365 napi_value MediaAssetManagerNapi::JSCancelRequest(napi_env env, napi_callback_info info)
1366 {
1367 size_t argc = ARGS_TWO;
1368 napi_value argv[ARGS_TWO];
1369 napi_value thisVar = nullptr;
1370
1371 GET_JS_ARGS(env, info, argc, argv, thisVar);
1372 NAPI_ASSERT(env, (argc == ARGS_TWO), "requires 2 paramters");
1373
1374 string requestId;
1375 CHECK_ARGS_THROW_INVALID_PARAM(env,
1376 MediaLibraryNapiUtils::GetParamStringWithLength(env, argv[ARGS_ONE], REQUEST_ID_MAX_LEN, requestId));
1377 std::string photoId = "";
1378 bool hasFastRequestInProcess = IsFastRequestCanceled(requestId, photoId);
1379 bool hasMapRecordInProcess = IsMapRecordCanceled(requestId, photoId, env);
1380 if (hasFastRequestInProcess || hasMapRecordInProcess) {
1381 unique_ptr<MediaAssetManagerAsyncContext> asyncContext = make_unique<MediaAssetManagerAsyncContext>();
1382 asyncContext->photoId = photoId;
1383 return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSCancelRequest", JSCancelRequestExecute,
1384 JSCancelRequestComplete);
1385 }
1386 RETURN_NAPI_UNDEFINED(env);
1387 }
1388
GetFdFromSandBoxUri(const std::string & sandBoxUri)1389 int32_t MediaAssetManagerNapi::GetFdFromSandBoxUri(const std::string &sandBoxUri)
1390 {
1391 AppFileService::ModuleFileUri::FileUri destUri(sandBoxUri);
1392 string destPath = destUri.GetRealPath();
1393 if (!MediaFileUtils::IsFileExists(destPath) && !MediaFileUtils::CreateFile(destPath)) {
1394 NAPI_DEBUG_LOG("Create empty dest file in sandbox failed, path:%{private}s", destPath.c_str());
1395 return E_ERR;
1396 }
1397 string absDestPath;
1398 if (!PathToRealPath(destPath, absDestPath)) {
1399 NAPI_DEBUG_LOG("PathToRealPath failed, path:%{private}s", destPath.c_str());
1400 return E_ERR;
1401 }
1402 return MediaFileUtils::OpenFile(absDestPath, MEDIA_FILEMODE_WRITETRUNCATE);
1403 }
1404
ParseArgsForLoadMovingPhoto(napi_env env,size_t argc,const napi_value argv[],unique_ptr<MediaAssetManagerAsyncContext> & context)1405 static napi_value ParseArgsForLoadMovingPhoto(napi_env env, size_t argc, const napi_value argv[],
1406 unique_ptr<MediaAssetManagerAsyncContext> &context)
1407 {
1408 CHECK_COND_WITH_MESSAGE(env, (argc == ARGS_THREE), "Invalid number of arguments");
1409
1410 std::string imageFileUri;
1411 CHECK_COND_WITH_MESSAGE(env,
1412 MediaLibraryNapiUtils::GetParamStringPathMax(env, argv[PARAM1], imageFileUri) == napi_ok,
1413 "Failed to parse image file uri");
1414 std::string videoFileUri;
1415 CHECK_COND_WITH_MESSAGE(env,
1416 MediaLibraryNapiUtils::GetParamStringPathMax(env, argv[PARAM2], videoFileUri) == napi_ok,
1417 "Failed to parse video file uri");
1418 std::string uri(imageFileUri + MOVING_PHOTO_URI_SPLIT + videoFileUri);
1419 context->photoUri = uri;
1420 RETURN_NAPI_TRUE(env);
1421 }
1422
JSLoadMovingPhotoComplete(napi_env env,napi_status status,void * data)1423 static void JSLoadMovingPhotoComplete(napi_env env, napi_status status, void *data)
1424 {
1425 MediaAssetManagerAsyncContext *context = static_cast<MediaAssetManagerAsyncContext*>(data);
1426 CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
1427
1428 MediaLibraryTracer tracer;
1429 tracer.Start("JSLoadMovingPhotoComplete");
1430
1431 unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
1432 jsContext->status = false;
1433
1434 if (context->error == ERR_DEFAULT) {
1435 napi_value movingPhoto = MovingPhotoNapi::NewMovingPhotoNapi(env, context->photoUri,
1436 SourceMode::EDITED_MODE);
1437 jsContext->data = movingPhoto;
1438 napi_get_undefined(env, &jsContext->error);
1439 jsContext->status = true;
1440 } else {
1441 context->HandleError(env, jsContext->error);
1442 napi_get_undefined(env, &jsContext->data);
1443 }
1444
1445 tracer.Finish();
1446 if (context->work != nullptr) {
1447 MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
1448 context->work, *jsContext);
1449 }
1450 delete context;
1451 }
1452
JSLoadMovingPhotoExecute(napi_env env,void * data)1453 static void JSLoadMovingPhotoExecute(napi_env env, void *data)
1454 {
1455 }
1456
JSLoadMovingPhoto(napi_env env,napi_callback_info info)1457 napi_value MediaAssetManagerNapi::JSLoadMovingPhoto(napi_env env, napi_callback_info info)
1458 {
1459 unique_ptr<MediaAssetManagerAsyncContext> asyncContext = make_unique<MediaAssetManagerAsyncContext>();
1460 CHECK_ARGS(env, napi_get_cb_info(env, info, &(asyncContext->argc), asyncContext->argv, nullptr, nullptr),
1461 JS_INNER_FAIL);
1462 CHECK_NULLPTR_RET(ParseArgsForLoadMovingPhoto(env, asyncContext->argc, asyncContext->argv, asyncContext));
1463 return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSLoadMovingPhoto", JSLoadMovingPhotoExecute,
1464 JSLoadMovingPhotoComplete);
1465 }
1466
CreateDataHandlerRef(napi_env env,const unique_ptr<MediaAssetManagerAsyncContext> & context,napi_ref & dataHandlerRef)1467 napi_status MediaAssetManagerNapi::CreateDataHandlerRef(napi_env env,
1468 const unique_ptr<MediaAssetManagerAsyncContext> &context, napi_ref &dataHandlerRef)
1469 {
1470 napi_status status = napi_create_reference(env, context->dataHandler, PARAM1, &dataHandlerRef);
1471 if (status != napi_ok) {
1472 dataHandlerRef = nullptr;
1473 NAPI_ERR_LOG("napi_create_reference failed");
1474 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "napi_create_reference fail");
1475 }
1476 return status;
1477 }
1478
CreateOnDataPreparedThreadSafeFunc(napi_env env,const unique_ptr<MediaAssetManagerAsyncContext> & context,napi_threadsafe_function & threadSafeFunc)1479 napi_status MediaAssetManagerNapi::CreateOnDataPreparedThreadSafeFunc(napi_env env,
1480 const unique_ptr<MediaAssetManagerAsyncContext> &context, napi_threadsafe_function &threadSafeFunc)
1481 {
1482 NAPI_DEBUG_LOG("CreateOnDataPreparedThreadSafeFunc");
1483 napi_value workName = nullptr;
1484 napi_create_string_utf8(env, "Data Prepared", NAPI_AUTO_LENGTH, &workName);
1485 napi_status status = napi_create_threadsafe_function(env, context->dataHandler, NULL, workName, 0, 1,
1486 NULL, NULL, NULL, MediaAssetManagerNapi::OnDataPrepared, &threadSafeFunc);
1487 if (status != napi_ok) {
1488 NAPI_ERR_LOG("napi_create_threadsafe_function fail");
1489 threadSafeFunc = nullptr;
1490 NapiError::ThrowError(env, JS_INNER_FAIL, "napi_create_threadsafe_function fail");
1491 }
1492 return status;
1493 }
1494
JSRequestExecute(napi_env env,void * data)1495 void MediaAssetManagerNapi::JSRequestExecute(napi_env env, void *data)
1496 {
1497 MediaLibraryTracer tracer;
1498 tracer.Start("JSRequestExecute");
1499 MediaAssetManagerAsyncContext *context = static_cast<MediaAssetManagerAsyncContext*>(data);
1500 CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
1501 OnHandleRequestImage(env, context);
1502 if (context->subType == PhotoSubType::MOVING_PHOTO) {
1503 string uri = LOG_MOVING_PHOTO;
1504 Uri logMovingPhotoUri(uri);
1505 DataShare::DataShareValuesBucket valuesBucket;
1506 string result;
1507 valuesBucket.Put("adapted", context->returnDataType == ReturnDataType::TYPE_MOVING_PHOTO);
1508 UserFileClient::InsertExt(logMovingPhotoUri, valuesBucket, result);
1509 }
1510 }
1511
JSRequestVideoFileExecute(napi_env env,void * data)1512 void MediaAssetManagerNapi::JSRequestVideoFileExecute(napi_env env, void *data)
1513 {
1514 MediaLibraryTracer tracer;
1515 tracer.Start("JSRequestVideoFileExecute");
1516 MediaAssetManagerAsyncContext *context = static_cast<MediaAssetManagerAsyncContext*>(data);
1517 CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
1518 OnHandleRequestVideo(env, context);
1519 }
1520
JSRequestComplete(napi_env env,napi_status,void * data)1521 void MediaAssetManagerNapi::JSRequestComplete(napi_env env, napi_status, void *data)
1522 {
1523 MediaAssetManagerAsyncContext *context = static_cast<MediaAssetManagerAsyncContext*>(data);
1524 CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
1525 if (context->dataHandlerRef != nullptr) {
1526 napi_delete_reference(env, context->dataHandlerRef);
1527 }
1528 if (context->dataHandlerRef2 != nullptr) {
1529 napi_delete_reference(env, context->dataHandlerRef2);
1530 }
1531
1532 MediaLibraryTracer tracer;
1533 tracer.Start("JSRequestComplete");
1534
1535 unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
1536 jsContext->status = false;
1537 if (context->assetHandler) {
1538 NotifyMediaDataPrepared(context->assetHandler);
1539 context->assetHandler = nullptr;
1540 }
1541 if (context->error == ERR_DEFAULT) {
1542 napi_value requestId;
1543 napi_create_string_utf8(env, context->requestId.c_str(), NAPI_AUTO_LENGTH, &requestId);
1544 jsContext->data = requestId;
1545 napi_get_undefined(env, &jsContext->error);
1546 jsContext->status = true;
1547 } else {
1548 context->HandleError(env, jsContext->error);
1549 napi_get_undefined(env, &jsContext->data);
1550 }
1551
1552 if (context->work != nullptr) {
1553 MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
1554 context->work, *jsContext);
1555 }
1556 delete context;
1557 }
1558
JSCancelRequestExecute(napi_env env,void * data)1559 void MediaAssetManagerNapi::JSCancelRequestExecute(napi_env env, void *data)
1560 {
1561 MediaAssetManagerAsyncContext *context = static_cast<MediaAssetManagerAsyncContext*>(data);
1562 CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
1563 MediaAssetManagerNapi::CancelProcessImage(context->photoId);
1564 }
1565
JSCancelRequestComplete(napi_env env,napi_status,void * data)1566 void MediaAssetManagerNapi::JSCancelRequestComplete(napi_env env, napi_status, void *data)
1567 {
1568 MediaAssetManagerAsyncContext *context = static_cast<MediaAssetManagerAsyncContext*>(data);
1569 CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
1570
1571 MediaLibraryTracer tracer;
1572 tracer.Start("JSCancelRequestComplete");
1573
1574 unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
1575 jsContext->status = false;
1576
1577 if (context->error == ERR_DEFAULT) {
1578 napi_get_undefined(env, &jsContext->error);
1579 jsContext->status = true;
1580 } else {
1581 context->HandleError(env, jsContext->error);
1582 }
1583 napi_get_undefined(env, &jsContext->data);
1584
1585 if (context->work != nullptr) {
1586 MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
1587 context->work, *jsContext);
1588 }
1589 delete context;
1590 }
1591 } // namespace Media
1592 } // namespace OHOS