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