* Copyright (C) 2021-2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define MLOG_TAG "MediaLibraryNapi"
#define ABILITY_WANT_PARAMS_UIEXTENSIONTARGETTYPE "ability.want.params.uiExtensionTargetType"
#include "media_library_napi.h"
#include <sys/sendfile.h>
#include <new>
#include <unordered_map>
#include <unordered_set>
#include <uuid.h>
#include "accesstoken_kit.h"
#include "active_analysis_error_utils.h"
#include "active_analysis_napi_callback.h"
#include "invoke_analysis_tool_vo.h"
#include "cancel_analysis_tool_vo.h"
#include "album_order_napi.h"
#include "confirm_callback.h"
#include "deep_optimize_space_napi_callback.h"
#include "deep_optimize_space_vo.h"
#include "default_album_name_callback.h"
#include "directory_ex.h"
#include "locale_config.h"
#include "media_analysis_progress_column.h"
#include "media_device_column.h"
#include "media_directory_type_column.h"
#include "media_file_asset_columns.h"
#include "media_file_utils.h"
#include "media_change_request_napi.h"
#include "media_app_uri_permission_column.h"
#include "media_app_uri_sensitive_column.h"
#include "media_old_photos_column.h"
#include "media_smart_album_column.h"
#include "media_smart_map_column.h"
#include "medialibrary_data_manager.h"
#include "medialibrary_napi_enum_comm.h"
#include "medialibrary_peer_info.h"
#include "medialibrary_tracer.h"
#include "modal_ui_callback.h"
#include "napi_common_want.h"
#include "photo_album_napi.h"
#include "result_set_utils.h"
#include "search_column.h"
#include "short_term_callback.h"
#include "request_photo_uris_read_permission_callback.h"
#include "smart_album_napi.h"
#include "story_album_column.h"
#include "vision_total_column.h"
#include "file_asset_napi.h"
#include "form_map.h"
#include "media_facard_photos_column.h"
#include "rdb_utils.h"
#ifdef HAS_ACE_ENGINE_PART
#endif
#include "ui_extension_context.h"
#include "delete_callback.h"
#include "permission_utils.h"
#include "user_photography_info_column.h"
#include "foreground_analysis_meta.h"
#include "smart_album_column.h"
#include "album_operation_uri.h"
#include "data_secondary_directory_uri.h"
#include "user_define_ipc_client.h"
#include "form_info_vo.h"
#include "medialibrary_business_code.h"
#include "create_asset_vo.h"
#include "create_album_vo.h"
#include "delete_albums_vo.h"
#include "trash_photos_vo.h"
#include "file_uri.h"
#include "grant_photo_uri_permission_vo.h"
#include "grant_photo_uris_permission_vo.h"
#include "check_photo_uris_read_permission_vo.h"
#include "cancel_photo_uri_permission_vo.h"
#include "start_thumbnail_creation_task_vo.h"
#include "stop_thumbnail_creation_task_vo.h"
#include "get_index_construct_progress_vo.h"
#include "get_assets_vo.h"
#include "query_albums_vo.h"
#include "get_albums_by_ids_vo.h"
#include "start_asset_analysis_vo.h"
#include "get_photo_index_vo.h"
#include "query_result_vo.h"
#include "get_analysis_process_vo.h"
#include "get_photo_album_object_vo.h"
#include "set_photo_album_order_vo.h"
#include "result_set_napi.h"
#include "heif_transcoding_check_vo.h"
#include "media_old_albums_column.h"
#include "get_cloned_album_uris_vo.h"
#include "acquire_debug_database_vo.h"
#include "compatible_info_vo.h"
#include "preferred_compatible_mode_vo.h"
#include "transcode_compatible_info_operations.h"
#include "media_map_const_utils.h"
#include "release_debug_database_vo.h"
#include "query_media_data_status_vo.h"
#include "check_single_photo_permission_vo.h"
#include "start_active_analysis_vo.h"
#include "stop_active_analysis_vo.h"
#include "check_db_availability_vo.h"
#include "userfilemgr_uri.h"
#include "convert_to_asset_vo.h"
#include "parcel.h"
#include "medialibrary_notify_utils.h"
#include "qos.h"
#include "vision_ocr_column.h"
#include "vision_video_label_column.h"
#include "vision_label_column.h"
#include "vision_image_face_column.h"
#include "register_unregister_handler_functions.h"
#include "get_albumid_by_lpath_vo.h"
#include "report_event.h"
#include "media_audio_column.h"
#include "media_library_error_code.h"
#include "media_upgrade.h"
#include "media_string_utils.h"
#include "preferred_compatible_mode_check_utils.h"
#include "photo_file_utils.h"
#include "change_request_move_assets_to_dir_vo.h"
#include "change_request_move_assets_by_path_vo.h"
#include "asset_cancel_task_vo.h"
#include "progress_observer_manager.h"
#include "media_progress_change_info.h"
#include "task_signal_napi.h"
#include "progress_callback_observer.h"
#include "medialibrary_errno.h"
#include "modify_album_default_cover_order_vo.h"
#include "query_deep_optimize_space_vo.h"
#include "query_deep_optimizable_space_vo.h"
#include "media_empty_obj_vo.h"
using namespace std;
using namespace OHOS::AppExecFwk;
using namespace OHOS::NativeRdb;
using namespace OHOS::DataShare;
using namespace OHOS::Security::AccessToken;
namespace OHOS {
namespace Media {
using ChangeType = AAFwk::ChangeInfo::ChangeType;
const string URI_SEPARATOR = "file:media";
const string URI_SEPARATOR_PERMISSION = "file:media:permission";
thread_local unique_ptr<ChangeListenerNapi> g_listObj = nullptr;
const int32_t SECOND_ENUM = 2;
const int32_t THIRD_ENUM = 3;
const int32_t FORMID_MAX_LEN = 19;
const int32_t SLEEP_TIME = 10;
const int64_t MAX_INT64 = 9223372036854775807;
const int32_t MAX_QUERY_LIMIT = 150;
const int32_t MAX_CREATE_ASSET_LIMIT = 500;
const int32_t MAX_CHECK_PHOTO_PERMISSION_URI_COUNT = 500;
const int32_t MAX_QUERY_ALBUM_LIMIT = 500;
const int32_t MAX_LPATH_BUNDLENAME_LENGTH = 255;
const int32_t MAX_LEN_LIMIT = 9999;
constexpr uint32_t CONFIRM_BOX_ARRAY_MAX_LENGTH = 100;
const string DATE_FUNCTION = "DATE(";
const size_t MAX_SET_ORDER_ARRAY_SIZE = 1000;
const size_t MAX_TAB_OLD_PHOTOS_URI_COUNT = 100;
const size_t MAX_TAB_OLD_ALBUMS_URI_COUNT = 100;
const int32_t BETA_ISSUE_ID_LENGTH = 10;
const int32_t MAX_FILE_FD = 1023;
const std::string CONTROL_IMAGEVIDEO_ANALYSIS_PERMISSION = "ohos.permission.CONTROL_IMAGEVIDEO_ANALYSIS";
const std::string TARGET_DIR = "/storage/media/local/files";
const int32_t MAX_ASSETS_NUMBER = 200;
const std::string DOCS_DIR = "/Docs/";
const std::string DOCS_DIR_PRE = "/Docs";
const std::string FILE_MANAGEMENT_PREFIX = "/storage/media/local/files";
const std::string MEDIA_PROGRESS_REGISTER_PREFIX = "datashare:///media/custom_progress/";
const std::unordered_set<std::string> FILE_MANAGER_EXCLUDED_DIR_NAMES = {
"HO_DATA_EXT_MISC",
".thumbs",
".Recent",
".backup",
".Trash",
".VMDocs",
".ohpm",
"PCEngine",
"appdata",
};
enum class PhotoPermissionState : int32_t {
URI_FORMAT_ERROR = 0,
FILE_NOT_EXIST = 1,
READ_PERMISSION = 2,
NO_READ_PERMISSION = 3,
};
const std::vector<std::pair<std::string, std::string>> VALID_ORDER_COMBINATIONS = {
{"date_taken", "display_name"},
{"date_added", "display_name"},
{"display_name", "date_taken"},
{"size", "display_name"},
{"hidden_time", "display_name"}
};
static const std::unordered_map<int32_t, std::string> NEED_COMPATIBLE_COLUMN_MAP = {
{ANALYSIS_LABEL, FEATURE},
{ANALYSIS_FACE, FEATURES},
{ANALYSIS_VIDEO_LABEL, VIDEO_PART_FEATURE},
{ANALYSIS_OCR, OCR_TEXT_MSG}
};
static const std::unordered_set<std::string> BETACLUB_FAULT_TREE_CODES = {
"1025_1041_1018"
};
mutex MediaLibraryNapi::sUserFileClientMutex_;
mutex MediaLibraryNapi::sOnOffMutex_;
string ChangeListenerNapi::trashAlbumUri_;
int32_t ChangeListenerNapi::trashAlbumUserId_ = -1;
std::mutex ChangeListenerNapi::trashMutex_;
static std::once_flag g_prewarmTrashOnce;
static std::unordered_map<int32_t, Notification::NotifyForUserDefineType> g_moveAssetsProgressMap;
static std::mutex g_moveAssetsProgressMutex;
ReportEvent MediaLibraryNapi::getPhotoAccessHelperEvent_;
ReportEvent MediaLibraryNapi::registerChangeEvent_;
mutex MediaLibraryNapi::thumbnailMutex_;
static void PrewarmTrashAlbumUriAsync()
{
std::call_once(g_prewarmTrashOnce, []() {
std::thread([]() {
(void)ChangeListenerNapi::GetTrashAlbumUri();
}).detach();
});
}
static SafeMap<int32_t, std::shared_ptr<ThumbnailBatchGenerateObserver>> thumbnailGenerateObserverMap;
static SafeMap<int32_t, std::shared_ptr<ThumbnailGenerateHandler>> thumbnailGenerateHandlerMap;
static std::atomic<int32_t> requestIdCounter_ = 0;
static std::atomic<int32_t> requestIdCallback_ = 0;
static map<string, ListenerType> ListenerTypeMaps = {
{"audioChange", AUDIO_LISTENER},
{"videoChange", VIDEO_LISTENER},
{"imageChange", IMAGE_LISTENER},
{"fileChange", FILE_LISTENER},
{"albumChange", ALBUM_LISTENER},
{"deviceChange", DEVICE_LISTENER},
{"remoteFileChange", REMOTEFILE_LISTENER}
};
const std::string SUBTYPE = "subType";
const std::string PAH_SUBTYPE = "subtype";
const std::string CAMERA_SHOT_KEY = "cameraShotKey";
const std::map<std::string, std::string> PHOTO_CREATE_OPTIONS_PARAM = {
{ SUBTYPE, PhotoColumn::PHOTO_SUBTYPE },
{ CAMERA_SHOT_KEY, PhotoColumn::CAMERA_SHOT_KEY },
{ PAH_SUBTYPE, PhotoColumn::PHOTO_SUBTYPE }
};
const std::string TITLE = "title";
const std::map<std::string, std::string> CREATE_OPTIONS_PARAM = {
{ TITLE, MediaColumn::MEDIA_TITLE }
};
const std::string EXTENSION = "fileNameExtension";
const std::string PHOTO_TYPE = "photoType";
const std::string PHOTO_SUB_TYPE = "subtype";
const std::string SHORT_TERM_TAG = "shortTerm";
const std::string SHORT_TERM_TITLE = "title";
const std::string SHORT_TERM_EXTENSION = "extension";
const std::string SHORT_TERM_PHOTO_TYPE = "photoType";
const std::string SHORT_TERM_PHOTO_SUB_TYPE = "photoSubType";
const std::string CONFIRM_BOX_PACKAGE_NAME = "com.ohos.photos";
const std::string CONFIRM_BOX_EXT_ABILITY_NAME = "SaveUIExtensionAbility";
const std::string CONFIRM_BOX_EXT_DEFAULT_ALBUM_NAME_ABILITY_NAME = "DefaultAlbumNameUIExtensionAbility";
const std::string CONFIRM_BOX_EXTENSION_TYPE = "ability.want.params.uiExtensionType";
const std::string CONFIRM_BOX_REQUEST_TYPE = "sysDialog/common";
const std::string CONFIRM_BOX_SRC_FILE_URIS = "ability.params.stream";
const std::string CONFIRM_BOX_TITLE_ARRAY = "titleArray";
const std::string CONFIRM_BOX_EXTENSION_ARRAY = "extensionArray";
const std::string CONFIRM_BOX_PHOTO_TYPE_ARRAY = "photoTypeArray";
const std::string CONFIRM_BOX_PHOTO_SUB_TYPE_ARRAY = "photoSubTypeArray";
const std::string CONFIRM_BOX_BUNDLE_NAME = "bundleName";
const std::string CONFIRM_BOX_APP_NAME = "appName";
const std::string CONFIRM_BOX_APP_ID = "appId";
const std::string CONFIRM_BOX_IMAGE_FULLY_DISPLAYED = "isImageFullyDisplayed";
const std::string TARGET_PAGE = "targetPage";
const std::string TOKEN_ID = "tokenId";
const std::string REQUEST_PHOTO_URIS_READPERMISSIONEX = "requestPhotoUrisReadPermissionEx";
const std::string ACTIVE_ANALYSIS_CONFIG_TYPES = "types";
const std::string ACTIVE_ANALYSIS_CONFIG_URIS = "uris";
const std::string ACTIVE_ANALYSIS_CONFIG_EXTRA_INFOS = "extraInfos";
const std::string ACTIVE_ANALYSIS_PARAM_ERROR_MSG = "The scenario parameter verification fails.";
const std::string ACTIVE_ANALYSIS_INNER_ERROR_MSG = "Internal system error";
constexpr size_t MAX_ACTIVE_ANALYSIS_TYPE_COUNT = static_cast<size_t>(
AnalysisType::ANALYSIS_TYPE_END - AnalysisType::ANALYSIS_TYPE_START - 1);
constexpr size_t MAX_ACTIVE_ANALYSIS_URI_COUNT = 100;
constexpr size_t MAX_ACTIVE_ANALYSIS_EXTRA_INFOS_LENGTH = 500;
const std::string LANGUAGE_ZH = "zh-Hans";
const std::string LANGUAGE_EN = "en-Latn-US";
const std::string LANGUAGE_ZH_TR = "zh-Hant";
const std::string URI_TYPE = "uriType";
const std::string TYPE_PHOTOS = "1";
thread_local napi_ref MediaLibraryNapi::sConstructor_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sMediaTypeEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sKeyFrameThumbnailTypeRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sDirectoryEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sVirtualAlbumTypeEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sFileKeyEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sPrivateAlbumEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sDeliveryModeEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sPreferredCompatibleModeEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sSourceModeEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sCompatibleModeEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sAnalysisToolTypeEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sPositionTypeEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sPhotoSubType_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sPhotoPermissionType_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sMediaAssetPermissionStateEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sHideSensitiveType_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sDynamicRangeType_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sHiddenPhotosDisplayModeEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sAuthorizationModeEnumRef_ = nullptr;
using CompleteCallback = napi_async_complete_callback;
using Context = MediaLibraryAsyncContext* ;
static int32_t NormalizeActiveAnalysisPromiseErrorCode(int32_t code)
{
switch (code) {
case OHOS_PERMISSION_DENIED_CODE:
case E_CHECK_SYSTEMAPP_FAIL:
case MEDIA_LIBRARY_INVALID_PARAMETER_ERROR:
case MEDIA_LIBRARY_INTERNAL_SYSTEM_ERROR:
return code;
default:
return MEDIA_LIBRARY_INTERNAL_SYSTEM_ERROR;
}
}
static std::string GetActiveAnalysisPromiseErrorMessage(int32_t code)
{
switch (code) {
case OHOS_PERMISSION_DENIED_CODE:
return "Permission denied";
case E_CHECK_SYSTEMAPP_FAIL:
return "This interface can be called only by system apps";
case MEDIA_LIBRARY_INVALID_PARAMETER_ERROR:
return ACTIVE_ANALYSIS_PARAM_ERROR_MSG;
case MEDIA_LIBRARY_INTERNAL_SYSTEM_ERROR:
default:
return ACTIVE_ANALYSIS_INNER_ERROR_MSG;
}
}
using ClientObserverListMap = std::map<std::string, std::vector<std::shared_ptr<ClientObserver>>>;
using GlobalObserverMap = std::map<Notification::NotifyUriType, ClientObserverListMap>;
using ClientObserverListMapIter = ClientObserverListMap::iterator;
constexpr const char *SUPPORTED_COMPATIBLE_HEIC_MIME_TYPE = "image/heic";
constexpr const char *SUPPORTED_COMPATIBLE_JPEG_MIME_TYPE = "image/jpeg";
constexpr size_t MAX_SUPPORTED_COMPATIBLE_MIME_TYPES = 2;
thread_local napi_ref MediaLibraryNapi::userFileMgrConstructor_ = nullptr;
thread_local napi_ref MediaLibraryNapi::photoAccessHelperConstructor_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sUserFileMgrFileKeyEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sAudioKeyEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sImageVideoKeyEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sPhotoKeysEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sAlbumKeyEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sAlbumType_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sAlbumSubType_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sNotifyType_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sDefaultChangeUriRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sAlbumAttributeEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sAlbumOperationTypeEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sAnalysisType_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sRequestPhotoTypeEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sResourceTypeEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sHighlightAlbumInfoType_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sHighlightUserActionType_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sHighlightAlbumChangeAttributeEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sMovingPhotoEffectModeEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sImageFileTypeEnumEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sCloudEnhancementTaskStageEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sCloudEnhancementStateEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sSupportedWatermarkTypeEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sVideoEnhancementTypeEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sCloudMediaDownloadTypeEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sCloudMediaRetainTypeEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sCloudMediaAssetTaskStatusEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sCloudMediaTaskPauseCauseEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sNotifyChangeTypeEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sThumbnailChangeStatusEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sStrongAssociationTypeEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sCompositeDisplayModeEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sSupportedImageFormatEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sHdrModeRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sVideoModeRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sRiskStatusEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sAppLinkStateRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sLivePhoto4dStatusEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sAvailabilityStatusEnumRef_ = nullptr;
thread_local napi_ref MediaLibraryNapi::sDeepOptimizeStateRef_ = nullptr;
constexpr int32_t DEFAULT_REFCOUNT = 1;
constexpr int32_t DEFAULT_ALBUM_COUNT = 1;
MediaLibraryNapi::MediaLibraryNapi()
: env_(nullptr) {}
MediaLibraryNapi::~MediaLibraryNapi() = default;
void MediaLibraryNapi::MediaLibraryNapiDestructor(napi_env env, void *nativeObject, void *finalize_hint)
{
MediaLibraryNapi *mediaLibrary = reinterpret_cast<MediaLibraryNapi*>(nativeObject);
if (mediaLibrary != nullptr) {
delete mediaLibrary;
mediaLibrary = nullptr;
}
}
napi_value MediaLibraryNapi::Init(napi_env env, napi_value exports)
{
napi_property_descriptor media_library_properties[] = {
DECLARE_NAPI_FUNCTION("getPublicDirectory", JSGetPublicDirectory),
DECLARE_NAPI_FUNCTION("getFileAssets", JSGetFileAssets),
DECLARE_NAPI_FUNCTION("getAlbums", JSGetAlbums),
DECLARE_NAPI_FUNCTION("createAsset", JSCreateAsset),
DECLARE_NAPI_FUNCTION("deleteAsset", JSDeleteAsset),
DECLARE_NAPI_FUNCTION("on", JSOnCallback),
DECLARE_NAPI_FUNCTION("off", JSOffCallback),
DECLARE_NAPI_FUNCTION("release", JSRelease),
DECLARE_NAPI_FUNCTION("getSmartAlbum", JSGetSmartAlbums),
DECLARE_NAPI_FUNCTION("getPrivateAlbum", JSGetPrivateAlbum),
DECLARE_NAPI_FUNCTION("createSmartAlbum", JSCreateSmartAlbum),
DECLARE_NAPI_FUNCTION("deleteSmartAlbum", JSDeleteSmartAlbum),
DECLARE_NAPI_FUNCTION("getActivePeers", JSGetActivePeers),
DECLARE_NAPI_FUNCTION("getAllPeers", JSGetAllPeers),
DECLARE_NAPI_FUNCTION("storeMediaAsset", JSStoreMediaAsset),
DECLARE_NAPI_FUNCTION("startImagePreview", JSStartImagePreview),
};
napi_property_descriptor static_prop[] = {
DECLARE_NAPI_STATIC_FUNCTION("getMediaLibrary", GetMediaLibraryNewInstance),
DECLARE_NAPI_STATIC_FUNCTION("getMediaLibraryAsync", GetMediaLibraryNewInstanceAsync),
DECLARE_NAPI_PROPERTY("MediaType", CreateMediaTypeEnum(env)),
DECLARE_NAPI_PROPERTY("FileKey", CreateFileKeyEnum(env)),
DECLARE_NAPI_PROPERTY("DirectoryType", CreateDirectoryTypeEnum(env)),
DECLARE_NAPI_PROPERTY("PrivateAlbumType", CreatePrivateAlbumTypeEnum(env)),
};
napi_value ctorObj;
napi_status status = napi_define_class(env, MEDIA_LIB_NAPI_CLASS_NAME.c_str(), NAPI_AUTO_LENGTH,
MediaLibraryNapiConstructor, nullptr,
sizeof(media_library_properties) / sizeof(media_library_properties[PARAM0]),
media_library_properties, &ctorObj);
if (status == napi_ok) {
int32_t refCount = 1;
if (napi_create_reference(env, ctorObj, refCount, &sConstructor_) == napi_ok) {
status = napi_set_named_property(env, exports, MEDIA_LIB_NAPI_CLASS_NAME.c_str(), ctorObj);
if (status == napi_ok && napi_define_properties(env, exports,
sizeof(static_prop) / sizeof(static_prop[PARAM0]), static_prop) == napi_ok) {
return exports;
}
}
}
return nullptr;
}
napi_value MediaLibraryNapi::UserFileMgrInit(napi_env env, napi_value exports)
{
NapiClassInfo info = {
USERFILE_MGR_NAPI_CLASS_NAME,
&userFileMgrConstructor_,
MediaLibraryNapiConstructor,
{
DECLARE_NAPI_FUNCTION("getPhotoAssets", JSGetPhotoAssets),
DECLARE_NAPI_FUNCTION("getAudioAssets", JSGetAudioAssets),
DECLARE_NAPI_FUNCTION("getPhotoAlbums", JSGetPhotoAlbums),
DECLARE_NAPI_FUNCTION("createPhotoAsset", UserFileMgrCreatePhotoAsset),
DECLARE_NAPI_FUNCTION("createAudioAsset", UserFileMgrCreateAudioAsset),
DECLARE_NAPI_FUNCTION("delete", UserFileMgrTrashAsset),
DECLARE_NAPI_FUNCTION("on", UserFileMgrOnCallback),
DECLARE_NAPI_FUNCTION("off", UserFileMgrOffCallback),
DECLARE_NAPI_FUNCTION("getPrivateAlbum", UserFileMgrGetPrivateAlbum),
DECLARE_NAPI_FUNCTION("getActivePeers", JSGetActivePeers),
DECLARE_NAPI_FUNCTION("getAllPeers", JSGetAllPeers),
DECLARE_NAPI_FUNCTION("release", JSRelease),
DECLARE_NAPI_FUNCTION("createAlbum", CreatePhotoAlbum),
DECLARE_NAPI_FUNCTION("deleteAlbums", DeletePhotoAlbums),
DECLARE_NAPI_FUNCTION("getAlbums", GetPhotoAlbums),
DECLARE_NAPI_FUNCTION("getPhotoIndex", JSGetPhotoIndex),
DECLARE_NAPI_FUNCTION("setHidden", SetHidden),
}
};
MediaLibraryNapiUtils::NapiDefineClass(env, exports, info);
const vector<napi_property_descriptor> staticProps = {
DECLARE_NAPI_STATIC_FUNCTION("getUserFileMgr", GetUserFileMgr),
DECLARE_NAPI_STATIC_FUNCTION("getUserFileMgrAsync", GetUserFileMgrAsync),
DECLARE_NAPI_PROPERTY("FileType", CreateMediaTypeUserFileEnum(env)),
DECLARE_NAPI_PROPERTY("FileKey", UserFileMgrCreateFileKeyEnum(env)),
DECLARE_NAPI_PROPERTY("AudioKey", CreateAudioKeyEnum(env)),
DECLARE_NAPI_PROPERTY("ImageVideoKey", CreateImageVideoKeyEnum(env)),
DECLARE_NAPI_PROPERTY("AlbumKey", CreateAlbumKeyEnum(env)),
DECLARE_NAPI_PROPERTY("PrivateAlbumType", CreatePrivateAlbumTypeEnum(env)),
DECLARE_NAPI_PROPERTY("AlbumType", CreateAlbumTypeEnum(env)),
DECLARE_NAPI_PROPERTY("AlbumSubType", CreateAlbumSubTypeEnum(env)),
DECLARE_NAPI_PROPERTY("PositionType", CreatePositionTypeEnum(env)),
DECLARE_NAPI_PROPERTY("PhotoSubType", CreatePhotoSubTypeEnum(env)),
DECLARE_NAPI_PROPERTY("ThumbnailType", CreateKeyFrameThumbnailTypeEnum(env)),
DECLARE_NAPI_PROPERTY("PhotoPermissionType", CreatePhotoPermissionTypeEnum(env)),
DECLARE_NAPI_PROPERTY("HideSensitiveType", CreateHideSensitiveTypeEnum(env)),
DECLARE_NAPI_PROPERTY("DynamicRangeType", CreateDynamicRangeTypeEnum(env)),
DECLARE_NAPI_PROPERTY("NotifyType", CreateNotifyTypeEnum(env)),
DECLARE_NAPI_PROPERTY("DefaultChangeUri", CreateDefaultChangeUriEnum(env)),
DECLARE_NAPI_PROPERTY("HiddenPhotosDisplayMode", CreateHiddenPhotosDisplayModeEnum(env)),
DECLARE_NAPI_PROPERTY("RequestPhotoType", CreateRequestPhotoTypeEnum(env)),
DECLARE_NAPI_PROPERTY("PhotoRiskStatus", CreatePhotoRiskStatusEnum(env)),
DECLARE_NAPI_PROPERTY("AppLinkState", CreateAppLinkStateEnum(env)),
DECLARE_NAPI_PROPERTY("LivePhoto4dStatus", CreateLivePhoto4dStatusEnum(env)),
DECLARE_NAPI_PROPERTY("DeepOptimizeState", CreateDeepOptimizeStateEnum(env)),
};
MediaLibraryNapiUtils::NapiAddStaticProps(env, exports, staticProps);
return exports;
}
napi_value MediaLibraryNapi::PhotoAccessHelperInit(napi_env env, napi_value exports)
{
NapiClassInfo info = { PHOTOACCESSHELPER_NAPI_CLASS_NAME, &photoAccessHelperConstructor_,
MediaLibraryNapiConstructor,
{
DECLARE_NAPI_FUNCTION("getAssets", PhotoAccessGetPhotoAssets),
DECLARE_NAPI_FUNCTION("getBurstAssets", PhotoAccessGetBurstAssets),
DECLARE_WRITABLE_NAPI_FUNCTION("createAsset", PhotoAccessHelperCreatePhotoAsset),
DECLARE_WRITABLE_NAPI_FUNCTION("createPhotoAsset", PhotoAccessCreatePhotoAsset),
DECLARE_NAPI_FUNCTION("registerChange", PhotoAccessHelperOnCallback),
DECLARE_NAPI_FUNCTION("unRegisterChange", PhotoAccessHelperOffCallback),
DECLARE_NAPI_FUNCTION("deleteAssets", PhotoAccessHelperTrashAsset),
DECLARE_NAPI_FUNCTION("release", JSRelease),
DECLARE_NAPI_FUNCTION("createAlbum", PhotoAccessCreatePhotoAlbum),
DECLARE_NAPI_FUNCTION("deleteAlbums", PhotoAccessDeletePhotoAlbums),
DECLARE_NAPI_FUNCTION("getAlbums", PahGetAlbums),
DECLARE_NAPI_FUNCTION("getAlbumsByOldUris", PhotoAcessGetAlbumsByOldUris),
DECLARE_NAPI_FUNCTION("getClonedAlbumUris", PhotoAcessGetAlbumsByOldUris),
DECLARE_NAPI_FUNCTION("getAlbumsByIds", PhotoAccessGetPhotoAlbumsByIds),
DECLARE_NAPI_FUNCTION("getAlbumIdByLpath", PhotoAccessGetAlbumIdByLpath),
DECLARE_NAPI_FUNCTION("getAlbumIdByBundleName", PhotoAccessGetAlbumIdByBundleName),
DECLARE_NAPI_FUNCTION("getPhotoIndex", PhotoAccessGetPhotoIndex),
DECLARE_NAPI_FUNCTION("getIndexConstructProgress", PhotoAccessGetIndexConstructProgress),
DECLARE_NAPI_FUNCTION("setHidden", SetHidden),
DECLARE_NAPI_FUNCTION("getHiddenAlbums", PahGetHiddenAlbums),
DECLARE_WRITABLE_NAPI_FUNCTION("applyChanges", JSApplyChanges),
DECLARE_NAPI_FUNCTION("saveFormInfo", PhotoAccessSaveFormInfo),
DECLARE_NAPI_FUNCTION("saveGalleryFormInfo", PhotoAccessSaveGalleryFormInfo),
DECLARE_NAPI_FUNCTION("getAssetsByOldUris", PhotoAccessGetPhotoAssetsByOldUris),
DECLARE_NAPI_FUNCTION("getClonedAssetUris", PhotoAccessGetPhotoAssetsByOldUris),
DECLARE_NAPI_FUNCTION("removeFormInfo", PhotoAccessRemoveFormInfo),
DECLARE_NAPI_FUNCTION("removeGalleryFormInfo", PhotoAccessRemoveGalleryFormInfo),
DECLARE_NAPI_FUNCTION("updateGalleryFormInfo", PhotoAccessUpdateGalleryFormInfo),
DECLARE_NAPI_FUNCTION("getAssetsSync", PhotoAccessGetPhotoAssetsSync),
DECLARE_NAPI_FUNCTION("getAlbumsSync", PhotoAccessGetPhotoAlbumsSync),
DECLARE_NAPI_FUNCTION("getFileAssetsInfo", PhotoAccessGetFileAssetsInfo),
DECLARE_NAPI_FUNCTION("startCreateThumbnailTask", PhotoAccessStartCreateThumbnailTask),
DECLARE_NAPI_FUNCTION("stopCreateThumbnailTask", PhotoAccessStopCreateThumbnailTask),
DECLARE_NAPI_FUNCTION("startThumbnailCreationTask", PhotoAccessStartCreateThumbnailTask),
DECLARE_NAPI_FUNCTION("stopThumbnailCreationTask", PhotoAccessStopCreateThumbnailTask),
DECLARE_NAPI_FUNCTION("createAssetsForApp", PhotoAccessHelperAgentCreateAssets),
DECLARE_NAPI_FUNCTION("createAssetsHasPermission", CreateAssetsHasPermission),
DECLARE_NAPI_FUNCTION("grantPhotoUriPermission", PhotoAccessGrantPhotoUriPermission),
DECLARE_NAPI_FUNCTION("grantPhotoUrisPermission", PhotoAccessGrantPhotoUrisPermission),
DECLARE_NAPI_FUNCTION("checkPhotoUrisReadPermission", PhotoAccessCheckPhotoUrisReadPermission),
DECLARE_NAPI_FUNCTION("cancelPhotoUriPermission", PhotoAccessCancelPhotoUriPermission),
DECLARE_NAPI_FUNCTION("createAssetsForAppWithMode", PhotoAccessHelperAgentCreateAssetsWithMode),
DECLARE_NAPI_FUNCTION("getDataAnalysisProgress", PhotoAccessHelperGetDataAnalysisProgress),
DECLARE_NAPI_FUNCTION("getSharedPhotoAssets", PhotoAccessGetSharedPhotoAssets),
DECLARE_NAPI_FUNCTION("getSupportedPhotoFormats", PhotoAccessGetSupportedPhotoFormats),
DECLARE_NAPI_FUNCTION("setForceHideSensitiveType", PhotoAccessHelperSetForceHideSensitiveType),
DECLARE_NAPI_FUNCTION("getAnalysisData", PhotoAccessHelperGetAnalysisData),
DECLARE_NAPI_FUNCTION("createAssetsForAppWithAlbum", CreateAssetsForAppWithAlbum),
DECLARE_NAPI_FUNCTION("createAssetsWithAlbum", CreateAssetsWithAlbum),
DECLARE_NAPI_FUNCTION("batchGetPhotoAssetParams", PhotoAccessHelperGetAssetMemberBatch),
DECLARE_NAPI_FUNCTION("startAssetAnalysis", PhotoAccessStartAssetAnalysis),
DECLARE_NAPI_FUNCTION("startAssetAnalysisAsync", PhotoAccessStartActiveAnalysis),
DECLARE_NAPI_FUNCTION("stopAssetAnalysis", PhotoAccessStopActiveAnalysis),
DECLARE_NAPI_FUNCTION("invokeAnalysisTool", PhotoAccessInvokeAnalysisTool),
DECLARE_NAPI_FUNCTION("cancelAnalysisTool", PhotoAccessCancelAnalysisTool),
DECLARE_NAPI_FUNCTION("query", PhotoAccessQuery),
DECLARE_NAPI_FUNCTION("on", PhotoAccessRegisterCallback),
DECLARE_NAPI_FUNCTION("off", PhotoAccessUnregisterCallback),
DECLARE_NAPI_FUNCTION("getPhotoAlbums", PhotoAccessGetPhotoAlbumsWithoutSubtype),
DECLARE_NAPI_FUNCTION("getPhotoAlbumOrder", PhotoAccessGetPhotoAlbumOrder),
DECLARE_NAPI_FUNCTION("setPhotoAlbumOrder", PhotoAccessSetPhotoAlbumOrder),
DECLARE_NAPI_FUNCTION("isCompatibleDuplicateSupported", CanSupportedCompatibleDuplicate),
DECLARE_NAPI_FUNCTION("acquireDebugDatabase", PhotoAccessAcquireDebugDatabase),
DECLARE_NAPI_FUNCTION("releaseDebugDatabase", PhotoAccessReleaseDebugDatabase),
DECLARE_NAPI_FUNCTION("onSinglePhotoChange", SinglePhotoAccessRegisterCallback),
DECLARE_NAPI_FUNCTION("onSinglePhotoAlbumChange", SinglePhotoAlbumRegisterCallback),
DECLARE_NAPI_FUNCTION("offSinglePhotoChange", SinglePhotoAccessUnregisterCallback),
DECLARE_NAPI_FUNCTION("offSinglePhotoAlbumChange", SinglePhotoAlbumUnregisterCallback),
DECLARE_NAPI_FUNCTION("onAnalysisPhotoChange", AnalysisPhotoAccessRegisterCallback),
DECLARE_NAPI_FUNCTION("offAnalysisPhotoChange", AnalysisPhotoAccessUnregisterCallback),
DECLARE_NAPI_FUNCTION("onAnalysisAlbumChange", AnalysisAlbumAccessRegisterCallback),
DECLARE_NAPI_FUNCTION("offAnalysisAlbumChange", AnalysisAlbumAccessUnregisterCallback),
DECLARE_NAPI_FUNCTION("isMediaDataReady", QueryMediaDataReady),
DECLARE_NAPI_FUNCTION("convertToAsset", JSConvertToAsset),
DECLARE_NAPI_FUNCTION("cloneToAlbum", JSCloneToAlbum),
DECLARE_NAPI_FUNCTION("cloneToDir", JSCloneToDir),
DECLARE_NAPI_FUNCTION("cloneAssetsByPath", JSCloneAssetsByPath),
DECLARE_NAPI_FUNCTION("setAssetCompatibleCapability", PhotoAccessHelperSetFileCompatibleConfig),
DECLARE_NAPI_FUNCTION("getAssetCompatibleCapability", PhotoAccessHelperGetAssetCompatibleConfig),
DECLARE_NAPI_FUNCTION("setPreferredCompatibleMode", PhotoAccessHelperSetPreferredCompatibleMode),
DECLARE_NAPI_FUNCTION("getPreferredCompatibleMode", PhotoAccessHelperGetPreferredCompatibleMode),
DECLARE_NAPI_FUNCTION("onMediaLibraryAvailability", AvailabilityRegisterCallback),
DECLARE_NAPI_FUNCTION("offMediaLibraryAvailability", AvailabilityUnregisterCallback),
DECLARE_NAPI_FUNCTION("getAssetCompatibleUris", GetAssetCompatibleUris),
DECLARE_NAPI_FUNCTION("moveAssetsToDir", MoveAssetsToDir),
DECLARE_NAPI_FUNCTION("moveAssetsByPath", MoveAssetsByPath),
DECLARE_NAPI_FUNCTION("startDeepOptimizeSpace", PhotoAccessStartDeepOptimizeSpace),
DECLARE_NAPI_FUNCTION("stopDeepOptimizeSpace", PhotoAccessStopDeepOptimizeSpace),
DECLARE_NAPI_FUNCTION("modifyAlbumDefaultCoverOrder", PhotoAccessModifyAlbumDefaultCoverOrder),
DECLARE_NAPI_FUNCTION("modifyHiddenAlbumDefaultCoverOrder", PhotoAccessModifyHiddenAlbumDefaultCoverOrder),
DECLARE_NAPI_FUNCTION("canPerformDeepOptimizeSpace", CanPerformDeepOptimizeSpace),
DECLARE_NAPI_FUNCTION("getDeepOptimizeSpace", GetDeepOptimizeSpace),
DECLARE_NAPI_FUNCTION("transAssetToCompatibleAsset", PhotoAccessTransAssetToCompatibleAsset),
}
};
MediaLibraryNapiUtils::NapiDefineClass(env, exports, info);
const vector<napi_property_descriptor> staticProps = {
DECLARE_NAPI_STATIC_FUNCTION("getPhotoAccessHelper", GetPhotoAccessHelper),
DECLARE_NAPI_STATIC_FUNCTION("startPhotoPicker", StartPhotoPicker),
DECLARE_NAPI_STATIC_FUNCTION("getPhotoAccessHelperAsync", GetPhotoAccessHelperAsync),
DECLARE_NAPI_STATIC_FUNCTION("createDeleteRequest", CreateDeleteRequest),
DECLARE_NAPI_STATIC_FUNCTION("showAssetsCreationDialog", ShowAssetsCreationDialog),
DECLARE_NAPI_STATIC_FUNCTION("checkShortTermPermission", CheckShortTermPermission),
DECLARE_NAPI_STATIC_FUNCTION("createAssetWithShortTermPermission", CreateAssetWithShortTermPermission),
DECLARE_NAPI_PROPERTY("ThumbnailType", CreateKeyFrameThumbnailTypeEnum(env)),
DECLARE_NAPI_STATIC_FUNCTION("requestPhotoUrisReadPermission", RequestPhotoUrisReadPermission),
DECLARE_NAPI_STATIC_FUNCTION("requestPhotoUrisReadPermissionEx", RequestPhotoUrisReadPermissionEx),
DECLARE_NAPI_PROPERTY("PhotoType", CreateMediaTypeUserFileEnum(env)),
DECLARE_NAPI_PROPERTY("AlbumKeys", CreateAlbumKeyEnum(env)),
DECLARE_NAPI_PROPERTY("AlbumType", CreateAlbumTypeEnum(env)),
DECLARE_NAPI_PROPERTY("PhotoKeys", CreatePhotoKeysEnum(env)),
DECLARE_NAPI_PROPERTY("AlbumSubtype", CreateAlbumSubTypeEnum(env)),
DECLARE_NAPI_PROPERTY("PositionType", CreatePositionTypeEnum(env)),
DECLARE_NAPI_PROPERTY("PhotoSubtype", CreatePhotoSubTypeEnum(env)),
DECLARE_NAPI_PROPERTY("PhotoPermissionType", CreatePhotoPermissionTypeEnum(env)),
DECLARE_NAPI_PROPERTY("MediaAssetPermissionState", CreateMediaAssetPermissionStateEnum(env)),
DECLARE_NAPI_PROPERTY("HideSensitiveType", CreateHideSensitiveTypeEnum(env)),
DECLARE_NAPI_PROPERTY("NotifyType", CreateNotifyTypeEnum(env)),
DECLARE_NAPI_PROPERTY("DefaultChangeUri", CreateDefaultChangeUriEnum(env)),
DECLARE_NAPI_PROPERTY("AlbumAttribute", CreateAlbumAttributeEnum(env)),
DECLARE_NAPI_PROPERTY("AlbumOperationType", CreateAlbumOperationTypeEnum(env)),
DECLARE_NAPI_PROPERTY("HiddenPhotosDisplayMode", CreateHiddenPhotosDisplayModeEnum(env)),
DECLARE_NAPI_PROPERTY("AnalysisType", CreateAnalysisTypeEnum(env)),
DECLARE_NAPI_PROPERTY("AnalysisToolType", CreateAnalysisToolTypeEnum(env)),
DECLARE_NAPI_PROPERTY("RequestPhotoType", CreateRequestPhotoTypeEnum(env)),
DECLARE_NAPI_PROPERTY("ResourceType", CreateResourceTypeEnum(env)),
DECLARE_NAPI_PROPERTY("DeliveryMode", CreateDeliveryModeEnum(env)),
DECLARE_NAPI_PROPERTY("PreferredCompatibleMode", CreatePreferredCompatibleModeEnum(env)),
DECLARE_NAPI_PROPERTY("SourceMode", CreateSourceModeEnum(env)),
DECLARE_NAPI_PROPERTY("CompatibleMode", CreateCompatibleModeEnum(env)),
DECLARE_NAPI_PROPERTY("HighlightAlbumInfoType", CreateHighlightAlbumInfoTypeEnum(env)),
DECLARE_NAPI_PROPERTY("HighlightUserActionType", CreateHighlightUserActionTypeEnum(env)),
DECLARE_NAPI_PROPERTY("HighlightAlbumChangeAttribute", CreateHighlightAlbumChangeAttributeEnum(env)),
DECLARE_NAPI_PROPERTY("MovingPhotoEffectMode", CreateMovingPhotoEffectModeEnum(env)),
DECLARE_NAPI_PROPERTY("ImageFileType", CreateImageFileTypeEnum(env)),
DECLARE_NAPI_PROPERTY("CloudEnhancementTaskStage", CreateCloudEnhancementTaskStageEnum(env)),
DECLARE_NAPI_PROPERTY("CloudEnhancementState", CreateCloudEnhancementStateEnum(env)),
DECLARE_NAPI_PROPERTY("AuthorizationMode", CreateAuthorizationModeEnum(env)),
DECLARE_NAPI_PROPERTY("WatermarkType", CreateSupportedWatermarkTypeEnum(env)),
DECLARE_NAPI_PROPERTY("VideoEnhancementType", CreateVideoEnhancementTypeEnum(env)),
DECLARE_NAPI_PROPERTY("CloudMediaDownloadType", CreateCloudMediaDownloadTypeEnum(env)),
DECLARE_NAPI_PROPERTY("CloudMediaRetainType", CreateCloudMediaRetainTypeEnum(env)),
DECLARE_NAPI_PROPERTY("CloudMediaAssetTaskStatus", CreateCloudMediaAssetTaskStatusEnum(env)),
DECLARE_NAPI_PROPERTY("CloudMediaTaskPauseCause", CreateCloudMediaTaskPauseCauseEnum(env)),
DECLARE_NAPI_STATIC_FUNCTION("getPhotoPickerComponentDefaultAlbumName",
GetPhotoPickerComponentDefaultAlbumName),
DECLARE_NAPI_PROPERTY("NotifyChangeType", CreateNotifyChangeTypeEnum(env)),
DECLARE_NAPI_PROPERTY("ThumbnailChangeStatus", CreateThumbnailChangeStatusEnum(env)),
DECLARE_NAPI_PROPERTY("StrongAssociationType", CreateStrongAssociationTypeEnum(env)),
DECLARE_NAPI_PROPERTY("CompositeDisplayMode", CreateCompositeDisplayModeEnum(env)),
DECLARE_NAPI_PROPERTY("SupportedImageFormat", CreateSupportedImageFormatEnum(env)),
DECLARE_NAPI_PROPERTY("HdrMode", CreateHdrModeEnum(env)),
DECLARE_NAPI_PROPERTY("VideoMode", CreateVideoModeEnum(env)),
DECLARE_NAPI_PROPERTY("DynamicRangeType", CreateDynamicRangeTypeEnum(env)),
DECLARE_NAPI_PROPERTY("AvailabilityStatus", CreateAvailabilityStatusEnum(env)),
};
MediaLibraryNapiUtils::NapiAddStaticProps(env, exports, staticProps);
return exports;
}
static napi_status CheckWhetherAsync(napi_env env, napi_callback_info info, bool &isAsync)
{
isAsync = false;
size_t argc = ARGS_TWO;
napi_value argv[ARGS_TWO] = {0};
napi_status status = napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
if (status != napi_ok) {
NAPI_ERR_LOG("Error while obtaining js environment information");
return status;
}
if (argc == ARGS_ONE) {
return napi_ok;
} else if (argc == ARGS_TWO) {
napi_valuetype valueType = napi_undefined;
status = napi_typeof(env, argv[ARGS_ONE], &valueType);
if (status != napi_ok) {
NAPI_ERR_LOG("Error while obtaining js environment information");
return status;
}
if (valueType == napi_number) {
return napi_ok;
}
if (valueType == napi_boolean) {
isAsync = true;
}
status = napi_get_value_bool(env, argv[ARGS_ONE], &isAsync);
return status;
} else {
NAPI_ERR_LOG("argc %{public}d, is invalid", static_cast<int>(argc));
return napi_invalid_arg;
}
}
static int32_t ParseUserIdFormCbInfo(napi_env env, napi_callback_info info)
{
size_t argc = ARGS_TWO;
napi_value argv[ARGS_TWO] = {0};
napi_status status;
int userId = -1;
status = napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
if (status == napi_ok) {
napi_valuetype valueType = napi_undefined;
status = napi_typeof(env, argv[ARGS_ONE], &valueType);
if (status == napi_ok && valueType == napi_number) {
napi_get_value_int32(env, argv[ARGS_ONE], &userId);
}
}
return userId;
}
static void SetUserIdFromObjectInfo(unique_ptr<MediaLibraryAsyncContext> &asyncContext)
{
if (asyncContext == nullptr || asyncContext->objectInfo == nullptr) {
NAPI_ERR_LOG("objectInfo is nullptr");
return;
}
asyncContext->userId = asyncContext->objectInfo->GetUserId();
}
napi_value MediaLibraryNapi::MediaLibraryNapiConstructor(napi_env env, napi_callback_info info)
{
napi_status status;
napi_value result = nullptr;
napi_value thisVar = nullptr;
MediaLibraryTracer tracer;
tracer.Start("MediaLibraryNapiConstructor");
int32_t userId = ParseUserIdFormCbInfo(env, info);
UserFileClient::SetUserId(userId);
NAPI_CALL(env, napi_get_undefined(env, &result));
GET_JS_OBJ_WITH_ZERO_ARGS(env, info, status, thisVar);
if (status != napi_ok || thisVar == nullptr) {
NAPI_ERR_LOG("Error while obtaining js environment information, status: %{public}d", status);
return result;
}
unique_ptr<MediaLibraryNapi> obj = make_unique<MediaLibraryNapi>();
if (obj == nullptr) {
return result;
}
obj->env_ = env;
obj->SetUserId(userId);
if (g_listObj == nullptr) {
g_listObj = make_unique<ChangeListenerNapi>(env);
}
bool isAsync = false;
NAPI_CALL(env, CheckWhetherAsync(env, info, isAsync));
if (!isAsync) {
unique_lock<mutex> helperLock(sUserFileClientMutex_);
if (!UserFileClient::IsValid(obj->GetUserId())) {
ChangeListenerNapi::InvalidateTrashAlbumUri();
UserFileClient::Init(env, info, obj->GetUserId());
if (!UserFileClient::IsValid(obj->GetUserId())) {
NAPI_ERR_LOG("UserFileClient creation failed");
helperLock.unlock();
return result;
}
}
helperLock.unlock();
}
obj->getPhotoAccessHelperEvent_.apiName_ = "getPhotoAccessHelper";
obj->registerChangeEvent_.apiName_ = "registerChange";
status = napi_wrap(env, thisVar, reinterpret_cast<void *>(obj.get()),
MediaLibraryNapi::MediaLibraryNapiDestructor, nullptr, nullptr);
if (status == napi_ok) {
obj.release();
return thisVar;
} else {
NAPI_ERR_LOG("Failed to wrap the native media lib client object with JS, status: %{public}d", status);
}
return result;
}
static bool CheckWhetherInitSuccess(napi_env env, napi_value value, bool checkIsValid, const int32_t userId = -1)
{
napi_value propertyNames;
uint32_t propertyLength;
napi_valuetype valueType = napi_undefined;
NAPI_CALL_BASE(env, napi_typeof(env, value, &valueType), false);
if (valueType != napi_object) {
return false;
}
NAPI_CALL_BASE(env, napi_get_property_names(env, value, &propertyNames), false);
NAPI_CALL_BASE(env, napi_get_array_length(env, propertyNames, &propertyLength), false);
if (propertyLength == 0) {
return false;
}
if (checkIsValid && (!UserFileClient::IsValid(userId))) {
NAPI_ERR_LOG("UserFileClient is not valid");
return false;
}
return true;
}
static napi_value CreateNewInstance(napi_env env, napi_callback_info info, napi_ref ref,
bool isAsync = false)
{
constexpr size_t ARG_CONTEXT = 1;
size_t argc = ARG_CONTEXT;
napi_value argv[ARGS_TWO] = {0};
napi_value thisVar = nullptr;
napi_value ctor = nullptr;
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
NAPI_CALL(env, napi_get_reference_value(env, ref, &ctor));
if (isAsync) {
argc = ARGS_TWO;
NAPI_CALL(env, napi_get_boolean(env, true, &argv[ARGS_ONE]));
argv[ARGS_ONE] = argv[ARG_CONTEXT];
}
int32_t userId = -1;
if (argc > 1 && !isAsync) {
argc = ARGS_TWO;
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
NAPI_CALL(env, napi_get_reference_value(env, ref, &ctor));
napi_valuetype valueType = napi_undefined;
napi_typeof(env, argv[ARGS_ONE], &valueType);
if (valueType == napi_number) {
NAPI_CALL(env, napi_get_value_int32(env, argv[ARGS_ONE], &userId));
if (userId != -1 && !MediaLibraryNapiUtils::IsSystemApp()) {
NAPI_ERR_LOG("CreateNewInstance failed, target is not system app");
return nullptr;
}
UserFileClient::SetUserId(userId);
NAPI_INFO_LOG("CreateNewInstance for other user is %{public}d", userId);
}
}
napi_value result = nullptr;
NAPI_CALL(env, napi_new_instance(env, ctor, argc, argv, &result));
if (!CheckWhetherInitSuccess(env, result, !isAsync, userId)) {
NAPI_ERR_LOG("Init MediaLibrary Instance is failed");
NAPI_CALL(env, napi_get_undefined(env, &result));
}
return result;
}
napi_value MediaLibraryNapi::GetMediaLibraryNewInstance(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("getMediaLibrary");
napi_value result = nullptr;
napi_value ctor;
size_t argc = ARGS_ONE;
napi_value argv[ARGS_ONE] = {0};
napi_value thisVar = nullptr;
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
napi_status status = napi_get_reference_value(env, sConstructor_, &ctor);
if (status == napi_ok) {
status = napi_new_instance(env, ctor, argc, argv, &result);
if (status == napi_ok) {
if (CheckWhetherInitSuccess(env, result, true)) {
return result;
} else {
NAPI_ERR_LOG("Init MediaLibrary Instance is failed");
}
} else {
NAPI_ERR_LOG("New instance could not be obtained status: %{public}d", status);
}
} else {
NAPI_ERR_LOG("status = %{public}d", status);
}
napi_get_undefined(env, &result);
return result;
}
static void GetMediaLibraryAsyncExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("GetMediaLibraryAsyncExecute");
MediaLibraryInitContext *asyncContext = static_cast<MediaLibraryInitContext *>(data);
if (asyncContext == nullptr) {
NAPI_ERR_LOG("Async context is null");
return;
}
asyncContext->error = ERR_DEFAULT;
unique_lock<mutex> helperLock(MediaLibraryNapi::sUserFileClientMutex_);
if (!UserFileClient::IsValid()) {
ChangeListenerNapi::InvalidateTrashAlbumUri();
UserFileClient::Init(asyncContext->token_, true);
if (!UserFileClient::IsValid()) {
NAPI_ERR_LOG("UserFileClient creation failed");
asyncContext->error = ERR_INVALID_OUTPUT;
helperLock.unlock();
return;
}
}
helperLock.unlock();
}
static void GetMediaLibraryAsyncComplete(napi_env env, napi_status status, void *data)
{
MediaLibraryInitContext *context = static_cast<MediaLibraryInitContext *>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
napi_value result = nullptr;
if (napi_get_reference_value(env, context->resultRef_, &result) != napi_ok) {
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, ERR_INVALID_OUTPUT,
"Get result from context ref failed");
}
napi_valuetype valueType;
if (napi_typeof(env, result, &valueType) != napi_ok || valueType != napi_object) {
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, ERR_INVALID_OUTPUT,
"Get result type failed " + to_string((int) valueType));
}
if (context->error == ERR_DEFAULT) {
jsContext->data = result;
jsContext->status = true;
napi_get_undefined(env, &jsContext->error);
} else {
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, context->error,
"Failed to get MediaLibrary");
napi_get_undefined(env, &jsContext->data);
}
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
napi_delete_reference(env, context->resultRef_);
context->resultRef_ = nullptr;
delete context;
}
napi_value MediaLibraryNapi::GetMediaLibraryNewInstanceAsync(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("getMediaLibraryAsync");
unique_ptr<MediaLibraryInitContext> asyncContext = make_unique<MediaLibraryInitContext>();
if (asyncContext == nullptr) {
NapiError::ThrowError(env, E_FAIL, "Failed to allocate memory for asyncContext");
return nullptr;
}
asyncContext->argc = ARGS_TWO;
napi_value thisVar = nullptr;
NAPI_CALL(env, napi_get_cb_info(env, info, &asyncContext->argc, &(asyncContext->argv[ARGS_ZERO]),
&thisVar, nullptr));
napi_value result = CreateNewInstance(env, info, sConstructor_, true);
napi_valuetype valueType;
NAPI_CALL(env, napi_typeof(env, result, &valueType));
if (valueType == napi_undefined) {
NapiError::ThrowError(env, E_FAIL, "Failed to get userFileMgr instance");
return nullptr;
}
NAPI_CALL(env, MediaLibraryNapiUtils::GetParamCallback(env, asyncContext));
NAPI_CALL(env, napi_create_reference(env, result, NAPI_INIT_REF_COUNT, &asyncContext->resultRef_));
bool isStage = false;
NAPI_CALL(env, UserFileClient::CheckIsStage(env, info, isStage));
if (isStage) {
asyncContext->token_ = UserFileClient::ParseTokenInStageMode(env, info);
} else {
asyncContext->token_ = UserFileClient::ParseTokenInAbility(env, info);
}
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "GetUserFileMgrAsync",
GetMediaLibraryAsyncExecute, GetMediaLibraryAsyncComplete);
}
napi_value MediaLibraryNapi::GetUserFileMgr(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("getUserFileManager");
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "Only system apps can get userFileManger instance");
return nullptr;
}
return CreateNewInstance(env, info, userFileMgrConstructor_);
}
napi_value MediaLibraryNapi::GetUserFileMgrAsync(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("getUserFileManagerAsync");
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "Only system apps can get userFileManger instance");
return nullptr;
}
unique_ptr<MediaLibraryInitContext> asyncContext = make_unique<MediaLibraryInitContext>();
if (asyncContext == nullptr) {
NapiError::ThrowError(env, E_FAIL, "Failed to allocate memory for asyncContext");
return nullptr;
}
asyncContext->argc = ARGS_TWO;
napi_value thisVar = nullptr;
NAPI_CALL(env, napi_get_cb_info(env, info, &asyncContext->argc, &(asyncContext->argv[ARGS_ZERO]),
&thisVar, nullptr));
napi_value result = CreateNewInstance(env, info, userFileMgrConstructor_, true);
napi_valuetype valueType;
NAPI_CALL(env, napi_typeof(env, result, &valueType));
if (valueType == napi_undefined) {
NapiError::ThrowError(env, E_FAIL, "Failed to get userFileMgr instance");
return nullptr;
}
NAPI_CALL(env, MediaLibraryNapiUtils::GetParamCallback(env, asyncContext));
NAPI_CALL(env, napi_create_reference(env, result, NAPI_INIT_REF_COUNT, &asyncContext->resultRef_));
bool isStage = false;
NAPI_CALL(env, UserFileClient::CheckIsStage(env, info, isStage));
if (isStage) {
asyncContext->token_ = UserFileClient::ParseTokenInStageMode(env, info);
} else {
asyncContext->token_ = UserFileClient::ParseTokenInAbility(env, info);
}
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "GetUserFileMgrAsync",
GetMediaLibraryAsyncExecute, GetMediaLibraryAsyncComplete);
}
static void ProcessMediaLibraryHitEvent(ReportEvent& event, int64_t startTime,
int64_t endTime, const std::string& errCode) {
if (event.beginTime_ == 0 || (startTime - event.beginTime_) > TEN_MINUTE_MS) {
event.beginTime_ = startTime;
event.isReport_ = true;
}
event.CountTimeAndNum(startTime, endTime, errCode);
}
static void RecordKitApiHit(MediaLibraryAsyncContext *asyncContext)
{
if (asyncContext->objectInfo == nullptr) {
NAPI_ERR_LOG("Async context objectInfo is null");
return;
}
auto* mediaLibInstance = static_cast<MediaLibraryNapi*>(asyncContext->objectInfo);
bool isReported = false;
switch (asyncContext->eventType) {
case MediaLibraryAsyncContext::PHOTO_ACCESS_HELPER: {
auto& event = mediaLibInstance->getPhotoAccessHelperEvent_;
isReported = event.isReport_;
if (ReportEvent::processorId_ == ProcessorIdStatus::DEFAULT_PROCESSOR_ID) {
ReportEvent::AddEventProcessor();
}
if (ReportEvent::processorId_ != ProcessorIdStatus::NON_APPLICATION_PROCESSOR_ID) {
event.WriteCallStatusEvent();
}
break;
}
case MediaLibraryAsyncContext::REGISTER_CHANGE: {
auto& event = mediaLibInstance->registerChangeEvent_;
isReported = event.isReport_;
if (ReportEvent::processorId_ == ProcessorIdStatus::DEFAULT_PROCESSOR_ID) {
ReportEvent::AddEventProcessor();
}
if (ReportEvent::processorId_ != ProcessorIdStatus::NON_APPLICATION_PROCESSOR_ID) {
event.WriteCallStatusEvent();
}
break;
}
default:
NAPI_ERR_LOG("Unknown event type in async complete");
}
if (!isReported) {
NAPI_INFO_LOG("The function call does not report hit within 10 minute");
}
}
static void RecordKitApiHitAsyncExecute(napi_env env, void *data)
{
int64_t endTime = MediaFileUtils::UTCTimeMilliSeconds();
MediaLibraryAsyncContext *asyncContext = static_cast<MediaLibraryAsyncContext *>(data);
if (asyncContext == nullptr || asyncContext->objectInfo == nullptr) {
NAPI_ERR_LOG("Async context or objectInfo is null");
return;
}
auto* mediaLibInstance = static_cast<MediaLibraryNapi*>(asyncContext->objectInfo);
const std::string& errCode = asyncContext->errCode;
switch (asyncContext->eventType) {
case MediaLibraryAsyncContext::PHOTO_ACCESS_HELPER:
ProcessMediaLibraryHitEvent(mediaLibInstance->getPhotoAccessHelperEvent_,
asyncContext->startTime, endTime, errCode);
break;
case MediaLibraryAsyncContext::REGISTER_CHANGE:
ProcessMediaLibraryHitEvent(mediaLibInstance->registerChangeEvent_,
asyncContext->startTime, endTime, errCode);
break;
default:
NAPI_ERR_LOG("Unknown event type in async execute");
return;
}
RecordKitApiHit(asyncContext);
}
static void RecordKitApiHitAsyncComplete(napi_env env, napi_status status, void *data)
{
MediaLibraryAsyncContext *asyncContext = static_cast<MediaLibraryAsyncContext *>(data);
if (asyncContext == nullptr) {
NAPI_ERR_LOG("Async context is null");
return;
}
delete asyncContext;
}
static void CreateRecordKitAsyncWork(napi_env env, MediaLibraryNapi* obj, int64_t startTime,
MediaLibraryAsyncContext::EventType eventType, const std::string& errCode = "0")
{
if (obj == nullptr) {
NAPI_ERR_LOG("MediaLibraryNapi instance is null");
return;
}
std::unique_ptr<MediaLibraryAsyncContext> asyncContext = std::make_unique<MediaLibraryAsyncContext>();
asyncContext->startTime = startTime;
asyncContext->objectInfo = obj;
asyncContext->eventType = eventType;
asyncContext->errCode = errCode;
const std::string asyncName = "RecordKitApiHitAsync";
MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, asyncName,
RecordKitApiHitAsyncExecute, RecordKitApiHitAsyncComplete);
}
void ProcessPhotoAccessHelperHitAsyncTask(napi_env env, napi_value result, int64_t startTime, std::string& errCode) {
if (result == nullptr) {
NAPI_ERR_LOG("result is null, skip process");
return;
}
void* objectPtr = nullptr;
napi_status unwrapStatus = napi_unwrap(env, result, &objectPtr);
auto* obj = static_cast<MediaLibraryNapi*>(objectPtr);
if (unwrapStatus != napi_ok || objectPtr == nullptr) {
NAPI_ERR_LOG("napi_unwrap failed or objectPtr is null");
return;
}
CreateRecordKitAsyncWork(env, obj, startTime, MediaLibraryAsyncContext::PHOTO_ACCESS_HELPER, errCode);
}
napi_value MediaLibraryNapi::GetPhotoAccessHelper(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("GetPhotoAccessHelper");
int64_t startTime = MediaFileUtils::UTCTimeMilliSeconds();
std::string errCode = "0";
napi_value result = CreateNewInstance(env, info, photoAccessHelperConstructor_);
ProcessPhotoAccessHelperHitAsyncTask(env, result, startTime, errCode);
return result;
}
napi_value MediaLibraryNapi::GetPhotoAccessHelperAsync(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("GetPhotoAccessHelperAsync");
unique_ptr<MediaLibraryInitContext> asyncContext = make_unique<MediaLibraryInitContext>();
if (asyncContext == nullptr) {
NapiError::ThrowError(env, E_FAIL, "Failed to allocate memory for asyncContext");
return nullptr;
}
asyncContext->argc = ARGS_TWO;
napi_value thisVar = nullptr;
NAPI_CALL(env, napi_get_cb_info(env, info, &asyncContext->argc, &(asyncContext->argv[ARGS_ZERO]),
&thisVar, nullptr));
napi_value result = CreateNewInstance(env, info, photoAccessHelperConstructor_, true);
napi_valuetype valueType;
NAPI_CALL(env, napi_typeof(env, result, &valueType));
if (valueType == napi_undefined) {
NapiError::ThrowError(env, E_FAIL, "Failed to get userFileMgr instance");
return nullptr;
}
NAPI_CALL(env, MediaLibraryNapiUtils::GetParamCallback(env, asyncContext));
NAPI_CALL(env, napi_create_reference(env, result, NAPI_INIT_REF_COUNT, &asyncContext->resultRef_));
bool isStage = false;
NAPI_CALL(env, UserFileClient::CheckIsStage(env, info, isStage));
if (isStage) {
asyncContext->token_ = UserFileClient::ParseTokenInStageMode(env, info);
} else {
asyncContext->token_ = UserFileClient::ParseTokenInAbility(env, info);
}
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "GetPhotoAccessHelperAsync",
GetMediaLibraryAsyncExecute, GetMediaLibraryAsyncComplete);
}
static napi_status AddIntegerNamedProperty(napi_env env, napi_value object,
const string &name, int32_t enumValue)
{
napi_value enumNapiValue;
napi_status status = napi_create_int32(env, enumValue, &enumNapiValue);
if (status == napi_ok) {
status = napi_set_named_property(env, object, name.c_str(), enumNapiValue);
}
return status;
}
static napi_value CreateNumberEnumProperty(napi_env env, vector<string> properties, napi_ref &ref, int32_t offset = 0)
{
napi_value result = nullptr;
NAPI_CALL(env, napi_create_object(env, &result));
for (size_t i = 0; i < properties.size(); i++) {
NAPI_CALL(env, AddIntegerNamedProperty(env, result, properties[i], static_cast<int32_t>(i) + offset));
}
NAPI_CALL(env, napi_create_reference(env, result, NAPI_INIT_REF_COUNT, &ref));
return result;
}
static napi_value CreateNumberEnumPropertyByMap(napi_env env, vector<pair<string, int32_t>> properties, napi_ref &ref)
{
napi_value result = nullptr;
NAPI_CALL(env, napi_create_object(env, &result));
for (size_t i = 0; i < properties.size(); i++) {
NAPI_CALL(env, AddIntegerNamedProperty(env, result, properties[i].first, properties[i].second));
}
NAPI_CALL(env, napi_create_reference(env, result, NAPI_INIT_REF_COUNT, &ref));
return result;
}
static napi_status AddStringNamedProperty(napi_env env, napi_value object,
const string &name, string enumValue)
{
napi_value enumNapiValue;
napi_status status = napi_create_string_utf8(env, enumValue.c_str(), NAPI_AUTO_LENGTH, &enumNapiValue);
if (status == napi_ok) {
status = napi_set_named_property(env, object, name.c_str(), enumNapiValue);
}
return status;
}
static napi_value CreateStringEnumProperty(napi_env env, vector<pair<string, string>> properties, napi_ref &ref)
{
napi_value result = nullptr;
NAPI_CALL(env, napi_create_object(env, &result));
for (unsigned int i = 0; i < properties.size(); i++) {
NAPI_CALL(env, AddStringNamedProperty(env, result, properties[i].first, properties[i].second));
}
NAPI_CALL(env, napi_create_reference(env, result, NAPI_INIT_REF_COUNT, &ref));
return result;
}
napi_value MediaLibraryNapi::CreatePhotoRiskStatusEnum(napi_env env)
{
return CreateNumberEnumProperty(env, photoRiskStatusEnum, sRiskStatusEnumRef_);
}
static void DealWithCommonParam(napi_env env, napi_value arg,
const MediaLibraryAsyncContext &context, bool &err, bool &present)
{
MediaLibraryAsyncContext *asyncContext = const_cast<MediaLibraryAsyncContext *>(&context);
CHECK_NULL_PTR_RETURN_VOID(asyncContext, "Async context is null");
string propertyName = "selections";
string tmp = MediaLibraryNapiUtils::GetStringFetchProperty(env, arg, err, present, propertyName);
if (!tmp.empty()) {
asyncContext->selection = tmp;
}
propertyName = "order";
tmp = MediaLibraryNapiUtils::GetStringFetchProperty(env, arg, err, present, propertyName);
if (!tmp.empty()) {
asyncContext->order = tmp;
}
propertyName = "uri";
tmp = MediaLibraryNapiUtils::GetStringFetchProperty(env, arg, err, present, propertyName);
if (!tmp.empty()) {
asyncContext->uri = tmp;
}
propertyName = "networkId";
tmp = MediaLibraryNapiUtils::GetStringFetchProperty(env, arg, err, present, propertyName);
if (!tmp.empty()) {
asyncContext->networkId = tmp;
}
propertyName = "extendArgs";
tmp = MediaLibraryNapiUtils::GetStringFetchProperty(env, arg, err, present, propertyName);
if (!tmp.empty()) {
asyncContext->extendArgs = tmp;
}
}
static void GetFetchOptionsParam(napi_env env, napi_value arg, const MediaLibraryAsyncContext &context, bool &err)
{
MediaLibraryAsyncContext *asyncContext = const_cast<MediaLibraryAsyncContext *>(&context);
CHECK_NULL_PTR_RETURN_VOID(asyncContext, "Async context is null");
napi_value property = nullptr;
napi_value stringItem = nullptr;
bool present = false;
DealWithCommonParam(env, arg, context, err, present);
napi_has_named_property(env, arg, "selectionArgs", &present);
if (present && napi_get_named_property(env, arg, "selectionArgs", &property) == napi_ok) {
uint32_t len = 0;
napi_get_array_length(env, property, &len);
char buffer[PATH_MAX];
for (size_t i = 0; i < len; i++) {
napi_get_element(env, property, i, &stringItem);
size_t res = 0;
napi_get_value_string_utf8(env, stringItem, buffer, PATH_MAX, &res);
asyncContext->selectionArgs.push_back(string(buffer));
CHECK_IF_EQUAL(memset_s(buffer, PATH_MAX, 0, sizeof(buffer)) == 0, "Memset for buffer failed");
}
} else {
NAPI_ERR_LOG("Could not get the string argument!");
err = true;
}
}
static napi_value ConvertJSArgsToNative(napi_env env, size_t argc, const napi_value argv[],
MediaLibraryAsyncContext &asyncContext)
{
bool err = false;
const int32_t refCount = 1;
auto context = &asyncContext;
NAPI_ASSERT(env, argv != nullptr, "Argument list is empty");
for (size_t i = PARAM0; i < argc; i++) {
napi_valuetype valueType = napi_undefined;
napi_typeof(env, argv[i], &valueType);
if (i == PARAM0 && valueType == napi_object) {
GetFetchOptionsParam(env, argv[PARAM0], asyncContext, err);
} else if (i == PARAM0 && valueType == napi_function) {
napi_create_reference(env, argv[i], refCount, &context->callbackRef);
break;
} else if (i == PARAM1 && valueType == napi_function) {
napi_create_reference(env, argv[i], refCount, &context->callbackRef);
break;
} else {
NAPI_ASSERT(env, false, "type mismatch");
}
if (err) {
NAPI_ERR_LOG("fetch options retrieval failed, err: %{public}d", err);
NAPI_ASSERT(env, false, "type mismatch");
}
}
napi_value result;
napi_get_boolean(env, true, &result);
return result;
}
static void GetPublicDirectoryExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("GetPublicDirectoryExecute");
MediaLibraryAsyncContext *context = static_cast<MediaLibraryAsyncContext*>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
vector<string> selectionArgs;
vector<string> columns;
DataSharePredicates predicates;
selectionArgs.push_back(to_string(context->dirType));
predicates.SetWhereClause(DIRECTORY_DB_DIRECTORY_TYPE + " = ?");
predicates.SetWhereArgs(selectionArgs);
string queryUri = CONST_MEDIALIBRARY_DIRECTORY_URI;
Uri uri(queryUri);
int errCode = 0;
shared_ptr<DataShareResultSet> resultSet = UserFileClient::Query(uri, predicates, columns, errCode,
context->userId);
if (resultSet != nullptr) {
auto count = 0;
auto ret = resultSet->GetRowCount(count);
if (ret != NativeRdb::E_OK) {
NAPI_ERR_LOG("get rdbstore failed");
context->error = JS_INNER_FAIL;
return;
}
if (count == 0) {
NAPI_ERR_LOG("Query for get publicDirectory form db failed");
context->error = JS_INNER_FAIL;
return;
}
NAPI_INFO_LOG("Query for get publicDirectory count = %{private}d", count);
if (resultSet->GoToFirstRow() == NativeRdb::E_OK) {
context->directoryRelativePath = get<string>(
ResultSetUtils::GetValFromColumn(DIRECTORY_DB_DIRECTORY, resultSet, TYPE_STRING));
}
if (context->dirType == DirType::DIR_DOCUMENTS) {
context->directoryRelativePath = DOC_DIR_VALUES;
} else if (context->dirType == DirType::DIR_DOWNLOAD) {
context->directoryRelativePath = DOWNLOAD_DIR_VALUES;
}
return;
} else {
context->SaveError(errCode);
NAPI_ERR_LOG("Query for get publicDirectory failed! errorCode is = %{public}d", errCode);
}
}
static void GetPublicDirectoryCallbackComplete(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("GetPublicDirectoryCallbackComplete");
MediaLibraryAsyncContext *context = static_cast<MediaLibraryAsyncContext*>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
if (context->error == ERR_DEFAULT) {
napi_create_string_utf8(env, context->directoryRelativePath.c_str(), NAPI_AUTO_LENGTH, &jsContext->data);
jsContext->status = true;
napi_get_undefined(env, &jsContext->error);
} else {
context->HandleError(env, jsContext->error);
napi_get_undefined(env, &jsContext->data);
}
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
napi_value MediaLibraryNapi::JSGetPublicDirectory(napi_env env, napi_callback_info info)
{
napi_status status;
napi_value result = nullptr;
size_t argc = ARGS_TWO;
napi_value argv[ARGS_TWO] = {0};
napi_value thisVar = nullptr;
const int32_t refCount = 1;
MediaLibraryTracer tracer;
tracer.Start("JSGetPublicDirectory");
GET_JS_ARGS(env, info, argc, argv, thisVar);
NAPI_ASSERT(env, (argc == ARGS_ONE || argc == ARGS_TWO), "requires 2 parameters maximum");
napi_get_undefined(env, &result);
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&asyncContext->objectInfo));
if (status == napi_ok && asyncContext->objectInfo != nullptr) {
for (size_t i = PARAM0; i < argc; i++) {
napi_valuetype valueType = napi_undefined;
napi_typeof(env, argv[i], &valueType);
if (i == PARAM0 && valueType == napi_number) {
napi_get_value_uint32(env, argv[i], &asyncContext->dirType);
} else if (i == PARAM1 && valueType == napi_function) {
napi_create_reference(env, argv[i], refCount, &asyncContext->callbackRef);
break;
} else {
NAPI_ASSERT(env, false, "type mismatch");
}
}
SetUserIdFromObjectInfo(asyncContext);
result = MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSGetPublicDirectory",
GetPublicDirectoryExecute, GetPublicDirectoryCallbackComplete);
}
return result;
}
#ifdef MEDIALIBRARY_COMPATIBILITY
static string GetVirtualIdFromApi10Uri(const string &uri)
{
string fileId = MediaFileUtils::GetIdFromUri(uri);
if (!all_of(fileId.begin(), fileId.end(), ::isdigit)) {
return fileId;
}
int32_t id;
if (!StrToInt(fileId, id)) {
NAPI_ERR_LOG("invalid fileuri %{private}s", uri.c_str());
return fileId;
}
if (uri.find(PhotoColumn::PHOTO_URI_PREFIX) != string::npos) {
return to_string(MediaFileUtils::GetVirtualIdByType(id, MediaType::MEDIA_TYPE_IMAGE));
} else if (uri.find(AudioColumn::AUDIO_URI_PREFIX) != string::npos) {
return to_string(MediaFileUtils::GetVirtualIdByType(id, MediaType::MEDIA_TYPE_AUDIO));
} else {
return fileId;
}
}
#endif
static void GetFileAssetUpdateSelections(MediaLibraryAsyncContext *context)
{
if (!context->uri.empty()) {
NAPI_ERR_LOG("context->uri is = %{private}s", context->uri.c_str());
context->networkId = MediaFileUtils::GetNetworkIdFromUri(context->uri);
#ifdef MEDIALIBRARY_COMPATIBILITY
string fileId = GetVirtualIdFromApi10Uri(context->uri);
#else
string fileId = MediaFileUtils::::GetIdFromUri(context->uri);
#endif
if (!fileId.empty()) {
string idPrefix = string(CONST_MEDIA_DATA_DB_ID) + " = ? ";
#ifdef MEDIALIBRARY_COMPATIBILITY
context->selection = idPrefix;
context->selectionArgs.clear();
context->selectionArgs.emplace_back(fileId);
#else
MediaLibraryNapiUtils::AppendFetchOptionSelection(context->selection, idPrefix);
context->selectionArgs.emplace_back(fileId);
#endif
}
}
#ifdef MEDIALIBRARY_COMPATIBILITY
MediaLibraryNapiUtils::AppendFetchOptionSelection(context->selection, MediaUpgrade::ASSETS_QUERY_FILTER);
MediaLibraryNapi::ReplaceSelection(context->selection, context->selectionArgs, CONST_MEDIA_DATA_DB_RELATIVE_PATH,
CONST_MEDIA_DATA_DB_RELATIVE_PATH, ReplaceSelectionMode::ADD_DOCS_TO_RELATIVE_PATH);
#else
string trashPrefix = string(CONST_MEDIA_DATA_DB_DATE_TRASHED) + " = ? ";
MediaLibraryNapiUtils::AppendFetchOptionSelection(context->selection, trashPrefix);
context->selectionArgs.emplace_back("0");
#endif
string prefix = string(CONST_MEDIA_DATA_DB_MEDIA_TYPE) + " <> ? ";
MediaLibraryNapiUtils::AppendFetchOptionSelection(context->selection, prefix);
context->selectionArgs.emplace_back(to_string(MEDIA_TYPE_ALBUM));
}
static void LogMedialibraryAPI(const string& saveUri)
{
string logMedialibraryAPI = MEDIALIBRARY_DATA_URI + "/" + CONST_MISC_OPERATION + "/" + "log_medialibrary_api";
Uri logUri(logMedialibraryAPI);
DataShare::DataShareValuesBucket valuesBucket;
string result;
valuesBucket.Put("saveUri", saveUri);
UserFileClient::InsertExt(logUri, valuesBucket, result);
}
static void GetFileAssetsExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("GetFileAssetsExecute");
MediaLibraryAsyncContext *context = static_cast<MediaLibraryAsyncContext*>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
GetFileAssetUpdateSelections(context);
context->fetchColumn = FILE_ASSET_COLUMNS;
if (context->extendArgs.find(DATE_FUNCTION) != string::npos) {
string group(" GROUP BY (");
group += context->extendArgs + " )";
context->selection += group;
context->fetchColumn.insert(context->fetchColumn.begin(), "count(*)");
}
MediaLibraryNapiUtils::FixSpecialDateType(context->selection);
context->predicates.SetWhereClause(context->selection);
context->predicates.SetWhereArgs(context->selectionArgs);
context->predicates.SetOrder(context->order);
LogMedialibraryAPI("");
string queryUri = MEDIALIBRARY_DATA_URI;
if (!context->networkId.empty()) {
queryUri = CONST_MEDIALIBRARY_DATA_ABILITY_PREFIX + context->networkId + CONST_MEDIALIBRARY_DATA_URI_IDENTIFIER;
}
Uri uri(queryUri);
int errCode = 0;
shared_ptr<DataShare::DataShareResultSet> resultSet = UserFileClient::Query(uri,
context->predicates, context->fetchColumn, errCode, context->userId);
if (resultSet != nullptr) {
context->fetchFileResult = make_unique<FetchResult<FileAsset>>(move(resultSet));
context->fetchFileResult->SetNetworkId(context->networkId);
return;
} else {
context->SaveError(errCode);
NAPI_ERR_LOG("Query for get publicDirectory failed! errorCode is = %{public}d", errCode);
}
}
static void GetNapiFileResult(napi_env env, MediaLibraryAsyncContext *context,
unique_ptr<JSAsyncContextOutput> &jsContext)
{
if (context->fetchFileResult == nullptr) {
NAPI_ERR_LOG("No fetch file result found!");
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, ERR_INVALID_OUTPUT,
"Failed to obtain Fetch File Result");
return;
}
napi_value fileResult = FetchFileResultNapi::CreateFetchFileResult(env, move(context->fetchFileResult));
if (fileResult == nullptr) {
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, ERR_INVALID_OUTPUT,
"Failed to create js object for Fetch File Result");
} else {
jsContext->data = fileResult;
jsContext->status = true;
napi_get_undefined(env, &jsContext->error);
}
}
static void GetFileAssetsAsyncCallbackComplete(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("GetFileAssetsAsyncCallbackComplete");
MediaLibraryAsyncContext *context = static_cast<MediaLibraryAsyncContext*>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
napi_get_undefined(env, &jsContext->data);
if (context->error != ERR_DEFAULT) {
context->HandleError(env, jsContext->error);
} else {
GetNapiFileResult(env, context, jsContext);
}
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
napi_value MediaLibraryNapi::JSGetFileAssets(napi_env env, napi_callback_info info)
{
napi_status status;
napi_value result = nullptr;
size_t argc = ARGS_TWO;
napi_value argv[ARGS_TWO] = {0};
napi_value thisVar = nullptr;
MediaLibraryTracer tracer;
tracer.Start("JSGetFileAssets");
GET_JS_ARGS(env, info, argc, argv, thisVar);
NAPI_ASSERT(env, (argc == ARGS_ONE || argc == ARGS_TWO), "requires 2 parameters maximum");
napi_get_undefined(env, &result);
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->mediaTypes.clear();
asyncContext->resultNapiType = ResultNapiType::TYPE_MEDIALIBRARY;
status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&asyncContext->objectInfo));
if (status == napi_ok && asyncContext->objectInfo != nullptr) {
result = ConvertJSArgsToNative(env, argc, argv, *asyncContext);
CHECK_NULL_PTR_RETURN_UNDEFINED(env, result, result, "Failed to obtain arguments");
SetUserIdFromObjectInfo(asyncContext);
result = MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSGetFileAssets", GetFileAssetsExecute,
GetFileAssetsAsyncCallbackComplete);
}
return result;
}
#ifdef MEDIALIBRARY_COMPATIBILITY
static void CompatSetAlbumCoverUri(MediaLibraryAsyncContext *context, unique_ptr<AlbumAsset> &album)
{
MediaLibraryTracer tracer;
tracer.Start("CompatSetAlbumCoverUri");
DataSharePredicates predicates;
int err;
if (album->GetAlbumType() == PhotoAlbumType::USER) {
err = MediaLibraryNapiUtils::GetUserAlbumPredicates(album->GetAlbumId(), predicates, false);
} else {
err = MediaLibraryNapiUtils::GetSystemAlbumPredicates(album->GetAlbumSubType(), predicates, false);
}
if (err < 0) {
NAPI_WARN_LOG("Failed to set cover uri for album subtype: %{public}d", album->GetAlbumSubType());
return;
}
predicates.OrderByDesc(MediaColumn::MEDIA_DATE_ADDED);
predicates.Limit(1, 0);
Uri uri(CONST_URI_QUERY_PHOTO_MAP);
vector<string> columns;
columns.assign(MediaColumn::DEFAULT_FETCH_COLUMNS.begin(), MediaColumn::DEFAULT_FETCH_COLUMNS.end());
auto resultSet = UserFileClient::Query(uri, predicates, columns, err, context->userId);
if (resultSet == nullptr) {
NAPI_ERR_LOG("Query for Album uri failed! errorCode is = %{public}d", err);
context->SaveError(err);
return;
}
auto fetchResult = make_unique<FetchResult<FileAsset>>(move(resultSet));
if (fetchResult->GetCount() == 0) {
return;
}
auto fileAsset = fetchResult->GetFirstObject();
if (fileAsset == nullptr) {
NAPI_WARN_LOG("Failed to get cover asset!");
return;
}
album->SetCoverUri(fileAsset->GetUri());
}
static void SetCompatAlbumName(AlbumAsset *albumData)
{
string albumName;
switch (albumData->GetAlbumSubType()) {
case PhotoAlbumSubType::CAMERA:
albumName = CONST_CAMERA_ALBUM_NAME;
break;
case PhotoAlbumSubType::SCREENSHOT:
albumName = CONST_SCREEN_SHOT_ALBUM_NAME;
break;
default:
NAPI_WARN_LOG("Ignore unsupported compat album type: %{public}d", albumData->GetAlbumSubType());
}
albumData->SetAlbumName(albumName);
}
static void CompatSetAlbumCount(unique_ptr<AlbumAsset> &album)
{
MediaLibraryTracer tracer;
tracer.Start("CompatSetAlbumCount");
DataSharePredicates predicates;
int err;
if (album->GetAlbumType() == PhotoAlbumType::USER) {
err = MediaLibraryNapiUtils::GetUserAlbumPredicates(album->GetAlbumId(), predicates, false);
} else {
err = MediaLibraryNapiUtils::GetSystemAlbumPredicates(album->GetAlbumSubType(), predicates, false);
}
if (err < 0) {
NAPI_WARN_LOG("Failed to set count for album subtype: %{public}d", album->GetAlbumSubType());
album->SetCount(0);
return;
}
Uri uri(CONST_URI_QUERY_PHOTO_MAP);
vector<string> columns;
auto resultSet = UserFileClient::Query(uri, predicates, columns, err);
if (resultSet == nullptr) {
NAPI_WARN_LOG("Query for assets failed! errorCode is = %{public}d", err);
album->SetCount(0);
return;
}
auto fetchResult = make_unique<FetchResult<FileAsset>>(move(resultSet));
int32_t count = fetchResult->GetCount();
album->SetCount(count);
}
#else
static void SetAlbumCoverUri(MediaLibraryAsyncContext *context, unique_ptr<AlbumAsset> &album)
{
MediaLibraryTracer tracer;
tracer.Start("SetAlbumCoverUri");
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
DataShare::DataSharePredicates predicates;
predicates.SetWhereClause(string(CONST_MEDIA_DATA_DB_BUCKET_ID) + " = ? ");
predicates.SetWhereArgs({ to_string(album->GetAlbumId()) });
predicates.SetOrder(string(CONST_MEDIA_DATA_DB_DATE_ADDED) + " DESC LIMIT 0,1 ");
vector<string> columns;
string queryUri = MEDIALIBRARY_DATA_URI;
if (!context->networkId.empty()) {
queryUri = CONST_MEDIALIBRARY_DATA_ABILITY_PREFIX + context->networkId + CONST_MEDIALIBRARY_DATA_URI_IDENTIFIER;
NAPI_DEBUG_LOG("querycoverUri is = %{private}s", queryUri.c_str());
}
Uri uri(queryUri);
int errCode = 0;
shared_ptr<DataShare::DataShareResultSet> resultSet = UserFileClient::Query(
uri, predicates, columns, errCode);
if (resultSet == nullptr) {
NAPI_ERR_LOG("Query for Album uri failed! errorCode is = %{public}d", errCode);
return;
}
unique_ptr<FetchResult<FileAsset>> fetchFileResult = make_unique<FetchResult<FileAsset>>(move(resultSet));
fetchFileResult->SetNetworkId(context->networkId);
unique_ptr<FileAsset> fileAsset = fetchFileResult->GetFirstObject();
CHECK_NULL_PTR_RETURN_VOID(fileAsset, "SetAlbumCoverUr:FileAsset is nullptr");
string coverUri = fileAsset->GetUri();
album->SetCoverUri(coverUri);
NAPI_DEBUG_LOG("coverUri is = %{private}s", album->GetCoverUri().c_str());
}
#endif
void SetAlbumData(AlbumAsset* albumData, shared_ptr<DataShare::DataShareResultSet> resultSet,
const string &networkId)
{
#ifdef MEDIALIBRARY_COMPATIBILITY
albumData->SetAlbumId(get<int32_t>(ResultSetUtils::GetValFromColumn(PhotoAlbumColumns::ALBUM_ID, resultSet,
TYPE_INT32)));
albumData->SetAlbumType(static_cast<PhotoAlbumType>(
get<int32_t>(ResultSetUtils::GetValFromColumn(PhotoAlbumColumns::ALBUM_TYPE, resultSet, TYPE_INT32))));
albumData->SetAlbumSubType(static_cast<PhotoAlbumSubType>(
get<int32_t>(ResultSetUtils::GetValFromColumn(PhotoAlbumColumns::ALBUM_SUBTYPE, resultSet, TYPE_INT32))));
SetCompatAlbumName(albumData);
#else
albumData->SetAlbumId(get<int32_t>(ResultSetUtils::GetValFromColumn(CONST_MEDIA_DATA_DB_BUCKET_ID, resultSet,
TYPE_INT32)));
albumData->SetAlbumName(get<string>(ResultSetUtils::GetValFromColumn(CONST_MEDIA_DATA_DB_TITLE, resultSet,
TYPE_STRING)));
#endif
albumData->SetCount(get<int32_t>(ResultSetUtils::GetValFromColumn(CONST_MEDIA_DATA_DB_COUNT,
resultSet, TYPE_INT32)));
MediaFileUri fileUri(MEDIA_TYPE_ALBUM, to_string(albumData->GetAlbumId()), networkId,
MEDIA_API_VERSION_DEFAULT);
albumData->SetAlbumUri(fileUri.ToString());
albumData->SetAlbumRelativePath(get<string>(ResultSetUtils::GetValFromColumn(CONST_MEDIA_DATA_DB_RELATIVE_PATH,
resultSet, TYPE_STRING)));
albumData->SetAlbumDateModified(get<int64_t>(ResultSetUtils::GetValFromColumn(CONST_MEDIA_DATA_DB_DATE_MODIFIED,
resultSet, TYPE_INT64)));
}
static void GetAlbumResult(MediaLibraryAsyncContext *context, shared_ptr<DataShareResultSet> resultSet)
{
if (context->resultNapiType == ResultNapiType::TYPE_USERFILE_MGR ||
context->resultNapiType == ResultNapiType::TYPE_PHOTOACCESS_HELPER) {
context->fetchAlbumResult = make_unique<FetchResult<AlbumAsset>>(move(resultSet));
context->fetchAlbumResult->SetNetworkId(context->networkId);
context->fetchAlbumResult->SetResultNapiType(context->resultNapiType);
return;
}
while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
unique_ptr<AlbumAsset> albumData = make_unique<AlbumAsset>();
if (albumData != nullptr) {
SetAlbumData(albumData.get(), resultSet, context->networkId);
#ifdef MEDIALIBRARY_COMPATIBILITY
CompatSetAlbumCoverUri(context, albumData);
CompatSetAlbumCount(albumData);
#else
SetAlbumCoverUri(context, albumData);
#endif
context->albumNativeArray.push_back(move(albumData));
} else {
context->SaveError(E_NO_MEMORY);
}
}
}
#ifdef MEDIALIBRARY_COMPATIBILITY
static void ReplaceAlbumName(const string &arg, string &argInstead)
{
if (arg == CONST_CAMERA_ALBUM_NAME) {
argInstead = to_string(PhotoAlbumSubType::CAMERA);
} else if (arg == CONST_SCREEN_SHOT_ALBUM_NAME) {
argInstead = to_string(PhotoAlbumSubType::SCREENSHOT);
} else if (arg == CONST_SCREEN_RECORD_ALBUM_NAME) {
argInstead = to_string(PhotoAlbumSubType::SCREENSHOT);
} else {
argInstead = arg;
}
}
static bool DoReplaceRelativePath(const string &arg, string &argInstead)
{
if (arg == CONST_CAMERA_PATH) {
argInstead = to_string(PhotoAlbumSubType::CAMERA);
} else if (arg == CONST_SCREEN_SHOT_PATH) {
argInstead = to_string(PhotoAlbumSubType::SCREENSHOT);
} else if (arg == CONST_SCREEN_RECORD_PATH) {
argInstead = to_string(PhotoAlbumSubType::SCREENSHOT);
} else if (arg.empty()) {
argInstead = arg;
return false;
} else {
argInstead = arg;
}
return true;
}
static inline void ReplaceRelativePath(string &selection, size_t pos, const string &keyInstead, const string &arg,
string &argInstead)
{
bool shouldReplace = DoReplaceRelativePath(arg, argInstead);
if (shouldReplace) {
selection.replace(pos, string(CONST_MEDIA_DATA_DB_RELATIVE_PATH).length(), keyInstead);
}
}
void MediaLibraryNapi::ReplaceSelection(string &selection, vector<string> &selectionArgs,
const string &key, const string &keyInstead, const int32_t mode)
{
for (size_t pos = 0; pos != string::npos;) {
pos = selection.find(key, pos);
if (pos == string::npos) {
break;
}
size_t argPos = selection.find('?', pos);
if (argPos == string::npos) {
break;
}
size_t argIndex = 0;
for (size_t i = 0; i < argPos; i++) {
if (selection[i] == '?') {
argIndex++;
}
}
if (argIndex > selectionArgs.size() - 1) {
NAPI_WARN_LOG("SelectionArgs size is not valid, selection format maybe incorrect: %{private}s",
selection.c_str());
break;
}
const string &arg = selectionArgs[argIndex];
string argInstead = arg;
if (key == CONST_MEDIA_DATA_DB_RELATIVE_PATH) {
if (mode == ReplaceSelectionMode::ADD_DOCS_TO_RELATIVE_PATH) {
argInstead = MediaFileUtils::AddDocsToRelativePath(arg);
} else {
ReplaceRelativePath(selection, pos, keyInstead, arg, argInstead);
}
} else if (key == CONST_MEDIA_DATA_DB_BUCKET_NAME) {
ReplaceAlbumName(arg, argInstead);
selection.replace(pos, key.length(), keyInstead);
} else if (key == CONST_MEDIA_DATA_DB_BUCKET_ID) {
selection.replace(pos, key.length(), keyInstead);
}
selectionArgs[argIndex] = argInstead;
argPos = selection.find('?', pos);
if (argPos == string::npos) {
break;
}
pos = argPos + 1;
}
}
static void UpdateCompatSelection(MediaLibraryAsyncContext *context)
{
MediaLibraryNapi::ReplaceSelection(context->selection, context->selectionArgs,
CONST_MEDIA_DATA_DB_BUCKET_ID, PhotoAlbumColumns::ALBUM_ID);
MediaLibraryNapi::ReplaceSelection(context->selection, context->selectionArgs,
CONST_MEDIA_DATA_DB_BUCKET_NAME, PhotoAlbumColumns::ALBUM_SUBTYPE);
MediaLibraryNapi::ReplaceSelection(context->selection, context->selectionArgs,
CONST_MEDIA_DATA_DB_RELATIVE_PATH, PhotoAlbumColumns::ALBUM_SUBTYPE);
static const string COMPAT_QUERY_FILTER = PhotoAlbumColumns::ALBUM_SUBTYPE + " IN (" +
to_string(PhotoAlbumSubType::SCREENSHOT) + "," +
to_string(PhotoAlbumSubType::CAMERA) + ")";
if (!context->selection.empty()) {
context->selection = COMPAT_QUERY_FILTER + " AND " + context->selection;
} else {
context->selection = COMPAT_QUERY_FILTER;
}
}
#endif
static void GetResultDataExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("GetResultDataExecute");
MediaLibraryAsyncContext *context = static_cast<MediaLibraryAsyncContext*>(data);
#ifdef MEDIALIBRARY_COMPATIBILITY
UpdateCompatSelection(context);
#else
MediaLibraryNapiUtils::UpdateMediaTypeSelections(context);
#endif
context->predicates.SetWhereClause(context->selection);
context->predicates.SetWhereArgs(context->selectionArgs);
if (!context->order.empty()) {
context->predicates.SetOrder(context->order);
}
#ifdef MEDIALIBRARY_COMPATIBILITY
vector<string> columns;
const set<string> &defaultFetchCols = PhotoAlbumColumns::DEFAULT_FETCH_COLUMNS;
columns.assign(defaultFetchCols.begin(), defaultFetchCols.end());
columns.push_back(PhotoAlbumColumns::ALBUM_DATE_MODIFIED);
#else
vector<string> columns;
#endif
string queryUri = MEDIALIBRARY_DATA_URI + "/" + CONST_MEDIA_ALBUMOPRN_QUERYALBUM;
if (!context->networkId.empty()) {
queryUri = string(CONST_MEDIALIBRARY_DATA_ABILITY_PREFIX) + context->networkId +
CONST_MEDIALIBRARY_DATA_URI_IDENTIFIER + "/" + CONST_MEDIA_ALBUMOPRN_QUERYALBUM;
NAPI_DEBUG_LOG("queryAlbumUri is = %{private}s", queryUri.c_str());
}
Uri uri(queryUri);
int errCode = 0;
shared_ptr<DataShareResultSet> resultSet = UserFileClient::Query(uri, context->predicates, columns, errCode,
context->userId);
if (resultSet == nullptr) {
NAPI_ERR_LOG("GetMediaResultData resultSet is nullptr, errCode is %{public}d", errCode);
context->SaveError(errCode);
return;
}
GetAlbumResult(context, resultSet);
}
static void MediaLibAlbumsAsyncResult(napi_env env, MediaLibraryAsyncContext *context,
unique_ptr<JSAsyncContextOutput> &jsContext)
{
if (context->albumNativeArray.empty()) {
napi_value albumNoArray = nullptr;
napi_create_array(env, &albumNoArray);
jsContext->status = true;
napi_get_undefined(env, &jsContext->error);
jsContext->data = albumNoArray;
} else {
napi_value albumArray = nullptr;
napi_create_array_with_length(env, context->albumNativeArray.size(), &albumArray);
for (size_t i = 0; i < context->albumNativeArray.size(); i++) {
napi_value albumNapiObj = AlbumNapi::CreateAlbumNapi(env, context->albumNativeArray[i]);
napi_set_element(env, albumArray, i, albumNapiObj);
}
jsContext->status = true;
napi_get_undefined(env, &jsContext->error);
jsContext->data = albumArray;
}
}
static void UserFileMgrAlbumsAsyncResult(napi_env env, MediaLibraryAsyncContext *context,
unique_ptr<JSAsyncContextOutput> &jsContext)
{
if (context->fetchAlbumResult->GetCount() < 0) {
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, ERR_MEM_ALLOCATION,
"find no data by options");
} else {
napi_value fileResult = FetchFileResultNapi::CreateFetchFileResult(env, move(context->fetchAlbumResult));
if (fileResult == nullptr) {
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, ERR_INVALID_OUTPUT,
"Failed to create js object for Fetch Album Result");
} else {
jsContext->data = fileResult;
jsContext->status = true;
napi_get_undefined(env, &jsContext->error);
}
}
}
static void AlbumsAsyncResult(napi_env env, MediaLibraryAsyncContext *context,
unique_ptr<JSAsyncContextOutput> &jsContext)
{
if (context->resultNapiType == ResultNapiType::TYPE_MEDIALIBRARY) {
MediaLibAlbumsAsyncResult(env, context, jsContext);
} else {
UserFileMgrAlbumsAsyncResult(env, context, jsContext);
}
}
static void AlbumsAsyncCallbackComplete(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("AlbumsAsyncCallbackComplete");
MediaLibraryAsyncContext *context = static_cast<MediaLibraryAsyncContext*>(data);
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
napi_get_undefined(env, &jsContext->error);
if (context->error != ERR_DEFAULT) {
napi_get_undefined(env, &jsContext->data);
context->HandleError(env, jsContext->error);
} else {
AlbumsAsyncResult(env, context, jsContext);
}
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
napi_value MediaLibraryNapi::JSGetAlbums(napi_env env, napi_callback_info info)
{
napi_status status;
napi_value result = nullptr;
size_t argc = ARGS_TWO;
napi_value argv[ARGS_TWO] = {0};
napi_value thisVar = nullptr;
MediaLibraryTracer tracer;
tracer.Start("JSGetAlbums");
GET_JS_ARGS(env, info, argc, argv, thisVar);
NAPI_ASSERT(env, (argc == ARGS_ONE || argc == ARGS_TWO), "requires 2 parameters maximum");
napi_get_undefined(env, &result);
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&asyncContext->objectInfo));
if (status == napi_ok && asyncContext->objectInfo != nullptr) {
result = ConvertJSArgsToNative(env, argc, argv, *asyncContext);
CHECK_NULL_PTR_RETURN_UNDEFINED(env, result, result, "Failed to obtain arguments");
SetUserIdFromObjectInfo(asyncContext);
result = MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSGetAlbums", GetResultDataExecute,
AlbumsAsyncCallbackComplete);
}
return result;
}
#ifndef MEDIALIBRARY_COMPATIBILITY
static void getFileAssetById(int32_t id, const string &networkId, MediaLibraryAsyncContext *context)
{
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
vector<string> columns;
DataShare::DataSharePredicates predicates;
predicates.SetWhereClause(string(CONST_MEDIA_DATA_DB_ID) + " = ? ");
predicates.SetWhereArgs({ to_string(id) });
string queryUri = MEDIALIBRARY_DATA_URI;
Uri uri(queryUri);
int errCode = 0;
auto resultSet = UserFileClient::Query(uri, predicates, columns, errCode, context->userId);
CHECK_NULL_PTR_RETURN_VOID(resultSet, "Failed to get file asset by id, query resultSet is nullptr");
context->fetchFileResult = make_unique<FetchResult<FileAsset>>(move(resultSet));
CHECK_NULL_PTR_RETURN_VOID(context->fetchFileResult, "Failed to get file asset by id, fetchFileResult is nullptr");
context->fetchFileResult->SetNetworkId(networkId);
if (context->resultNapiType == ResultNapiType::TYPE_USERFILE_MGR ||
context->resultNapiType == ResultNapiType::TYPE_PHOTOACCESS_HELPER) {
context->fetchFileResult->SetResultNapiType(context->resultNapiType);
}
if (context->fetchFileResult->GetCount() < 1) {
NAPI_ERR_LOG("Failed to query file by id: %{public}d, query count is 0", id);
return;
}
unique_ptr<FileAsset> fileAsset = context->fetchFileResult->GetFirstObject();
CHECK_NULL_PTR_RETURN_VOID(fileAsset, "getFileAssetById: fileAsset is nullptr");
context->fileAsset = move(fileAsset);
}
#endif
#ifdef MEDIALIBRARY_COMPATIBILITY
static void SetFileAssetByIdV9(int32_t id, const string &networkId, MediaLibraryAsyncContext *context)
{
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
bool isValid = false;
string displayName = context->valuesBucket.Get(CONST_MEDIA_DATA_DB_NAME, isValid);
if (!isValid) {
NAPI_ERR_LOG("get title is invalid");
return;
}
string relativePath = context->valuesBucket.Get(CONST_MEDIA_DATA_DB_RELATIVE_PATH, isValid);
if (!isValid) {
NAPI_ERR_LOG("get relativePath is invalid");
return;
}
unique_ptr<FileAsset> fileAsset = make_unique<FileAsset>();
fileAsset->SetId(id);
MediaType mediaType = MediaFileUtils::GetMediaType(displayName);
string uri;
if (MediaStringUtils::StartsWith(relativePath, DOCS_PATH + DOC_DIR_VALUES) ||
MediaStringUtils::StartsWith(relativePath, DOCS_PATH + DOWNLOAD_DIR_VALUES)) {
uri = MediaFileUtils::GetVirtualUriFromRealUri(MediaFileUri(MediaType::MEDIA_TYPE_FILE,
to_string(id), networkId, MEDIA_API_VERSION_V9).ToString());
relativePath = MediaFileUtils::RemoveDocsFromRelativePath(relativePath);
} else {
uri = MediaFileUtils::GetVirtualUriFromRealUri(MediaFileUri(mediaType,
to_string(id), networkId, MEDIA_API_VERSION_V9).ToString());
}
fileAsset->SetUri(uri);
fileAsset->SetMediaType(mediaType);
fileAsset->SetDisplayName(displayName);
fileAsset->SetTitle(MediaFileUtils::GetTitleFromDisplayName(displayName));
fileAsset->SetResultNapiType(ResultNapiType::TYPE_MEDIALIBRARY);
fileAsset->SetRelativePath(relativePath);
context->fileAsset = move(fileAsset);
}
#endif
static void SetFileAssetByIdV10(int32_t id, const string &networkId, const string &uri,
MediaLibraryAsyncContext *context)
{
bool isValid = false;
string displayName = context->valuesBucket.Get(CONST_MEDIA_DATA_DB_NAME, isValid);
if (!isValid) {
NAPI_ERR_LOG("getting title is invalid");
return;
}
unique_ptr<FileAsset> fileAsset = make_unique<FileAsset>();
fileAsset->SetId(id);
MediaType mediaType = MediaFileUtils::GetMediaType(displayName);
fileAsset->SetUri(uri);
fileAsset->SetMediaType(mediaType);
fileAsset->SetDisplayName(displayName);
fileAsset->SetTitle(MediaFileUtils::GetTitleFromDisplayName(displayName));
fileAsset->SetResultNapiType(ResultNapiType::TYPE_USERFILE_MGR);
fileAsset->SetTimePending(UNCREATE_FILE_TIMEPENDING);
fileAsset->SetUserId(context->userId);
CHECK_NULL_PTR_RETURN_VOID(fileAsset, "SetFileAssetByIdV10: fileAsset is nullptr");
context->fileAsset = move(fileAsset);
}
static void PhotoAccessSetFileAssetByIdV10(int32_t id, const string &networkId, const string &uri,
MediaLibraryAsyncContext *context)
{
bool isValid = false;
string displayName = context->valuesBucket.Get(CONST_MEDIA_DATA_DB_NAME, isValid);
if (!isValid) {
NAPI_ERR_LOG("getting title is invalid");
return;
}
auto fileAsset = make_unique<FileAsset>();
fileAsset->SetId(id);
MediaType mediaType = MediaFileUtils::GetMediaType(displayName);
fileAsset->SetUri(uri);
fileAsset->SetMediaType(mediaType);
fileAsset->SetDisplayName(displayName);
fileAsset->SetTitle(MediaFileUtils::GetTitleFromDisplayName(displayName));
fileAsset->SetResultNapiType(ResultNapiType::TYPE_PHOTOACCESS_HELPER);
fileAsset->SetTimePending(UNCREATE_FILE_TIMEPENDING);
fileAsset->SetUserId(context->userId);
CHECK_NULL_PTR_RETURN_VOID(fileAsset, "PhotoAccessSetFileAssetByIdV10: fileAsset is nullptr");
context->fileAsset = move(fileAsset);
}
static void JSCreateUriArrayInCallback(napi_env env, MediaLibraryAsyncContext *context,
unique_ptr<JSAsyncContextOutput> &jsContext)
{
napi_value jsObject = nullptr;
if (context->uriArray.empty()) {
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, ERR_INVALID_OUTPUT,
"Obtain file asset uri array failed");
napi_get_undefined(env, &jsContext->data);
} else {
napi_status status = napi_create_array(env, &jsObject);
int count = 0;
for (const auto &uri : context->uriArray) {
napi_value uriObject = nullptr;
if (uri.empty()) {
napi_get_null(env, &uriObject);
} else {
status = napi_create_string_utf8(env, uri.c_str(), NAPI_AUTO_LENGTH, &uriObject);
}
if (status != napi_ok || uriObject == nullptr) {
NAPI_ERR_LOG("Failed to get file asset uri array napi object");
napi_get_undefined(env, &jsContext->data);
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, JS_INNER_FAIL,
"System inner fail");
return;
}
status = napi_set_element(env, jsObject, count, uriObject);
if (status != napi_ok) {
NAPI_ERR_LOG("Failed to get file asset uri array napi object");
napi_get_undefined(env, &jsContext->data);
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, JS_INNER_FAIL,
"System inner fail");
return;
}
++count;
}
if (status != napi_ok || jsObject == nullptr) {
NAPI_ERR_LOG("Failed to get file asset uri array napi object");
napi_get_undefined(env, &jsContext->data);
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, JS_INNER_FAIL,
"System inner fail");
} else {
jsContext->data = jsObject;
napi_get_undefined(env, &jsContext->error);
jsContext->status = true;
}
}
}
static void JSCreateUriInCallback(napi_env env, MediaLibraryAsyncContext *context,
unique_ptr<JSAsyncContextOutput> &jsContext)
{
napi_value jsObject = nullptr;
if (context->uri.empty()) {
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, ERR_INVALID_OUTPUT,
"Obtain file asset uri failed");
napi_get_undefined(env, &jsContext->data);
} else {
napi_status status = napi_create_string_utf8(env, context->uri.c_str(), NAPI_AUTO_LENGTH, &jsObject);
if (status != napi_ok || jsObject == nullptr) {
NAPI_ERR_LOG("Failed to get file asset uri napi object");
napi_get_undefined(env, &jsContext->data);
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, JS_INNER_FAIL,
"System inner fail");
} else {
jsContext->data = jsObject;
napi_get_undefined(env, &jsContext->error);
jsContext->status = true;
}
}
}
static void JSCreateAssetInCallback(napi_env env, MediaLibraryAsyncContext *context,
unique_ptr<JSAsyncContextOutput> &jsContext)
{
napi_value jsFileAsset = nullptr;
CHECK_NULL_PTR_RETURN_VOID(context, "JSCreateAssetInCallback context is nullptr");
if (context->fileAsset == nullptr) {
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, ERR_INVALID_OUTPUT,
"Obtain file asset failed");
napi_get_undefined(env, &jsContext->data);
} else {
context->fileAsset->SetUserId(context->userId);
jsFileAsset = FileAssetNapi::CreateFileAsset(env, context->fileAsset);
if (jsFileAsset == nullptr) {
NAPI_ERR_LOG("Failed to get file asset napi object");
napi_get_undefined(env, &jsContext->data);
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, JS_INNER_FAIL,
"System inner fail");
} else {
NAPI_DEBUG_LOG("JSCreateAssetCompleteCallback jsFileAsset != nullptr");
jsContext->data = jsFileAsset;
napi_get_undefined(env, &jsContext->error);
jsContext->status = true;
}
}
}
static void JSCreateAssetCompleteCallback(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSCreateAssetCompleteCallback");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
auto jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
if (context->error == ERR_DEFAULT) {
if (context->isCreateByAgent) {
JSCreateUriArrayInCallback(env, context, jsContext);
} else if (context->isCreateByComponent) {
JSCreateUriInCallback(env, context, jsContext);
} else {
JSCreateAssetInCallback(env, context, jsContext);
}
} else {
context->HandleError(env, jsContext->error);
napi_get_undefined(env, &jsContext->data);
}
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
HILOG_COMM_INFO("%{public}s:{%{public}s:%{public}d} End create asset.",
MLOG_TAG, __FUNCTION__, __LINE__);
delete context;
}
static void JSPhotoUriPermissionCallback(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSPhotoUriPermissionCallback");
MediaLibraryAsyncContext *context = static_cast<MediaLibraryAsyncContext*>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), JS_INNER_FAIL);
if (context->error != ERR_DEFAULT) {
context->HandleError(env, jsContext->error);
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->data), JS_INNER_FAIL);
} else {
CHECK_ARGS_RET_VOID(env, napi_create_int32(env, context->retVal, &jsContext->data), JS_INNER_FAIL);
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), JS_INNER_FAIL);
jsContext->status = true;
}
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
static bool CheckDisplayNameParams(MediaLibraryAsyncContext *context)
{
if (context == nullptr) {
NAPI_ERR_LOG("Async context is null");
return false;
}
if (!context->isCreateByComponent) {
bool isValid = false;
string displayName = context->valuesBucket.Get(CONST_MEDIA_DATA_DB_NAME, isValid);
if (!isValid) {
NAPI_ERR_LOG("getting displayName is invalid");
return false;
}
if (displayName.empty()) {
return false;
}
}
return true;
}
static string GetFirstDirName(const string &relativePath)
{
string firstDirName = "";
if (!relativePath.empty()) {
string::size_type pos = relativePath.find_first_of('/');
if (pos == relativePath.length()) {
return relativePath;
}
firstDirName = relativePath.substr(0, pos + 1);
NAPI_DEBUG_LOG("firstDirName substr = %{private}s", firstDirName.c_str());
}
return firstDirName;
}
static bool IsDirectory(const string &dirName)
{
struct stat statInfo {};
if (stat((ROOT_MEDIA_DIR + dirName).c_str(), &statInfo) == E_SUCCESS) {
if (statInfo.st_mode & S_IFDIR) {
return true;
}
}
return false;
}
static bool CheckTypeOfType(const string &firstDirName, int32_t fileMediaType)
{
if (!strcmp(firstDirName.c_str(), directoryEnumValues[0].c_str())) {
if (fileMediaType == MEDIA_TYPE_IMAGE || fileMediaType == MEDIA_TYPE_VIDEO) {
return true;
} else {
return false;
}
}
if (!strcmp(firstDirName.c_str(), directoryEnumValues[1].c_str())) {
if (fileMediaType == MEDIA_TYPE_VIDEO) {
return true;
} else {
return false;
}
}
if (!strcmp(firstDirName.c_str(), directoryEnumValues[SECOND_ENUM].c_str())) {
if (fileMediaType == MEDIA_TYPE_IMAGE || fileMediaType == MEDIA_TYPE_VIDEO) {
return true;
} else {
NAPI_INFO_LOG("CheckTypeOfType RETURN FALSE");
return false;
}
}
if (!strcmp(firstDirName.c_str(), directoryEnumValues[THIRD_ENUM].c_str())) {
if (fileMediaType == MEDIA_TYPE_AUDIO) {
return true;
} else {
return false;
}
}
return true;
}
static bool CheckRelativePathParams(MediaLibraryAsyncContext *context)
{
if (context == nullptr) {
NAPI_ERR_LOG("Async context is null");
return false;
}
bool isValid = false;
string relativePath = context->valuesBucket.Get(CONST_MEDIA_DATA_DB_RELATIVE_PATH, isValid);
if (!isValid) {
NAPI_DEBUG_LOG("getting relativePath is invalid");
return false;
}
isValid = false;
int32_t fileMediaType = context->valuesBucket.Get(CONST_MEDIA_DATA_DB_MEDIA_TYPE, isValid);
if (!isValid) {
NAPI_DEBUG_LOG("getting fileMediaType is invalid");
return false;
}
if (relativePath.empty()) {
return false;
}
if (IsDirectory(relativePath)) {
return true;
}
string firstDirName = GetFirstDirName(relativePath);
if (!firstDirName.empty() && IsDirectory(firstDirName)) {
return true;
}
if (!firstDirName.empty()) {
NAPI_DEBUG_LOG("firstDirName = %{private}s", firstDirName.c_str());
for (unsigned int i = 0; i < directoryEnumValues.size(); i++) {
NAPI_DEBUG_LOG("directoryEnumValues%{private}d = %{private}s", i, directoryEnumValues[i].c_str());
if (!strcmp(firstDirName.c_str(), directoryEnumValues[i].c_str())) {
return CheckTypeOfType(firstDirName, fileMediaType);
}
if (!strcmp(firstDirName.c_str(), DOCS_PATH.c_str())) {
return true;
}
}
NAPI_ERR_LOG("Failed to check relative path, firstDirName = %{private}s", firstDirName.c_str());
}
return false;
}
napi_value GetJSArgsForCreateAsset(napi_env env, size_t argc, const napi_value argv[],
MediaLibraryAsyncContext &asyncContext)
{
const int32_t refCount = 1;
napi_value result = nullptr;
auto context = &asyncContext;
CHECK_NULL_PTR_RETURN_UNDEFINED(env, context, result, "Async context is null");
int32_t fileMediaType = 0;
size_t res = 0;
char relativePathBuffer[PATH_MAX];
char titleBuffer[PATH_MAX];
NAPI_ASSERT(env, argv != nullptr, "Argument list is empty");
for (size_t i = PARAM0; i < argc; i++) {
napi_valuetype valueType = napi_undefined;
napi_typeof(env, argv[i], &valueType);
if (i == PARAM0 && valueType == napi_number) {
napi_get_value_int32(env, argv[i], &fileMediaType);
} else if (i == PARAM1 && valueType == napi_string) {
napi_get_value_string_utf8(env, argv[i], titleBuffer, PATH_MAX, &res);
NAPI_DEBUG_LOG("displayName = %{private}s", string(titleBuffer).c_str());
} else if (i == PARAM2 && valueType == napi_string) {
napi_get_value_string_utf8(env, argv[i], relativePathBuffer, PATH_MAX, &res);
NAPI_DEBUG_LOG("relativePath = %{private}s", string(relativePathBuffer).c_str());
} else if (i == PARAM3 && valueType == napi_function) {
napi_create_reference(env, argv[i], refCount, &context->callbackRef);
} else {
NAPI_DEBUG_LOG("type mismatch, valueType: %{public}d", valueType);
return result;
}
}
context->valuesBucket.Put(CONST_MEDIA_DATA_DB_MEDIA_TYPE, fileMediaType);
context->valuesBucket.Put(CONST_MEDIA_DATA_DB_NAME, string(titleBuffer));
context->valuesBucket.Put(CONST_MEDIA_DATA_DB_RELATIVE_PATH, string(relativePathBuffer));
context->assetType = TYPE_DEFAULT;
if (fileMediaType == MediaType::MEDIA_TYPE_IMAGE || fileMediaType == MediaType::MEDIA_TYPE_VIDEO) {
context->assetType = TYPE_PHOTO;
} else if (fileMediaType == MediaType::MEDIA_TYPE_AUDIO) {
context->assetType = TYPE_AUDIO;
}
NAPI_DEBUG_LOG("GetJSArgsForCreateAsset END");
napi_get_boolean(env, true, &result);
return result;
}
static void GetCreateUri(MediaLibraryAsyncContext *context, string &uri)
{
if (context->resultNapiType == ResultNapiType::TYPE_USERFILE_MGR ||
context->resultNapiType == ResultNapiType::TYPE_PHOTOACCESS_HELPER) {
switch (context->assetType) {
case TYPE_PHOTO:
if (context->resultNapiType == ResultNapiType::TYPE_USERFILE_MGR) {
uri = (context->isCreateByComponent) ? CONST_UFM_CREATE_PHOTO_COMPONENT : CONST_UFM_CREATE_PHOTO;
} else {
uri = (context->isCreateByComponent) ? CONST_PAH_CREATE_PHOTO_COMPONENT :
(context->needSystemApp ? CONST_PAH_SYS_CREATE_PHOTO : CONST_PAH_CREATE_PHOTO);
}
break;
case TYPE_AUDIO:
uri = (context->isCreateByComponent) ? CONST_UFM_CREATE_AUDIO_COMPONENT : CONST_UFM_CREATE_AUDIO;
break;
default:
NAPI_ERR_LOG("Unsupported creation napitype %{public}d", static_cast<int32_t>(context->assetType));
return;
}
MediaLibraryNapiUtils::UriAppendKeyValue(uri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
} else {
#ifdef MEDIALIBRARY_COMPATIBILITY
bool isValid = false;
string relativePath = context->valuesBucket.Get(CONST_MEDIA_DATA_DB_RELATIVE_PATH, isValid);
if (MediaStringUtils::StartsWith(relativePath, DOCS_PATH + DOC_DIR_VALUES) ||
MediaStringUtils::StartsWith(relativePath, DOCS_PATH + DOWNLOAD_DIR_VALUES)) {
uri = MEDIALIBRARY_DATA_URI + "/" + CONST_MEDIA_FILEOPRN + "/" + CONST_MEDIA_FILEOPRN_CREATEASSET;
MediaLibraryNapiUtils::UriAppendKeyValue(uri, API_VERSION, to_string(MEDIA_API_VERSION_V9));
return;
}
switch (context->assetType) {
case TYPE_PHOTO:
uri = MEDIALIBRARY_DATA_URI + "/" + CONST_MEDIA_PHOTOOPRN + "/" + CONST_MEDIA_FILEOPRN_CREATEASSET;
break;
case TYPE_AUDIO:
uri = MEDIALIBRARY_DATA_URI + "/" + CONST_MEDIA_AUDIOOPRN + "/" + CONST_MEDIA_FILEOPRN_CREATEASSET;
break;
case TYPE_DEFAULT:
uri = MEDIALIBRARY_DATA_URI + "/" + CONST_MEDIA_FILEOPRN + "/" + CONST_MEDIA_FILEOPRN_CREATEASSET;
break;
default:
NAPI_ERR_LOG("Unsupported creation napi type %{public}d", static_cast<int32_t>(context->assetType));
return;
}
MediaLibraryNapiUtils::UriAppendKeyValue(uri, API_VERSION, to_string(MEDIA_API_VERSION_V9));
#else
uri = MEDIALIBRARY_DATA_URI + "/" + CONST_MEDIA_FILEOPRN + "/" + CONST_MEDIA_FILEOPRN_CREATEASSET;
#endif
}
}
static void JSCreateAssetExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSCreateAssetExecute");
NAPI_INFO_LOG("JSCreateAssetExecute start");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
if (!CheckDisplayNameParams(context)) {
context->error = JS_E_DISPLAYNAME;
return;
}
if ((context->resultNapiType != ResultNapiType::TYPE_USERFILE_MGR) && (!CheckRelativePathParams(context))) {
context->error = JS_E_RELATIVEPATH;
context->errorMsg = "Invalid relativePath";
return;
}
bool isValid = false;
string relativePath = context->valuesBucket.Get(CONST_MEDIA_DATA_DB_RELATIVE_PATH, isValid);
if (isValid) {
if (MediaStringUtils::StartsWith(relativePath, DOC_DIR_VALUES) ||
MediaStringUtils::StartsWith(relativePath, DOWNLOAD_DIR_VALUES)) {
context->valuesBucket.valuesMap.erase(CONST_MEDIA_DATA_DB_RELATIVE_PATH);
context->valuesBucket.Put(CONST_MEDIA_DATA_DB_RELATIVE_PATH, DOCS_PATH + relativePath);
}
}
string uri;
GetCreateUri(context, uri);
Uri createFileUri(uri);
string outUri;
int index = UserFileClient::InsertExt(createFileUri, context->valuesBucket, outUri,
context->userId);
if (index < 0) {
context->SaveError(index);
} else {
if (context->resultNapiType == ResultNapiType::TYPE_USERFILE_MGR) {
if (context->isCreateByComponent) {
context->uri = outUri;
} else {
SetFileAssetByIdV10(index, "", outUri, context);
}
} else {
#ifdef MEDIALIBRARY_COMPATIBILITY
SetFileAssetByIdV9(index, "", context);
#else
getFileAssetById(index, "", context);
#endif
LogMedialibraryAPI(context->fileAsset->GetUri());
}
}
}
napi_value MediaLibraryNapi::JSCreateAsset(napi_env env, napi_callback_info info)
{
napi_status status;
napi_value result = nullptr;
size_t argc = ARGS_FOUR;
napi_value argv[ARGS_FOUR] = {0};
napi_value thisVar = nullptr;
MediaLibraryTracer tracer;
tracer.Start("JSCreateAsset");
GET_JS_ARGS(env, info, argc, argv, thisVar);
NAPI_ASSERT(env, (argc == ARGS_THREE || argc == ARGS_FOUR), "requires 4 parameters maximum");
napi_get_undefined(env, &result);
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_MEDIALIBRARY;
status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&asyncContext->objectInfo));
if (status == napi_ok && asyncContext->objectInfo != nullptr) {
result = GetJSArgsForCreateAsset(env, argc, argv, *asyncContext);
ASSERT_NULLPTR_CHECK(env, result);
SetUserIdFromObjectInfo(asyncContext);
result = MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSCreateAsset", JSCreateAssetExecute,
JSCreateAssetCompleteCallback);
}
return result;
}
#ifdef MEDIALIBRARY_COMPATIBILITY
static void HandleCompatTrashAudio(MediaLibraryAsyncContext *context, const string &deleteId)
{
DataShareValuesBucket valuesBucket;
valuesBucket.Put(CONST_MEDIA_DATA_DB_DATE_TRASHED, MediaFileUtils::UTCTimeMilliSeconds());
DataSharePredicates predicates;
predicates.SetWhereClause(string(CONST_MEDIA_DATA_DB_ID) + " = ? ");
predicates.SetWhereArgs({ deleteId });
Uri uri(CONST_URI_DELETE_AUDIO);
int32_t changedRows = UserFileClient::Delete(uri, predicates);
if (changedRows < 0) {
context->SaveError(changedRows);
return;
}
context->retVal = changedRows;
}
static void HandleCompatDeletePhoto(MediaLibraryAsyncContext *context,
const string &mediaType, const string &deleteId)
{
Uri uri(CONST_URI_COMPAT_DELETE_PHOTOS);
DataSharePredicates predicates;
predicates.In(MediaColumn::MEDIA_ID, vector<string>({ deleteId }));
DataShareValuesBucket valuesBucket;
valuesBucket.Put(MediaColumn::MEDIA_ID, deleteId);
int changedRows = UserFileClient::Update(uri, predicates, valuesBucket);
if (changedRows < 0) {
context->SaveError(changedRows);
return;
}
context->retVal = changedRows;
}
static inline void HandleCompatDelete(MediaLibraryAsyncContext *context,
const string &mediaType, const string &deleteId)
{
if (mediaType == CONST_IMAGE_ASSET_TYPE || mediaType == CONST_VIDEO_ASSET_TYPE) {
return HandleCompatDeletePhoto(context, mediaType, deleteId);
}
if (mediaType == CONST_AUDIO_ASSET_TYPE) {
return HandleCompatTrashAudio(context, deleteId);
}
NAPI_WARN_LOG("Ignore unsupported media type deletion: %{private}s", mediaType.c_str());
}
#endif
static void JSDeleteAssetExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSDeleteAssetExecute");
MediaLibraryAsyncContext *context = static_cast<MediaLibraryAsyncContext*>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
string mediaType;
string deleteId;
bool isValid = false;
string notifyUri = context->valuesBucket.Get(CONST_MEDIA_DATA_DB_URI, isValid);
if (!isValid) {
context->error = ERR_INVALID_OUTPUT;
return;
}
#ifdef MEDIALIBRARY_COMPATIBILITY
notifyUri = MediaFileUtils::GetRealUriFromVirtualUri(notifyUri);
#endif
size_t index = notifyUri.rfind('/');
if (index != string::npos) {
deleteId = notifyUri.substr(index + 1);
notifyUri = notifyUri.substr(0, index);
size_t indexType = notifyUri.rfind('/');
if (indexType != string::npos) {
mediaType = notifyUri.substr(indexType + 1);
}
}
if (MediaFileUtils::IsUriV10(mediaType)) {
NAPI_ERR_LOG("Unsupported media type: %{private}s", mediaType.c_str());
context->SaveError(E_INVALID_URI);
return;
}
#ifdef MEDIALIBRARY_COMPATIBILITY
if (mediaType == CONST_IMAGE_ASSET_TYPE || mediaType == CONST_VIDEO_ASSET_TYPE || mediaType == CONST_AUDIO_ASSET_TYPE) {
return HandleCompatDelete(context, mediaType, deleteId);
}
#endif
notifyUri = MEDIALIBRARY_DATA_URI + "/" + mediaType;
string deleteUri = MEDIALIBRARY_DATA_URI + "/" + CONST_MEDIA_FILEOPRN + "/" + CONST_MEDIA_FILEOPRN_DELETEASSET;
Uri deleteAssetUri(deleteUri);
DataSharePredicates predicates;
predicates.EqualTo(string(CONST_MEDIA_DATA_DB_ID), deleteId);
int retVal = UserFileClient::Delete(deleteAssetUri, predicates);
if (retVal < 0) {
context->SaveError(retVal);
} else {
context->retVal = retVal;
Uri deleteNotify(notifyUri);
UserFileClient::NotifyChange(deleteNotify);
}
}
static void JSDeleteAssetCompleteCallback(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSDeleteAssetCompleteCallback");
MediaLibraryAsyncContext *context = static_cast<MediaLibraryAsyncContext*>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
if (context->error == ERR_DEFAULT) {
NAPI_DEBUG_LOG("Delete result = %{public}d", context->retVal);
napi_create_int32(env, context->retVal, &jsContext->data);
napi_get_undefined(env, &jsContext->error);
jsContext->status = true;
} else {
context->HandleError(env, jsContext->error);
napi_get_undefined(env, &jsContext->data);
}
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
static void JSTrashAssetExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSTrashAssetExecute");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
string uri = context->uri;
if (uri.empty()) {
context->error = ERR_INVALID_OUTPUT;
return;
}
MediaFileUri::RemoveAllFragment(uri);
string trashId = MediaFileUtils::GetIdFromUri(uri);
string trashUri;
if (uri.find(PhotoColumn::PHOTO_URI_PREFIX) != string::npos) {
trashUri = CONST_UFM_UPDATE_PHOTO;
} else if (uri.find(AudioColumn::AUDIO_URI_PREFIX) != string::npos) {
trashUri = CONST_UFM_UPDATE_AUDIO;
} else {
context->error = E_VIOLATION_PARAMETERS;
return;
}
MediaLibraryNapiUtils::UriAppendKeyValue(trashUri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
Uri updateAssetUri(trashUri);
DataSharePredicates predicates;
predicates.SetWhereClause(MediaColumn::MEDIA_ID + " = ? ");
predicates.SetWhereArgs({ trashId });
DataShareValuesBucket valuesBucket;
valuesBucket.Put(MediaColumn::MEDIA_DATE_TRASHED, MediaFileUtils::UTCTimeMilliSeconds());
int32_t changedRows = UserFileClient::Update(updateAssetUri, predicates, valuesBucket);
if (changedRows < 0) {
context->SaveError(changedRows);
NAPI_ERR_LOG("Media asset delete failed, err: %{public}d", changedRows);
}
}
static void JSTrashAssetCompleteCallback(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSTrashAssetCompleteCallback");
MediaLibraryAsyncContext *context = static_cast<MediaLibraryAsyncContext*>(data);
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
napi_get_undefined(env, &jsContext->data);
if (context->error == ERR_DEFAULT) {
jsContext->status = true;
} else {
context->HandleError(env, jsContext->error);
}
if (context->work != nullptr) {
tracer.Finish();
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
napi_value GetJSArgsForDeleteAsset(napi_env env, size_t argc, const napi_value argv[],
MediaLibraryAsyncContext &asyncContext)
{
const int32_t refCount = 1;
napi_value result = nullptr;
auto context = &asyncContext;
CHECK_NULL_PTR_RETURN_UNDEFINED(env, context, result, "Async context is null");
size_t res = 0;
char buffer[PATH_MAX];
NAPI_ASSERT(env, argv != nullptr, "Argument list is empty");
for (size_t i = PARAM0; i < argc; i++) {
napi_valuetype valueType = napi_undefined;
napi_typeof(env, argv[i], &valueType);
if (i == PARAM0 && valueType == napi_string) {
napi_get_value_string_utf8(env, argv[i], buffer, PATH_MAX, &res);
} else if (i == PARAM1 && valueType == napi_function) {
napi_create_reference(env, argv[i], refCount, &context->callbackRef);
break;
} else {
NAPI_ASSERT(env, false, "type mismatch");
}
}
context->valuesBucket.Put(CONST_MEDIA_DATA_DB_URI, string(buffer));
napi_get_boolean(env, true, &result);
return result;
}
napi_value MediaLibraryNapi::JSDeleteAsset(napi_env env, napi_callback_info info)
{
napi_status status;
napi_value result = nullptr;
size_t argc = ARGS_TWO;
napi_value argv[ARGS_TWO] = {0};
napi_value thisVar = nullptr;
MediaLibraryTracer tracer;
tracer.Start("JSDeleteAsset");
GET_JS_ARGS(env, info, argc, argv, thisVar);
NAPI_ASSERT(env, (argc == ARGS_ONE || argc == ARGS_TWO), "requires 2 parameters maximum");
napi_get_undefined(env, &result);
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&asyncContext->objectInfo));
if (status == napi_ok && asyncContext->objectInfo != nullptr) {
result = GetJSArgsForDeleteAsset(env, argc, argv, *asyncContext);
CHECK_NULL_PTR_RETURN_UNDEFINED(env, result, result, "Failed to obtain arguments");
SetUserIdFromObjectInfo(asyncContext);
result = MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSDeleteAsset", JSDeleteAssetExecute,
JSDeleteAssetCompleteCallback);
}
return result;
}
static napi_status SetValueInt32(const napi_env& env, const char* fieldStr, const int intValue, napi_value& result)
{
napi_value value;
napi_status status = napi_create_int32(env, intValue, &value);
if (status != napi_ok) {
NAPI_ERR_LOG("Set value create int32 error! field: %{public}s", fieldStr);
return status;
}
status = napi_set_named_property(env, result, fieldStr, value);
if (status != napi_ok) {
NAPI_ERR_LOG("Set int32 named property error! field: %{public}s, status: %{public}d", fieldStr, status);
}
return status;
}
static napi_status SetValueArray(const napi_env& env,
const char* fieldStr, const std::list<Uri> listValue, napi_value& result)
{
napi_value value = nullptr;
napi_status status = napi_create_array_with_length(env, listValue.size(), &value);
if (status != napi_ok) {
NAPI_ERR_LOG("Create array error! field: %{public}s", fieldStr);
return status;
}
int elementIndex = 0;
for (auto uri : listValue) {
napi_value uriRet = nullptr;
napi_create_string_utf8(env, uri.ToString().c_str(), NAPI_AUTO_LENGTH, &uriRet);
status = napi_set_element(env, value, elementIndex++, uriRet);
if (status != napi_ok) {
NAPI_ERR_LOG("Set lite item failed, error: %d", status);
}
}
status = napi_set_named_property(env, result, fieldStr, value);
if (status != napi_ok) {
NAPI_ERR_LOG("Set array named property error! field: %{public}s", fieldStr);
}
return status;
}
napi_status ChangeListenerNapi::SetSharedAssetArray(const napi_env& env, const char* fieldStr,
ChangeListenerNapi::JsOnChangeCallbackWrapper *wrapper, napi_value& result, bool isPhoto)
{
MediaLibraryTracer tracer;
tracer.Start("SolveOnChange BuildSharedPhotoAssetsObj");
std::vector<std::string> assetIds;
napi_status status = napi_ok;
napi_value assetResults = ChangeListenerNapi::BuildSharedPhotoAssetsObj(env, wrapper, isPhoto);
if (assetResults == nullptr) {
NAPI_ERR_LOG("Failed to get assets Result from rdb");
status = napi_invalid_arg;
return status;
}
status = napi_set_named_property(env, result, fieldStr, assetResults);
if (status != napi_ok) {
NAPI_ERR_LOG("set array named property error: %{public}s", fieldStr);
}
return status;
}
napi_status ChangeListenerNapi::SetExtraSharedAssetArray(const napi_env& env, const char* fieldStr,
ChangeListenerNapi::JsOnChangeCallbackWrapper *wrapper, napi_value& result)
{
MediaLibraryTracer tracer;
tracer.Start("SolveOnChange BuildExtraSharedPhotoAssetsObj");
napi_status status = napi_ok;
napi_value assetResults = ChangeListenerNapi::BuildExtraSharedPhotoAssetsObj(env, wrapper);
if (assetResults == nullptr) {
NAPI_ERR_LOG("Failed to get assets Result from rdb");
}
status = napi_set_named_property(env, result, fieldStr, assetResults);
if (status != napi_ok) {
NAPI_ERR_LOG("set array named property error: %{public}s", fieldStr);
}
return status;
}
napi_status ChangeListenerNapi::SetSubUris(const napi_env& env, ChangeListenerNapi::JsOnChangeCallbackWrapper *wrapper,
napi_value& result)
{
MediaLibraryTracer tracer;
tracer.Start("SolveOnChange SetSubUris");
uint32_t len = wrapper->extraUris_.size();
napi_status status = napi_invalid_arg;
napi_value subUriArray = nullptr;
napi_create_array_with_length(env, len, &subUriArray);
int subElementIndex = 0;
for (auto iter = wrapper->extraUris_.begin(); iter != wrapper->extraUris_.end(); iter++) {
string subUri = *iter;
if (subUri.empty()) {
NAPI_ERR_LOG("Failed to read sub uri");
return status;
}
napi_value subUriRet = nullptr;
napi_create_string_utf8(env, subUri.c_str(), NAPI_AUTO_LENGTH, &subUriRet);
napi_set_element(env, subUriArray, subElementIndex++, subUriRet);
}
status = napi_set_named_property(env, result, "extraUris", subUriArray);
if (status != napi_ok) {
NAPI_ERR_LOG("Set subUri named property error!");
}
ChangeListenerNapi::SetExtraSharedAssetArray(env, "sharedExtraPhotoAssets", wrapper, result);
return status;
}
void ChangeListenerNapi::InvalidateTrashAlbumUri()
{
std::lock_guard<std::mutex> lk(trashMutex_);
trashAlbumUri_.clear();
trashAlbumUserId_ = -1;
}
string ChangeListenerNapi::GetTrashAlbumUri()
{
MediaLibraryTracer tracer;
tracer.Start("ChangeListenerNapi::GetTrashAlbumUri");
std::lock_guard<std::mutex> lk(trashMutex_);
int32_t currentUserId = UserFileClient::GetUserId();
if (!trashAlbumUri_.empty() && trashAlbumUserId_ == currentUserId) {
return trashAlbumUri_;
}
string queryUri = CONST_UFM_QUERY_PHOTO_ALBUM;
Uri uri(queryUri);
int errCode = 0;
DataSharePredicates predicates;
predicates.EqualTo(PhotoAlbumColumns::ALBUM_SUBTYPE, to_string(PhotoAlbumSubType::TRASH));
vector<string> columns;
auto resultSet = UserFileClient::Query(uri, predicates, columns, errCode);
unique_ptr<FetchResult<PhotoAlbum>> albumSet = make_unique<FetchResult<PhotoAlbum>>(move(resultSet));
if (albumSet == nullptr) {
return trashAlbumUri_;
}
if (albumSet->GetCount() != 1) {
return trashAlbumUri_;
}
unique_ptr<PhotoAlbum> albumAssetPtr = albumSet->GetFirstObject();
if (albumAssetPtr == nullptr) {
return trashAlbumUri_;
}
trashAlbumUri_ = albumAssetPtr->GetAlbumUri();
trashAlbumUserId_ = currentUserId;
return trashAlbumUri_;
}
napi_value ChangeListenerNapi::SolveOnChange(napi_env env, ChangeListenerNapi::JsOnChangeCallbackWrapper* wrapper)
{
MediaLibraryTracer tracer;
tracer.Start("ChangeListenerNapi::SolveOnChange");
UvChangeMsg* msg = wrapper->msg_;
static napi_value result;
if (msg->changeInfo_.uris_.empty()) {
napi_get_undefined(env, &result);
NAPI_ERR_LOG("changeInfo_.uris_ is empty");
return result;
}
napi_create_object(env, &result);
SetValueArray(env, "uris", msg->changeInfo_.uris_, result);
if (msg->strUri_.find(PhotoAlbumColumns::DEFAULT_PHOTO_ALBUM_URI) != std::string::npos) {
ChangeListenerNapi::SetSharedAssetArray(env, "sharedAlbumAssets", wrapper, result, false);
} else if (msg->strUri_.find(PhotoColumn::DEFAULT_PHOTO_URI) != std::string::npos) {
ChangeListenerNapi::SetSharedAssetArray(env, "sharedPhotoAssets", wrapper, result, true);
} else {
NAPI_DEBUG_LOG("other albums notify");
}
if (msg->changeInfo_.uris_.size() == DEFAULT_ALBUM_COUNT) {
const std::string &trash = wrapper->trashAlbumUriCached_;
if (!trash.empty() &&
msg->changeInfo_.uris_.front().ToString().compare(trash) == 0) {
if (!MediaLibraryNapiUtils::IsSystemApp()) {
napi_get_undefined(env, &result);
NAPI_ERR_LOG("the app is not system");
return nullptr;
}
}
}
if (msg->data_ != nullptr && msg->changeInfo_.size_ > 0) {
if ((int)msg->changeInfo_.changeType_ == ChangeType::INSERT) {
SetValueInt32(env, "type", (int)NotifyType::NOTIFY_ALBUM_ADD_ASSET, result);
} else {
SetValueInt32(env, "type", (int)NotifyType::NOTIFY_ALBUM_REMOVE_ASSET, result);
}
napi_status status = SetSubUris(env, wrapper, result);
if (status != napi_ok) {
NAPI_ERR_LOG("Set subArray named property error! field: subUris");
}
} else {
SetValueInt32(env, "type", (int)msg->changeInfo_.changeType_, result);
}
return result;
}
std::shared_ptr<NativeRdb::ResultSet> ChangeListenerNapi::GetSharedResultSetFromIds(std::vector<string>& Ids,
bool isPhoto)
{
string queryString = isPhoto ? CONST_PAH_QUERY_PHOTO : CONST_PAH_QUERY_PHOTO_ALBUM;
MediaLibraryNapiUtils::UriAppendKeyValue(queryString, API_VERSION, to_string(MEDIA_API_VERSION_V10));
Uri queryUri(queryString);
DataShare::DataSharePredicates predicates;
if (isPhoto) {
predicates.In(MediaColumn::MEDIA_ID, Ids);
} else {
predicates.In(PhotoAlbumColumns::ALBUM_ID, Ids);
}
std::vector<std::string> columns = isPhoto ? PHOTO_COLUMN : ALBUM_COLUMN;
return UserFileClient::QueryRdb(queryUri, predicates, columns);
}
void ChangeListenerNapi::GetIdsFromUris(std::list<Uri>& listValue, std::vector<std::string>& ids, bool isPhoto)
{
for (auto& uri : listValue) {
string assetId = isPhoto ? MediaLibraryNapiUtils::GetFileIdFromUriString(uri.ToString()) :
MediaLibraryNapiUtils::GetAlbumIdFromUriString(uri.ToString());
if (assetId == "") {
NAPI_WARN_LOG("Failed to read assetId");
continue;
}
ids.push_back(assetId);
}
}
void ChangeListenerNapi::HandleMessageData(UvChangeMsg *msg, ChangeListenerNapi::JsOnChangeCallbackWrapper* wrapper)
{
shared_ptr<MessageParcel> parcel = make_shared<MessageParcel>();
std::vector<string> extraIds = {};
if (parcel->ParseFrom(reinterpret_cast<uintptr_t>(msg->data_), msg->changeInfo_.size_)) {
uint32_t len = 0;
if (!parcel->ReadUint32(len)) {
NAPI_ERR_LOG("Failed to read sub uri list length");
return;
}
if (len > MAX_LEN_LIMIT) {
NAPI_ERR_LOG("len exceed the limit.");
return;
}
for (uint32_t i = 0; i < len; i++) {
string subUri = parcel->ReadString();
if (subUri.empty()) {
NAPI_ERR_LOG("Failed to read sub uri");
continue;
}
wrapper->extraUris_.push_back(subUri);
string fileId = MediaLibraryNapiUtils::GetFileIdFromUriString(subUri);
if (!fileId.empty()) {
extraIds.push_back(fileId);
}
}
if (len > MAX_QUERY_LIMIT) {
NAPI_INFO_LOG("subUri length exceed the limit.");
wrapper->extraSharedAssets_ = nullptr;
return;
}
if (extraIds.size() != 0) {
wrapper->extraSharedAssets_ = GetSharedResultSetFromIds(extraIds, true);
}
} else {
free(msg->data_);
}
}
void ChangeListenerNapi::GetResultSetFromMsg(UvChangeMsg *msg, JsOnChangeCallbackWrapper* wrapper)
{
CHECK_NULL_PTR_RETURN_VOID(msg, "msg is nullptr");
CHECK_NULL_PTR_RETURN_VOID(wrapper, "wrapper is nullptr");
std::vector<string> ids = {};
std::shared_ptr<NativeRdb::ResultSet> sharedAssets = nullptr;
if (msg->strUri_.find(PhotoAlbumColumns::DEFAULT_PHOTO_ALBUM_URI) != std::string::npos) {
GetIdsFromUris(msg->changeInfo_.uris_, ids, false);
sharedAssets = GetSharedResultSetFromIds(ids, false);
} else if (msg->strUri_.find(PhotoColumn::DEFAULT_PHOTO_URI) != std::string::npos) {
GetIdsFromUris(msg->changeInfo_.uris_, ids, true);
sharedAssets = GetSharedResultSetFromIds(ids, true);
} else {
NAPI_DEBUG_LOG("other albums notify");
}
wrapper->uriSize_ = ids.size();
wrapper->sharedAssets_ = sharedAssets;
HandleMessageData(msg, wrapper);
}
void ChangeListenerNapi::OnChange(MediaChangeListener &listener, const napi_ref cbRef)
{
UvChangeMsg *msg = new (std::nothrow) UvChangeMsg(env_, cbRef, listener.changeInfo, listener.strUri);
if (msg == nullptr) {
return;
}
if (!listener.changeInfo.uris_.empty()) {
if (listener.changeInfo.changeType_ == DataShare::DataShareObserver::ChangeType::OTHER) {
NAPI_ERR_LOG("changeInfo.changeType_ is other");
delete msg;
return;
}
if (msg->changeInfo_.size_ > 0) {
msg->data_ = (uint8_t *)malloc(msg->changeInfo_.size_);
if (msg->data_ == nullptr) {
NAPI_ERR_LOG("new msg->data failed");
delete msg;
return;
}
int copyRet = memcpy_s(msg->data_, msg->changeInfo_.size_, msg->changeInfo_.data_, msg->changeInfo_.size_);
if (copyRet != 0) {
NAPI_ERR_LOG("Parcel data copy failed, err = %{public}d", copyRet);
free(msg->data_);
delete msg;
return;
}
}
}
QueryRdbAndNotifyChange(msg);
}
void ChangeListenerNapi::QueryRdbAndNotifyChange(UvChangeMsg *msg)
{
JsOnChangeCallbackWrapper* wrapper = new (std::nothrow) JsOnChangeCallbackWrapper();
if (wrapper == nullptr) {
NAPI_ERR_LOG("JsOnChangeCallbackWrapper allocation failed");
delete msg;
return;
}
wrapper->msg_ = msg;
MediaLibraryTracer tracer;
tracer.Start("GetResultSetFromMsg");
GetResultSetFromMsg(msg, wrapper);
wrapper->trashAlbumUriCached_ = ChangeListenerNapi::GetTrashAlbumUri();
tracer.Finish();
int ret = 0;
if (msg->strUri_.find(PhotoAlbumColumns::DEFAULT_PHOTO_ALBUM_URI) != std::string::npos) {
ret = ChangeListenerNapi::ParseSharedPhotoAssets(wrapper, false);
} else if (msg->strUri_.find(PhotoColumn::DEFAULT_PHOTO_URI) != std::string::npos) {
ret = ChangeListenerNapi::ParseSharedPhotoAssets(wrapper, true);
} else {
NAPI_INFO_LOG("other albums notify");
}
if (ret != 0) {
wrapper->sharedAssetsRowObjVector_.clear();
NAPI_WARN_LOG("Failed to ParseSharedPhotoAssets, ret: %{public}d", ret);
}
ChangeListenerNapi::ParseExtraSharedPhotoAssets(wrapper);
std::function<void()> task = [wrapper, this]() {
UvQueueWork(wrapper);
};
ret = napi_send_event(env_, task, napi_eprio_immediate, "MLB_MediaLib_off");
if (ret != 0) {
NAPI_ERR_LOG("Failed to execute napi_send_event, ret: %{public}d", ret);
delete msg;
delete wrapper;
}
}
void ChangeListenerNapi::UvQueueWork(JsOnChangeCallbackWrapper* wrapper)
{
if (wrapper == nullptr) {
return;
}
UvChangeMsg* msg = reinterpret_cast<UvChangeMsg *>(wrapper->msg_);
do {
if (msg == nullptr) {
NAPI_ERR_LOG("UvChangeMsg is null");
break;
}
napi_env env = msg->env_;
NapiScopeHandler scopeHandler(env);
if (!scopeHandler.IsValid()) {
NAPI_ERR_LOG("scopeHandler is invalid");
break;
}
napi_value jsCallback = nullptr;
napi_status status = napi_get_reference_value(env, msg->ref_, &jsCallback);
if (status != napi_ok) {
NAPI_ERR_LOG("Create reference fail, status: %{public}d", status);
break;
}
napi_value retVal = nullptr;
napi_value result[ARGS_ONE];
result[PARAM0] = ChangeListenerNapi::SolveOnChange(env, wrapper);
if (result[PARAM0] == nullptr) {
NAPI_ERR_LOG("result is nullptr");
break;
}
MediaLibraryTracer tracer;
tracer.Start("ChangeListenerNapi::napi_call_function");
napi_call_function(env, nullptr, jsCallback, ARGS_ONE, result, &retVal);
if (status != napi_ok) {
NAPI_ERR_LOG("CallJs napi_call_function fail, status: %{public}d", status);
break;
}
} while (0);
delete msg;
delete wrapper;
}
int ChangeListenerNapi::ParseSharedPhotoAssets(ChangeListenerNapi::JsOnChangeCallbackWrapper *wrapper, bool isPhoto)
{
MediaLibraryTracer tracer;
std::string traceName = std::string("ParseSharedPhotoAssets to wrapper for ") + (isPhoto ? "photo" : "album");
tracer.Start(traceName.c_str());
int ret = -1;
if (wrapper->uriSize_ > MAX_QUERY_LIMIT) {
return ret;
}
std::shared_ptr<NativeRdb::ResultSet> result = wrapper->sharedAssets_;
if (result == nullptr) {
NAPI_WARN_LOG("ParseSharedPhotoAssets result is nullptr");
return ret;
}
wrapper->sharedAssetsRowObjVector_.clear();
while (result->GoToNextRow() == NativeRdb::E_OK) {
std::shared_ptr<RowObject> rowObj = std::make_shared<RowObject>();
if (isPhoto) {
ret = MediaLibraryNapiUtils::ParseNextRowObject(rowObj, result, true);
} else {
ret = MediaLibraryNapiUtils::ParseNextRowAlbumObject(rowObj, result);
}
if (ret != NativeRdb::E_OK) {
result->Close();
return ret;
}
wrapper->sharedAssetsRowObjVector_.emplace_back(std::move(rowObj));
}
result->Close();
return ret;
}
int ChangeListenerNapi::ParseExtraSharedPhotoAssets(ChangeListenerNapi::JsOnChangeCallbackWrapper *wrapper)
{
MediaLibraryTracer tracer;
tracer.Start("ParseExtraSharedPhotoAssets");
int ret = -1;
std::shared_ptr<NativeRdb::ResultSet> result = wrapper->extraSharedAssets_;
if (result == nullptr) {
NAPI_ERR_LOG("ParseExtraSharedPhotoAssets result is nullptr");
return ret;
}
wrapper->extraSharedAssetsRowObjVector_.clear();
while (result->GoToNextRow() == NativeRdb::E_OK) {
std::shared_ptr<RowObject> rowObj = std::make_shared<RowObject>();
ret = MediaLibraryNapiUtils::ParseNextRowObject(rowObj, result, true);
if (ret != NativeRdb::E_OK) {
NAPI_ERR_LOG("ParseExtraSharedPhotoAssets result failed");
wrapper->extraSharedAssetsRowObjVector_.clear();
result->Close();
return ret;
}
wrapper->extraSharedAssetsRowObjVector_.emplace_back(std::move(rowObj));
}
result->Close();
return ret;
}
napi_value ChangeListenerNapi::BuildSharedPhotoAssetsObj(const napi_env& env,
ChangeListenerNapi::JsOnChangeCallbackWrapper *wrapper, bool isPhoto)
{
napi_value value = nullptr;
napi_status status = napi_create_array_with_length(env, wrapper->sharedAssetsRowObjVector_.size(), &value);
CHECK_COND_RET(status == napi_ok, nullptr, "Create array error!");
napi_value tmpValue = nullptr;
status = napi_create_array_with_length(env, 0, &tmpValue);
CHECK_COND_RET(status == napi_ok, nullptr, "Create array error!");
if (wrapper->uriSize_ > MAX_QUERY_LIMIT) {
NAPI_WARN_LOG("BuildSharedPhotoAssetsObj uriSize is over limit");
return tmpValue;
}
if (wrapper->sharedAssets_ == nullptr) {
NAPI_WARN_LOG("wrapper sharedAssets is nullptr");
return tmpValue;
}
size_t elementIndex = 0;
while (elementIndex < wrapper->sharedAssetsRowObjVector_.size()) {
napi_value assetValue;
if (isPhoto) {
assetValue = MediaLibraryNapiUtils::BuildNextRowObject(
env, wrapper->sharedAssetsRowObjVector_[elementIndex], true);
} else {
assetValue = MediaLibraryNapiUtils::BuildNextRowAlbumObject(
env, wrapper->sharedAssetsRowObjVector_[elementIndex]);
}
if (assetValue == nullptr) {
wrapper->sharedAssets_->Close();
return tmpValue;
}
status = napi_set_element(env, value, elementIndex++, assetValue);
if (status != napi_ok) {
NAPI_ERR_LOG("Set photo asset value failed");
wrapper->sharedAssets_->Close();
return tmpValue;
}
}
wrapper->sharedAssets_->Close();
return value;
}
napi_value ChangeListenerNapi::BuildExtraSharedPhotoAssetsObj(const napi_env& env,
ChangeListenerNapi::JsOnChangeCallbackWrapper *wrapper)
{
napi_value value = nullptr;
napi_status status = napi_create_array_with_length(env, wrapper->extraSharedAssetsRowObjVector_.size(), &value);
CHECK_COND_RET(status == napi_ok, nullptr, "Create array error!");
if (wrapper->extraSharedAssets_ == nullptr) {
NAPI_WARN_LOG("wrapper extraSharedAssets is nullptr");
return nullptr;
}
size_t elementIndex = 0;
while (elementIndex < wrapper->extraSharedAssetsRowObjVector_.size()) {
napi_value assetValue;
assetValue = MediaLibraryNapiUtils::BuildNextRowObject(
env, wrapper->extraSharedAssetsRowObjVector_[elementIndex], true);
if (assetValue == nullptr) {
wrapper->extraSharedAssets_->Close();
return nullptr;
}
status = napi_set_element(env, value, elementIndex++, assetValue);
if (status != napi_ok) {
NAPI_ERR_LOG("Set photo asset value failed");
wrapper->extraSharedAssets_->Close();
return nullptr;
}
}
wrapper->extraSharedAssets_->Close();
return value;
}
int32_t MediaLibraryNapi::GetListenerType(const string &str) const
{
auto iter = ListenerTypeMaps.find(str);
if (iter == ListenerTypeMaps.end()) {
NAPI_ERR_LOG("Invalid Listener Type %{public}s", str.c_str());
return INVALID_LISTENER;
}
return iter->second;
}
void MediaLibraryNapi::RegisterChange(napi_env env, const string &type, ChangeListenerNapi &listObj)
{
NAPI_DEBUG_LOG("Register change type = %{public}s", type.c_str());
int32_t typeEnum = GetListenerType(type);
switch (typeEnum) {
case AUDIO_LISTENER:
listObj.audioDataObserver_ = new(nothrow) MediaObserver(listObj, MEDIA_TYPE_AUDIO);
UserFileClient::RegisterObserver(Uri(CONST_MEDIALIBRARY_AUDIO_URI), listObj.audioDataObserver_);
break;
case VIDEO_LISTENER:
listObj.videoDataObserver_ = new(nothrow) MediaObserver(listObj, MEDIA_TYPE_VIDEO);
UserFileClient::RegisterObserver(Uri(CONST_MEDIALIBRARY_VIDEO_URI), listObj.videoDataObserver_);
break;
case IMAGE_LISTENER:
listObj.imageDataObserver_ = new(nothrow) MediaObserver(listObj, MEDIA_TYPE_IMAGE);
UserFileClient::RegisterObserver(Uri(CONST_MEDIALIBRARY_IMAGE_URI), listObj.imageDataObserver_);
break;
case FILE_LISTENER:
listObj.fileDataObserver_ = new(nothrow) MediaObserver(listObj, MEDIA_TYPE_FILE);
UserFileClient::RegisterObserver(Uri(CONST_MEDIALIBRARY_FILE_URI), listObj.fileDataObserver_);
break;
case SMARTALBUM_LISTENER:
listObj.smartAlbumDataObserver_ = new(nothrow) MediaObserver(listObj, MEDIA_TYPE_SMARTALBUM);
UserFileClient::RegisterObserver(Uri(CONST_MEDIALIBRARY_SMARTALBUM_CHANGE_URI),
listObj.smartAlbumDataObserver_);
break;
case DEVICE_LISTENER:
listObj.deviceDataObserver_ = new(nothrow) MediaObserver(listObj, MEDIA_TYPE_DEVICE);
UserFileClient::RegisterObserver(Uri(CONST_MEDIALIBRARY_DEVICE_URI), listObj.deviceDataObserver_);
break;
case REMOTEFILE_LISTENER:
listObj.remoteFileDataObserver_ = new(nothrow) MediaObserver(listObj, MEDIA_TYPE_REMOTEFILE);
UserFileClient::RegisterObserver(Uri(CONST_MEDIALIBRARY_REMOTEFILE_URI), listObj.remoteFileDataObserver_);
break;
case ALBUM_LISTENER:
listObj.albumDataObserver_ = new(nothrow) MediaObserver(listObj, MEDIA_TYPE_ALBUM);
UserFileClient::RegisterObserver(Uri(CONST_MEDIALIBRARY_ALBUM_URI), listObj.albumDataObserver_);
break;
default:
NAPI_ERR_LOG("Invalid Media Type!");
}
PrewarmTrashAlbumUriAsync();
}
void MediaLibraryNapi::RegisterNotifyChange(napi_env env,
const std::string &uri, bool isDerived, napi_ref ref, ChangeListenerNapi &listObj)
{
Uri notifyUri(uri);
shared_ptr<MediaOnNotifyObserver> observer= make_shared<MediaOnNotifyObserver>(listObj, uri, ref);
UserFileClient::RegisterObserverExt(notifyUri,
static_cast<shared_ptr<DataShare::DataShareObserver>>(observer), isDerived);
lock_guard<mutex> lock(sOnOffMutex_);
listObj.observers_.push_back(observer);
PrewarmTrashAlbumUriAsync();
}
napi_value MediaLibraryNapi::JSOnCallback(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("JSOnCallback");
napi_value undefinedResult = nullptr;
napi_get_undefined(env, &undefinedResult);
size_t argc = ARGS_TWO;
napi_value argv[ARGS_TWO] = {nullptr};
napi_value thisVar = nullptr;
GET_JS_ARGS(env, info, argc, argv, thisVar);
NAPI_ASSERT(env, argc == ARGS_TWO, "requires 2 parameters");
MediaLibraryNapi *obj = nullptr;
napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void**>(&obj));
if (status == napi_ok && obj != nullptr) {
napi_valuetype valueType = napi_undefined;
if (napi_typeof(env, argv[PARAM0], &valueType) != napi_ok || valueType != napi_string ||
napi_typeof(env, argv[PARAM1], &valueType) != napi_ok || valueType != napi_function) {
return undefinedResult;
}
char buffer[ARG_BUF_SIZE];
size_t res = 0;
if (napi_get_value_string_utf8(env, argv[PARAM0], buffer, ARG_BUF_SIZE, &res) != napi_ok) {
NAPI_ERR_LOG("Failed to get value string utf8 for type");
return undefinedResult;
}
string type = string(buffer);
const int32_t refCount = 1;
napi_create_reference(env, argv[PARAM1], refCount, &g_listObj->cbOnRef_);
tracer.Start("RegisterChange");
obj->RegisterChange(env, type, *g_listObj);
tracer.Finish();
}
return undefinedResult;
}
bool MediaLibraryNapi::CheckRef(napi_env env,
napi_ref ref, ChangeListenerNapi &listObj, bool isOff, const string &uri)
{
napi_value offCallback = nullptr;
napi_status status = napi_get_reference_value(env, ref, &offCallback);
if (status != napi_ok) {
NAPI_ERR_LOG("Create reference fail, status: %{public}d", status);
return false;
}
bool isSame = false;
shared_ptr<DataShare::DataShareObserver> obs;
string obsUri;
{
lock_guard<mutex> lock(sOnOffMutex_);
for (auto it = listObj.observers_.begin(); it < listObj.observers_.end(); it++) {
napi_value onCallback = nullptr;
status = napi_get_reference_value(env, (*it)->ref_, &onCallback);
if (status != napi_ok) {
NAPI_ERR_LOG("Create reference fail, status: %{public}d", status);
return false;
}
napi_strict_equals(env, offCallback, onCallback, &isSame);
if (isSame) {
obsUri = (*it)->uri_;
if ((isOff) && (uri.compare(obsUri) == 0)) {
obs = static_cast<shared_ptr<DataShare::DataShareObserver>>(*it);
listObj.observers_.erase(it);
break;
}
if (uri.compare(obsUri) != 0) {
return true;
}
return false;
}
}
}
if (isSame && isOff) {
if (obs != nullptr) {
UserFileClient::UnregisterObserverExt(Uri(obsUri), obs);
}
}
return true;
}
napi_value MediaLibraryNapi::UserFileMgrOnCallback(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("UserFileMgrOnCallback");
napi_value undefinedResult = nullptr;
napi_get_undefined(env, &undefinedResult);
size_t argc = ARGS_THREE;
napi_value argv[ARGS_THREE] = {nullptr};
napi_value thisVar = nullptr;
GET_JS_ARGS(env, info, argc, argv, thisVar);
if (argc == ARGS_TWO) {
return JSOnCallback(env, info);
}
NAPI_ASSERT(env, argc == ARGS_THREE, "requires 3 parameters");
MediaLibraryNapi *obj = nullptr;
napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void**>(&obj));
if (status == napi_ok && obj != nullptr) {
napi_valuetype valueType = napi_undefined;
if (napi_typeof(env, argv[PARAM0], &valueType) != napi_ok || valueType != napi_string ||
napi_typeof(env, argv[PARAM1], &valueType) != napi_ok || valueType != napi_boolean ||
napi_typeof(env, argv[PARAM2], &valueType) != napi_ok || valueType != napi_function) {
NapiError::ThrowError(env, JS_ERR_PARAMETER_INVALID);
return undefinedResult;
}
char buffer[ARG_BUF_SIZE];
size_t res = 0;
if (napi_get_value_string_utf8(env, argv[PARAM0], buffer, ARG_BUF_SIZE, &res) != napi_ok) {
NapiError::ThrowError(env, JS_ERR_PARAMETER_INVALID);
return undefinedResult;
}
string uri = string(buffer);
bool isDerived = false;
if (napi_get_value_bool(env, argv[PARAM1], &isDerived) != napi_ok) {
NapiError::ThrowError(env, JS_ERR_PARAMETER_INVALID);
return undefinedResult;
}
const int32_t refCount = 1;
napi_ref cbOnRef = nullptr;
napi_create_reference(env, argv[PARAM2], refCount, &cbOnRef);
tracer.Start("RegisterNotifyChange");
if (CheckRef(env, cbOnRef, *g_listObj, false, uri)) {
obj->RegisterNotifyChange(env, uri, isDerived, cbOnRef, *g_listObj);
} else {
NapiError::ThrowError(env, JS_ERR_PARAMETER_INVALID);
napi_delete_reference(env, cbOnRef);
cbOnRef = nullptr;
return undefinedResult;
}
tracer.Finish();
}
return undefinedResult;
}
void MediaLibraryNapi::UnregisterChange(napi_env env, const string &type, ChangeListenerNapi &listObj)
{
NAPI_DEBUG_LOG("Unregister change type = %{public}s", type.c_str());
MediaType mediaType;
int32_t typeEnum = GetListenerType(type);
switch (typeEnum) {
case AUDIO_LISTENER:
CHECK_NULL_PTR_RETURN_VOID(listObj.audioDataObserver_, "Failed to obtain audio data observer");
mediaType = MEDIA_TYPE_AUDIO;
UserFileClient::UnregisterObserver(Uri(CONST_MEDIALIBRARY_AUDIO_URI), listObj.audioDataObserver_);
listObj.audioDataObserver_ = nullptr;
break;
case VIDEO_LISTENER:
CHECK_NULL_PTR_RETURN_VOID(listObj.videoDataObserver_, "Failed to obtain video data observer");
mediaType = MEDIA_TYPE_VIDEO;
UserFileClient::UnregisterObserver(Uri(CONST_MEDIALIBRARY_VIDEO_URI), listObj.videoDataObserver_);
listObj.videoDataObserver_ = nullptr;
break;
case IMAGE_LISTENER:
CHECK_NULL_PTR_RETURN_VOID(listObj.imageDataObserver_, "Failed to obtain image data observer");
mediaType = MEDIA_TYPE_IMAGE;
UserFileClient::UnregisterObserver(Uri(CONST_MEDIALIBRARY_IMAGE_URI), listObj.imageDataObserver_);
listObj.imageDataObserver_ = nullptr;
break;
case FILE_LISTENER:
CHECK_NULL_PTR_RETURN_VOID(listObj.fileDataObserver_, "Failed to obtain file data observer");
mediaType = MEDIA_TYPE_FILE;
UserFileClient::UnregisterObserver(Uri(CONST_MEDIALIBRARY_FILE_URI), listObj.fileDataObserver_);
listObj.fileDataObserver_ = nullptr;
break;
case SMARTALBUM_LISTENER:
CHECK_NULL_PTR_RETURN_VOID(listObj.smartAlbumDataObserver_, "Failed to obtain smart album data observer");
mediaType = MEDIA_TYPE_SMARTALBUM;
UserFileClient::UnregisterObserver(Uri(CONST_MEDIALIBRARY_SMARTALBUM_CHANGE_URI),
listObj.smartAlbumDataObserver_);
listObj.smartAlbumDataObserver_ = nullptr;
break;
case DEVICE_LISTENER:
CHECK_NULL_PTR_RETURN_VOID(listObj.deviceDataObserver_, "Failed to obtain device data observer");
mediaType = MEDIA_TYPE_DEVICE;
UserFileClient::UnregisterObserver(Uri(CONST_MEDIALIBRARY_DEVICE_URI), listObj.deviceDataObserver_);
listObj.deviceDataObserver_ = nullptr;
break;
case REMOTEFILE_LISTENER:
CHECK_NULL_PTR_RETURN_VOID(listObj.remoteFileDataObserver_, "Failed to obtain remote file data observer");
mediaType = MEDIA_TYPE_REMOTEFILE;
UserFileClient::UnregisterObserver(Uri(CONST_MEDIALIBRARY_REMOTEFILE_URI), listObj.remoteFileDataObserver_);
listObj.remoteFileDataObserver_ = nullptr;
break;
case ALBUM_LISTENER:
CHECK_NULL_PTR_RETURN_VOID(listObj.albumDataObserver_, "Failed to obtain album data observer");
mediaType = MEDIA_TYPE_ALBUM;
UserFileClient::UnregisterObserver(Uri(CONST_MEDIALIBRARY_ALBUM_URI), listObj.albumDataObserver_);
listObj.albumDataObserver_ = nullptr;
break;
default:
NAPI_ERR_LOG("Invalid Media Type");
return;
}
if (listObj.cbOffRef_ != nullptr) {
MediaChangeListener listener;
listener.mediaType = mediaType;
listObj.OnChange(listener, listObj.cbOffRef_);
}
}
void MediaLibraryNapi::UnRegisterNotifyChange(napi_env env,
const std::string &uri, napi_ref ref, ChangeListenerNapi &listObj)
{
if (ref != nullptr) {
CheckRef(env, ref, listObj, true, uri);
return;
}
if (listObj.observers_.size() == 0) {
return;
}
std::vector<std::shared_ptr<MediaOnNotifyObserver>> offObservers;
{
lock_guard<mutex> lock(sOnOffMutex_);
for (auto iter = listObj.observers_.begin(); iter != listObj.observers_.end();) {
if (uri.compare((*iter)->uri_) == 0) {
offObservers.push_back(*iter);
vector<shared_ptr<MediaOnNotifyObserver>>::iterator tmp = iter;
iter = listObj.observers_.erase(tmp);
} else {
iter++;
}
}
}
for (auto obs : offObservers) {
UserFileClient::UnregisterObserverExt(Uri(uri),
static_cast<shared_ptr<DataShare::DataShareObserver>>(obs));
}
}
napi_value MediaLibraryNapi::JSOffCallback(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("JSOffCallback");
napi_value undefinedResult = nullptr;
napi_get_undefined(env, &undefinedResult);
size_t argc = ARGS_TWO;
napi_value argv[ARGS_TWO] = {nullptr};
napi_value thisVar = nullptr;
GET_JS_ARGS(env, info, argc, argv, thisVar);
NAPI_ASSERT(env, ARGS_ONE <= argc && argc <= ARGS_TWO, "requires one or two parameters");
if (thisVar == nullptr || argv[PARAM0] == nullptr) {
NAPI_ERR_LOG("Failed to retrieve details about the callback");
return undefinedResult;
}
MediaLibraryNapi *obj = nullptr;
napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void**>(&obj));
if (status == napi_ok && obj != nullptr) {
napi_valuetype valueType = napi_undefined;
if (napi_typeof(env, argv[PARAM0], &valueType) != napi_ok || valueType != napi_string) {
return undefinedResult;
}
if (argc == ARGS_TWO) {
auto status = napi_typeof(env, argv[PARAM1], &valueType);
if (status == napi_ok && (valueType == napi_undefined || valueType == napi_null)) {
argc -= 1;
}
}
size_t res = 0;
char buffer[ARG_BUF_SIZE];
if (napi_get_value_string_utf8(env, argv[PARAM0], buffer, ARG_BUF_SIZE, &res) != napi_ok) {
NAPI_ERR_LOG("Failed to get value string utf8 for type");
return undefinedResult;
}
string type = string(buffer);
if (argc == ARGS_TWO) {
if (napi_typeof(env, argv[PARAM1], &valueType) != napi_ok || valueType != napi_function ||
g_listObj == nullptr) {
return undefinedResult;
}
const int32_t refCount = 1;
napi_create_reference(env, argv[PARAM1], refCount, &g_listObj->cbOffRef_);
}
tracer.Start("UnregisterChange");
obj->UnregisterChange(env, type, *g_listObj);
tracer.Finish();
}
return undefinedResult;
}
static napi_value UserFileMgrOffCheckArgs(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
napi_value thisVar = nullptr;
context->argc = ARGS_TWO;
GET_JS_ARGS(env, info, context->argc, context->argv, thisVar);
NAPI_ASSERT(env, ARGS_ONE <= context->argc && context->argc<= ARGS_TWO, "requires one or two parameters");
if (thisVar == nullptr || context->argv[PARAM0] == nullptr) {
return nullptr;
}
napi_valuetype valueType = napi_undefined;
if (napi_typeof(env, context->argv[PARAM0], &valueType) != napi_ok || valueType != napi_string) {
NapiError::ThrowError(env, JS_ERR_PARAMETER_INVALID);
return nullptr;
}
if (context->argc == ARGS_TWO) {
auto status = napi_typeof(env, context->argv[PARAM1], &valueType);
if (status == napi_ok && (valueType == napi_undefined || valueType == napi_null)) {
context->argc -= 1;
}
}
return thisVar;
}
static napi_value GetOldUriMap(napi_env env, MediaLibraryAsyncContext *context)
{
napi_status status;
napi_value mapNapiValue {nullptr};
status = napi_create_map(env, &mapNapiValue);
CHECK_COND_RET(status == napi_ok && mapNapiValue != nullptr, nullptr,
"Failed to create map napi value, napi status: %{public}d", static_cast<int>(status));
for (auto &iter : context->uriMap) {
napi_value key {nullptr};
napi_value value {nullptr};
status = napi_create_string_utf8(env, iter.first.c_str(), NAPI_AUTO_LENGTH, &key);
CHECK_COND_RET(status == napi_ok && key != nullptr, nullptr,
"Failed to create string key, napi status: %{public}d", static_cast<int>(status));
status = napi_create_string_utf8(env, iter.second.c_str(), NAPI_AUTO_LENGTH, &value);
CHECK_COND_RET(status == napi_ok && value != nullptr, nullptr,
"Failed to create string value, napi status: %{public}d", static_cast<int>(status));
status = napi_map_set_property(env, mapNapiValue, key, value);
CHECK_COND_RET(status == napi_ok, nullptr,
"Failed to set uriMap property, napi status: %{public}d", static_cast<int>(status));
}
return mapNapiValue;
}
static void GetOldUriQueryResult(napi_env env, MediaLibraryAsyncContext *context,
unique_ptr<JSAsyncContextOutput> &jsContext)
{
napi_value fileResult = GetOldUriMap(env, context);
if (fileResult == nullptr) {
CHECK_ARGS_RET_VOID_WITH_MEG(env, napi_get_undefined(env, &jsContext->data), JS_E_INNER_FAIL,
"Failed to create initialize data field");
MediaLibraryNapiUtils::CreateNapiErrorObject(
env, jsContext->error, ERR_INVALID_OUTPUT,
"Failed to create js object for Fetch Album Result"
);
return;
}
jsContext->data = fileResult;
jsContext->status = true;
CHECK_ARGS_RET_VOID_WITH_MEG(env, napi_get_undefined(env, &jsContext->error), JS_E_INNER_FAIL,
"Failed to initialize error field");
}
static void JSGetAssetsByOldUrisCompleteCallback(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSGetAssetsByOldUrisCompleteCallback");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
CHECK_ARGS_RET_VOID_WITH_MEG(env, napi_get_undefined(env, &jsContext->error), JS_E_INNER_FAIL,
"Failed to initialize error field");
if (context->error == ERR_DEFAULT) {
napi_value mapNapiValue {nullptr};
napi_create_map(env, &mapNapiValue);
jsContext->data = mapNapiValue;
jsContext->status = true;
CHECK_ARGS_RET_VOID_WITH_MEG(env, napi_get_undefined(env, &jsContext->error), JS_E_INNER_FAIL,
"Failed to initialize error field.");
GetOldUriQueryResult(env, context, jsContext);
}
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(
env, context->deferred, context->callbackRef, context->work, *jsContext
);
}
delete context;
}
static bool ExtractOldFileIds(const std::vector<std::string>& oldUris,
std::map<std::string, std::string>& resultMap,
std::vector<std::string>& fileIdsOld,
std::map<std::string, std::vector<std::string>>& oldIdToUris)
{
fileIdsOld.reserve(oldUris.size());
for (const auto& oldUri : oldUris) {
resultMap[oldUri] = "";
std::string fileIdOld = MediaFileUtils::GetIdFromUri(oldUri);
if (!fileIdOld.empty()) {
auto& uriList = oldIdToUris[fileIdOld];
if (uriList.empty()) {
fileIdsOld.push_back(fileIdOld);
}
uriList.push_back(oldUri);
} else {
NAPI_ERR_LOG("ExtractOldFileIds: Failed to extract fileId from URI: %{private}s", oldUri.c_str());
}
}
if (fileIdsOld.empty()) {
NAPI_ERR_LOG("ExtractOldFileIds: No valid fileIds extracted from any URI");
return false;
}
return true;
}
static bool QueryOldPhotosTable(const std::vector<std::string>& fileIdsOld,
std::map<std::string, int32_t>& oldToNewIdMapping,
std::vector<std::string>& fileIdsNew)
{
DataSharePredicates predicates;
predicates.In(TabOldPhotosColumn::MEDIA_OLD_ID, fileIdsOld);
predicates.OrderByDesc(TabOldPhotosColumn::MEDIA_CLONE_SEQUENCE);
std::vector<std::string> columns = {
TabOldPhotosColumn::MEDIA_ID,
TabOldPhotosColumn::MEDIA_OLD_ID
};
Uri uri(CONST_QUERY_TAB_OLD_PHOTO);
int errCode = 0;
auto resultSet = UserFileClient::Query(uri, predicates, columns, errCode);
if (resultSet == nullptr) {
NAPI_ERR_LOG("QueryOldPhotosTable: Query failed - resultSet is nullptr, errCode: %{public}d", errCode);
return false;
}
std::set<std::string> processedOldIds;
fileIdsNew.reserve(fileIdsOld.size());
while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
std::string oldId = GetStringVal(TabOldPhotosColumn::MEDIA_OLD_ID, resultSet);
if (processedOldIds.emplace(oldId).second) {
int32_t newId = GetInt32Val(TabOldPhotosColumn::MEDIA_ID, resultSet);
oldToNewIdMapping.emplace(oldId, newId);
fileIdsNew.emplace_back(std::to_string(newId));
}
}
resultSet->Close();
if (oldToNewIdMapping.empty()) {
NAPI_WARN_LOG("QueryOldPhotosTable: No matching records found in old photos table");
return false;
}
return true;
}
static bool QueryDisplayNamesAndFilePaths(const std::vector<std::string>& fileIdsNew,
std::map<std::string, std::string>& newIdToDisplayName,
std::map<std::string, std::string>& newIdToFilePath)
{
DataSharePredicates photoPredicates;
photoPredicates.In(MediaColumn::MEDIA_ID, fileIdsNew);
std::vector<std::string> photoColumns = {
MediaColumn::MEDIA_NAME,
MediaColumn::MEDIA_ID,
MediaColumn::MEDIA_FILE_PATH
};
Uri photoUri(CONST_PAH_QUERY_PHOTO);
int errCode = 0;
auto photoResultSet = UserFileClient::Query(photoUri, photoPredicates, photoColumns, errCode);
if (photoResultSet == nullptr) {
NAPI_ERR_LOG("QueryDisplayNamesAndFilePaths: Query failed - resultSet is nullptr, errCode: %{public}d", errCode);
return false;
}
while (photoResultSet->GoToNextRow() == NativeRdb::E_OK) {
std::string newId = GetStringVal(MediaColumn::MEDIA_ID, photoResultSet);
std::string displayName = GetStringVal(MediaColumn::MEDIA_NAME, photoResultSet);
std::string filePath = GetStringVal(MediaColumn::MEDIA_FILE_PATH, photoResultSet);
newIdToDisplayName.emplace(newId, std::move(displayName));
newIdToFilePath.emplace(newId, std::move(filePath));
}
photoResultSet->Close();
return true;
}
static void GenerateFinalUris(const std::map<std::string, int32_t>& oldToNewIdMapping,
const std::map<std::string, std::string>& newIdToDisplayName,
const std::map<std::string, std::string>& newIdToFilePath,
const std::map<std::string, std::vector<std::string>>& oldIdToUris,
std::map<std::string, std::string>& resultMap)
{
for (const auto& [oldId, newId] : oldToNewIdMapping) {
std::string newIdStr = std::to_string(newId);
auto displayNameIt = newIdToDisplayName.find(newIdStr);
auto filePathIt = newIdToFilePath.find(newIdStr);
if (displayNameIt != newIdToDisplayName.end() && filePathIt != newIdToFilePath.end()) {
std::string newUri = MediaFileUtils::GetUriByExtrConditions(
PhotoColumn::PHOTO_URI_PREFIX,
newIdStr,
MediaFileUtils::GetExtraUri(displayNameIt->second, filePathIt->second)
);
for (const std::string& oldUri : oldIdToUris.at(oldId)) {
resultMap[oldUri] = newUri;
}
} else {
const auto& affectedUris = oldIdToUris.at(oldId);
NAPI_WARN_LOG(
"GenerateFinalUris: Failed to generate newUri for oldId %{public}s (missing display name or file path), affects %{public}zu URIs",
oldId.c_str(), affectedUris.size()
);
}
}
}
static std::map<std::string, std::string> GetNewUriByOldUriBatch(const std::vector<std::string>& oldUris)
{
if (oldUris.empty()) {
NAPI_WARN_LOG("GetNewUriByOldUriBatch: Input URIs vector is empty");
return {};
}
std::map<std::string, std::string> resultMap;
std::vector<std::string> fileIdsOld;
std::map<std::string, std::vector<std::string>> oldIdToUris;
if (!ExtractOldFileIds(oldUris, resultMap, fileIdsOld, oldIdToUris)) {
return resultMap;
}
std::map<std::string, int32_t> oldToNewIdMapping;
std::vector<std::string> fileIdsNew;
if (!QueryOldPhotosTable(fileIdsOld, oldToNewIdMapping, fileIdsNew)) {
return resultMap;
}
std::map<std::string, std::string> newIdToDisplayName;
std::map<std::string, std::string> newIdToFilePath;
if (!QueryDisplayNamesAndFilePaths(fileIdsNew, newIdToDisplayName, newIdToFilePath)) {
return resultMap;
}
GenerateFinalUris(oldToNewIdMapping, newIdToDisplayName, newIdToFilePath, oldIdToUris, resultMap);
return resultMap;
}
static void PhotoAccessGetAssetsByOldUrisExecute(napi_env env, void *data)
{
auto *context = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
std::map<std::string, std::string> resultMap = GetNewUriByOldUriBatch(context->uris);
context->uriMap = std::move(resultMap);
}
static napi_value ParseArgsGetAssetsByOldUris(napi_env env, napi_callback_info info,
std::unique_ptr<MediaLibraryAsyncContext> &context)
{
AccessTokenID tokenCaller = IPCSkeleton::GetSelfTokenID();
int haveReadPermission = AccessTokenKit::VerifyAccessToken(tokenCaller, PERM_READ_IMAGEVIDEO);
if(haveReadPermission != PermissionState::PERMISSION_GRANTED) {
NapiError::ThrowError(env, E_PERMISSION_DENIED, "Permission Denied : PERM_READ_IMAGEVIDEO required.");
return nullptr;
}
vector<string> uris;
CHECK_ARGS(env, MediaLibraryNapiUtils::ParseArgsStringArrayCallback(env, info, context, uris),
JS_E_PARAM_INVALID);
if (uris.empty()) {
NapiError::ThrowError(env, JS_E_PARAM_INVALID, "Failed to check empty uri!");
return nullptr;
}
for (const auto &uri : uris) {
if (uri.find(MEDIA_URI_PREFIX) == string::npos) {
NapiError::ThrowError(env, JS_E_PARAM_INVALID, "Failed to check uri format, not a photo uri!");
return nullptr;
}
}
if (uris.size() > MAX_TAB_OLD_PHOTOS_URI_COUNT) {
NapiError::ThrowError(env, JS_E_PARAM_INVALID, "Too many URIs, maximum allowed is 100!");
return nullptr;
}
context->uris = uris;
napi_value result = nullptr;
CHECK_ARGS_WITH_MSG(env, napi_get_boolean(env, true, &result), JS_E_INNER_FAIL, "Failed to create boolean value");
return result;
}
napi_value MediaLibraryNapi::PhotoAccessGetPhotoAssetsByOldUris(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessGetPhotoAssetsByOldUris");
std::unique_ptr<MediaLibraryAsyncContext> asyncContext = std::make_unique<MediaLibraryAsyncContext>();
CHECK_NULLPTR_RET(ParseArgsGetAssetsByOldUris(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(
env,
asyncContext,
"PhotoAccessGetPhotoAssetsByOldUris",
PhotoAccessGetAssetsByOldUrisExecute,
JSGetAssetsByOldUrisCompleteCallback
);
}
napi_value MediaLibraryNapi::UserFileMgrOffCallback(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("UserFileMgrOffCallback");
napi_value undefinedResult = nullptr;
napi_get_undefined(env, &undefinedResult);
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
napi_value thisVar = UserFileMgrOffCheckArgs(env, info, asyncContext);
MediaLibraryNapi *obj = nullptr;
napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void**>(&obj));
if (status != napi_ok || obj == nullptr || g_listObj == nullptr) {
return undefinedResult;
}
size_t res = 0;
char buffer[ARG_BUF_SIZE];
if (napi_get_value_string_utf8(env, asyncContext->argv[PARAM0], buffer, ARG_BUF_SIZE, &res) != napi_ok) {
NapiError::ThrowError(env, JS_ERR_PARAMETER_INVALID);
return undefinedResult;
}
string uri = string(buffer);
napi_valuetype valueType = napi_undefined;
if (ListenerTypeMaps.find(uri) != ListenerTypeMaps.end()) {
if (asyncContext->argc == ARGS_TWO) {
if (napi_typeof(env, asyncContext->argv[PARAM1], &valueType) != napi_ok || valueType != napi_function) {
return undefinedResult;
}
const int32_t refCount = 1;
napi_create_reference(env, asyncContext->argv[PARAM1], refCount, &g_listObj->cbOffRef_);
}
obj->UnregisterChange(env, uri, *g_listObj);
return undefinedResult;
}
napi_ref cbOffRef = nullptr;
if (asyncContext->argc == ARGS_TWO) {
if (napi_typeof(env, asyncContext->argv[PARAM1], &valueType) != napi_ok || valueType != napi_function) {
NapiError::ThrowError(env, JS_ERR_PARAMETER_INVALID);
return undefinedResult;
}
const int32_t refCount = 1;
napi_create_reference(env, asyncContext->argv[PARAM1], refCount, &cbOffRef);
}
tracer.Start("UnRegisterNotifyChange");
obj->UnRegisterNotifyChange(env, uri, cbOffRef, *g_listObj);
return undefinedResult;
}
static void JSReleaseCompleteCallback(napi_env env, napi_status status,
MediaLibraryAsyncContext *context)
{
MediaLibraryTracer tracer;
tracer.Start("JSReleaseCompleteCallback");
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
napi_create_int32(env, E_SUCCESS, &jsContext->data);
jsContext->status = true;
napi_get_undefined(env, &jsContext->error);
tracer.Finish();
MediaLibraryNapiUtils::InvokeJSAsyncMethodWithoutWork(env, context->deferred, context->callbackRef,
*jsContext);
delete context;
}
napi_value MediaLibraryNapi::JSRelease(napi_env env, napi_callback_info info)
{
napi_status status;
napi_value result = nullptr;
size_t argc = ARGS_ONE;
napi_value argv[ARGS_ONE] = {0};
napi_value thisVar = nullptr;
int32_t refCount = 1;
MediaLibraryTracer tracer;
tracer.Start("JSRelease");
GET_JS_ARGS(env, info, argc, argv, thisVar);
NAPI_ASSERT(env, (argc == ARGS_ONE || argc == ARGS_ZERO), "requires 1 parameters maximum");
napi_get_undefined(env, &result);
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&asyncContext->objectInfo));
NAPI_ASSERT(env, status == napi_ok && asyncContext->objectInfo != nullptr, "Failed to get object info");
if (argc == PARAM1) {
napi_valuetype valueType = napi_undefined;
napi_typeof(env, argv[PARAM0], &valueType);
if (valueType == napi_function) {
napi_create_reference(env, argv[PARAM0], refCount, &asyncContext->callbackRef);
}
}
CHECK_NULL_PTR_RETURN_UNDEFINED(env, result, result, "Failed to obtain arguments");
NAPI_CALL(env, napi_remove_wrap(env, thisVar, reinterpret_cast<void**>(&asyncContext->objectInfo)));
asyncContext->objectInfo = nullptr;
NAPI_CREATE_PROMISE(env, asyncContext->callbackRef, asyncContext->deferred, result);
MediaLibraryAsyncContext *context = asyncContext.get();
std::function<void()> task = [env, status, context]() {
JSReleaseCompleteCallback(env, status, context);
};
status = napi_send_event(env, task, napi_eprio_immediate, "MLB_MediaLib_release");
if (status != napi_ok) {
napi_get_undefined(env, &result);
} else {
napi_queue_async_work_with_qos(env, asyncContext->work, napi_qos_user_initiated);
asyncContext.release();
}
return result;
}
static void SetSmartAlbumCoverUri(MediaLibraryAsyncContext *context, unique_ptr<SmartAlbumAsset> &smartAlbum)
{
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
if (smartAlbum == nullptr) {
NAPI_ERR_LOG("SmartAlbumAsset is nullptr");
return;
}
if (smartAlbum->GetAlbumCapacity() == 0) {
return;
}
string trashPrefix;
if (smartAlbum->GetAlbumId() == TRASH_ALBUM_ID_VALUES) {
trashPrefix = string(CONST_MEDIA_DATA_DB_DATE_TRASHED) + " <> ? AND " + SMARTALBUMMAP_DB_ALBUM_ID + " = ? ";
} else {
trashPrefix = string(CONST_MEDIA_DATA_DB_DATE_TRASHED) + " = ? AND " + SMARTALBUMMAP_DB_ALBUM_ID + " = ? ";
}
MediaLibraryNapiUtils::AppendFetchOptionSelection(context->selection, trashPrefix);
context->selectionArgs.emplace_back("0");
context->selectionArgs.emplace_back(to_string(smartAlbum->GetAlbumId()));
DataShare::DataSharePredicates predicates;
predicates.SetOrder(SMARTALBUMMAP_DB_ID + " DESC LIMIT 0,1 ");
predicates.SetWhereClause(context->selection);
predicates.SetWhereArgs(context->selectionArgs);
vector<string> columns;
Uri uri(MEDIALIBRARY_DATA_URI + "/" + CONST_MEDIA_ALBUMOPRN_QUERYALBUM + "/" + CONST_ASSETMAP_VIEW_NAME);
int errCode = 0;
shared_ptr<DataShare::DataShareResultSet> resultSet = UserFileClient::Query(uri, predicates, columns, errCode,
context->userId);
if (resultSet == nullptr) {
NAPI_ERR_LOG("resultSet is nullptr, errCode is %{public}d", errCode);
return;
}
unique_ptr<FetchResult<FileAsset>> fetchFileResult = make_unique<FetchResult<FileAsset>>(move(resultSet));
unique_ptr<FileAsset> fileAsset = fetchFileResult->GetFirstObject();
CHECK_NULL_PTR_RETURN_VOID(fileAsset, "SetSmartAlbumCoverUri fileAsset is nullptr");
string coverUri = fileAsset->GetUri();
smartAlbum->SetCoverUri(coverUri);
NAPI_DEBUG_LOG("coverUri is = %{private}s", smartAlbum->GetCoverUri().c_str());
}
static void SetSmartAlbumData(SmartAlbumAsset* smartAlbumData, shared_ptr<DataShare::DataShareResultSet> resultSet,
MediaLibraryAsyncContext *context)
{
CHECK_NULL_PTR_RETURN_VOID(smartAlbumData, "albumData is null");
smartAlbumData->SetAlbumId(get<int32_t>(ResultSetUtils::GetValFromColumn(SMARTALBUM_DB_ID, resultSet, TYPE_INT32)));
smartAlbumData->SetAlbumName(get<string>(ResultSetUtils::GetValFromColumn(SMARTALBUM_DB_NAME, resultSet,
TYPE_STRING)));
smartAlbumData->SetAlbumCapacity(get<int32_t>(ResultSetUtils::GetValFromColumn(SMARTALBUMASSETS_ALBUMCAPACITY,
resultSet, TYPE_INT32)));
MediaFileUri fileUri(MEDIA_TYPE_SMARTALBUM, to_string(smartAlbumData->GetAlbumId()), context->networkId,
MEDIA_API_VERSION_DEFAULT);
smartAlbumData->SetAlbumUri(fileUri.ToString());
smartAlbumData->SetDescription(get<string>(ResultSetUtils::GetValFromColumn(SMARTALBUM_DB_DESCRIPTION, resultSet,
TYPE_STRING)));
smartAlbumData->SetExpiredTime(get<int32_t>(ResultSetUtils::GetValFromColumn(SMARTALBUM_DB_EXPIRED_TIME, resultSet,
TYPE_INT32)));
smartAlbumData->SetCoverUri(get<string>(ResultSetUtils::GetValFromColumn(SMARTALBUM_DB_COVER_URI, resultSet,
TYPE_STRING)));
smartAlbumData->SetResultNapiType(context->resultNapiType);
}
#ifndef MEDIALIBRARY_COMPATIBILITY
static void GetAllSmartAlbumResultDataExecute(MediaLibraryAsyncContext *context)
{
MediaLibraryTracer tracer;
tracer.Start("GetAllSmartAlbumResultDataExecute");
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
NAPI_INFO_LOG("context->privateAlbumType = %{public}d", context->privateAlbumType);
if (context->privateAlbumType == TYPE_TRASH) {
context->predicates.SetWhereClause(SMARTALBUM_DB_ID + " = " + to_string(TRASH_ALBUM_ID_VALUES));
NAPI_INFO_LOG("context->privateAlbumType == TYPE_TRASH");
}
if (context->privateAlbumType == TYPE_FAVORITE) {
context->predicates.SetWhereClause(SMARTALBUM_DB_ID + " = " + to_string(FAVOURITE_ALBUM_ID_VALUES));
NAPI_INFO_LOG("context->privateAlbumType == TYPE_FAVORITE");
}
vector<string> columns;
string uriStr = MEDIALIBRARY_DATA_URI + "/" + CONST_MEDIA_ALBUMOPRN_QUERYALBUM + "/" + CONST_SMARTALBUM_TABLE;
if (!context->networkId.empty()) {
uriStr = CONST_MEDIALIBRARY_DATA_ABILITY_PREFIX + context->networkId + CONST_MEDIALIBRARY_DATA_URI_IDENTIFIER +
"/" + CONST_MEDIA_ALBUMOPRN_QUERYALBUM + "/" + CONST_SMARTALBUM_TABLE;
}
Uri uri(uriStr);
int errCode = 0;
auto resultSet = UserFileClient::Query(uri, context->predicates, columns, errCode,
context->userId);
if (resultSet == nullptr) {
NAPI_ERR_LOG("resultSet == nullptr, errCode is %{public}d", errCode);
context->error = E_PERMISSION_DENIED;
return;
}
if (context->resultNapiType == ResultNapiType::TYPE_USERFILE_MGR ||
context->resultNapiType == ResultNapiType::TYPE_PHOTOACCESS_HELPER) {
context->fetchSmartAlbumResult = make_unique<FetchResult<SmartAlbumAsset>>(move(resultSet));
context->fetchSmartAlbumResult->SetNetworkId(context->networkId);
context->fetchSmartAlbumResult->SetResultNapiType(context->resultNapiType);
return;
}
while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
unique_ptr<SmartAlbumAsset> albumData = make_unique<SmartAlbumAsset>();
SetSmartAlbumData(albumData.get(), resultSet, context);
if (albumData->GetCoverUri().empty()) {
SetSmartAlbumCoverUri(context, albumData);
}
context->privateSmartAlbumNativeArray.push_back(move(albumData));
}
}
static void MediaLibSmartAlbumsAsyncResult(napi_env env, MediaLibraryAsyncContext *context,
unique_ptr<JSAsyncContextOutput> &jsContext)
{
if (context->smartAlbumData != nullptr) {
NAPI_ERR_LOG("context->smartAlbumData != nullptr");
jsContext->status = true;
napi_value albumNapiObj = SmartAlbumNapi::CreateSmartAlbumNapi(env, context->smartAlbumData);
napi_get_undefined(env, &jsContext->error);
jsContext->data = albumNapiObj;
} else if (!context->privateSmartAlbumNativeArray.empty()) {
jsContext->status = true;
napi_value albumArray = nullptr;
napi_create_array(env, &albumArray);
for (size_t i = 0; i < context->privateSmartAlbumNativeArray.size(); i++) {
napi_value albumNapiObj = SmartAlbumNapi::CreateSmartAlbumNapi(env,
context->privateSmartAlbumNativeArray[i]);
napi_set_element(env, albumArray, i, albumNapiObj);
}
napi_get_undefined(env, &jsContext->error);
jsContext->data = albumArray;
} else {
NAPI_ERR_LOG("No fetch file result found!");
napi_get_undefined(env, &jsContext->data);
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, ERR_INVALID_OUTPUT,
"Failed to obtain Fetch File Result");
}
}
static void UserFileMgrSmartAlbumsAsyncResult(napi_env env, MediaLibraryAsyncContext *context,
unique_ptr<JSAsyncContextOutput> &jsContext)
{
if (context->fetchSmartAlbumResult->GetCount() < 0) {
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, ERR_MEM_ALLOCATION,
"find no data by options");
} else {
napi_value fileResult = FetchFileResultNapi::CreateFetchFileResult(env, move(context->fetchSmartAlbumResult));
if (fileResult == nullptr) {
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, ERR_INVALID_OUTPUT,
"Failed to create js object for Fetch SmartAlbum Result");
} else {
jsContext->data = fileResult;
jsContext->status = true;
napi_get_undefined(env, &jsContext->error);
}
}
}
static void SmartAlbumsAsyncResult(napi_env env, MediaLibraryAsyncContext *context,
unique_ptr<JSAsyncContextOutput> &jsContext)
{
if (context->resultNapiType == ResultNapiType::TYPE_MEDIALIBRARY) {
MediaLibSmartAlbumsAsyncResult(env, context, jsContext);
} else {
UserFileMgrSmartAlbumsAsyncResult(env, context, jsContext);
}
}
static void GetPrivateAlbumCallbackComplete(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("GetPrivateAlbumCallbackComplete");
auto context = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
napi_get_undefined(env, &jsContext->error);
if (context->error != ERR_DEFAULT) {
napi_get_undefined(env, &jsContext->data);
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, context->error,
"Query for get fileAssets failed");
} else {
SmartAlbumsAsyncResult(env, context, jsContext);
}
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
#endif
static void GetSmartAlbumResultDataExecute(MediaLibraryAsyncContext *context)
{
DataShare::DataSharePredicates predicates;
predicates.SetWhereClause(context->selection);
predicates.SetWhereArgs(context->selectionArgs);
vector<string> columns;
Uri uri(MEDIALIBRARY_DATA_URI + "/" + SMARTALBUMASSETS_VIEW_NAME);
int errCode = 0;
auto resultSet = UserFileClient::Query(uri, predicates, columns, errCode, context->userId);
if (resultSet == nullptr) {
NAPI_ERR_LOG("ResultSet is nullptr, errCode is %{public}d", errCode);
context->error = ERR_INVALID_OUTPUT;
return;
}
if (resultSet->GoToFirstRow() == NativeRdb::E_OK) {
context->smartAlbumData = make_unique<SmartAlbumAsset>();
SetSmartAlbumData(context->smartAlbumData.get(), resultSet, context);
SetSmartAlbumCoverUri(context, context->smartAlbumData);
} else {
NAPI_ERR_LOG("Failed to goToFirstRow");
context->error = ERR_INVALID_OUTPUT;
return;
}
}
static void SmartAlbumsAsyncCallbackComplete(napi_env env, napi_status status, void *data)
{
auto context = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
napi_get_undefined(env, &jsContext->error);
if (context->error != ERR_DEFAULT) {
napi_get_undefined(env, &jsContext->data);
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, context->error,
"Query for get smartAlbums failed");
} else {
if (!context->smartAlbumNativeArray.empty()) {
jsContext->status = true;
napi_value albumArray = nullptr;
napi_create_array(env, &albumArray);
for (size_t i = 0; i < context->smartAlbumNativeArray.size(); i++) {
napi_value albumNapiObj = SmartAlbumNapi::CreateSmartAlbumNapi(env,
context->smartAlbumNativeArray[i]);
napi_set_element(env, albumArray, i, albumNapiObj);
}
napi_get_undefined(env, &jsContext->error);
jsContext->data = albumArray;
} else {
NAPI_ERR_LOG("No SmartAlbums result found!");
napi_get_undefined(env, &jsContext->data);
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, ERR_INVALID_OUTPUT,
"Failed to obtain SmartAlbums Result");
}
}
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
napi_value GetJSArgsForGetSmartAlbum(napi_env env, size_t argc, const napi_value argv[],
MediaLibraryAsyncContext &asyncContext)
{
napi_value result = nullptr;
auto context = &asyncContext;
CHECK_NULL_PTR_RETURN_UNDEFINED(env, context, result, "Async context is null");
NAPI_ASSERT(env, argv != nullptr, "Argument list is empty");
for (size_t i = 0; i < argc; i++) {
napi_valuetype valueType = napi_undefined;
napi_typeof(env, argv[i], &valueType);
if (i == 0 && valueType == napi_number) {
napi_get_value_int32(env, argv[i], &context->parentSmartAlbumId);
} else if ((i == PARAM1) && valueType == napi_function) {
napi_create_reference(env, argv[i], DEFAULT_REFCOUNT, &context->callbackRef);
break;
} else {
NAPI_ASSERT(env, false, "type mismatch");
}
}
if (context->parentSmartAlbumId < 0) {
NAPI_ASSERT(env, false, "type mismatch");
}
napi_get_boolean(env, true, &result);
return result;
}
static void GetSmartAlbumsResultDataExecute(napi_env env, void *data)
{
auto context = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
if (context->parentSmartAlbumId < 0) {
context->error = ERR_INVALID_OUTPUT;
NAPI_ERR_LOG("ParentSmartAlbumId is invalid");
return;
}
DataShare::DataSharePredicates predicates;
if (context->parentSmartAlbumId == 0) {
predicates.SetWhereClause(SMARTABLUMASSETS_PARENTID + " ISNULL");
} else {
predicates.SetWhereClause(SMARTABLUMASSETS_PARENTID + " = ? ");
predicates.SetWhereArgs({ to_string(context->parentSmartAlbumId) });
}
vector<string> columns;
Uri uri(MEDIALIBRARY_DATA_URI + "/" + SMARTALBUMASSETS_VIEW_NAME);
int errCode = 0;
auto resultSet = UserFileClient::Query(uri, predicates, columns, errCode, context->userId);
if (resultSet == nullptr) {
NAPI_ERR_LOG("ResultSet is nullptr, errCode is %{public}d", errCode);
context->error = ERR_INVALID_OUTPUT;
return;
}
while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
unique_ptr<SmartAlbumAsset> albumData = make_unique<SmartAlbumAsset>();
SetSmartAlbumData(albumData.get(), resultSet, context);
if (albumData->GetCoverUri().empty()) {
SetSmartAlbumCoverUri(context, albumData);
}
context->smartAlbumNativeArray.push_back(move(albumData));
}
}
napi_value MediaLibraryNapi::JSGetSmartAlbums(napi_env env, napi_callback_info info)
{
napi_status status;
napi_value result = nullptr;
size_t argc = ARGS_TWO;
napi_value argv[ARGS_TWO] = {0};
napi_value thisVar = nullptr;
GET_JS_ARGS(env, info, argc, argv, thisVar);
NAPI_ASSERT(env, (argc == ARGS_ONE || argc == ARGS_TWO), "requires 2 parameters maximum");
napi_get_undefined(env, &result);
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_MEDIALIBRARY;
CHECK_NULL_PTR_RETURN_UNDEFINED(env, asyncContext, result, "Async context is null");
status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&asyncContext->objectInfo));
if (status == napi_ok && asyncContext->objectInfo != nullptr) {
result = GetJSArgsForGetSmartAlbum(env, argc, argv, *asyncContext);
CHECK_NULL_PTR_RETURN_UNDEFINED(env, result, result, "Failed to obtain arguments");
NAPI_CREATE_PROMISE(env, asyncContext->callbackRef, asyncContext->deferred, result);
SetUserIdFromObjectInfo(asyncContext);
result = MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSGetSmartAlbums",
GetSmartAlbumsResultDataExecute, SmartAlbumsAsyncCallbackComplete);
}
return result;
}
static bool CheckAlbumFetchColumns(const vector<string> &fetchColumn)
{
for (const auto &column : fetchColumn) {
if (!PhotoAlbumColumns::IsPhotoAlbumColumn(column) && column.compare(CONST_MEDIA_DATA_DB_URI) != 0) {
return false;
}
}
return true;
}
static void AddDefaultPhotoAlbumColumns(vector<string> &fetchColumn)
{
auto columns = PhotoAlbumColumns::DEFAULT_FETCH_COLUMNS;
for (const auto &column : fetchColumn) {
if (column.compare(CONST_MEDIA_DATA_DB_URI) == 0) {
continue;
}
if (columns.count(column) == 0) {
columns.insert(column);
}
}
fetchColumn.assign(columns.begin(), columns.end());
}
static void AddDefaultColumnsForNonAnalysisAlbums(MediaLibraryAsyncContext& context)
{
if (!context.isAnalysisAlbum) {
context.fetchColumn.push_back(PhotoAlbumColumns::ALBUM_IMAGE_COUNT);
context.fetchColumn.push_back(PhotoAlbumColumns::ALBUM_VIDEO_COUNT);
context.fetchColumn.push_back(PhotoAlbumColumns::ALBUM_LPATH);
context.fetchColumn.push_back(PhotoAlbumColumns::ALBUM_DATE_ADDED);
}
}
#ifdef MEDIALIBRARY_COMPATIBILITY
static void CompatGetPrivateAlbumExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("CompatGetPrivateAlbumExecute");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
string queryUri = CONST_URI_QUERY_PHOTO_ALBUM;
Uri uri(queryUri);
int err = 0;
auto resultSet = UserFileClient::Query(uri, context->predicates, context->fetchColumn, err,
context->userId);
if (resultSet == nullptr) {
NAPI_ERR_LOG("resultSet == nullptr, errCode is %{public}d", err);
context->SaveError(err);
return;
}
err = resultSet->GoToFirstRow();
if (err != NativeRdb::E_OK) {
context->SaveError(E_HAS_DB_ERROR);
return;
}
auto albumData = make_unique<AlbumAsset>();
SetAlbumData(albumData.get(), resultSet, "");
CompatSetAlbumCoverUri(context, albumData);
context->albumNativeArray.push_back(move(albumData));
}
static void CompatGetPhotoAlbumQueryResult(napi_env env, MediaLibraryAsyncContext *context,
unique_ptr<JSAsyncContextOutput> &jsContext)
{
jsContext->status = true;
napi_value albumArray = nullptr;
CHECK_ARGS_RET_VOID(env, napi_create_array(env, &albumArray), JS_INNER_FAIL);
for (size_t i = 0; i < context->albumNativeArray.size(); i++) {
napi_value albumNapiObj = AlbumNapi::CreateAlbumNapi(env, context->albumNativeArray[i]);
CHECK_ARGS_RET_VOID(env, napi_set_element(env, albumArray, i, albumNapiObj), JS_INNER_FAIL);
}
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), JS_INNER_FAIL);
jsContext->data = albumArray;
}
static void CompatGetPrivateAlbumComplete(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSGetPhotoAlbumsCompleteCallback");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
auto jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), JS_INNER_FAIL);
if (context->error != ERR_DEFAULT || context->albumNativeArray.empty()) {
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->data), JS_INNER_FAIL);
context->HandleError(env, jsContext->error);
} else {
CompatGetPhotoAlbumQueryResult(env, context, jsContext);
}
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
napi_value ParseArgsGetPrivateAlbum(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
int32_t privateAlbumType = -1;
CHECK_ARGS(env, MediaLibraryNapiUtils::ParseArgsNumberCallback(env, info, context, privateAlbumType),
JS_ERR_PARAMETER_INVALID);
if (privateAlbumType != PrivateAlbumType::TYPE_FAVORITE && privateAlbumType != PrivateAlbumType::TYPE_TRASH) {
NapiError::ThrowError(env, JS_ERR_PARAMETER_INVALID, "Invalid private album type");
return nullptr;
}
PhotoAlbumSubType subType = ANY;
switch (privateAlbumType) {
case PrivateAlbumType::TYPE_FAVORITE: {
subType = PhotoAlbumSubType::FAVORITE;
break;
}
case PrivateAlbumType::TYPE_TRASH: {
subType = PhotoAlbumSubType::TRASH;
break;
}
default: {
NapiError::ThrowError(env, JS_ERR_PARAMETER_INVALID, "Invalid private album type");
return nullptr;
}
}
context->predicates.EqualTo(PhotoAlbumColumns::ALBUM_TYPE, to_string(PhotoAlbumType::SYSTEM));
context->predicates.EqualTo(PhotoAlbumColumns::ALBUM_SUBTYPE, to_string(subType));
CHECK_COND(env, CheckAlbumFetchColumns(context->fetchColumn), JS_ERR_PARAMETER_INVALID);
AddDefaultPhotoAlbumColumns(context->fetchColumn);
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
napi_value CompatGetPrivateAlbum(napi_env env, napi_callback_info info)
{
auto asyncContext = make_unique<MediaLibraryAsyncContext>();
CHECK_NULLPTR_RET(ParseArgsGetPrivateAlbum(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "CompatGetPrivateAlbum",
CompatGetPrivateAlbumExecute, CompatGetPrivateAlbumComplete);
}
#endif
napi_value MediaLibraryNapi::JSGetPrivateAlbum(napi_env env, napi_callback_info info)
{
#ifdef MEDIALIBRARY_COMPATIBILITY
return CompatGetPrivateAlbum(env, info);
#else
napi_status status;
napi_value result = nullptr;
size_t argc = ARGS_TWO;
napi_value argv[ARGS_TWO] = {0};
napi_value thisVar = nullptr;
const int32_t refCount = 1;
GET_JS_ARGS(env, info, argc, argv, thisVar);
NAPI_ASSERT(env, (argc == ARGS_ONE || argc == ARGS_TWO), "requires 2 parameters maximum");
napi_get_undefined(env, &result);
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_MEDIALIBRARY;
status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&asyncContext->objectInfo));
if (status == napi_ok && asyncContext->objectInfo != nullptr) {
for (size_t i = PARAM0; i < argc; i++) {
napi_valuetype valueType = napi_undefined;
napi_typeof(env, argv[i], &valueType);
if (i == PARAM0 && valueType == napi_number) {
napi_get_value_int32(env, argv[i], &asyncContext->privateAlbumType);
} else if (i == PARAM1 && valueType == napi_function) {
napi_create_reference(env, argv[i], refCount, &asyncContext->callbackRef);
break;
} else {
NAPI_ASSERT(env, false, "type mismatch");
}
}
SetUserIdFromObjectInfo(asyncContext);
result = MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSGetPrivateAlbum",
[](napi_env env, void *data) {
auto context = static_cast<MediaLibraryAsyncContext *>(data);
GetAllSmartAlbumResultDataExecute(context);
}, GetPrivateAlbumCallbackComplete);
}
return result;
#endif
}
napi_value GetJSArgsForCreateSmartAlbum(napi_env env, size_t argc, const napi_value argv[],
MediaLibraryAsyncContext &asyncContext)
{
const int32_t refCount = 1;
napi_value result = nullptr;
auto context = &asyncContext;
CHECK_NULL_PTR_RETURN_UNDEFINED(env, context, result, "Async context is null");
size_t res = 0;
char buffer[PATH_MAX];
NAPI_ASSERT(env, argv != nullptr, "Argument list is empty");
for (size_t i = 0; i < argc; i++) {
napi_valuetype valueType = napi_undefined;
napi_typeof(env, argv[i], &valueType);
if (i == 0 && valueType == napi_number) {
napi_get_value_int32(env, argv[i], &context->parentSmartAlbumId);
} else if (i == PARAM1 && valueType == napi_string) {
napi_get_value_string_utf8(env, argv[i], buffer, PATH_MAX, &res);
} else if (i == PARAM2 && valueType == napi_function) {
napi_create_reference(env, argv[i], refCount, &context->callbackRef);
break;
} else {
NAPI_ASSERT(env, false, "type mismatch");
}
}
if (context->parentSmartAlbumId < 0) {
NAPI_ASSERT(env, false, "type mismatch");
}
string smartName = string(buffer);
if (smartName.empty()) {
NAPI_ASSERT(env, false, "type mismatch");
}
context->valuesBucket.Put(SMARTALBUM_DB_NAME, smartName);
napi_get_boolean(env, true, &result);
return result;
}
static void JSCreateSmartAlbumCompleteCallback(napi_env env, napi_status status,
MediaLibraryAsyncContext *context)
{
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
if (context->error == ERR_DEFAULT) {
if (context->smartAlbumData == nullptr) {
NAPI_ERR_LOG("No albums found");
napi_get_undefined(env, &jsContext->data);
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, ERR_INVALID_OUTPUT,
"No albums found");
} else {
jsContext->status = true;
napi_value albumNapiObj = SmartAlbumNapi::CreateSmartAlbumNapi(env, context->smartAlbumData);
jsContext->data = albumNapiObj;
napi_get_undefined(env, &jsContext->error);
}
} else {
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, context->error,
"File asset creation failed");
napi_get_undefined(env, &jsContext->data);
}
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
static void CreateSmartAlbumExecute(MediaLibraryAsyncContext *context)
{
context->valuesBucket.Put(SMARTALBUMMAP_DB_ALBUM_ID, context->parentSmartAlbumId);
Uri CreateSmartAlbumUri(MEDIALIBRARY_DATA_URI + "/" + CONST_MEDIA_SMARTALBUMOPRN + "/" +
CONST_MEDIA_SMARTALBUMOPRN_CREATEALBUM);
int retVal = UserFileClient::Insert(CreateSmartAlbumUri, context->valuesBucket);
if (retVal < 0) {
context->SaveError(retVal);
NAPI_ERR_LOG("CreateSmartAlbum failed, retVal = %{private}d", retVal);
return;
}
context->selection = SMARTALBUM_DB_ID + " = ?";
context->selectionArgs = { to_string(retVal) };
GetSmartAlbumResultDataExecute(context);
if (context->parentSmartAlbumId != 0) {
DataShare::DataShareValuesBucket valuesBucket;
valuesBucket.Put(SMARTALBUMMAP_DB_ALBUM_ID, context->parentSmartAlbumId);
valuesBucket.Put(SMARTALBUMMAP_DB_CHILD_ALBUM_ID, retVal);
NAPI_DEBUG_LOG("CreateSmartAlbumExecute retVal = %{public}d, parentSmartAlbumId = %{public}d",
retVal, context->parentSmartAlbumId);
Uri addAsseturi(MEDIALIBRARY_DATA_URI +
"/" + CONST_MEDIA_SMARTALBUMMAPOPRN + "/" + CONST_MEDIA_SMARTALBUMMAPOPRN_ADDSMARTALBUM);
int32_t changedRows = UserFileClient::Insert(addAsseturi, valuesBucket);
context->SaveError(changedRows);
}
}
napi_value MediaLibraryNapi::JSCreateSmartAlbum(napi_env env, napi_callback_info info)
{
napi_status status;
napi_value result = nullptr;
size_t argc = ARGS_THREE;
napi_value argv[ARGS_THREE] = {0};
napi_value thisVar = nullptr;
napi_value resource = nullptr;
GET_JS_ARGS(env, info, argc, argv, thisVar);
NAPI_ASSERT(env, (argc == ARGS_TWO || argc == ARGS_THREE), "requires 3 parameters maximum");
napi_get_undefined(env, &result);
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_MEDIALIBRARY;
status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&asyncContext->objectInfo));
if (status == napi_ok && asyncContext->objectInfo != nullptr) {
result = GetJSArgsForCreateSmartAlbum(env, argc, argv, *asyncContext);
CHECK_NULL_PTR_RETURN_UNDEFINED(env, result, result, "Failed to obtain arguments");
SetUserIdFromObjectInfo(asyncContext);
NAPI_CREATE_PROMISE(env, asyncContext->callbackRef, asyncContext->deferred, result);
NAPI_CREATE_RESOURCE_NAME(env, resource, "JSCreateSmartAlbum", asyncContext);
status = napi_create_async_work(
env, nullptr, resource, [](napi_env env, void *data) {
auto context = static_cast<MediaLibraryAsyncContext *>(data);
CreateSmartAlbumExecute(context);
},
reinterpret_cast<CompleteCallback>(JSCreateSmartAlbumCompleteCallback),
static_cast<void *>(asyncContext.get()), &asyncContext->work);
if (status != napi_ok) {
napi_get_undefined(env, &result);
} else {
napi_queue_async_work_with_qos(env, asyncContext->work, napi_qos_user_initiated);
asyncContext.release();
}
}
return result;
}
static void JSDeleteSmartAlbumExecute(MediaLibraryAsyncContext *context)
{
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
if (context->smartAlbumId == TYPE_TRASH) {
NAPI_ERR_LOG("Trash smartalbum can not be deleted");
context->error = E_TRASHALBUM_CAN_NOT_DELETE;
return;
}
if (context->smartAlbumId == TYPE_FAVORITE) {
NAPI_ERR_LOG("Facorite smartalbum can not be deleted");
context->error = E_FAVORITEALBUM_CAN_NOT_DELETE;
return;
}
DataShare::DataShareValuesBucket valuesBucket;
valuesBucket.Put(SMARTALBUM_DB_ID, context->smartAlbumId);
Uri DeleteSmartAlbumUri(MEDIALIBRARY_DATA_URI + "/" +
CONST_MEDIA_SMARTALBUMOPRN + "/" + CONST_MEDIA_SMARTALBUMOPRN_DELETEALBUM);
int retVal = UserFileClient::Insert(DeleteSmartAlbumUri, valuesBucket);
NAPI_DEBUG_LOG("JSDeleteSmartAlbumExecute retVal = %{private}d, smartAlbumId = %{private}d",
retVal, context->smartAlbumId);
if (retVal < 0) {
context->SaveError(retVal);
} else {
context->retVal = retVal;
}
}
napi_value GetJSArgsForDeleteSmartAlbum(napi_env env, size_t argc, const napi_value argv[],
MediaLibraryAsyncContext &asyncContext)
{
napi_value result = nullptr;
auto context = &asyncContext;
CHECK_NULL_PTR_RETURN_UNDEFINED(env, context, result, "Async context is null");
NAPI_ASSERT(env, argv != nullptr, "Argument list is empty");
for (size_t i = 0; i < argc; i++) {
napi_valuetype valueType = napi_undefined;
napi_typeof(env, argv[i], &valueType);
if (i == 0 && valueType == napi_number) {
napi_get_value_int32(env, argv[i], &context->smartAlbumId);
} else if (i == PARAM1 && valueType == napi_function) {
napi_create_reference(env, argv[i], DEFAULT_REFCOUNT, &context->callbackRef);
break;
} else {
NAPI_ASSERT(env, false, "type mismatch");
}
}
if (context->smartAlbumId < 0) {
NAPI_ASSERT(env, false, "type mismatch");
}
napi_get_boolean(env, true, &result);
return result;
}
static void JSDeleteSmartAlbumCompleteCallback(napi_env env, napi_status status,
MediaLibraryAsyncContext *context)
{
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
if (context->error == ERR_DEFAULT) {
napi_create_int32(env, context->retVal, &jsContext->data);
napi_get_undefined(env, &jsContext->error);
jsContext->status = true;
} else {
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, context->error,
"UserFileClient is invalid");
napi_get_undefined(env, &jsContext->data);
}
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
napi_value MediaLibraryNapi::JSDeleteSmartAlbum(napi_env env, napi_callback_info info)
{
napi_status status;
napi_value result = nullptr;
size_t argc = ARGS_TWO;
napi_value argv[ARGS_TWO] = {0};
napi_value thisVar = nullptr;
napi_value resource = nullptr;
GET_JS_ARGS(env, info, argc, argv, thisVar);
NAPI_ASSERT(env, (argc == ARGS_ONE || argc == ARGS_TWO), "requires 2 parameters maximum");
napi_get_undefined(env, &result);
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&asyncContext->objectInfo));
if (status == napi_ok && asyncContext->objectInfo != nullptr) {
result = GetJSArgsForDeleteSmartAlbum(env, argc, argv, *asyncContext);
CHECK_NULL_PTR_RETURN_UNDEFINED(env, result, result, "Failed to obtain arguments");
SetUserIdFromObjectInfo(asyncContext);
NAPI_CREATE_PROMISE(env, asyncContext->callbackRef, asyncContext->deferred, result);
NAPI_CREATE_RESOURCE_NAME(env, resource, "JSDeleteSmartAlbum", asyncContext);
status = napi_create_async_work(
env, nullptr, resource, [](napi_env env, void *data) {
auto context = static_cast<MediaLibraryAsyncContext *>(data);
JSDeleteSmartAlbumExecute(context);
},
reinterpret_cast<CompleteCallback>(JSDeleteSmartAlbumCompleteCallback),
static_cast<void *>(asyncContext.get()), &asyncContext->work);
if (status != napi_ok) {
napi_get_undefined(env, &result);
} else {
napi_queue_async_work_with_qos(env, asyncContext->work, napi_qos_user_initiated);
asyncContext.release();
}
}
return result;
}
static napi_status SetValueUtf8String(const napi_env& env, const char* fieldStr, const char* str, napi_value& result)
{
napi_value value;
napi_status status = napi_create_string_utf8(env, str, NAPI_AUTO_LENGTH, &value);
if (status != napi_ok) {
NAPI_ERR_LOG("Set value create utf8 string error! field: %{public}s", fieldStr);
return status;
}
status = napi_set_named_property(env, result, fieldStr, value);
if (status != napi_ok) {
NAPI_ERR_LOG("Set utf8 string named property error! field: %{public}s", fieldStr);
}
return status;
}
static napi_status SetValueBool(const napi_env& env, const char* fieldStr, const bool boolvalue, napi_value& result)
{
napi_value value = nullptr;
napi_status status = napi_get_boolean(env, boolvalue, &value);
if (status != napi_ok) {
NAPI_ERR_LOG("Set value create boolean error! field: %{public}s", fieldStr);
return status;
}
status = napi_set_named_property(env, result, fieldStr, value);
if (status != napi_ok) {
NAPI_ERR_LOG("Set boolean named property error! field: %{public}s", fieldStr);
}
return status;
}
static void PeerInfoToJsArray(const napi_env &env, const vector<unique_ptr<PeerInfo>> &vecPeerInfo,
const int32_t idx, napi_value &arrayResult)
{
if (idx >= (int32_t) vecPeerInfo.size()) {
return;
}
auto info = vecPeerInfo[idx].get();
if (info == nullptr) {
return;
}
NapiScopeHandler scopeHandler(env);
if (!scopeHandler.IsValid()) {
return;
}
napi_value result = nullptr;
napi_create_object(env, &result);
SetValueUtf8String(env, "deviceName", info->deviceName.c_str(), result);
SetValueUtf8String(env, "networkId", info->networkId.c_str(), result);
#ifdef DEVICE_MANAGER_SUPPORT
SetValueInt32(env, "deviceTypeId", (int) info->deviceTypeId, result);
#endif
SetValueBool(env, "isOnline", info->isOnline, result);
napi_status status = napi_set_element(env, arrayResult, idx, result);
if (status != napi_ok) {
NAPI_ERR_LOG("PeerInfo To JsArray set element error: %d", status);
}
}
shared_ptr<DataShare::DataShareResultSet> QueryActivePeer(int &errCode,
MediaLibraryAsyncContext *context, string &uriType)
{
vector<string> columns;
DataShare::DataSharePredicates predicates;
Uri uri(uriType);
if (uriType == MEDIALIBRARY_DATA_URI + "/" + CONST_MEDIA_DEVICE_QUERYACTIVEDEVICE) {
string strQueryCondition = DEVICE_DB_DATE_MODIFIED + " = 0";
predicates.SetWhereClause(strQueryCondition);
} else if (uriType == MEDIALIBRARY_DATA_URI + "/" + CONST_MEDIA_DEVICE_QUERYALLDEVICE) {
predicates.SetWhereClause(context->selection);
}
predicates.SetWhereArgs(context->selectionArgs);
return UserFileClient::Query(uri, predicates, columns, errCode, context->userId);
}
void JSGetActivePeersCompleteCallback(napi_env env, napi_status status,
MediaLibraryAsyncContext *context)
{
napi_value jsPeerInfoArray = nullptr;
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
napi_get_undefined(env, &jsContext->data);
string uriType = MEDIALIBRARY_DATA_URI + "/" + CONST_MEDIA_DEVICE_QUERYACTIVEDEVICE;
int errCode = 0;
shared_ptr<DataShare::DataShareResultSet> resultSet = QueryActivePeer(errCode, context, uriType);
if (resultSet == nullptr) {
NAPI_ERR_LOG("JSGetActivePeers resultSet is null, errCode is %{public}d", errCode);
delete context;
return;
}
vector<unique_ptr<PeerInfo>> peerInfoArray;
while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
unique_ptr<PeerInfo> peerInfo = make_unique<PeerInfo>();
if (peerInfo != nullptr) {
peerInfo->deviceName = get<string>(ResultSetUtils::GetValFromColumn(DEVICE_DB_NAME, resultSet,
TYPE_STRING));
peerInfo->networkId = get<string>(ResultSetUtils::GetValFromColumn(DEVICE_DB_NETWORK_ID, resultSet,
TYPE_STRING));
#ifdef DEVICE_MANAGER_SUPPORT
peerInfo->deviceTypeId = (DistributedHardware::DmDeviceType)
(get<int32_t>(ResultSetUtils::GetValFromColumn(DEVICE_DB_TYPE, resultSet, TYPE_INT32)));
#endif
peerInfo->isOnline = true;
peerInfoArray.push_back(move(peerInfo));
}
}
if (napi_create_array(env, &jsPeerInfoArray) == napi_ok) {
for (size_t i = 0; i < peerInfoArray.size(); ++i) {
PeerInfoToJsArray(env, peerInfoArray, i, jsPeerInfoArray);
}
jsContext->data = jsPeerInfoArray;
napi_get_undefined(env, &jsContext->error);
jsContext->status = true;
}
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
void JSGetAllPeersCompleteCallback(napi_env env, napi_status status,
MediaLibraryAsyncContext *context)
{
napi_value jsPeerInfoArray = nullptr;
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
napi_get_undefined(env, &jsContext->data);
string uriType = MEDIALIBRARY_DATA_URI + "/" + CONST_MEDIA_DEVICE_QUERYALLDEVICE;
int errCode = 0;
shared_ptr<DataShare::DataShareResultSet> resultSet = QueryActivePeer(errCode, context, uriType);
if (resultSet == nullptr) {
NAPI_ERR_LOG("JSGetAllPeers resultSet is null, errCode is %{public}d", errCode);
delete context;
return;
}
vector<unique_ptr<PeerInfo>> peerInfoArray;
while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
unique_ptr<PeerInfo> peerInfo = make_unique<PeerInfo>();
if (peerInfo != nullptr) {
peerInfo->deviceName = get<string>(ResultSetUtils::GetValFromColumn(DEVICE_DB_NAME, resultSet,
TYPE_STRING));
peerInfo->networkId = get<string>(ResultSetUtils::GetValFromColumn(DEVICE_DB_NETWORK_ID, resultSet,
TYPE_STRING));
#ifdef DEVICE_MANAGER_SUPPORT
peerInfo->deviceTypeId = (DistributedHardware::DmDeviceType)
(get<int32_t>(ResultSetUtils::GetValFromColumn(DEVICE_DB_TYPE, resultSet, TYPE_INT32)));
#endif
peerInfo->isOnline = (get<int32_t>(ResultSetUtils::GetValFromColumn(DEVICE_DB_DATE_MODIFIED, resultSet,
TYPE_INT32)) == 0);
peerInfoArray.push_back(move(peerInfo));
}
}
if (napi_create_array(env, &jsPeerInfoArray) == napi_ok) {
for (size_t i = 0; i < peerInfoArray.size(); ++i) {
PeerInfoToJsArray(env, peerInfoArray, i, jsPeerInfoArray);
}
jsContext->data = jsPeerInfoArray;
napi_get_undefined(env, &jsContext->error);
jsContext->status = true;
}
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
napi_value MediaLibraryNapi::JSGetActivePeers(napi_env env, napi_callback_info info)
{
napi_status status;
napi_value result = nullptr;
const int32_t refCount = 1;
size_t argc = ARGS_ONE;
napi_value argv[ARGS_ONE] = {0};
napi_value thisVar = nullptr;
MediaLibraryTracer tracer;
tracer.Start("JSGetActivePeers");
GET_JS_ARGS(env, info, argc, argv, thisVar);
NAPI_ASSERT(env, argc <= ARGS_ONE, "requires 1 parameter maximum");
napi_get_undefined(env, &result);
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&asyncContext->objectInfo));
if (status == napi_ok && asyncContext->objectInfo != nullptr) {
if (argc == ARGS_ONE) {
GET_JS_ASYNC_CB_REF(env, argv[PARAM0], refCount, asyncContext->callbackRef);
}
SetUserIdFromObjectInfo(asyncContext);
NAPI_CREATE_PROMISE(env, asyncContext->callbackRef, asyncContext->deferred, result);
NAPI_CALL(env, napi_remove_wrap(env, thisVar, reinterpret_cast<void**>(&asyncContext->objectInfo)));
NAPI_CREATE_PROMISE(env, asyncContext->callbackRef, asyncContext->deferred, result);
MediaLibraryAsyncContext *context = asyncContext.get();
std::function<void()> task = [env, status, context]() {
JSGetActivePeersCompleteCallback(env, status, context);
};
status = napi_send_event(env, task, napi_eprio_immediate, "MLB_MediaLib_getActivePeers");
if (status != napi_ok) {
napi_get_undefined(env, &result);
} else {
napi_queue_async_work_with_qos(env, asyncContext->work, napi_qos_user_initiated);
asyncContext.release();
}
}
return result;
}
napi_value MediaLibraryNapi::JSGetAllPeers(napi_env env, napi_callback_info info)
{
napi_status status;
napi_value result = nullptr;
const int32_t refCount = 1;
size_t argc = ARGS_ONE;
napi_value argv[ARGS_ONE] = {0};
napi_value thisVar = nullptr;
MediaLibraryTracer tracer;
tracer.Start("JSGetAllPeers");
GET_JS_ARGS(env, info, argc, argv, thisVar);
NAPI_ASSERT(env, argc <= ARGS_ONE, "requires 1 parameter maximum");
napi_get_undefined(env, &result);
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&asyncContext->objectInfo));
if (status == napi_ok && asyncContext->objectInfo != nullptr) {
if (argc == ARGS_ONE) {
GET_JS_ASYNC_CB_REF(env, argv[PARAM0], refCount, asyncContext->callbackRef);
}
SetUserIdFromObjectInfo(asyncContext);
NAPI_CREATE_PROMISE(env, asyncContext->callbackRef, asyncContext->deferred, result);
MediaLibraryAsyncContext *context = asyncContext.get();
std::function<void()> task = [env, status, context]() {
JSGetAllPeersCompleteCallback(env, status, context);
};
status = napi_send_event(env, task, napi_eprio_immediate, "MLB_MediaLib_getAllPeers");
if (status != napi_ok) {
napi_get_undefined(env, &result);
} else {
napi_queue_async_work_with_qos(env, asyncContext->work, napi_qos_user_initiated);
asyncContext.release();
}
}
return result;
}
static int32_t CloseAsset(MediaLibraryAsyncContext *context, string uri)
{
string abilityUri = MEDIALIBRARY_DATA_URI;
Uri closeAssetUri(CONST_URI_CLOSE_FILE);
context->valuesBucket.Clear();
context->valuesBucket.Put(CONST_MEDIA_DATA_DB_URI, uri);
int32_t ret = UserFileClient::Insert(closeAssetUri, context->valuesBucket);
NAPI_DEBUG_LOG("File close asset %{public}d", ret);
if (ret != E_SUCCESS) {
context->error = ret;
NAPI_ERR_LOG("File close asset fail, %{public}d", ret);
}
return ret;
}
static void GetStoreMediaAssetUri(MediaLibraryAsyncContext *context, string &uri)
{
bool isValid = false;
string relativePath = context->valuesBucket.Get(CONST_MEDIA_DATA_DB_RELATIVE_PATH, isValid);
if (relativePath.find(CAMERA_DIR_VALUES) == 0 ||
relativePath.find(VIDEO_DIR_VALUES) == 0 ||
relativePath.find(PIC_DIR_VALUES) == 0) {
uri = CONST_URI_CREATE_PHOTO;
} else if (relativePath.find(AUDIO_DIR_VALUES) == 0) {
uri = CONST_URI_CREATE_AUDIO;
} else {
uri = CONST_URI_CREATE_FILE;
}
}
static void JSGetStoreMediaAssetExecute(MediaLibraryAsyncContext *context)
{
string realPath;
CHECK_NULL_PTR_RETURN_VOID(context, "JSGetStoreMediaAssetExecute context is nullptr");
if (!PathToRealPath(context->storeMediaSrc, realPath)) {
NAPI_ERR_LOG("src path is not exist, %{public}d", errno);
context->error = JS_ERR_NO_SUCH_FILE;
return;
}
context->error = JS_E_RELATIVEPATH;
int32_t srcFd = open(realPath.c_str(), O_RDWR);
CHECK_IF_EQUAL(srcFd != -1, "src path open fail, %{public}d", errno);
struct stat statSrc;
if (fstat(srcFd, &statSrc) == -1) {
close(srcFd);
NAPI_DEBUG_LOG("File get stat failed, %{public}d", errno);
return;
}
string uriString;
GetStoreMediaAssetUri(context, uriString);
Uri createFileUri(uriString);
int index = UserFileClient::Insert(createFileUri, context->valuesBucket);
if (index < 0) {
close(srcFd);
NAPI_ERR_LOG("storeMedia fail, file already exist %{public}d", index);
return;
}
SetFileAssetByIdV9(index, "", context);
if (context->fileAsset == nullptr) {
close(srcFd);
NAPI_ERR_LOG("JSGetStoreMediaAssetExecute: context->fileAsset is nullptr");
return;
}
LogMedialibraryAPI(context->fileAsset->GetUri());
Uri openFileUri(context->fileAsset->GetUri());
int32_t destFd = UserFileClient::OpenFile(openFileUri, MEDIA_FILEMODE_READWRITE, context->userId);
if (destFd < 0) {
context->error = destFd;
NAPI_DEBUG_LOG("File open asset failed");
close(srcFd);
return;
}
if (sendfile(destFd, srcFd, nullptr, statSrc.st_size) == -1) {
close(srcFd);
close(destFd);
CloseAsset(context, context->fileAsset->GetUri());
NAPI_ERR_LOG("copy file fail %{public}d ", errno);
return;
}
close(srcFd);
close(destFd);
CloseAsset(context, context->fileAsset->GetUri());
context->error = ERR_DEFAULT;
}
static void JSGetStoreMediaAssetCompleteCallback(napi_env env, napi_status status,
MediaLibraryAsyncContext *context)
{
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
CHECK_NULL_PTR_RETURN_VOID(jsContext, "Async context is null");
jsContext->status = false;
napi_get_undefined(env, &jsContext->data);
if (context->error != ERR_DEFAULT) {
NAPI_ERR_LOG("JSGetStoreMediaAssetCompleteCallback failed %{public}d ", context->error);
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, context->error,
"storeMediaAsset fail");
} else {
napi_create_string_utf8(env, context->fileAsset->GetUri().c_str(), NAPI_AUTO_LENGTH, &jsContext->data);
jsContext->status = true;
napi_get_undefined(env, &jsContext->error);
}
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
static int ConvertMediaType(const string &mimeType)
{
string res;
size_t slash = mimeType.find('/');
if (slash != string::npos) {
res = mimeType.substr(0, slash);
if (res.empty()) {
return MediaType::MEDIA_TYPE_FILE;
}
}
if (res == "image") {
return MediaType::MEDIA_TYPE_IMAGE;
} else if (res == "video") {
return MediaType::MEDIA_TYPE_VIDEO;
} else if (res == "audio") {
return MediaType::MEDIA_TYPE_AUDIO;
}
return MediaType::MEDIA_TYPE_FILE;
}
static bool GetStoreMediaAssetProper(napi_env env, napi_value param, const string &proper, string &res)
{
napi_value value = MediaLibraryNapiUtils::GetPropertyValueByName(env, param, proper.c_str());
if (value == nullptr) {
NAPI_ERR_LOG("GetPropertyValueByName %{public}s fail", proper.c_str());
return false;
}
unique_ptr<char[]> tmp;
bool succ;
tie(succ, tmp, ignore) = MediaLibraryNapiUtils::ToUTF8String(env, value);
if (!succ) {
NAPI_ERR_LOG("param %{public}s fail", proper.c_str());
return false;
}
res = string(tmp.get());
return true;
}
static string GetDefaultDirectory(int mediaType)
{
string relativePath;
if (mediaType == MediaType::MEDIA_TYPE_IMAGE) {
relativePath = "Pictures/";
} else if (mediaType == MediaType::MEDIA_TYPE_VIDEO) {
relativePath = "Videos/";
} else if (mediaType == MediaType::MEDIA_TYPE_AUDIO) {
relativePath = "Audios/";
} else {
relativePath = DOCS_PATH + DOC_DIR_VALUES;
}
return relativePath;
}
static napi_value GetStoreMediaAssetArgs(napi_env env, napi_value param,
MediaLibraryAsyncContext &asyncContext)
{
auto context = &asyncContext;
if (!GetStoreMediaAssetProper(env, param, "src", context->storeMediaSrc)) {
NAPI_ERR_LOG("param get fail");
return nullptr;
}
string fileName = MediaFileUtils::GetFileName(context->storeMediaSrc);
if (fileName.empty() || (fileName.at(0) == '.')) {
NAPI_ERR_LOG("src file name is not proper");
context->error = JS_E_RELATIVEPATH;
return nullptr;
};
context->valuesBucket.Put(CONST_MEDIA_DATA_DB_NAME, fileName);
string mimeType;
if (!GetStoreMediaAssetProper(env, param, "mimeType", mimeType)) {
NAPI_ERR_LOG("param get fail");
return nullptr;
}
auto mediaType = ConvertMediaType(mimeType);
context->valuesBucket.Put(CONST_MEDIA_DATA_DB_MEDIA_TYPE, mediaType);
string relativePath;
if (!GetStoreMediaAssetProper(env, param, "relativePath", relativePath)) {
NAPI_DEBUG_LOG("optional relativePath param empty");
relativePath = GetDefaultDirectory(mediaType);
}
relativePath = MediaFileUtils::AddDocsToRelativePath(relativePath);
context->valuesBucket.Put(CONST_MEDIA_DATA_DB_RELATIVE_PATH, relativePath);
NAPI_DEBUG_LOG("src:%{private}s mime:%{private}s relp:%{private}s filename:%{private}s",
context->storeMediaSrc.c_str(), mimeType.c_str(), relativePath.c_str(), fileName.c_str());
napi_value result = nullptr;
napi_get_undefined(env, &result);
return result;
}
napi_value MediaLibraryNapi::JSStoreMediaAsset(napi_env env, napi_callback_info info)
{
size_t argc = ARGS_TWO;
napi_value argv[ARGS_TWO] = {0};
napi_value thisVar = nullptr;
GET_JS_ARGS(env, info, argc, argv, thisVar);
napi_value result = nullptr;
napi_get_undefined(env, &result);
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
CHECK_NULL_PTR_RETURN_UNDEFINED(env, asyncContext, result, "Failed to get asyncContext");
napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&asyncContext->objectInfo));
if ((status == napi_ok) && (asyncContext->objectInfo != nullptr)) {
napi_value res = GetStoreMediaAssetArgs(env, argv[PARAM0], *asyncContext);
CHECK_NULL_PTR_RETURN_UNDEFINED(env, res, res, "Failed to obtain arguments");
if (argc == ARGS_TWO) {
const int32_t refCount = 1;
GET_JS_ASYNC_CB_REF(env, argv[PARAM1], refCount, asyncContext->callbackRef);
}
SetUserIdFromObjectInfo(asyncContext);
NAPI_CREATE_PROMISE(env, asyncContext->callbackRef, asyncContext->deferred, result);
napi_value resource = nullptr;
NAPI_CREATE_RESOURCE_NAME(env, resource, "JSStoreMediaAsset", asyncContext);
status = napi_create_async_work(env, nullptr, resource, [](napi_env env, void *data) {
auto context = static_cast<MediaLibraryAsyncContext *>(data);
JSGetStoreMediaAssetExecute(context);
},
reinterpret_cast<CompleteCallback>(JSGetStoreMediaAssetCompleteCallback),
static_cast<void *>(asyncContext.get()), &asyncContext->work);
if (status != napi_ok) {
napi_get_undefined(env, &result);
} else {
napi_queue_async_work_with_qos(env, asyncContext->work, napi_qos_user_initiated);
asyncContext.release();
}
}
return result;
}
static Ability *CreateAsyncCallbackInfo(napi_env env)
{
if (env == nullptr) {
NAPI_ERR_LOG("env == nullptr.");
return nullptr;
}
napi_status ret;
napi_value global = 0;
const napi_extended_error_info *errorInfo = nullptr;
ret = napi_get_global(env, &global);
if (ret != napi_ok) {
napi_get_last_error_info(env, &errorInfo);
NAPI_ERR_LOG("get_global=%{public}d err:%{public}s", ret, errorInfo->error_message);
}
napi_value abilityObj = 0;
ret = napi_get_named_property(env, global, "ability", &abilityObj);
if (ret != napi_ok) {
napi_get_last_error_info(env, &errorInfo);
NAPI_ERR_LOG("get_named_property=%{public}d e:%{public}s", ret, errorInfo->error_message);
}
Ability *ability = nullptr;
ret = napi_get_value_external(env, abilityObj, reinterpret_cast<void**>(&ability));
if (ret != napi_ok) {
napi_get_last_error_info(env, &errorInfo);
NAPI_ERR_LOG("get_value_external=%{public}d e:%{public}s", ret, errorInfo->error_message);
}
return ability;
}
static napi_value GetImagePreviewArgsUri(napi_env env, napi_value param, MediaLibraryAsyncContext &context)
{
uint32_t arraySize = 0;
if (!MediaLibraryNapiUtils::IsArrayForNapiValue(env, param, arraySize)) {
NAPI_ERR_LOG("GetImagePreviewArgs get args fail, not array");
return nullptr;
}
string uri = "";
for (uint32_t i = 0; i < arraySize; i++) {
napi_value jsValue = nullptr;
if ((napi_get_element(env, param, i, &jsValue)) != napi_ok) {
NAPI_ERR_LOG("GetImagePreviewArgs get args fail");
return nullptr;
}
unique_ptr<char[]> inputStr;
bool succ;
tie(succ, inputStr, ignore) = MediaLibraryNapiUtils::ToUTF8String(env, jsValue);
if (!succ) {
NAPI_ERR_LOG("GetImagePreviewArgs get string fail");
return nullptr;
}
uri += MediaLibraryNapiUtils::TransferUri(string(inputStr.get()));
uri += "?";
}
context.uri = uri.substr(0, uri.length() - 1);
NAPI_DEBUG_LOG("GetImagePreviewArgs res %{private}s", context.uri.c_str());
napi_value res;
napi_get_undefined(env, &res);
return res;
}
static napi_value GetImagePreviewArgsNum(napi_env env, napi_value param, MediaLibraryAsyncContext &context)
{
context.imagePreviewIndex = 0;
napi_valuetype valueType = napi_undefined;
napi_typeof(env, param, &valueType);
if (valueType != napi_number) {
NAPI_ERR_LOG("not napi value");
return nullptr;
}
if (napi_get_value_int32(env, param, &context.imagePreviewIndex) != napi_ok) {
NAPI_ERR_LOG("get property value fail");
}
NAPI_ERR_LOG("GetImagePreviewArgs num %{public}d", context.imagePreviewIndex);
napi_value res;
napi_get_undefined(env, &res);
return res;
}
static void JSStartImagePreviewExecute(MediaLibraryAsyncContext *context)
{
if (context->ability_ == nullptr) {
NAPI_ERR_LOG("ability_ is not exist");
context->error = ERR_INVALID_OUTPUT;
return;
}
Want want;
want.SetType("image/jpeg");
want.SetAction("ohos.want.action.viewData");
want.SetUri(context->uri);
want.SetParam("viewIndex", context->imagePreviewIndex + 1);
context->error = context->ability_->StartAbility(want);
}
static void JSGetJSStartImagePreviewCompleteCallback(napi_env env, napi_status status,
MediaLibraryAsyncContext *context)
{
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
CHECK_NULL_PTR_RETURN_VOID(jsContext, "get jsContext failed");
jsContext->status = true;
napi_get_undefined(env, &jsContext->data);
if (context->error != 0) {
jsContext->status = false;
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, ERR_INVALID_OUTPUT,
"startImagePreview currently fail");
}
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
napi_value MediaLibraryNapi::JSStartImagePreview(napi_env env, napi_callback_info info)
{
size_t argc = ARGS_THREE;
napi_value argv[ARGS_THREE] = {0};
napi_value thisVar = nullptr;
GET_JS_ARGS(env, info, argc, argv, thisVar);
napi_value result = nullptr;
napi_get_undefined(env, &result);
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
CHECK_NULL_PTR_RETURN_UNDEFINED(env, asyncContext, result, "Failed to get asyncContext");
napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&asyncContext->objectInfo));
if (status == napi_ok && asyncContext->objectInfo != nullptr) {
napi_value res = GetImagePreviewArgsUri(env, argv[PARAM0], *asyncContext);
CHECK_NULL_PTR_RETURN_UNDEFINED(env, res, result, "Failed to obtain arguments uri");
GetImagePreviewArgsNum(env, argv[PARAM1], *asyncContext);
asyncContext->ability_ = CreateAsyncCallbackInfo(env);
CHECK_NULL_PTR_RETURN_UNDEFINED(env, asyncContext->ability_, result, "Failed to obtain ability");
const int32_t refCount = 1;
if (argc == ARGS_THREE) {
GET_JS_ASYNC_CB_REF(env, argv[PARAM2], refCount, asyncContext->callbackRef);
} else if (argc == ARGS_TWO && MediaLibraryNapiUtils::CheckJSArgsTypeAsFunc(env, argv[PARAM1])) {
GET_JS_ASYNC_CB_REF(env, argv[PARAM1], refCount, asyncContext->callbackRef);
}
SetUserIdFromObjectInfo(asyncContext);
NAPI_CREATE_PROMISE(env, asyncContext->callbackRef, asyncContext->deferred, result);
napi_value resource = nullptr;
NAPI_CREATE_RESOURCE_NAME(env, resource, "JSStartImagePreview", asyncContext);
status = napi_create_async_work(env, nullptr, resource, [](napi_env env, void *data) {
auto context = static_cast<MediaLibraryAsyncContext *>(data);
JSStartImagePreviewExecute(context);
},
reinterpret_cast<CompleteCallback>(JSGetJSStartImagePreviewCompleteCallback),
static_cast<void *>(asyncContext.get()), &asyncContext->work);
if (status != napi_ok) {
napi_get_undefined(env, &result);
} else {
napi_queue_async_work_with_qos(env, asyncContext->work, napi_qos_user_initiated);
asyncContext.release();
}
}
return result;
}
static napi_status CheckCreateOption(MediaLibraryAsyncContext &context)
{
bool isValid = false;
int32_t subtype = context.valuesBucket.Get(PhotoColumn::PHOTO_SUBTYPE, isValid);
string cameraShotKey = context.valuesBucket.Get(PhotoColumn::CAMERA_SHOT_KEY, isValid);
if (isValid) {
if (cameraShotKey.size() < CAMERA_SHOT_KEY_SIZE) {
NAPI_ERR_LOG("cameraShotKey is not null with but is less than CAMERA_SHOT_KEY_SIZE");
return napi_invalid_arg;
}
if (subtype == static_cast<int32_t>(PhotoSubType::SCREENSHOT)) {
NAPI_ERR_LOG("cameraShotKey is not null with subtype is SCREENSHOT");
return napi_invalid_arg;
} else {
context.valuesBucket.Put(PhotoColumn::PHOTO_SUBTYPE, static_cast<int32_t>(PhotoSubType::CAMERA));
}
}
return napi_ok;
}
static napi_status ParsePhotoAssetCreateOption(napi_env env, napi_value arg, MediaLibraryAsyncContext &context)
{
for (const auto &iter : PHOTO_CREATE_OPTIONS_PARAM) {
string param = iter.first;
bool present = false;
napi_status result = napi_has_named_property(env, arg, param.c_str(), &present);
CHECK_COND_RET(result == napi_ok, result, "failed to check named property");
if (!present) {
continue;
}
napi_value value;
result = napi_get_named_property(env, arg, param.c_str(), &value);
CHECK_COND_RET(result == napi_ok, result, "failed to get named property");
napi_valuetype valueType = napi_undefined;
result = napi_typeof(env, value, &valueType);
CHECK_COND_RET(result == napi_ok, result, "failed to get value type");
if (valueType == napi_number) {
int32_t number = 0;
result = napi_get_value_int32(env, value, &number);
CHECK_COND_RET(result == napi_ok, result, "failed to get int32_t");
context.valuesBucket.Put(iter.second, number);
} else if (valueType == napi_boolean) {
bool isTrue = false;
result = napi_get_value_bool(env, value, &isTrue);
CHECK_COND_RET(result == napi_ok, result, "failed to get bool");
context.valuesBucket.Put(iter.second, isTrue);
} else if (valueType == napi_string) {
char buffer[ARG_BUF_SIZE];
size_t res = 0;
result = napi_get_value_string_utf8(env, value, buffer, ARG_BUF_SIZE, &res);
CHECK_COND_RET(result == napi_ok, result, "failed to get string");
context.valuesBucket.Put(iter.second, string(buffer));
} else if (valueType == napi_undefined || valueType == napi_null) {
continue;
} else {
NAPI_ERR_LOG("valueType %{public}d is unaccepted", static_cast<int>(valueType));
return napi_invalid_arg;
}
}
return CheckCreateOption(context);
}
static napi_status ParseCreateOptions(napi_env env, napi_value arg, MediaLibraryAsyncContext &context)
{
for (const auto &iter : CREATE_OPTIONS_PARAM) {
string param = iter.first;
bool present = false;
napi_status result = napi_has_named_property(env, arg, param.c_str(), &present);
CHECK_COND_RET(result == napi_ok, result, "failed to check named property");
if (!present) {
continue;
}
napi_value value;
result = napi_get_named_property(env, arg, param.c_str(), &value);
CHECK_COND_RET(result == napi_ok, result, "failed to get named property");
napi_valuetype valueType = napi_undefined;
result = napi_typeof(env, value, &valueType);
CHECK_COND_RET(result == napi_ok, result, "failed to get value type");
if (valueType == napi_number) {
int32_t number = 0;
result = napi_get_value_int32(env, value, &number);
CHECK_COND_RET(result == napi_ok, result, "failed to get int32_t");
context.valuesBucket.Put(iter.second, number);
} else if (valueType == napi_boolean) {
bool isTrue = false;
result = napi_get_value_bool(env, value, &isTrue);
CHECK_COND_RET(result == napi_ok, result, "failed to get bool");
context.valuesBucket.Put(iter.second, isTrue);
} else if (valueType == napi_string) {
char buffer[ARG_BUF_SIZE];
size_t res = 0;
result = napi_get_value_string_utf8(env, value, buffer, ARG_BUF_SIZE, &res);
CHECK_COND_RET(result == napi_ok, result, "failed to get string");
context.valuesBucket.Put(iter.second, string(buffer));
} else if (valueType == napi_undefined || valueType == napi_null) {
continue;
} else {
NAPI_ERR_LOG("ParseCreateOptions failed, valueType %{public}d is unaccepted",
static_cast<int>(valueType));
return napi_invalid_arg;
}
}
return napi_ok;
}
static napi_value ParseArgsCreatePhotoAssetSystem(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
napi_valuetype valueType;
MediaType mediaType;
string displayName;
NAPI_ASSERT(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[ARGS_ZERO], displayName) ==
napi_ok, "Failed to get displayName");
mediaType = MediaFileUtils::GetMediaType(displayName);
NAPI_ASSERT(env, (mediaType == MEDIA_TYPE_IMAGE || mediaType == MEDIA_TYPE_VIDEO), "invalid file type");
context->valuesBucket.Put(CONST_MEDIA_DATA_DB_NAME, displayName);
string albumUri;
if ((context->argc >= ARGS_TWO)) {
NAPI_ASSERT(env, napi_typeof(env, context->argv[ARGS_ONE], &valueType) == napi_ok, "Failed to get napi type");
if (valueType == napi_string) {
if (MediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[ARGS_ONE], albumUri) == napi_ok) {
context->valuesBucket.Put(CONST_MEDIA_DATA_DB_ALARM_URI, albumUri);
}
} else if (valueType == napi_object) {
NAPI_ASSERT(env, ParsePhotoAssetCreateOption(env, context->argv[ARGS_ONE], *context) == napi_ok,
"Parse asset create option failed");
}
}
context->valuesBucket.Put(CONST_MEDIA_DATA_DB_MEDIA_TYPE, static_cast<int32_t>(mediaType));
NAPI_ASSERT(env, MediaLibraryNapiUtils::GetParamCallback(env, context) == napi_ok, "Failed to get callback");
napi_value result = nullptr;
NAPI_CALL(env, napi_get_boolean(env, true, &result));
return result;
}
static napi_value ParseArgsCreatePhotoAssetComponent(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
napi_valuetype valueType;
MediaType mediaType;
int32_t type = 0;
NAPI_ASSERT(env, napi_get_value_int32(env, context->argv[ARGS_ZERO], &type) == napi_ok,
"Failed to get type value");
mediaType = static_cast<MediaType>(type);
NAPI_ASSERT(env, (mediaType == MEDIA_TYPE_IMAGE || mediaType == MEDIA_TYPE_VIDEO), "invalid file type");
string extension;
NAPI_ASSERT(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[ARGS_ONE], extension) ==
napi_ok, "Failed to get extension");
CHECK_COND_WITH_MESSAGE(env, mediaType == MediaFileUtils::GetMediaType("." + extension),
"Failed to check extension");
context->valuesBucket.Put(CONST_ASSET_EXTENTION, extension);
if (context->argc >= ARGS_THREE) {
NAPI_ASSERT(env, napi_typeof(env, context->argv[ARGS_TWO], &valueType) == napi_ok, "Failed to get napi type");
if (valueType == napi_object) {
NAPI_ASSERT(env, ParseCreateOptions(env, context->argv[ARGS_TWO], *context) == napi_ok,
"Parse asset create option failed");
} else if (valueType != napi_function) {
NAPI_ERR_LOG("Napi type is wrong in create options");
return nullptr;
}
}
context->valuesBucket.Put(CONST_MEDIA_DATA_DB_MEDIA_TYPE, static_cast<int32_t>(mediaType));
napi_value result = nullptr;
NAPI_CALL(env, napi_get_boolean(env, true, &result));
return result;
}
static napi_value ParseArgsCreatePhotoAsset(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
constexpr size_t minArgs = ARGS_ONE;
constexpr size_t maxArgs = ARGS_FOUR;
NAPI_ASSERT(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs) ==
napi_ok, "Failed to get object info");
napi_valuetype valueType;
NAPI_ASSERT(env, napi_typeof(env, context->argv[ARGS_ZERO], &valueType) == napi_ok, "Failed to get napi type");
if (valueType == napi_string) {
context->isCreateByComponent = false;
context->needSystemApp = true;
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
return ParseArgsCreatePhotoAssetSystem(env, info, context);
} else if (valueType == napi_number) {
context->isCreateByComponent = true;
return ParseArgsCreatePhotoAssetComponent(env, info, context);
} else {
NAPI_ERR_LOG("JS param type %{public}d is wrong", static_cast<int32_t>(valueType));
return nullptr;
}
}
static napi_status ParseTokenId(napi_env env, napi_value arg,
uint32_t &tokenId)
{
napi_valuetype valueType = napi_undefined;
CHECK_STATUS_RET(napi_typeof(env, arg, &valueType), "Failed to get type");
if (valueType == napi_number) {
CHECK_STATUS_RET(MediaLibraryNapiUtils::GetUInt32(env, arg, tokenId), "Failed to get tokenId");
CHECK_COND_RET(tokenId > 0, napi_invalid_arg, "Invalid tokenId");
} else {
NAPI_ERR_LOG("JS param type %{public}d is wrong", static_cast<int32_t>(valueType));
return napi_invalid_arg;
}
return napi_ok;
}
static napi_status ParsePermissionType(napi_env env, napi_value arg, int32_t &permissionType)
{
CHECK_STATUS_RET(MediaLibraryNapiUtils::GetInt32(env, arg, permissionType), "Failed to get permissionType");
if (AppUriPermissionColumn::PERMISSION_TYPES_PICKER.find((int)permissionType) ==
AppUriPermissionColumn::PERMISSION_TYPES_PICKER.end()) {
NAPI_ERR_LOG("invalid picker permissionType, permissionType=%{public}d", permissionType);
return napi_invalid_arg;
}
return napi_ok;
}
static napi_status ParseHidenSensitiveType(napi_env env, napi_value arg, int32_t &hideSensitiveType)
{
CHECK_STATUS_RET(MediaLibraryNapiUtils::GetInt32(env, arg, hideSensitiveType), "Failed to get hideSensitiveType");
if (AppUriSensitiveColumn::SENSITIVE_TYPES_ALL.find((int)hideSensitiveType) ==
AppUriSensitiveColumn::SENSITIVE_TYPES_ALL.end()) {
NAPI_ERR_LOG("invalid picker hideSensitiveType, hideSensitiveType=%{public}d", hideSensitiveType);
return napi_invalid_arg;
}
return napi_ok;
}
static napi_status ParseGrantMediaUris(napi_env env, napi_value arg, vector<string> &uris)
{
CHECK_STATUS_RET(MediaLibraryNapiUtils::GetStringArray(env, arg, uris), "Failed to get uris");
size_t urisMaxSize = 1000;
if (uris.empty() || uris.size() > urisMaxSize) {
NAPI_ERR_LOG("the size of uriList is invalid");
return napi_invalid_arg;
}
return napi_ok;
}
static napi_status ParseCheckPhotoUrisReadPermissionArgs(napi_env env, MediaLibraryAsyncContext *context)
{
CHECK_AND_RETURN_RET_WARN_LOG(context, napi_invalid_arg, "Async context is null");
CHECK_STATUS_RET(MediaLibraryNapiUtils::GetStringArray(env, context->argv[ARGS_ZERO], context->uris),
"Failed to get uris");
CHECK_COND_RET(!context->uris.empty() && context->uris.size() <= MAX_CHECK_PHOTO_PERMISSION_URI_COUNT,
napi_invalid_arg, "The size of uris is invalid, size: %{public}zu", context->uris.size());
std::lock_guard<std::mutex> lock(context->uriPermissionStateMapMutex);
for (const auto &uri : context->uris) {
CHECK_COND_RET(!uri.empty(), napi_invalid_arg, "The uri in uris should not be empty");
int32_t fileId = MediaLibraryNapiUtils::GetFileIdFromPhotoUri(uri);
if (fileId < 0) {
context->uriPermissionStateMap[uri] = static_cast<int32_t>(PhotoPermissionState::URI_FORMAT_ERROR);
continue;
}
context->checkPhotoPermissionUris.emplace_back(uri);
context->uriPermissionStateMap[uri] = static_cast<int32_t>(PhotoPermissionState::NO_READ_PERMISSION);
}
return napi_ok;
}
static napi_value ParseArgsCheckPhotoUrisReadPermission(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
constexpr size_t minArgs = ARGS_ONE;
constexpr size_t maxArgs = ARGS_ONE;
if (MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs) != napi_ok) {
NapiError::ThrowErrorWithIntCode(env, JS_E_PARAM_INVALID, "Failed to get object info");
return nullptr;
}
if (ParseCheckPhotoUrisReadPermissionArgs(env, context.get()) != napi_ok) {
NapiError::ThrowErrorWithIntCode(env, JS_E_PARAM_INVALID);
return nullptr;
}
napi_value result = nullptr;
NAPI_CALL(env, napi_get_boolean(env, true, &result));
return result;
}
static void CheckPhotoUrisReadPermissionExecute(napi_env env, void *data)
{
auto context = static_cast<MediaLibraryAsyncContext*>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
CHECK_IF_EQUAL(!context->checkPhotoPermissionUris.empty(), "checkPhotoPermissionUris is empty");
CheckPhotoUrisReadPermissionReqBody reqBody;
reqBody.uris = context->checkPhotoPermissionUris;
CheckPhotoUrisReadPermissionRespBody respBody;
uint32_t businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::PAH_CHECK_PHOTO_URIS_READ_PERMISSION);
NAPI_INFO_LOG("before IPC::UserDefineIPCClient().Call, PAH_CHECK_PHOTO_URIS_READ_PERMISSION");
int32_t result = IPC::UserDefineIPCClient().Call(businessCode, reqBody, respBody);
if (result < 0) {
context->SaveError(result);
NAPI_ERR_LOG("CheckPhotoUrisReadPermission fail, result: %{public}d.", result);
return;
}
std::unordered_set<std::string> validUris(context->checkPhotoPermissionUris.begin(),
context->checkPhotoPermissionUris.end());
std::lock_guard<std::mutex> lock(context->uriPermissionStateMapMutex);
for (const auto &[uri, state] : respBody.uriPermissionStateMap) {
if (validUris.find(uri) == validUris.end()) {
NAPI_INFO_LOG("Ignore unexpected uri in CheckPhotoUrisReadPermission response");
continue;
}
context->uriPermissionStateMap[uri] = state;
}
}
static napi_value CreatePhotoUriPermissionStateMap(napi_env env, const MediaLibraryAsyncContext *context)
{
unordered_map<string, int32_t> uriPermissionStateMapSnapshot;
{
std::lock_guard<std::mutex> lock(context->uriPermissionStateMapMutex);
uriPermissionStateMapSnapshot = context->uriPermissionStateMap;
}
napi_status status;
napi_value mapNapiValue = nullptr;
status = napi_create_map(env, &mapNapiValue);
CHECK_COND_RET(status == napi_ok && mapNapiValue != nullptr, nullptr,
"Failed to create map napi value, napi status: %{public}d", static_cast<int>(status));
for (const auto &uri : context->uris) {
napi_value key = nullptr;
status = napi_create_string_utf8(env, uri.c_str(), NAPI_AUTO_LENGTH, &key);
CHECK_COND_RET(status == napi_ok && key != nullptr, nullptr,
"Failed to create map key, napi status: %{public}d", static_cast<int>(status));
int32_t state = static_cast<int32_t>(PhotoPermissionState::URI_FORMAT_ERROR);
auto iter = uriPermissionStateMapSnapshot.find(uri);
if (iter != uriPermissionStateMapSnapshot.end()) {
state = iter->second;
}
napi_value value = nullptr;
status = napi_create_int32(env, state, &value);
CHECK_COND_RET(status == napi_ok && value != nullptr, nullptr,
"Failed to create map value, napi status: %{public}d", static_cast<int>(status));
status = napi_map_set_property(env, mapNapiValue, key, value);
CHECK_COND_RET(status == napi_ok, nullptr,
"Failed to set map property, napi status: %{public}d", static_cast<int>(status));
}
return mapNapiValue;
}
static void CheckPhotoUrisReadPermissionComplete(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("CheckPhotoUrisReadPermissionComplete");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
napi_get_undefined(env, &jsContext->data);
if (context->error != ERR_DEFAULT) {
context->HandleError(env, jsContext->error, true);
} else {
napi_value resultMap = CreatePhotoUriPermissionStateMap(env, context);
if (resultMap == nullptr) {
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, ERR_INVALID_OUTPUT,
"Failed to create js object for uri permission state map", true);
} else {
jsContext->data = resultMap;
jsContext->status = true;
napi_get_undefined(env, &jsContext->error);
}
}
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
napi_value MediaLibraryNapi::PhotoAccessCheckPhotoUrisReadPermission(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessCheckPhotoUrisReadPermission");
NAPI_DEBUG_LOG("MediaLibraryNapi::PhotoAccessCheckPhotoUrisReadPermission start");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
CHECK_NULLPTR_RET(ParseArgsCheckPhotoUrisReadPermission(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "CheckPhotoUrisReadPermission",
CheckPhotoUrisReadPermissionExecute, CheckPhotoUrisReadPermissionComplete);
}
static napi_value ParseArgsGrantPhotoUriPermissionInner(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
uint32_t tokenId;
NAPI_ASSERT(env, ParseTokenId(env, context->argv[ARGS_ZERO], tokenId) ==
napi_ok, "Invalid args[0]");
context->valuesBucket.Put(AppUriPermissionColumn::TARGET_TOKENID, static_cast<int64_t>(tokenId));
uint32_t srcTokenId = IPCSkeleton::GetCallingTokenID();
context->valuesBucket.Put(AppUriSensitiveColumn::SOURCE_TOKENID, static_cast<int64_t>(srcTokenId));
string uri;
NAPI_ASSERT(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[ARGS_ONE], uri) ==
napi_ok, "Failed to get uri");
int32_t fileId = MediaLibraryNapiUtils::GetFileIdFromPhotoUri(uri);
NAPI_ASSERT(env, fileId >= 0, "Invalid uri");
context->valuesBucket.Put(AppUriPermissionColumn::FILE_ID, fileId);
int32_t permissionType;
NAPI_ASSERT(env, ParsePermissionType(env, context->argv[ARGS_TWO], permissionType) ==
napi_ok, "Invalid PermissionType");
context->valuesBucket.Put(AppUriPermissionColumn::PERMISSION_TYPE, permissionType);
int32_t hideSensitiveType;
NAPI_ASSERT(env, ParseHidenSensitiveType(env, context->argv[ARGS_THREE],
hideSensitiveType) == napi_ok, "Invalid SensitiveType");
context->valuesBucket.Put(AppUriSensitiveColumn::HIDE_SENSITIVE_TYPE, hideSensitiveType);
context->valuesBucket.Put(AppUriPermissionColumn::URI_TYPE, AppUriPermissionColumn::URI_PHOTO);
napi_value result = nullptr;
NAPI_CALL(env, napi_get_boolean(env, true, &result));
return result;
}
static napi_value ParseArgsGrantPhotoUriPermission(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
constexpr size_t minArgs = ARGS_ONE;
constexpr size_t maxArgs = ARGS_FOUR;
NAPI_ASSERT(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs) ==
napi_ok, "Failed to get object info");
context->isCreateByComponent = false;
context->needSystemApp = true;
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
return ParseArgsGrantPhotoUriPermissionInner(env, info, context);
}
static napi_status ParseUriTypes(std::vector<std::string> &uris, unique_ptr<MediaLibraryAsyncContext> &context)
{
std::set<int32_t> fileIdSet;
for (const auto &uri : uris) {
OHOS::DataShare::DataShareValuesBucket valuesBucket;
int32_t fileId = MediaLibraryNapiUtils::GetFileIdFromPhotoUri(uri);
if (fileId < 0) {
NAPI_ERR_LOG("invalid uri can not find fileid");
return napi_invalid_arg;
}
if (fileIdSet.find(fileId) != fileIdSet.end()) {
continue;
}
fileIdSet.insert(fileId);
valuesBucket = context->valuesBucket;
valuesBucket.Put(AppUriPermissionColumn::FILE_ID, fileId);
valuesBucket.Put(AppUriPermissionColumn::URI_TYPE, AppUriPermissionColumn::URI_PHOTO);
context->valuesBucketArray.push_back(move(valuesBucket));
}
return napi_ok;
}
static napi_value ParseArgsGrantPhotoUrisForForceSensitive(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
constexpr size_t minArgs = ARGS_ONE;
constexpr size_t maxArgs = ARGS_TWO;
NAPI_ASSERT(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs) ==
napi_ok, "Failed to get object info");
context->isCreateByComponent = false;
context->needSystemApp = true;
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
uint32_t tokenId = IPCSkeleton::GetCallingTokenID();
context->valuesBucket.Put(AppUriSensitiveColumn::SOURCE_TOKENID, static_cast<int64_t>(tokenId));
context->valuesBucket.Put(AppUriSensitiveColumn::TARGET_TOKENID, static_cast<int64_t>(tokenId));
vector<string> uris;
NAPI_ASSERT(env, ParseGrantMediaUris(env, context->argv[ARGS_ZERO], uris) ==
napi_ok, "Invalid uri");
int32_t hideSensitiveType;
NAPI_ASSERT(env, ParseHidenSensitiveType(env, context->argv[ARGS_ONE],
hideSensitiveType) == napi_ok, "Invalid SensitiveType");
context->valuesBucket.Put(AppUriSensitiveColumn::HIDE_SENSITIVE_TYPE, hideSensitiveType);
NAPI_ASSERT(env, ParseUriTypes(uris, context) == napi_ok, "ParseUriTypes failed");
napi_value result = nullptr;
NAPI_CALL(env, napi_get_boolean(env, true, &result));
return result;
}
static napi_value ParseArgsGrantPhotoUrisPermission(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
NAPI_ASSERT(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, ARGS_ONE, ARGS_FOUR) ==
napi_ok, "Failed to get object info");
context->isCreateByComponent = false;
context->needSystemApp = true;
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
uint32_t tokenId;
NAPI_ASSERT(env, ParseTokenId(env, context->argv[ARGS_ZERO], tokenId) ==
napi_ok, "Invalid tokenId");
context->valuesBucket.Put(AppUriPermissionColumn::TARGET_TOKENID, static_cast<int64_t>(tokenId));
uint32_t srcTokenId = IPCSkeleton::GetCallingTokenID();
context->valuesBucket.Put(AppUriSensitiveColumn::SOURCE_TOKENID, static_cast<int64_t>(srcTokenId));
int32_t permissionType;
NAPI_ASSERT(env, ParsePermissionType(env, context->argv[ARGS_TWO], permissionType) ==
napi_ok, "Invalid PermissionType");
context->valuesBucket.Put(AppUriPermissionColumn::PERMISSION_TYPE, permissionType);
int32_t hideSensitiveType;
NAPI_ASSERT(env, ParseHidenSensitiveType(env, context->argv[ARGS_THREE], hideSensitiveType) ==
napi_ok, "Invalid SensitiveType");
context->valuesBucket.Put(AppUriSensitiveColumn::HIDE_SENSITIVE_TYPE, hideSensitiveType);
context->valuesBucket.Put(AppUriPermissionColumn::URI_TYPE, AppUriPermissionColumn::URI_PHOTO);
vector<string> uris;
NAPI_ASSERT(env, ParseGrantMediaUris(env, context->argv[ARGS_ONE], uris) ==
napi_ok, "Invalid uris");
NAPI_ASSERT(env, ParseUriTypes(uris, context) == napi_ok, "ParseUriTypes failed");
napi_value result = nullptr;
NAPI_CALL(env, napi_get_boolean(env, true, &result));
return result;
}
static napi_value ParseArgsCancelPhotoUriPermission(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
constexpr size_t minArgs = ARGS_ONE;
constexpr size_t maxArgs = ARGS_THREE;
NAPI_ASSERT(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs) ==
napi_ok, "Failed to get object info");
context->isCreateByComponent = false;
context->needSystemApp = true;
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
uint32_t tokenId;
NAPI_ASSERT(env, ParseTokenId(env, context->argv[ARGS_ZERO], tokenId) ==
napi_ok, "Invalid args[0]");
context->valuesBucket.Put(AppUriPermissionColumn::TARGET_TOKENID, static_cast<int64_t>(tokenId));
uint32_t callerTokenId = IPCSkeleton::GetCallingTokenID();
context->valuesBucket.Put(AppUriSensitiveColumn::SOURCE_TOKENID, static_cast<int64_t>(callerTokenId));
string uri;
NAPI_ASSERT(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[ARGS_ONE], uri) ==
napi_ok, "Failed to get uri");
int32_t fileId = MediaLibraryNapiUtils::GetFileIdFromPhotoUri(uri);
if (fileId < 0) {
return nullptr;
}
context->valuesBucket.Put(AppUriPermissionColumn::FILE_ID, fileId);
int32_t permissionType;
NAPI_ASSERT(env, ParsePermissionType(env, context->argv[ARGS_TWO], permissionType) ==
napi_ok, "Invalid args[2]");
context->valuesBucket.Put(AppUriPermissionColumn::PERMISSION_TYPE, permissionType);
context->valuesBucket.Put(AppUriPermissionColumn::URI_TYPE, AppUriPermissionColumn::URI_PHOTO);
napi_value result = nullptr;
NAPI_CALL(env, napi_get_boolean(env, true, &result));
return result;
}
static void HandleBundleInfo(OHOS::DataShare::DataShareValuesBucket &valuesBucket, bool isAuthorization,
BundleInfo bundleInfo)
{
if (isAuthorization) {
valuesBucket.Put(CONST_MEDIA_DATA_DB_OWNER_PACKAGE, bundleInfo.bundleName);
valuesBucket.Put(CONST_MEDIA_DATA_DB_OWNER_APPID, bundleInfo.appId);
valuesBucket.Put(CONST_MEDIA_DATA_DB_PACKAGE_NAME, bundleInfo.packageName);
}
if (!bundleInfo.ownerAlbumId.empty()) {
valuesBucket.Put(PhotoColumn::PHOTO_OWNER_ALBUM_ID, bundleInfo.ownerAlbumId);
NAPI_INFO_LOG("client put ownerAlbumId: %{public}s", bundleInfo.ownerAlbumId.c_str());
}
}
static napi_status ParseCreateConfig(napi_env env, napi_value arg,
BundleInfo bundleInfo, MediaLibraryAsyncContext &context, bool isAuthorization = true)
{
const std::map<std::string, std::string> PHOTO_CREATE_CONFIG_PARAM = {
{ PHOTO_TYPE, CONST_MEDIA_DATA_DB_MEDIA_TYPE },
{ PHOTO_SUB_TYPE, PhotoColumn::PHOTO_SUBTYPE },
{ TITLE, MediaColumn::MEDIA_TITLE },
{ EXTENSION, CONST_ASSET_EXTENTION }
};
OHOS::DataShare::DataShareValuesBucket valuesBucket;
for (const auto &iter : PHOTO_CREATE_CONFIG_PARAM) {
string param = iter.first;
bool present = false;
napi_status result = napi_has_named_property(env, arg, param.c_str(), &present);
CHECK_COND_RET(result == napi_ok, result, "failed to check named property");
if (!present) {
continue;
}
napi_value value;
result = napi_get_named_property(env, arg, param.c_str(), &value);
CHECK_COND_RET(result == napi_ok, result, "failed to get named property");
napi_valuetype valueType = napi_undefined;
result = napi_typeof(env, value, &valueType);
CHECK_COND_RET(result == napi_ok, result, "failed to get value type");
if (valueType == napi_number) {
int32_t number = 0;
result = napi_get_value_int32(env, value, &number);
CHECK_COND_RET(result == napi_ok, result, "failed to get int32_t");
valuesBucket.Put(iter.second, number);
} else if (valueType == napi_string) {
char buffer[ARG_BUF_SIZE];
size_t res = 0;
result = napi_get_value_string_utf8(env, value, buffer, ARG_BUF_SIZE, &res);
CHECK_COND_RET(result == napi_ok, result, "failed to get string");
string bufferString(buffer);
if (!bufferString.empty()) {
valuesBucket.Put(iter.second, bufferString);
}
} else if (valueType == napi_undefined || valueType == napi_null) {
continue;
} else {
NAPI_ERR_LOG("ParseCreateConfig failed, valueType %{public}d is unaccepted", static_cast<int>(valueType));
return napi_invalid_arg;
}
}
HandleBundleInfo(valuesBucket, isAuthorization, bundleInfo);
if (isAuthorization) {
context.tokenId = bundleInfo.tokenId;
}
context.valuesBucketArray.push_back(move(valuesBucket));
return napi_ok;
}
static napi_value ParseCreateSource(napi_env env, napi_value arg, BundleInfo &bundleInfo)
{
napi_value valueBundleName = MediaLibraryNapiUtils::GetPropertyValueByName(env, arg,
CONFIRM_BOX_BUNDLE_NAME.c_str());
CHECK_NULLPTR_RET(valueBundleName);
CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, valueBundleName,
bundleInfo.bundleName) == napi_ok, "Failed to get bundleName");
napi_value valueAppName = MediaLibraryNapiUtils::GetPropertyValueByName(env, arg,
CONFIRM_BOX_APP_NAME.c_str());
CHECK_NULLPTR_RET(valueAppName);
CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, valueAppName,
bundleInfo.packageName) == napi_ok, "Failed to get appName");
napi_value valueAppId = MediaLibraryNapiUtils::GetPropertyValueByName(env, arg,
CONFIRM_BOX_APP_ID.c_str());
CHECK_NULLPTR_RET(valueAppId);
CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, valueAppId,
bundleInfo.appId) == napi_ok, "Failed to get appId");
napi_value valueTokenId = MediaLibraryNapiUtils::GetPropertyValueByName(env, arg,
TOKEN_ID.c_str());
CHECK_NULLPTR_RET(valueTokenId);
CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::GetUInt32(env, valueTokenId,
bundleInfo.tokenId) == napi_ok, "Failed to get appId");
napi_value result = nullptr;
NAPI_CALL(env, napi_get_boolean(env, true, &result));
return result;
}
static napi_value ParseArgsCreatePhotoAssetForAppWithAlbum(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
constexpr size_t minArgs = ARGS_FOUR;
constexpr size_t maxArgs = ARGS_FOUR;
NAPI_ASSERT(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs) ==
napi_ok, "Failed to get object info");
context->isCreateByComponent = false;
context->isCreateByAgent = true;
BundleInfo bundleInfo;
string albumUri;
bool isAuthorization = false;
ParseCreateSource(env, context->argv[ARGS_ZERO], bundleInfo);
CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[ARGS_ONE],
albumUri) == napi_ok, "Failed to get albumUri");
CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::GetParamBool(env, context->argv[ARGS_TWO],
isAuthorization) == napi_ok, "Failed to get isAuthorization");
MediaFileUri fileUri = MediaFileUri(albumUri);
CHECK_COND_WITH_MESSAGE(env, fileUri.GetUriType() == API10_PHOTOALBUM_URI, "Failed to get photoAlbum");
bundleInfo.ownerAlbumId = MediaFileUtils::GetIdFromUri(albumUri);
context->isContainsAlbumUri = true;
if (isAuthorization) {
context->tokenId = bundleInfo.tokenId;
}
napi_value result = nullptr;
NAPI_CALL(env, napi_get_boolean(env, true, &result));
vector<napi_value> napiValues;
CHECK_NULLPTR_RET(MediaLibraryNapiUtils::GetNapiValueArray(env, context->argv[ARGS_THREE], napiValues));
if (napiValues.empty() || napiValues.size() > MAX_CREATE_ASSET_LIMIT) {
NAPI_ERR_LOG("the size of albumid is invalid");
return nullptr;
}
for (const auto& napiValue : napiValues) {
CHECK_COND_WITH_MESSAGE(env, ParseCreateConfig(env, napiValue, bundleInfo, *context,
isAuthorization) == napi_ok, "Parse asset create config failed");
}
CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::GetParamCallback(env, context)
== napi_ok, "Failed to get callback");
return result;
}
static napi_value ParseArgsCreatePhotoAssetWithAlbum(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
constexpr size_t minArgs = ARGS_TWO;
constexpr size_t maxArgs = ARGS_THREE;
NAPI_ASSERT(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs) ==
napi_ok, "Failed to get object info");
vector<napi_value> napiValues;
BundleInfo bundleInfo;
if (context->argc == ARGS_THREE) {
napi_valuetype valueType = napi_undefined;
napi_typeof(env, context->argv[ARGS_TWO], &valueType);
if (valueType == napi_string) {
string albumUri;
CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[ARGS_TWO],
albumUri) == napi_ok, "Failed to get albumUri");
context->isContainsAlbumUri = true;
MediaFileUri fileUri = MediaFileUri(albumUri);
bundleInfo.ownerAlbumId = fileUri.GetUriType() == API10_PHOTOALBUM_URI ?
MediaFileUtils::GetIdFromUri(albumUri) : "";
}
}
napi_value result = nullptr;
NAPI_CALL(env, napi_get_boolean(env, true, &result));
CHECK_NULLPTR_RET(MediaLibraryNapiUtils::GetNapiValueArray(env, context->argv[ARGS_ZERO], napiValues));
if (napiValues.empty() || napiValues.size() > MAX_CREATE_ASSET_LIMIT) {
NapiError::ThrowError(env, MEDIA_LIBRARY_INVALID_PARAMETER_ERROR,
"CreationSettings array is invalid");
return nullptr;
}
for (const auto& napiValue : napiValues) {
CHECK_COND_WITH_MESSAGE(env, ParseCreateConfig(env, napiValue, bundleInfo, *context,
false) == napi_ok, "Parse asset create config failed");
}
bool isRealTimeThumb = false;
CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::GetParamBool(env, context->argv[ARGS_ONE],
isRealTimeThumb) == napi_ok, "Failed to get isRealTimeThumb");
context->isRealTimeThumb = isRealTimeThumb;
return result;
}
static napi_value ParseArgsCreateAgentCreateAssets(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
BundleInfo bundleInfo;
CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[ARGS_ZERO],
bundleInfo.bundleName) == napi_ok, "Failed to get bundleName");
CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[ARGS_ONE],
bundleInfo.packageName) == napi_ok, "Failed to get appName");
CHECK_COND_WITH_MESSAGE(env, ParseTokenId(env, context->argv[ARGS_TWO],
bundleInfo.tokenId) == napi_ok, "Failed to get tokenId");
napi_value result = nullptr;
NAPI_CALL(env, napi_get_boolean(env, true, &result));
vector<napi_value> napiValues;
CHECK_NULLPTR_RET(MediaLibraryNapiUtils::GetNapiValueArray(env, context->argv[ARGS_THREE], napiValues));
if (napiValues.empty()) {
return result;
}
for (const auto& napiValue : napiValues) {
CHECK_COND_WITH_MESSAGE(env, ParseCreateConfig(env, napiValue, bundleInfo, *context) == napi_ok,
"Parse asset create config failed");
}
CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::GetParamCallback(env, context)
== napi_ok, "Failed to get callback");
return result;
}
static napi_value ParseArgsAgentCreateAssets(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
constexpr size_t minArgs = ARGS_FOUR;
constexpr size_t maxArgs = ARGS_FOUR;
NAPI_ASSERT(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs) ==
napi_ok, "Failed to get object info");
context->isCreateByComponent = false;
context->isCreateByAgent = true;
return ParseArgsCreateAgentCreateAssets(env, info, context);
}
static napi_value ParseArgsCreateAudioAssetSystem(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
string displayName;
NAPI_ASSERT(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[ARGS_ZERO], displayName) ==
napi_ok, "Failed to get displayName");
context->valuesBucket.Put(CONST_MEDIA_DATA_DB_MEDIA_TYPE, MEDIA_TYPE_AUDIO);
context->valuesBucket.Put(CONST_MEDIA_DATA_DB_NAME, displayName);
napi_value result = nullptr;
NAPI_CALL(env, napi_get_boolean(env, true, &result));
return result;
}
static napi_value ParseArgsCreateAudioAssetComponent(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
napi_valuetype valueType;
MediaType mediaType;
int32_t type = 0;
NAPI_ASSERT(env, napi_get_value_int32(env, context->argv[ARGS_ZERO], &type) == napi_ok,
"Failed to get type value");
mediaType = static_cast<MediaType>(type);
NAPI_ASSERT(env, (mediaType == MEDIA_TYPE_AUDIO), "invalid file type");
string extention;
NAPI_ASSERT(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[ARGS_ONE], extention) ==
napi_ok, "Failed to get extention");
context->valuesBucket.Put(CONST_ASSET_EXTENTION, extention);
if (context->argc >= ARGS_THREE) {
NAPI_ASSERT(env, napi_typeof(env, context->argv[ARGS_TWO], &valueType) == napi_ok, "Failed to get napi type");
if (valueType == napi_object) {
NAPI_ASSERT(env, ParseCreateOptions(env, context->argv[ARGS_TWO], *context) == napi_ok,
"Parse asset create option failed");
} else if (valueType != napi_function) {
NAPI_ERR_LOG("Napi type is wrong in create options");
return nullptr;
}
}
context->valuesBucket.Put(CONST_MEDIA_DATA_DB_MEDIA_TYPE, static_cast<int32_t>(mediaType));
NAPI_ASSERT(env, MediaLibraryNapiUtils::GetParamCallback(env, context) == napi_ok, "Failed to get callback");
napi_value result = nullptr;
NAPI_CALL(env, napi_get_boolean(env, true, &result));
return result;
}
static napi_value ParseArgsCreateAudioAsset(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
constexpr size_t minArgs = ARGS_ONE;
constexpr size_t maxArgs = ARGS_FOUR;
NAPI_ASSERT(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs) ==
napi_ok, "Failed to get object info");
napi_valuetype valueType;
NAPI_ASSERT(env, napi_typeof(env, context->argv[ARGS_ZERO], &valueType) == napi_ok, "Failed to get napi type");
if (valueType == napi_string) {
context->isCreateByComponent = false;
return ParseArgsCreateAudioAssetSystem(env, info, context);
} else if (valueType == napi_number) {
context->isCreateByComponent = true;
return ParseArgsCreateAudioAssetComponent(env, info, context);
} else {
NAPI_ERR_LOG("JS param type %{public}d is wrong", static_cast<int32_t>(valueType));
return nullptr;
}
}
static napi_value ParseArgsGetAssets(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
constexpr size_t minArgs = ARGS_ONE;
constexpr size_t maxArgs = ARGS_TWO;
CHECK_ARGS(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs),
JS_ERR_PARAMETER_INVALID);
CHECK_ARGS(env,
MediaLibraryNapiUtils::GetFetchOption(env, context->argv[PARAM0], ASSET_FETCH_OPT, context),
JS_INNER_FAIL);
switch (context->assetType) {
case TYPE_AUDIO: {
CHECK_NULLPTR_RET(MediaLibraryNapiUtils::AddDefaultAssetColumns(env, context->fetchColumn,
AudioColumn::IsAudioColumn, TYPE_AUDIO));
break;
}
case TYPE_PHOTO: {
CHECK_NULLPTR_RET(MediaLibraryNapiUtils::AddDefaultAssetColumns(env, context->fetchColumn,
PhotoColumn::IsPhotoColumn, TYPE_PHOTO));
break;
}
default: {
NapiError::ThrowError(env, JS_ERR_PARAMETER_INVALID);
return nullptr;
}
}
auto &predicates = context->predicates;
predicates.And()->EqualTo(MediaColumn::MEDIA_DATE_TRASHED, to_string(0));
predicates.And()->EqualTo(MediaColumn::MEDIA_TIME_PENDING, to_string(0));
if (context->assetType == TYPE_PHOTO) {
predicates.And()->EqualTo(MediaColumn::MEDIA_HIDDEN, to_string(0));
predicates.And()->EqualTo(PhotoColumn::PHOTO_IS_TEMP, to_string(false));
predicates.EqualTo(
PhotoColumn::PHOTO_BURST_COVER_LEVEL, to_string(static_cast<int32_t>(BurstCoverLevelType::COVER)));
}
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
static napi_value ParseArgsGetBurstAssets(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
constexpr size_t minArgs = ARGS_ONE;
constexpr size_t maxArgs = ARGS_TWO;
CHECK_ARGS(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs),
OHOS_INVALID_PARAM_CODE);
std::string burstKey;
CHECK_ARGS(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[PARAM0], burstKey),
OHOS_INVALID_PARAM_CODE);
if (burstKey.empty()) {
NAPI_ERR_LOG("The input burstkey cannot be empty");
return nullptr;
}
CHECK_ARGS(env, MediaLibraryNapiUtils::GetFetchOption(env, context->argv[PARAM1], ASSET_FETCH_OPT, context),
JS_INNER_FAIL);
auto &predicates = context->predicates;
if (context->assetType != TYPE_PHOTO) {
return nullptr;
}
CHECK_NULLPTR_RET(MediaLibraryNapiUtils::AddDefaultAssetColumns(env, context->fetchColumn,
PhotoColumn::IsPhotoColumn, TYPE_PHOTO));
predicates.And()->EqualTo(PhotoColumn::PHOTO_BURST_KEY, burstKey);
predicates.And()->EqualTo(MediaColumn::MEDIA_TIME_PENDING, to_string(0));
predicates.And()->EqualTo(PhotoColumn::PHOTO_IS_TEMP, to_string(0));
predicates.OrderByAsc(MediaColumn::MEDIA_NAME);
context->burstKey = burstKey;
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
static napi_status ParseArgsIndexUri(napi_env env, unique_ptr<MediaLibraryAsyncContext> &context, string &uri,
string &albumUri)
{
CHECK_STATUS_RET(MediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[ARGS_ZERO], uri),
"Failed to get first string argument");
CHECK_STATUS_RET(MediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[ARGS_ONE], albumUri),
"Failed to get second string argument");
return napi_ok;
}
static napi_value ParseArgsIndexof(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
constexpr size_t minArgs = ARGS_THREE;
constexpr size_t maxArgs = ARGS_FOUR;
CHECK_ARGS(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs),
JS_ERR_PARAMETER_INVALID);
string uri;
string album;
CHECK_ARGS(env, ParseArgsIndexUri(env, context, uri, album), JS_INNER_FAIL);
CHECK_ARGS(env, MediaLibraryNapiUtils::GetFetchOption(env, context->argv[PARAM2], ASSET_FETCH_OPT, context),
JS_INNER_FAIL);
auto &predicates = context->predicates;
predicates.And()->EqualTo(MediaColumn::MEDIA_DATE_TRASHED, to_string(0));
predicates.And()->EqualTo(MediaColumn::MEDIA_TIME_PENDING, to_string(0));
predicates.And()->EqualTo(MediaColumn::MEDIA_HIDDEN, to_string(0));
predicates.And()->EqualTo(PhotoColumn::PHOTO_IS_TEMP, to_string(0));
context->fetchColumn.clear();
MediaFileUri photoUri(uri);
CHECK_COND(env, photoUri.GetUriType() == API10_PHOTO_URI, JS_ERR_PARAMETER_INVALID);
context->fetchColumn.emplace_back(photoUri.GetFileId());
NAPI_INFO_LOG("current fileId: %{public}s", photoUri.GetFileId().c_str());
if (!album.empty()) {
MediaFileUri albumUri(album);
CHECK_COND(env, albumUri.GetUriType() == API10_PHOTOALBUM_URI ||
albumUri.GetUriType() == API10_ANALYSISALBUM_URI, JS_ERR_PARAMETER_INVALID);
context->isAnalysisAlbum = (albumUri.GetUriType() == API10_ANALYSISALBUM_URI);
context->fetchColumn.emplace_back(albumUri.GetFileId());
NAPI_INFO_LOG("current albumId: %{public}s", albumUri.GetFileId().c_str());
} else {
context->fetchColumn.emplace_back(album);
}
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
static void JSGetAssetsExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSGetAssetsExecute");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
string queryUri;
switch (context->assetType) {
case TYPE_AUDIO: {
queryUri = CONST_UFM_QUERY_AUDIO;
MediaLibraryNapiUtils::UriAppendKeyValue(queryUri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
break;
}
case TYPE_PHOTO: {
queryUri = CONST_UFM_QUERY_PHOTO;
MediaLibraryNapiUtils::UriAppendKeyValue(queryUri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
break;
}
default: {
context->SaveError(-EINVAL);
return;
}
}
Uri uri(queryUri);
int errCode = 0;
shared_ptr<DataShare::DataShareResultSet> resultSet = UserFileClient::Query(uri,
context->predicates, context->fetchColumn, errCode, context->userId);
if (resultSet == nullptr) {
context->SaveError(errCode);
return;
}
context->fetchFileResult = make_unique<FetchResult<FileAsset>>(move(resultSet));
context->fetchFileResult->SetResultNapiType(ResultNapiType::TYPE_USERFILE_MGR);
context->fetchFileResult->SetUserId(context->userId);
}
static void GetPhotoIndexAsyncCallbackComplete(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("GetPhotoIndexAsyncCallbackComplete");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
auto jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
if (napi_get_undefined(env, &jsContext->data) != napi_ok) {
delete context;
return;
}
if (context->error != ERR_DEFAULT) {
context->HandleError(env, jsContext->error);
} else {
int32_t photoIndex = context->photoIndex;
jsContext->status = true;
napi_create_int32(env, photoIndex, &jsContext->data);
}
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
static void GetPhotoIndexExec(napi_env env, void *data, ResultNapiType type)
{
MediaLibraryTracer tracer;
tracer.Start("JsGetPhotoIndexExec");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
string queryUri = context->isAnalysisAlbum ? CONST_PAH_GET_ANALYSIS_INDEX : CONST_UFM_GET_INDEX;
MediaLibraryNapiUtils::UriAppendKeyValue(queryUri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
Uri uri(queryUri);
int errCode = 0;
if (context->fetchColumn.size() < 2) {
context->SaveError(E_ERR);
return;
}
GetPhotoIndexReqBody reqBody;
reqBody.predicates = context->predicates;
reqBody.photoId = context->fetchColumn[0];
reqBody.albumId = context->fetchColumn[1];
reqBody.isAnalysisAlbum = context->isAnalysisAlbum;
QueryResultRespBody respBody;
errCode = IPC::UserDefineIPCClient().SetUserId(context->userId).Call(
static_cast<uint32_t>(MediaLibraryBusinessCode::GET_PHOTO_INDEX), reqBody, respBody);
auto resultSet = respBody.resultSet;
if (resultSet == nullptr) {
NAPI_ERR_LOG("resultSet is nullptr, errCode = %{public}d", errCode);
context->SaveError(errCode);
return;
}
auto fetchFileResult = make_unique<FetchResult<FileAsset>>(move(resultSet));
CHECK_NULL_PTR_RETURN_VOID(fetchFileResult, "fetchFileResult is nullptr.");
auto fileAsset = fetchFileResult->GetFirstObject();
if (fileAsset != nullptr) {
context->photoIndex = fileAsset->GetPhotoIndex();
}
}
static void PhotoAccessGetPhotoIndexExec(napi_env env, void *data)
{
GetPhotoIndexExec(env, data, ResultNapiType::TYPE_PHOTOACCESS_HELPER);
}
static void JsGetPhotoIndexExec(napi_env env, void *data)
{
GetPhotoIndexExec(env, data, ResultNapiType::TYPE_USERFILE_MGR);
}
napi_value MediaLibraryNapi::JSGetPhotoIndex(napi_env env, napi_callback_info info)
{
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
CHECK_NULLPTR_RET(ParseArgsIndexof(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSGetPhotoIndex",
JsGetPhotoIndexExec, GetPhotoIndexAsyncCallbackComplete);
}
napi_value MediaLibraryNapi::PhotoAccessGetPhotoIndex(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessGetPhotoIndex");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
CHECK_NULLPTR_RET(ParseArgsIndexof(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSGetPhotoIndex",
PhotoAccessGetPhotoIndexExec, GetPhotoIndexAsyncCallbackComplete);
}
static void GetIndexConstructProgressAsyncCallbackComplete(napi_env env, napi_status status, void *data)
{
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
auto jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->data), JS_ERR_PARAMETER_INVALID);
if (context->error != ERR_DEFAULT) {
context->HandleError(env, jsContext->error);
} else {
CHECK_ARGS_RET_VOID(
env, napi_create_string_utf8(env, context->indexProgress.c_str(), NAPI_AUTO_LENGTH, &jsContext->data),
JS_INNER_FAIL);
jsContext->status = true;
}
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
static void PhotoAccessGetIndexConstructProgressExec(napi_env env, void *data)
{
auto *context = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_IF_EQUAL(context != nullptr, "context is nullptr");
uint32_t businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::GET_INDEX_CONSTRUCT_PROGRESS);
GetIndexConstructProgressRespBody respBody;
int32_t errCode = IPC::UserDefineIPCClient().SetUserId(context->userId).Get(businessCode, respBody);
if (errCode != E_OK) {
NAPI_ERR_LOG("get index construct progress failed, errCode is %{public}d", errCode);
context->SaveError(errCode);
return;
}
context->indexProgress = respBody.indexProgress;
}
napi_value MediaLibraryNapi::PhotoAccessGetIndexConstructProgress(napi_env env, napi_callback_info info)
{
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
CHECK_ARGS(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, asyncContext, 0, 0),
JS_ERR_PARAMETER_INVALID);
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSGetIndexConstructProgress",
PhotoAccessGetIndexConstructProgressExec, GetIndexConstructProgressAsyncCallbackComplete);
}
static napi_status CheckFormId(string &formId)
{
if (formId.empty() || formId.length() > FORMID_MAX_LEN) {
return napi_invalid_arg;
}
for (uint32_t i = 0; i < formId.length(); i++) {
if (!isdigit(formId[i])) {
return napi_invalid_arg;
}
}
unsigned long long num = stoull(formId);
if (num > MAX_INT64) {
return napi_invalid_arg;
}
return napi_ok;
}
static napi_status ParseUpdateGalleryFormInfoOption(napi_env env, napi_value arg, MediaLibraryAsyncContext &context)
{
const std::string formIdKey = "formId";
const std::string assetUrisKey = "assetUris";
bool formIdPresent = false;
napi_status result = napi_has_named_property(env, arg, formIdKey.c_str(), &formIdPresent);
CHECK_COND_RET(result == napi_ok, result, "failed to check formId property");
if (!formIdPresent) {
return napi_invalid_arg;
}
napi_value formIdValue;
result = napi_get_named_property(env, arg, formIdKey.c_str(), &formIdValue);
CHECK_COND_RET(result == napi_ok, result, "failed to get formId property");
char formIdBuffer[ARG_BUF_SIZE];
size_t formIdLength = 0;
result = napi_get_value_string_utf8(env, formIdValue, formIdBuffer, ARG_BUF_SIZE, &formIdLength);
CHECK_COND_RET(result == napi_ok, result, "failed to get formId string");
std::string formId = std::string(formIdBuffer);
if (CheckFormId(formId) != napi_ok) {
return napi_invalid_arg;
}
context.formId = formId;
bool urisPresent = false;
result = napi_has_named_property(env, arg, assetUrisKey.c_str(), &urisPresent);
CHECK_COND_RET(result == napi_ok, result, "failed to check uris property");
if (!urisPresent) {
return napi_invalid_arg;
}
napi_value urisValue;
result = napi_get_named_property(env, arg, assetUrisKey.c_str(), &urisValue);
CHECK_COND_RET(result == napi_ok, result, "failed to get uris property");
bool isArray = false;
result = napi_is_array(env, urisValue, &isArray);
CHECK_COND_RET(result == napi_ok && isArray, napi_invalid_arg, "uris is not an array");
uint32_t arrayLength = 0;
result = napi_get_array_length(env, urisValue, &arrayLength);
CHECK_COND_RET(result == napi_ok, result, "failed to get array length");
if (arrayLength == 0) {
return napi_invalid_arg;
}
for (uint32_t i = 0; i < arrayLength; ++i) {
napi_value uriValue;
result = napi_get_element(env, urisValue, i, &uriValue);
CHECK_COND_RET(result == napi_ok, result, "failed to get array element");
char uriBuffer[ARG_BUF_SIZE];
size_t uriLength = 0;
result = napi_get_value_string_utf8(env, uriValue, uriBuffer, ARG_BUF_SIZE, &uriLength);
CHECK_COND_RET(result == napi_ok, result, "failed to get URI string");
std::string assetUri = std::string(uriBuffer);
OHOS::DataShare::DataShareValuesBucket bucket;
bucket.Put(TabFaCardPhotosColumn::FACARD_PHOTOS_FORM_ID, formId);
bucket.Put(TabFaCardPhotosColumn::FACARD_PHOTOS_ASSET_URI, assetUri);
context.valuesBucketArray.push_back(move(bucket));
}
return napi_ok;
}
static napi_status ParseSaveGalleryFormInfoOption(napi_env env, napi_value arg, MediaLibraryAsyncContext &context)
{
const std::string formIdKey = "formId";
const std::string assetUrisKey = "assetUris";
bool formIdPresent = false;
napi_status result = napi_has_named_property(env, arg, formIdKey.c_str(), &formIdPresent);
CHECK_COND_RET(result == napi_ok, result, "failed to check formId property");
if (!formIdPresent) {
NAPI_ERR_LOG("ParseSaveGalleryFormInfoOption formIdPresent is false");
return napi_invalid_arg;
}
napi_value formIdValue;
result = napi_get_named_property(env, arg, formIdKey.c_str(), &formIdValue);
CHECK_COND_RET(result == napi_ok, result, "failed to get formId property");
char formIdBuffer[ARG_BUF_SIZE];
size_t formIdLength = 0;
result = napi_get_value_string_utf8(env, formIdValue, formIdBuffer, ARG_BUF_SIZE, &formIdLength);
CHECK_COND_RET(result == napi_ok, result, "failed to get formId string");
std::string formId = std::string(formIdBuffer);
if (CheckFormId(formId) != napi_ok) {
return napi_invalid_arg;
}
bool urisPresent = false;
result = napi_has_named_property(env, arg, assetUrisKey.c_str(), &urisPresent);
CHECK_COND_RET(result == napi_ok, result, "failed to check uris property");
if (!urisPresent) {
NAPI_ERR_LOG("ParseSaveGalleryFormInfoOption urisPresent is false");
return napi_invalid_arg;
}
napi_value urisValue;
result = napi_get_named_property(env, arg, assetUrisKey.c_str(), &urisValue);
CHECK_COND_RET(result == napi_ok, result, "failed to get uris property");
bool isArray = false;
result = napi_is_array(env, urisValue, &isArray);
CHECK_COND_RET(result == napi_ok && isArray, napi_invalid_arg, "uris is not an array");
uint32_t arrayLength = 0;
result = napi_get_array_length(env, urisValue, &arrayLength);
CHECK_COND_RET(result == napi_ok, result, "failed to get array length");
if (arrayLength == 0) {
return napi_invalid_arg;
}
for (uint32_t i = 0; i < arrayLength; ++i) {
napi_value uriValue;
result = napi_get_element(env, urisValue, i, &uriValue);
CHECK_COND_RET(result == napi_ok, result, "failed to get array element");
char uriBuffer[ARG_BUF_SIZE];
size_t uriLength = 0;
result = napi_get_value_string_utf8(env, uriValue, uriBuffer, ARG_BUF_SIZE, &uriLength);
CHECK_COND_RET(result == napi_ok, result, "failed to get URI string");
std::string assetUri = std::string(uriBuffer);
OHOS::DataShare::DataShareValuesBucket bucket;
bucket.Put(TabFaCardPhotosColumn::FACARD_PHOTOS_FORM_ID, formId);
bucket.Put(TabFaCardPhotosColumn::FACARD_PHOTOS_ASSET_URI, assetUri);
context.valuesBucketArray.push_back(move(bucket));
}
return napi_ok;
}
static napi_status ParseSaveFormInfoOption(napi_env env, napi_value arg, MediaLibraryAsyncContext &context)
{
const std::string formId = "formId";
const std::string uri = "uri";
const std::map<std::string, std::string> saveFormInfoOptionsParam = {
{ formId, FormMap::FORMMAP_FORM_ID },
{ uri, FormMap::FORMMAP_URI }
};
for (const auto &iter : saveFormInfoOptionsParam) {
string param = iter.first;
bool present = false;
napi_status result = napi_has_named_property(env, arg, param.c_str(), &present);
CHECK_COND_RET(result == napi_ok, result, "failed to check named property");
if (!present) {
return napi_invalid_arg;
}
napi_value value;
result = napi_get_named_property(env, arg, param.c_str(), &value);
CHECK_COND_RET(result == napi_ok, result, "failed to get named property");
char buffer[ARG_BUF_SIZE];
size_t res = 0;
result = napi_get_value_string_utf8(env, value, buffer, ARG_BUF_SIZE, &res);
CHECK_COND_RET(result == napi_ok, result, "failed to get string");
context.valuesBucket.Put(iter.second, string(buffer));
}
bool isValid = false;
string tempFormId = context.valuesBucket.Get(FormMap::FORMMAP_FORM_ID, isValid);
if (!isValid) {
return napi_invalid_arg;
}
return CheckFormId(tempFormId);
}
static napi_value ParseArgsUpdateGalleryFormInfo(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
constexpr size_t minArgs = ARGS_ONE;
constexpr size_t maxArgs = ARGS_TWO;
CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs,
maxArgs) == napi_ok, "Failed to get object info");
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
CHECK_COND_WITH_MESSAGE(env, ParseUpdateGalleryFormInfoOption(env, context->argv[ARGS_ZERO], *context) == napi_ok,
"Parse formInfo Option failed");
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
static napi_value ParseArgsSaveGalleryFormInfo(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
constexpr size_t minArgs = ARGS_ONE;
constexpr size_t maxArgs = ARGS_TWO;
CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs,
maxArgs) == napi_ok, "Failed to get object info");
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
CHECK_COND_WITH_MESSAGE(env, ParseSaveGalleryFormInfoOption(env, context->argv[ARGS_ZERO], *context) == napi_ok,
"Parse formInfo Option failed");
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
static napi_value ParseArgsSaveFormInfo(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
constexpr size_t minArgs = ARGS_ONE;
constexpr size_t maxArgs = ARGS_TWO;
CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs,
maxArgs) == napi_ok, "Failed to get object info");
CHECK_COND_WITH_MESSAGE(env, ParseSaveFormInfoOption(env, context->argv[ARGS_ZERO], *context) == napi_ok,
"Parse formInfo Option failed");
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
static void SaveGalleryFormInfoExec(napi_env env, void *data, ResultNapiType type)
{
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
context->resultNapiType = type;
vector<string> formIds;
vector<string> fileUris;
bool isValid = false;
for (const auto& valueBucket : context->valuesBucketArray) {
formIds.emplace_back(valueBucket.Get(TabFaCardPhotosColumn::FACARD_PHOTOS_FORM_ID, isValid));
fileUris.emplace_back(valueBucket.Get(TabFaCardPhotosColumn::FACARD_PHOTOS_ASSET_URI, isValid));
}
FormInfoReqBody reqBody;
reqBody.formIds = formIds;
reqBody.fileUris = fileUris;
uint32_t businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::SAVE_GALLERY_FORM_INFO);
int32_t ret = IPC::UserDefineIPCClient().Call(businessCode, reqBody);
if (ret < 0) {
if (ret == E_PERMISSION_DENIED) {
context->error = OHOS_PERMISSION_DENIED_CODE;
} else if (ret == E_GET_PRAMS_FAIL) {
context->error = OHOS_INVALID_PARAM_CODE;
} else {
context->SaveError(ret);
}
NAPI_INFO_LOG("store formInfo failed, ret: %{public}d", ret);
}
}
static void SaveFormInfoExec(napi_env env, void *data, ResultNapiType type)
{
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
context->resultNapiType = type;
bool isValid = false;
string formId = context->valuesBucket.Get(FormMap::FORMMAP_FORM_ID, isValid);
string fileUri = context->valuesBucket.Get(FormMap::FORMMAP_URI, isValid);
FormInfoReqBody reqBody;
reqBody.formIds.emplace_back(formId);
reqBody.fileUris.emplace_back(fileUri);
uint32_t businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::SAVE_FORM_INFO);
int32_t ret = IPC::UserDefineIPCClient().Call(businessCode, reqBody);
if (ret < 0) {
if (ret == E_PERMISSION_DENIED) {
context->error = OHOS_PERMISSION_DENIED_CODE;
} else if (ret == E_GET_PRAMS_FAIL) {
context->error = OHOS_INVALID_PARAM_CODE;
} else {
context->SaveError(ret);
}
NAPI_ERR_LOG("store formInfo failed, ret: %{public}d", ret);
}
}
static void SaveFormInfoAsyncCallbackComplete(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("SaveFormInfoAsyncCallbackComplete");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
auto jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->data), JS_INNER_FAIL);
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), JS_INNER_FAIL);
if (context->error != ERR_DEFAULT) {
context->HandleError(env, jsContext->error);
} else {
jsContext->status = true;
}
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
static napi_value ParseArgsRemoveGalleryFormInfo(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
constexpr size_t minArgs = ARGS_ONE;
constexpr size_t maxArgs = ARGS_TWO;
CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs,
maxArgs) == napi_ok, "Failed to get object info");
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
bool present = false;
CHECK_COND_WITH_MESSAGE(env, napi_has_named_property(env, context->argv[ARGS_ZERO], "formId", &present) == napi_ok,
"Failed to get object info");
if (!present) {
NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "Failed to check empty formId!");
return nullptr;
}
napi_value value;
CHECK_COND_WITH_MESSAGE(env, napi_get_named_property(env, context->argv[ARGS_ZERO], "formId", &value) == napi_ok,
"failed to get named property");
char buffer[ARG_BUF_SIZE];
size_t res = 0;
CHECK_COND_WITH_MESSAGE(env, napi_get_value_string_utf8(env, value, buffer, ARG_BUF_SIZE, &res) == napi_ok,
"failed to get string param");
context->formId = string(buffer);
CHECK_COND_WITH_MESSAGE(env, CheckFormId(context->formId) == napi_ok, "FormId is invalid");
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
static napi_value ParseArgsRemoveFormInfo(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
constexpr size_t minArgs = ARGS_ONE;
constexpr size_t maxArgs = ARGS_TWO;
CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs,
maxArgs) == napi_ok, "Failed to get object info");
bool present = false;
CHECK_COND_WITH_MESSAGE(env, napi_has_named_property(env, context->argv[ARGS_ZERO], "formId", &present) == napi_ok,
"Failed to get object info");
if (!present) {
NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "Failed to check empty formId!");
return nullptr;
}
napi_value value;
CHECK_COND_WITH_MESSAGE(env, napi_get_named_property(env, context->argv[ARGS_ZERO], "formId", &value) == napi_ok,
"failed to get named property");
char buffer[ARG_BUF_SIZE];
size_t res = 0;
CHECK_COND_WITH_MESSAGE(env, napi_get_value_string_utf8(env, value, buffer, ARG_BUF_SIZE, &res) == napi_ok,
"failed to get string param");
context->formId = string(buffer);
CHECK_COND_WITH_MESSAGE(env, CheckFormId(context->formId) == napi_ok, "FormId is invalid");
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
static void UpdateGalleryFormInfoExec(napi_env env, void *data, ResultNapiType type)
{
MediaLibraryTracer tracer;
tracer.Start("UpdateGalleryFormInfoExec");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
context->resultNapiType = type;
vector<string> formIds;
vector<string> fileUris;
bool isValid = false;
for (auto valueBucket : context->valuesBucketArray) {
formIds.emplace_back(valueBucket.Get(TabFaCardPhotosColumn::FACARD_PHOTOS_FORM_ID, isValid));
fileUris.emplace_back(valueBucket.Get(TabFaCardPhotosColumn::FACARD_PHOTOS_ASSET_URI, isValid));
}
FormInfoReqBody reqBody;
reqBody.formIds = formIds;
reqBody.fileUris = fileUris;
uint32_t businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::UPDATE_GALLERY_FORM_INFO);
NAPI_INFO_LOG("before IPC::UserDefineIPCClient().Call");
int32_t ret = IPC::UserDefineIPCClient().Call(businessCode, reqBody);
NAPI_INFO_LOG("after IPC::UserDefineIPCClient().Call");
if (ret < 0) {
if (ret == E_PERMISSION_DENIED) {
context->error = OHOS_PERMISSION_DENIED_CODE;
} else if (ret == E_GET_PRAMS_FAIL) {
context->error = OHOS_INVALID_PARAM_CODE;
} else {
context->SaveError(ret);
}
NAPI_INFO_LOG("store formInfo failed, ret: %{public}d", ret);
}
}
static void RemoveGalleryFormInfoExec(napi_env env, void *data, ResultNapiType type)
{
MediaLibraryTracer tracer;
tracer.Start("RemoveGalleryFormInfoExec");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
context->resultNapiType = type;
string formId = context->formId;
if (formId.empty()) {
context->error = OHOS_INVALID_PARAM_CODE;
return;
}
FormInfoReqBody reqBody;
reqBody.formIds.emplace_back(formId);
uint32_t businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::REMOVE_GALLERY_FORM_INFO);
int32_t ret = IPC::UserDefineIPCClient().Call(businessCode, reqBody);
if (ret < 0) {
if (ret == E_PERMISSION_DENIED) {
context->error = OHOS_PERMISSION_DENIED_CODE;
} else {
context->SaveError(ret);
}
NAPI_ERR_LOG("remove formInfo failed, ret: %{public}d", ret);
}
}
static void RemoveFormInfoExec(napi_env env, void *data, ResultNapiType type)
{
MediaLibraryTracer tracer;
tracer.Start("RemoveFormInfoExec");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
context->resultNapiType = type;
string formId = context->formId;
if (formId.empty()) {
context->error = OHOS_INVALID_PARAM_CODE;
return;
}
FormInfoReqBody reqBody;
reqBody.formIds.emplace_back(formId);
uint32_t businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::REMOVE_FORM_INFO);
int32_t ret = IPC::UserDefineIPCClient().Call(businessCode, reqBody);
if (ret < 0) {
if (ret == E_PERMISSION_DENIED) {
context->error = OHOS_PERMISSION_DENIED_CODE;
} else {
context->SaveError(ret);
}
NAPI_ERR_LOG("remove formInfo failed, ret: %{public}d", ret);
}
}
static void RemoveFormInfoAsyncCallbackComplete(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("RemoveFormInfoAsyncCallbackComplete");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
auto jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->data), JS_INNER_FAIL);
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), JS_INNER_FAIL);
if (context->error != ERR_DEFAULT) {
context->HandleError(env, jsContext->error);
} else {
jsContext->status = true;
}
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
static void PhotoAccessSaveFormInfoExec(napi_env env, void *data)
{
SaveFormInfoExec(env, data, ResultNapiType::TYPE_PHOTOACCESS_HELPER);
}
napi_value MediaLibraryNapi::PhotoAccessSaveFormInfo(napi_env env, napi_callback_info info)
{
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
CHECK_NULLPTR_RET(ParseArgsSaveFormInfo(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "PhotoAccessSaveFormInfo",
PhotoAccessSaveFormInfoExec, SaveFormInfoAsyncCallbackComplete);
}
static void PhotoAccessSaveGalleryFormInfoExec(napi_env env, void *data)
{
SaveGalleryFormInfoExec(env, data, ResultNapiType::TYPE_PHOTOACCESS_HELPER);
}
napi_value MediaLibraryNapi::PhotoAccessSaveGalleryFormInfo(napi_env env, napi_callback_info info)
{
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
CHECK_NULLPTR_RET(ParseArgsSaveGalleryFormInfo(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "PhotoAccessSaveGalleryFormInfo",
PhotoAccessSaveGalleryFormInfoExec, SaveFormInfoAsyncCallbackComplete);
}
static void PhotoAccessRemoveFormInfoExec(napi_env env, void *data)
{
RemoveFormInfoExec(env, data, ResultNapiType::TYPE_PHOTOACCESS_HELPER);
}
static void PhotoAccessRemoveGalleryFormInfoExec(napi_env env, void *data)
{
RemoveGalleryFormInfoExec(env, data, ResultNapiType::TYPE_PHOTOACCESS_HELPER);
}
static void PhotoAccessUpdateGalleryFormInfoExec(napi_env env, void *data)
{
UpdateGalleryFormInfoExec(env, data, ResultNapiType::TYPE_PHOTOACCESS_HELPER);
}
napi_value MediaLibraryNapi::PhotoAccessRemoveGalleryFormInfo(napi_env env, napi_callback_info info)
{
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
CHECK_NULLPTR_RET(ParseArgsRemoveGalleryFormInfo(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "PhotoAccessRemoveGalleryFormInfo",
PhotoAccessRemoveGalleryFormInfoExec, RemoveFormInfoAsyncCallbackComplete);
}
napi_value MediaLibraryNapi::PhotoAccessUpdateGalleryFormInfo(napi_env env, napi_callback_info info)
{
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
CHECK_NULLPTR_RET(ParseArgsUpdateGalleryFormInfo(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "PhotoAccessRemoveGalleryFormInfo",
PhotoAccessUpdateGalleryFormInfoExec, RemoveFormInfoAsyncCallbackComplete);
}
napi_value MediaLibraryNapi::PhotoAccessRemoveFormInfo(napi_env env, napi_callback_info info)
{
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
CHECK_NULLPTR_RET(ParseArgsRemoveFormInfo(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "PhotoAccessRemoveFormInfo",
PhotoAccessRemoveFormInfoExec, RemoveFormInfoAsyncCallbackComplete);
}
static napi_value ParseArgsStartCreateThumbnailTask(napi_env env,
napi_callback_info info, unique_ptr<MediaLibraryAsyncContext> &context)
{
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
CHECK_ARGS(env, MediaLibraryNapiUtils::ParseArgsTwoCallback(
env, info, context, ARGS_TWO, ARGS_THREE), JS_E_PARAM_INVALID);
CHECK_COND_WITH_MESSAGE(env, context->callbackRef, "Can not get callback function");
CHECK_ARGS(env, MediaLibraryNapiUtils::ParsePredicates(env,
context->argv[PARAM0], context, ASSET_FETCH_OPT), JS_E_PARAM_INVALID);
napi_value result = nullptr;
CHECK_ARGS_WITH_MSG(env, napi_get_boolean(env, true, &result), JS_E_INNER_FAIL,
"Failed to create boolean value");
return result;
}
static void RegisterThumbnailGenerateObserver(napi_env env,
std::unique_ptr<MediaLibraryAsyncContext> &asyncContext, int32_t requestId)
{
std::shared_ptr<ThumbnailBatchGenerateObserver> dataObserver;
if (thumbnailGenerateObserverMap.Find(requestId, dataObserver)) {
NAPI_INFO_LOG("RequestId: %{public}d exist in observer map, no need to register", requestId);
return;
}
dataObserver = std::make_shared<ThumbnailBatchGenerateObserver>();
std::string observerUri = PhotoColumn::PHOTO_URI_PREFIX + std::to_string(requestId) + "," +
std::to_string(getpid());
UserFileClient::RegisterObserverExt(Uri(observerUri), dataObserver, false);
thumbnailGenerateObserverMap.Insert(requestId, dataObserver);
}
static void UnregisterThumbnailGenerateObserver(int32_t requestId)
{
std::shared_ptr<ThumbnailBatchGenerateObserver> dataObserver;
if (!thumbnailGenerateObserverMap.Find(requestId, dataObserver)) {
return;
}
std::string observerUri = PhotoColumn::PHOTO_URI_PREFIX + std::to_string(requestId) + "," +
std::to_string(getpid());
UserFileClient::UnregisterObserverExt(Uri(observerUri), dataObserver);
thumbnailGenerateObserverMap.Erase(requestId);
}
static void DeleteThumbnailHandler(int32_t requestId)
{
std::shared_ptr<ThumbnailGenerateHandler> dataHandler;
if (!thumbnailGenerateHandlerMap.Find(requestId, dataHandler)) {
return;
}
napi_release_threadsafe_function(dataHandler->threadSafeFunc_, napi_tsfn_release);
thumbnailGenerateHandlerMap.Erase(requestId);
}
static void ReleaseThumbnailTask(int32_t requestId)
{
UnregisterThumbnailGenerateObserver(requestId);
DeleteThumbnailHandler(requestId);
}
static void CreateThumbnailHandler(napi_env env,
std::unique_ptr<MediaLibraryAsyncContext> &asyncContext, int32_t requestId)
{
napi_value workName = nullptr;
napi_create_string_utf8(env, "ThumbSafeThread", NAPI_AUTO_LENGTH, &workName);
napi_threadsafe_function threadSafeFunc;
napi_status status = napi_create_threadsafe_function(env, asyncContext->argv[PARAM1], NULL, workName, 0, 1,
NULL, NULL, NULL, MediaLibraryNapi::OnThumbnailGenerated, &threadSafeFunc);
if (status != napi_ok) {
NAPI_ERR_LOG("napi_create_threadsafe_function fail");
ReleaseThumbnailTask(requestId);
if (threadSafeFunc != nullptr) {
napi_release_threadsafe_function(threadSafeFunc, napi_tsfn_release);
threadSafeFunc = nullptr;
}
asyncContext->SaveError(JS_INNER_FAIL);
return;
}
std::shared_ptr<ThumbnailGenerateHandler> dataHandler =
std::make_shared<ThumbnailGenerateHandler>(asyncContext->callbackRef, threadSafeFunc);
thumbnailGenerateHandlerMap.Insert(requestId, dataHandler);
}
void MediaLibraryNapi::OnThumbnailGenerated(napi_env env, napi_value cb, void *context, void *data)
{
if (env == nullptr) {
return;
}
std::shared_ptr<ThumbnailGenerateHandler> dataHandler;
if (!thumbnailGenerateHandlerMap.Find(requestIdCallback_, dataHandler)) {
return;
}
napi_status status = napi_get_reference_value(env, dataHandler->callbackRef_, &cb);
if (status != napi_ok) {
NapiError::ThrowError(env, JS_INNER_FAIL, "napi_get_reference_value fail");
return;
}
napi_value result = nullptr;
status = napi_call_function(env, nullptr, cb, 0, nullptr, &result);
if (status != napi_ok) {
NapiError::ThrowError(env, JS_INNER_FAIL, "calling onDataPrepared failed");
}
}
int32_t MediaLibraryNapi::AssignRequestId()
{
return ++requestIdCounter_;
}
static int32_t GetRequestId()
{
return requestIdCounter_;
}
static int32_t StartCreateThumbnailTask(napi_env env, MediaLibraryAsyncContext *asyncContext)
{
CHECK_COND_RET(asyncContext != nullptr, JS_INNER_FAIL, "Async context is null");
int32_t requestId = asyncContext->requestId;
StartThumbnailCreationTaskReqBody reqBody;
reqBody.predicates = asyncContext->predicates;
reqBody.requestId = requestId;
reqBody.pid = getpid();
uint32_t businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::PAH_START_THUMBNAIL_CREATION_TASK);
NAPI_INFO_LOG("before IPC::UserDefineIPCClient().Call, %{public}d", requestId);
int32_t err = IPC::UserDefineIPCClient().Call(businessCode, reqBody);
NAPI_INFO_LOG("after IPC::UserDefineIPCClient().Call");
if (err < 0) {
ReleaseThumbnailTask(requestId);
asyncContext->SaveError(err);
NAPI_ERR_LOG("Create thumbnail task, update failed, err: %{public}d", err);
}
return err;
}
static void StartCreateThumbnailTaskExecute(napi_env env, void *data)
{
auto *context = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_IF_EQUAL(context != nullptr, "Async context is null");
CHECK_IF_EQUAL(context->requestId == GetRequestId(), "RequestId is invalid");
StartCreateThumbnailTask(env, context);
}
static void StartCreateThumbnailTaskCompleteCallback(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("StartCreateThumbnailTaskCompleteCallback");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
CHECK_IF_EQUAL(context != nullptr, "Async context is null");
if (context->requestId != GetRequestId() && context->work != nullptr) {
napi_delete_reference(env, context->responseRef);
context->responseRef = nullptr;
napi_delete_async_work(env, context->work);
delete context;
return;
}
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
napi_get_undefined(env, &jsContext->error);
napi_get_undefined(env, &jsContext->data);
jsContext->status = false;
if (context->error == ERR_DEFAULT) {
jsContext->status = true;
napi_create_int32(env, 0, &jsContext->data);
} else if (context->error == E_THUMBNAIL_ASTC_ALL_EXIST) {
napi_create_int32(env, 1, &jsContext->data);
} else {
napi_create_int32(env, 0, &jsContext->data);
context->HandleError(env, jsContext->error);
}
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->responseRef,
context->work, *jsContext);
}
delete context;
}
napi_value MediaLibraryNapi::PhotoAccessStartCreateThumbnailTask(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessStartCreateThumbnailTask");
std::unique_ptr<MediaLibraryAsyncContext> asyncContext = std::make_unique<MediaLibraryAsyncContext>();
CHECK_NULLPTR_RET(ParseArgsStartCreateThumbnailTask(env, info, asyncContext));
lock_guard<mutex> lock(thumbnailMutex_);
ReleaseThumbnailTask(GetRequestId());
int32_t requestId = AssignRequestId();
RegisterThumbnailGenerateObserver(env, asyncContext, requestId);
CreateThumbnailHandler(env, asyncContext, requestId);
asyncContext->requestId = requestId;
napi_value result = nullptr;
NAPI_CALL(env, napi_get_undefined(env, &result));
if (asyncContext->responseRef != nullptr) {
MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "StartCreateThumbnailTask",
StartCreateThumbnailTaskExecute, StartCreateThumbnailTaskCompleteCallback);
} else {
int32_t err = StartCreateThumbnailTask(env, asyncContext.get());
if (err < 0) {
napi_create_int32(env, err, &result);
return result;
}
}
napi_create_int32(env, requestId, &result);
return result;
}
void ThumbnailBatchGenerateObserver::OnChange(const ChangeInfo &changeInfo)
{
if (changeInfo.changeType_ != static_cast<int32_t>(NotifyType::NOTIFY_THUMB_UPDATE)) {
return;
}
for (auto &uri : changeInfo.uris_) {
string uriString = uri.ToString();
auto pos = uriString.find_last_of('/');
if (pos == std::string::npos) {
continue;
}
std::string params = uriString.substr(pos + 1);
auto commaPos = params.find(',');
if (commaPos == std::string::npos) {
continue;
}
std::string requestIdStr = params.substr(0, commaPos);
std::string pidStr = params.substr(commaPos + 1);
if (!MediaFileUtils::IsValidInteger(requestIdStr)) {
continue;
}
requestIdCallback_ = std::stoi(requestIdStr.c_str());
if (!MediaFileUtils::IsValidInteger(pidStr)) {
continue;
}
pid_t pid = std::atoi(pidStr.c_str());
if (pid != getpid()) {
continue;
}
std::shared_ptr<ThumbnailGenerateHandler> dataHandler;
if (!thumbnailGenerateHandlerMap.Find(requestIdCallback_, dataHandler)) {
continue;
}
napi_status status = napi_acquire_threadsafe_function(dataHandler->threadSafeFunc_);
if (status != napi_ok) {
ReleaseThumbnailTask(requestIdCallback_);
NAPI_ERR_LOG("napi_acquire_threadsafe_function fail, status: %{public}d", static_cast<int32_t>(status));
continue;
}
status = napi_call_threadsafe_function(dataHandler->threadSafeFunc_, NULL, napi_tsfn_blocking);
if (status != napi_ok) {
ReleaseThumbnailTask(requestIdCallback_);
NAPI_ERR_LOG("napi_call_threadsafe_function fail, status: %{public}d", static_cast<int32_t>(status));
continue;
}
}
}
static napi_value ParseArgsStopCreateThumbnailTask(napi_env env,
napi_callback_info info, unique_ptr<MediaLibraryAsyncContext> &context)
{
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
CHECK_ARGS(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env,
info, context, ARGS_ONE, ARGS_ONE), JS_ERR_PARAMETER_INVALID);
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
static void StopCreateThumbnailTaskExecute(napi_env env, void *data)
{
auto *context = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_IF_EQUAL(context != nullptr, "Async context is null");
CHECK_IF_EQUAL(context->requestId == GetRequestId(), "RequestId is invalid");
StopThumbnailCreationTaskReqBody reqBody;
reqBody.requestId = context->requestId;
reqBody.pid = getpid();
uint32_t businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::PAH_STOP_THUMBNAIL_CREATION_TASK);
NAPI_INFO_LOG("before IPC::UserDefineIPCClient().Call");
int32_t err = IPC::UserDefineIPCClient().Call(businessCode, reqBody);
NAPI_INFO_LOG("after IPC::UserDefineIPCClient().Call");
if (err < 0) {
context->SaveError(err);
NAPI_ERR_LOG("Stop create thumbnail task, update failed, err: %{public}d", err);
}
}
static void StopCreateThumbnailTaskCompleteCallback(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("StopCreateThumbnailTaskCompleteCallback");
auto *context = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_IF_EQUAL(context != nullptr, "Async context is null");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->data), JS_INNER_FAIL);
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), JS_INNER_FAIL);
if (context->error == ERR_DEFAULT) {
jsContext->status = true;
} else {
context->HandleError(env, jsContext->error);
}
tracer.Finish();
if (context->work != nullptr) {
napi_delete_async_work(env, context->work);
}
delete context;
}
napi_value MediaLibraryNapi::PhotoAccessStopCreateThumbnailTask(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessStopCreateThumbnailTask");
std::unique_ptr<MediaLibraryAsyncContext> asyncContext = std::make_unique<MediaLibraryAsyncContext>();
CHECK_NULLPTR_RET(ParseArgsStopCreateThumbnailTask(env, info, asyncContext));
int32_t requestId = 0;
CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::GetInt32(env,
asyncContext->argv[PARAM0], requestId) == napi_ok, "Failed to get requestId");
if (requestId <= 0) {
NAPI_WARN_LOG("Invalid requestId: %{public}d", requestId);
RETURN_NAPI_UNDEFINED(env);
}
lock_guard<mutex> lock(thumbnailMutex_);
ReleaseThumbnailTask(requestId);
asyncContext->requestId = requestId;
MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "StopCreateThumbnailTask",
StopCreateThumbnailTaskExecute, StopCreateThumbnailTaskCompleteCallback);
RETURN_NAPI_UNDEFINED(env);
}
static void GetMediaAnalysisServiceProgress(nlohmann::json& jsonObj, unordered_map<int, string>& idxToCount,
MediaLibraryAsyncContext* context)
{
int errCode = 0;
GetAnalysisProcessReqBody reqBody;
reqBody.analysisType = AnalysisType::ANALYSIS_LABEL;
QueryResultRespBody respBody;
errCode = IPC::UserDefineIPCClient().Call(
static_cast<uint32_t>(MediaLibraryBusinessCode::GET_ANALYSIS_PROCESS), reqBody, respBody);
if (errCode != E_OK) {
if (errCode == E_PERMISSION_DENIED) {
context->error = OHOS_PERMISSION_DENIED_CODE;
} else {
context->SaveError(errCode);
}
NAPI_ERR_LOG("Get Label Analysis Progress failed! errCode is = %{public}d", errCode);
}
shared_ptr<DataShare::DataShareResultSet> ret = respBody.resultSet;
if (ret == nullptr) {
NAPI_ERR_LOG("ret is nullptr");
return;
}
if (ret->GoToFirstRow() != NativeRdb::E_OK) {
NAPI_ERR_LOG("GotoFirstRow failed, errCode is %{public}d", errCode);
ret->Close();
return;
}
for (size_t i = 0; i < idxToCount.size(); ++i) {
int tmp = -1;
ret->GetInt(i, tmp);
jsonObj[idxToCount[i]] = tmp;
}
ret->Close();
}
static void GetAnalysisProgress(MediaLibraryAsyncContext* context)
{
int errCode = 0;
GetAnalysisProcessReqBody reqBody;
reqBody.analysisType = AnalysisType::ANALYSIS_INVALID;
QueryResultRespBody respBody;
errCode = IPC::UserDefineIPCClient().Call(
static_cast<uint32_t>(MediaLibraryBusinessCode::GET_ANALYSIS_PROCESS), reqBody, respBody);
if (errCode != E_OK) {
if (errCode == E_PERMISSION_DENIED) {
context->error = OHOS_PERMISSION_DENIED_CODE;
} else {
context->SaveError(errCode);
}
NAPI_ERR_LOG("Get Analysis Progress failed! errCode is = %{public}d", errCode);
}
shared_ptr<DataShare::DataShareResultSet> ret = respBody.resultSet;
if (ret == nullptr) {
NAPI_ERR_LOG("ret is nullptr");
return;
}
if (ret->GoToFirstRow() != NativeRdb::E_OK) {
NAPI_ERR_LOG("GotoFirstRow failed, errCode is %{public}d", errCode);
ret->Close();
return;
}
vector<string> columnList = {SEARCH_FINISH_CNT, LOCATION_FINISH_CNT, FACE_FINISH_CNT, OBJECT_FINISH_CNT,
AESTHETIC_FINISH_CNT, OCR_FINISH_CNT, POSE_FINISH_CNT, SALIENCY_FINISH_CNT, RECOMMENDATION_FINISH_CNT,
SEGMENTATION_FINISH_CNT, BEAUTY_AESTHETIC_FINISH_CNT, HEAD_DETECT_FINISH_CNT, LABEL_DETECT_FINISH_CNT,
TOTAL_IMAGE_CNT, FULLY_ANALYZED_IMAGE_CNT, TOTAL_PROGRESS, CHECK_SPACE_FLAG};
int colIndex = 0;
int colValue = 0;
nlohmann::json jsonObj;
for (auto columnName : columnList) {
ret->GetColumnIndex(columnName, colIndex);
ret->GetInt(colIndex, colValue);
jsonObj[columnName] = colValue;
}
ret->Close();
context->analysisProgress = jsonObj.dump();
NAPI_INFO_LOG("Progress json is %{public}s", context->analysisProgress.c_str());
}
static void GetLabelAnalysisProgress(MediaLibraryAsyncContext* context)
{
unordered_map<int, string> idxToCount = {
{0, "totalCount"}, {1, "finishedCount"}, {2, "LabelCount"}
};
nlohmann::json jsonObj;
GetMediaAnalysisServiceProgress(jsonObj, idxToCount, context);
NAPI_INFO_LOG("Progress json is %{public}s", jsonObj.dump().c_str());
context->analysisProgress = jsonObj.dump();
}
static std::string GetTotalCount()
{
Uri uri(URI_TOTAL);
string clause = VISION_TOTAL_TABLE + "." + MediaColumn::MEDIA_ID + " = " + PhotoColumn::PHOTOS_TABLE+ "." +
MediaColumn::MEDIA_ID;
DataShare::DataSharePredicates predicates;
predicates.InnerJoin(PhotoColumn::PHOTOS_TABLE)->On({ clause });
predicates.EqualTo(PhotoColumn::PHOTO_HIDDEN_TIME, 0)->And()
->EqualTo(MediaColumn::MEDIA_DATE_TRASHED, 0)->And()
->EqualTo(MediaColumn::MEDIA_TIME_PENDING, 0);
vector<string> column = {
"SUM(CASE WHEN (media_type = 1 OR (media_type = 2 AND (position = 1 OR position = 3))) THEN 1 ELSE 0 END) AS "
"totalCount"
};
int errCode = 0;
shared_ptr<DataShare::DataShareResultSet> ret = UserFileClient::Query(uri, predicates, column, errCode);
if (ret == nullptr) {
NAPI_ERR_LOG("ret is nullptr");
return "";
}
if (ret->GoToFirstRow() != NativeRdb::E_OK) {
ret->Close();
NAPI_ERR_LOG("GotoFirstRow failed, errCode is %{public}d", errCode);
return "";
}
int totalCount = 0;
ret->GetInt(0, totalCount);
ret->Close();
return to_string(totalCount);
}
static void UpdateAnalysisProgress(const shared_ptr<DataShare::DataShareResultSet>& resultSet,
const string& curTotalCount, MediaLibraryAsyncContext* context)
{
string retJson = MediaLibraryNapiUtils::GetStringValueByColumn(resultSet, HIGHLIGHT_ANALYSIS_PROGRESS);
if (retJson == "" || !nlohmann::json::accept(retJson)) {
resultSet->Close();
NAPI_ERR_LOG("retJson is empty or invalid");
return;
}
nlohmann::json curJsonObj = nlohmann::json::parse(retJson);
if (!curJsonObj.contains("totalCount")) {
resultSet->Close();
NAPI_ERR_LOG("retJson do not contain totalCount");
return;
}
if (!curJsonObj["totalCount"].is_number_integer()) {
resultSet->Close();
NAPI_ERR_LOG("totalCount is not an integer type");
return;
}
int preTotalCount = curJsonObj["totalCount"];
if (to_string(preTotalCount) != curTotalCount) {
NAPI_ERR_LOG("preTotalCount != curTotalCount, curTotalCount is %{public}s, preTotalCount is %{public}d",
curTotalCount.c_str(), preTotalCount);
curJsonObj["totalCount"] = curTotalCount;
}
context->analysisProgress = curJsonObj.dump();
NAPI_INFO_LOG("GoToNextRow successfully and json is %{public}s", context->analysisProgress.c_str());
resultSet->Close();
}
static void GetFaceAnalysisProgress(MediaLibraryAsyncContext* context)
{
string curTotalCount = GetTotalCount();
int errCode = 0;
GetAnalysisProcessReqBody reqBody;
reqBody.analysisType = AnalysisType::ANALYSIS_FACE;
QueryResultRespBody respBody;
errCode = IPC::UserDefineIPCClient().Call(
static_cast<uint32_t>(MediaLibraryBusinessCode::GET_ANALYSIS_PROCESS), reqBody, respBody);
if (errCode != E_OK) {
if (errCode == E_PERMISSION_DENIED) {
context->error = OHOS_PERMISSION_DENIED_CODE;
} else {
context->SaveError(errCode);
}
NAPI_ERR_LOG("Get Face Analysis Progress failed! errCode is = %{public}d", errCode);
}
shared_ptr<DataShare::DataShareResultSet> ret = respBody.resultSet;
if (ret == nullptr) {
NAPI_ERR_LOG("ret is nullptr");
return;
}
if (ret->GoToNextRow() != NativeRdb::E_OK) {
ret->Close();
nlohmann::json jsonObj;
jsonObj["cvFinishedCount"] = 0;
jsonObj["geoFinishedCount"] = 0;
jsonObj["searchFinishedCount"] = 0;
jsonObj["totalCount"] = curTotalCount;
context->analysisProgress = jsonObj.dump();
NAPI_ERR_LOG("GetFaceAnalysisProgress failed, errCode is %{public}d, json is %{public}s", errCode,
context->analysisProgress.c_str());
return;
}
UpdateAnalysisProgress(ret, curTotalCount, context);
}
static void GetHighlightAnalysisProgress(MediaLibraryAsyncContext* context)
{
unordered_map<int, string> idxToCount = {
{0, "ClearCount"}, {1, "DeleteCount"}, {2, "NotProduceCount"}, {3, "ProduceCount"}, {4, "PushCount"}
};
int errCode = 0;
GetAnalysisProcessReqBody reqBody;
reqBody.analysisType = AnalysisType::ANALYSIS_HIGHLIGHT;
QueryResultRespBody respBody;
errCode = IPC::UserDefineIPCClient().Call(
static_cast<uint32_t>(MediaLibraryBusinessCode::GET_ANALYSIS_PROCESS), reqBody, respBody);
if (errCode != E_OK) {
if (errCode == E_PERMISSION_DENIED) {
context->error = OHOS_PERMISSION_DENIED_CODE;
} else {
context->SaveError(errCode);
}
NAPI_ERR_LOG("Get Highlight Analysis Progress failed! errCode is = %{public}d", errCode);
}
shared_ptr<DataShare::DataShareResultSet> ret = respBody.resultSet;
if (ret == nullptr) {
NAPI_ERR_LOG("ret is nullptr");
return;
}
if (ret->GoToFirstRow() != NativeRdb::E_OK) {
NAPI_ERR_LOG("GotoFirstRow failed, errCode is %{public}d", errCode);
ret->Close();
return;
}
nlohmann::json jsonObj;
for (size_t i = 0; i < idxToCount.size(); ++i) {
int tmp = -1;
ret->GetInt(i, tmp);
jsonObj[idxToCount[i]] = tmp;
}
ret->Close();
context->analysisProgress = jsonObj.dump();
NAPI_INFO_LOG("Progress json is %{public}s", context->analysisProgress.c_str());
}
static void JSGetAnalysisProgressExecute(MediaLibraryAsyncContext* context)
{
MediaLibraryTracer tracer;
tracer.Start("JSGetAnalysisProgressExecute");
std::vector<std::string> fetchColumn;
DataShare::DataSharePredicates predicates;
switch (context->analysisType) {
case ANALYSIS_INVALID: {
GetAnalysisProgress(context);
break;
}
case ANALYSIS_LABEL: {
GetLabelAnalysisProgress(context);
break;
}
case ANALYSIS_FACE: {
GetFaceAnalysisProgress(context);
break;
}
case ANALYSIS_HIGHLIGHT: {
GetHighlightAnalysisProgress(context);
break;
}
default:
break;
}
}
static void JSGetDataAnalysisProgressCompleteCallback(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSGetDataAnalysisProgressCompleteCallback");
auto *context = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->data), JS_INNER_FAIL);
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), JS_INNER_FAIL);
if (context->error == ERR_DEFAULT) {
CHECK_ARGS_RET_VOID(env, napi_create_string_utf8(env, context->analysisProgress.c_str(),
NAPI_AUTO_LENGTH, &jsContext->data), JS_INNER_FAIL);
jsContext->status = true;
} else {
context->HandleError(env, jsContext->error);
}
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef, context->work,
*jsContext);
}
delete context;
}
napi_status ParseArgsDataAnalysisProgress(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context, int32_t &value)
{
constexpr size_t minArgs = ARGS_ZERO;
constexpr size_t maxArgs = ARGS_ONE;
CHECK_STATUS_RET(MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs),
"Failed to get object info");
if (context->argc == maxArgs) {
CHECK_STATUS_RET(MediaLibraryNapiUtils::GetInt32(env, context->argv[ARGS_ZERO], value),
"Failed to get number argument");
}
return napi_ok;
}
napi_value MediaLibraryNapi::PhotoAccessHelperGetDataAnalysisProgress(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessHelperGetDataAnalysisProgress");
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
napi_value result = nullptr;
NAPI_CALL(env, napi_get_undefined(env, &result));
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
CHECK_NULL_PTR_RETURN_UNDEFINED(env, asyncContext, result, "asyncContext context is null");
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
CHECK_ARGS(env, ParseArgsDataAnalysisProgress(env, info, asyncContext, asyncContext->analysisType),
JS_ERR_PARAMETER_INVALID);
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "PhotoAccessHelperGetDataAnalysisProgress",
[](napi_env env, void *data) {
auto context = static_cast<MediaLibraryAsyncContext*>(data);
JSGetAnalysisProgressExecute(context);
}, reinterpret_cast<CompleteCallback>(JSGetDataAnalysisProgressCompleteCallback));
}
static void JSGetAnalysisDataCompleteCallback(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSGetAnalysisDataCompleteCallback");
auto *context = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->data), JS_INNER_FAIL);
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), JS_INNER_FAIL);
if (context->error == ERR_DEFAULT) {
napi_value returnArray;
napi_create_array(env, &returnArray);
for (size_t i = 0; i < context->analysisDatas.size(); ++i) {
napi_value element;
napi_create_string_utf8(env, context->analysisDatas[i].c_str(), NAPI_AUTO_LENGTH, &element);
napi_set_element(env, returnArray, i, element);
}
jsContext->data = returnArray;
jsContext->status = true;
} else {
context->HandleError(env, jsContext->error);
}
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
static bool CheckNapiCallerPermission(const std::string &permission)
{
MediaLibraryTracer tracer;
tracer.Start("CheckNapiCallerPermission");
AccessTokenID tokenCaller = IPCSkeleton::GetSelfTokenID();
int res = AccessTokenKit::VerifyAccessToken(tokenCaller, permission);
if (res != PermissionState::PERMISSION_GRANTED) {
NAPI_ERR_LOG("Have no media permission: %{public}s", permission.c_str());
return false;
}
return true;
}
static bool CheckGetAnalysisDataPermission(MediaLibraryAsyncContext *context)
{
if (context->analysisType == ANALYSIS_DETAIL_ADDRESS) {
const std::string PERMISSION_NAME_MEDIA_LOCATION = "ohos.permission.MEDIA_LOCATION";
auto err = CheckNapiCallerPermission(PERMISSION_NAME_MEDIA_LOCATION);
if (!err) {
context->SaveError(E_PERMISSION_DENIED);
return false;
}
}
return true;
}
static void JSGetAnalysisDataExecute(napi_env env, MediaLibraryAsyncContext *context)
{
MediaLibraryTracer tracer;
tracer.Start("JSGetAnalysisDataExecute");
CHECK_AND_RETURN(CheckGetAnalysisDataPermission(context));
std::string analysisUri;
if (context->isForce) {
analysisUri = CONST_PAH_QUERY_ANA_ADDRESS_ASSETS_ACTIVE;
} else {
analysisUri = CONST_PAH_QUERY_ANA_ADDRESS_ASSETS;
}
Uri uri(analysisUri);
DataSharePredicates predicates;
vector<string> columns;
if (context->analysisType == ANALYSIS_DETAIL_ADDRESS) {
columns = { PhotoColumn::PHOTOS_TABLE + "." + MediaColumn::MEDIA_ID, PhotoColumn::PHOTOS_TABLE + "." + LATITUDE,
PhotoColumn::PHOTOS_TABLE + "." + LONGITUDE, LANGUAGE, COUNTRY, ADMIN_AREA, SUB_ADMIN_AREA, LOCALITY,
SUB_LOCALITY, THOROUGHFARE, SUB_THOROUGHFARE, FEATURE_NAME, CITY_NAME, ADDRESS_DESCRIPTION, LOCATION_TYPE,
AOI, POI, FIRST_AOI, FIRST_POI, LOCATION_VERSION, FIRST_AOI_CATEGORY, FIRST_POI_CATEGORY};
string language = Global::I18n::LocaleConfig::GetSystemLanguage();
language = (language.find(LANGUAGE_ZH) == 0 || language.find(LANGUAGE_ZH_TR) == 0) ? LANGUAGE_ZH : LANGUAGE_EN;
vector<string> onClause = { PhotoColumn::PHOTOS_TABLE + "." + PhotoColumn::MEDIA_ID + " = " +
GEO_KNOWLEDGE_TABLE + "." + FILE_ID + " AND " +
GEO_KNOWLEDGE_TABLE + "." + LANGUAGE + " = \'" + language + "\'" };
predicates.LeftOuterJoin(GEO_KNOWLEDGE_TABLE)->On(onClause);
predicates.In(PhotoColumn::PHOTOS_TABLE + "." + MediaColumn::MEDIA_ID, context->uris);
} else {
predicates.In(MediaColumn::MEDIA_ID, context->uris);
}
int errCode = 0;
shared_ptr<DataShare::DataShareResultSet> resultSet = UserFileClient::Query(uri, predicates, columns, errCode,
context->userId);
if (resultSet == nullptr) {
NAPI_ERR_LOG("Query geo assets list failed");
return;
}
while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
nlohmann::json jsonObject;
for (uint32_t i = 0; i < columns.size(); i++) {
string columnName = columns[i];
auto it = NEED_COMPATIBLE_COLUMN_MAP.find(context->analysisType);
if (it != NEED_COMPATIBLE_COLUMN_MAP.end() && columnName == it->second) {
jsonObject[columnName] = MediaLibraryNapiUtils::ParseColumnNeedCompatible(resultSet,
context->analysisType, columnName);
} else {
jsonObject[columnName] = MediaLibraryNapiUtils::GetStringValueByColumn(resultSet, columnName);
}
}
context->analysisDatas.push_back(jsonObject.dump(-1, ' ', false, nlohmann::json::error_handler_t::replace));
}
}
static napi_value GetAssetsIdArray(napi_env env, napi_value arg, vector<string> &assetsArray)
{
bool isArray = false;
CHECK_ARGS(env, napi_is_array(env, arg, &isArray), JS_INNER_FAIL);
if (!isArray) {
NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "Failed to check array type");
return nullptr;
}
uint32_t len = 0;
CHECK_ARGS(env, napi_get_array_length(env, arg, &len), JS_INNER_FAIL);
if (len <= 0) {
NAPI_ERR_LOG("Failed to check array length: %{public}u", len);
NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "Failed to check array length");
return nullptr;
}
for (uint32_t i = 0; i < len; i++) {
napi_value asset = nullptr;
CHECK_ARGS(env, napi_get_element(env, arg, i, &asset), JS_INNER_FAIL);
if (asset == nullptr) {
NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "Failed to get asset element");
return nullptr;
}
FileAssetNapi *obj = nullptr;
CHECK_ARGS(env, napi_unwrap(env, asset, reinterpret_cast<void **>(&obj)), JS_INNER_FAIL);
if (obj == nullptr) {
NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "Failed to get asset napi object");
return nullptr;
}
if ((obj->GetMediaType() != MEDIA_TYPE_IMAGE && obj->GetMediaType() != MEDIA_TYPE_VIDEO)) {
NAPI_INFO_LOG("Skip invalid asset, mediaType: %{public}d", obj->GetMediaType());
continue;
}
assetsArray.push_back(to_string(obj->GetFileId()));
}
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
static napi_value ParseActiveAnalysisTypes(napi_env env, napi_value arg, MediaLibraryAsyncContext &context)
{
napi_value typesValue =
MediaLibraryNapiUtils::GetPropertyValueByName(env, arg, ACTIVE_ANALYSIS_CONFIG_TYPES.c_str());
CHECK_COND_WITH_ERR_MESSAGE(env, typesValue != nullptr, JS_E_PARAM_INVALID, "types invalid");
CHECK_ARGS(env, MediaLibraryNapiUtils::GetInt32Array(env, typesValue, context.activeAnalysisTypes),
JS_ERR_PARAMETER_INVALID);
CHECK_COND_WITH_ERR_MESSAGE(env, !context.activeAnalysisTypes.empty(), JS_E_PARAM_INVALID, "types invalid");
CHECK_COND_WITH_ERR_MESSAGE(env, context.activeAnalysisTypes.size() <= MAX_ACTIVE_ANALYSIS_TYPE_COUNT,
JS_E_PARAM_INVALID, "types invalid");
for (const auto &analysisType : context.activeAnalysisTypes) {
CHECK_COND_WITH_ERR_MESSAGE(env,
analysisType > AnalysisType::ANALYSIS_TYPE_START && analysisType < AnalysisType::ANALYSIS_TYPE_END,
JS_E_PARAM_INVALID, "types invalid");
}
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
static napi_value ParseActiveAnalysisUris(napi_env env, napi_value arg, MediaLibraryAsyncContext &context)
{
napi_value urisValue =
MediaLibraryNapiUtils::GetPropertyValueByName(env, arg, ACTIVE_ANALYSIS_CONFIG_URIS.c_str());
CHECK_COND_WITH_ERR_MESSAGE(env, urisValue != nullptr, JS_E_PARAM_INVALID, "uris invalid");
std::vector<std::string> uris;
CHECK_ARGS(env, MediaLibraryNapiUtils::GetStringArray(env, urisValue, uris), JS_ERR_PARAMETER_INVALID);
CHECK_COND_WITH_ERR_MESSAGE(env, uris.size() <= MAX_ACTIVE_ANALYSIS_URI_COUNT, JS_E_PARAM_INVALID,
"uris invalid");
context.uris = uris;
context.activeAnalysisFileIds.clear();
context.activeAnalysisFileIds.reserve(uris.size());
for (const auto &uri : uris) {
std::string fileId = MediaLibraryNapiUtils::GetFileIdFromUriString(uri);
CHECK_COND_WITH_ERR_MESSAGE(env, !fileId.empty(), JS_E_PARAM_INVALID,
"Failed to check uri format, not a photo uri!");
context.activeAnalysisFileIds.push_back(fileId);
}
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
static napi_value ParseActiveAnalysisExtraInfos(napi_env env, napi_value arg, MediaLibraryAsyncContext &context)
{
if (!MediaLibraryNapiUtils::IsExistsByPropertyName(env, arg, ACTIVE_ANALYSIS_CONFIG_EXTRA_INFOS.c_str())) {
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
napi_value extraInfosValue =
MediaLibraryNapiUtils::GetPropertyValueByName(env, arg, ACTIVE_ANALYSIS_CONFIG_EXTRA_INFOS.c_str());
CHECK_COND_WITH_ERR_MESSAGE(env, extraInfosValue != nullptr, JS_E_PARAM_INVALID, "extraInfos invalid");
CHECK_ARGS(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, extraInfosValue, context.activeAnalysisParam),
JS_ERR_PARAMETER_INVALID);
CHECK_COND_WITH_ERR_MESSAGE(env, !context.activeAnalysisParam.empty(), JS_E_PARAM_INVALID,
"extraInfos invalid");
CHECK_COND_WITH_ERR_MESSAGE(env,
context.activeAnalysisParam.size() <= MAX_ACTIVE_ANALYSIS_EXTRA_INFOS_LENGTH,
JS_E_PARAM_INVALID, "extraInfos invalid");
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
static napi_value ParseActiveAnalysisConfig(napi_env env, napi_value arg, MediaLibraryAsyncContext &context)
{
napi_valuetype valueType = napi_undefined;
CHECK_ARGS(env, napi_typeof(env, arg, &valueType), JS_ERR_PARAMETER_INVALID);
CHECK_COND_WITH_ERR_MESSAGE(env, valueType == napi_object, JS_E_PARAM_INVALID, "config invalid");
CHECK_NULLPTR_RET(ParseActiveAnalysisTypes(env, arg, context));
CHECK_NULLPTR_RET(ParseActiveAnalysisUris(env, arg, context));
CHECK_NULLPTR_RET(ParseActiveAnalysisExtraInfos(env, arg, context));
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
static napi_value ParseActiveAnalysisCommonArgs(
napi_env env, napi_callback_info info, unique_ptr<MediaLibraryAsyncContext> &context,
size_t minArgs, size_t maxArgs)
{
napi_value thisVar = nullptr;
context->argc = maxArgs;
CHECK_COND_WITH_ERR_MESSAGE(env, napi_get_cb_info(env, info, &context->argc, &(context->argv[ARGS_ZERO]),
&thisVar, nullptr) == napi_ok, JS_E_PARAM_INVALID, "Failed to get cb info");
CHECK_COND_WITH_ERR_MESSAGE(env, context->argc >= minArgs && context->argc <= maxArgs, JS_E_PARAM_INVALID,
"Number of args is invalid");
CHECK_COND_WITH_ERR_MESSAGE(env,
napi_unwrap(env, thisVar, reinterpret_cast<void **>(&context->objectInfo)) == napi_ok,
JS_E_PARAM_INVALID, "Failed to unwrap thisVar");
CHECK_COND_WITH_ERR_MESSAGE(env, context->objectInfo != nullptr, JS_E_PARAM_INVALID,
"Failed to get object info");
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
static napi_value ParseArgsStartActiveAnalysis(
napi_env env, napi_callback_info info, unique_ptr<MediaLibraryAsyncContext> &context)
{
constexpr size_t minArgs = ARGS_TWO;
constexpr size_t maxArgs = ARGS_TWO;
CHECK_NULLPTR_RET(ParseActiveAnalysisCommonArgs(env, info, context, minArgs, maxArgs));
CHECK_NULLPTR_RET(ParseActiveAnalysisConfig(env, context->argv[ARGS_ZERO], *context));
CHECK_COND_WITH_ERR_MESSAGE(env, MediaLibraryNapiUtils::CheckJSArgsTypeAsFunc(env, context->argv[ARGS_ONE]),
JS_E_PARAM_INVALID, "callback invalid");
CHECK_COND_WITH_ERR_MESSAGE(env,
ActiveAnalysisJsCallbackHolder::Create(env, context->argv[ARGS_ONE], context->activeAnalysisCallbackHolder)
== napi_ok, JS_E_INNER_FAIL, "Failed to create active analysis callback");
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
static napi_value ParseArgsStopActiveAnalysis(
napi_env env, napi_callback_info info, unique_ptr<MediaLibraryAsyncContext> &context)
{
constexpr size_t minArgs = ARGS_ONE;
constexpr size_t maxArgs = ARGS_ONE;
CHECK_NULLPTR_RET(ParseActiveAnalysisCommonArgs(env, info, context, minArgs, maxArgs));
CHECK_COND_WITH_ERR_MESSAGE(env, CheckNapiCallerPermission(PERM_WRITE_IMAGEVIDEO),
OHOS_PERMISSION_DENIED_CODE, "Have no write imagevideo permission");
CHECK_NULLPTR_RET(ParseActiveAnalysisConfig(env, context->argv[ARGS_ZERO], *context));
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
static napi_value ParseArgsStartAssetAnalysis(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
constexpr size_t minArgs = ARGS_ONE;
constexpr size_t maxArgs = ARGS_TWO;
CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs,
maxArgs) == napi_ok, "Failed to get object info");
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::GetInt32(env, context->argv[ARGS_ZERO],
context->analysisType) == napi_ok, "analysisType invalid");
CHECK_COND_WITH_MESSAGE(env, context->analysisType > AnalysisType::ANALYSIS_INVALID,
"analysisType invalid:" + std::to_string(context->analysisType));
const std::map<int32_t, std::string> FOREGROUND_ANALYSIS_ASSETS_MAP = {
{ ANALYSIS_SEARCH_INDEX, CONST_PAH_QUERY_ANA_FOREGROUND }
};
CHECK_COND_WITH_MESSAGE(env,
FOREGROUND_ANALYSIS_ASSETS_MAP.find(context->analysisType) != FOREGROUND_ANALYSIS_ASSETS_MAP.end(),
"analysisType is not supported:" + std::to_string(context->analysisType));
if (context->argc == ARGS_TWO) {
vector<string> uris;
CHECK_ARGS(env, MediaLibraryNapiUtils::GetStringArray(env, context->argv[ARGS_ONE], uris),
OHOS_INVALID_PARAM_CODE);
for (const auto &uri : uris) {
if (uri.find(PhotoColumn::PHOTO_URI_PREFIX) == string::npos) {
NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "Failed to check uri format, not a photo uri!");
return nullptr;
}
}
if (!uris.empty()) {
context->uris = uris;
}
} else if (context->argc == ARGS_ONE) {
context->isFullAnalysis = true;
}
if (context->objectInfo) {
context->userId = context->objectInfo->GetUserId();
} else {
NAPI_ERR_LOG("objectInfo is nullptr.");
}
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
static void JSStartAssetAnalysisCallback(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSStartAssetAnalysisCallback");
auto *context = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->data), JS_INNER_FAIL);
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), JS_INNER_FAIL);
if (context->error == ERR_DEFAULT) {
CHECK_ARGS_RET_VOID(env, napi_create_int32(env, context->taskId, &jsContext->data), JS_INNER_FAIL);
jsContext->status = true;
} else {
CHECK_ARGS_RET_VOID(env, napi_create_int32(env, -1, &jsContext->data), JS_INNER_FAIL);
context->HandleError(env, jsContext->error);
}
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
static napi_value ParseArgsAnalysisData(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
constexpr size_t minArgs = ARGS_ONE;
constexpr size_t maxArgs = ARGS_THREE;
NAPI_ASSERT(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs) ==
napi_ok, "Failed to get object info");
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
CHECK_ARGS(env, MediaLibraryNapiUtils::GetInt32(env, context->argv[ARGS_ZERO], context->analysisType),
JS_ERR_PARAMETER_INVALID);
vector<string> uris;
CHECK_NULLPTR_RET(GetAssetsIdArray(env, context->argv[ARGS_ONE], uris));
if (uris.empty()) {
NAPI_ERR_LOG("Geo assets list empty");
return nullptr;
}
context->uris = uris;
CHECK_ARGS(env, MediaLibraryNapiUtils::GetParamBool(env, context->argv[ARGS_TWO], context->isForce),
JS_ERR_PARAMETER_INVALID);
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
napi_value MediaLibraryNapi::PhotoAccessHelperGetAnalysisData(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessHelperGetAnalysisData");
napi_value result = nullptr;
NAPI_CALL(env, napi_get_undefined(env, &result));
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
CHECK_NULL_PTR_RETURN_UNDEFINED(env, asyncContext, result, "asyncContext context is null");
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
CHECK_NULLPTR_RET(ParseArgsAnalysisData(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "PhotoAccessHelperGetAnalysisData",
[](napi_env env, void *data) {
auto context = static_cast<MediaLibraryAsyncContext*>(data);
JSGetAnalysisDataExecute(env, context);
}, reinterpret_cast<CompleteCallback>(JSGetAnalysisDataCompleteCallback));
}
napi_value MediaLibraryNapi::JSGetPhotoAssets(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("JSGetPhotoAssets");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->assetType = TYPE_PHOTO;
CHECK_NULLPTR_RET(ParseArgsGetAssets(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSGetPhotoAssets",
JSGetAssetsExecute, GetFileAssetsAsyncCallbackComplete);
}
static bool EasterEgg(MediaLibraryAsyncContext* context)
{
if (context->uri == CONST_URI_FIND_ALL_DUPLICATE_ASSETS) {
context->businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::FIND_ALL_DUPLICATE_ASSETS);
} else if (context->uri == CONST_URI_FIND_ALL_DUPLICATE_ASSETS_TO_DELETE) {
context->businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::FIND_DUPLICATE_ASSETS_TO_DELETE);
} else {
return false;
}
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NAPI_ERR_LOG("Easter egg operation failed, target is not system app");
return false;
};
bool isQueryCount = find(context->fetchColumn.begin(), context->fetchColumn.end(), CONST_MEDIA_COLUMN_COUNT)
!= context->fetchColumn.end();
int64_t startTime = MediaFileUtils::UTCTimeMilliSeconds();
NAPI_INFO_LOG(
"Easter egg operation start: %{public}s, is query count: %{public}d",
context->businessCode == static_cast<uint32_t>(MediaLibraryBusinessCode::FIND_ALL_DUPLICATE_ASSETS) ?
"find all duplicate assets" : "find all duplicate assets to delete", isQueryCount);
GetAssetsReqBody reqBody;
reqBody.predicates = context->predicates;
reqBody.columns = context->fetchColumn;
GetAssetsRespBody respBody;
int32_t errCode =
IPC::UserDefineIPCClient().SetUserId(context->userId).Call(context->businessCode, reqBody, respBody);
if (errCode != E_OK || respBody.resultSet == nullptr) {
context->SaveError(errCode);
NAPI_ERR_LOG("Easter egg operation failed, errCode: %{public}d", errCode);
return true;
}
context->fetchFileResult = make_unique<FetchResult<FileAsset>>(move(respBody.resultSet));
context->fetchFileResult->SetResultNapiType(ResultNapiType::TYPE_PHOTOACCESS_HELPER);
NAPI_INFO_LOG(
"Easter egg operation end: %{public}s, is query count: %{public}d, cost time: %{public}" PRId64 "ms",
context->businessCode == static_cast<uint32_t>(MediaLibraryBusinessCode::FIND_ALL_DUPLICATE_ASSETS) ?
"find all duplicate assets" : "find all duplicate assets to delete", isQueryCount,
MediaFileUtils::UTCTimeMilliSeconds() - startTime);
return true;
}
static int GetAssetsIPCCall(MediaLibraryAsyncContext* context, GetAssetsRespBody &respBody)
{
GetAssetsReqBody reqBody;
reqBody.predicates = context->predicates;
reqBody.columns = context->fetchColumn;
reqBody.burstKey = context->burstKey;
auto client = IPC::UserDefineIPCClient().SetUserId(context->userId);
string fileId;
if (MediaLibraryNapiUtils::ParseFileIdFromPredicates(reqBody.predicates, fileId)) {
unordered_map<string, string> headerMap{{MediaColumn::MEDIA_ID, fileId}, {URI_TYPE, TYPE_PHOTOS}};
client.SetHeader(headerMap);
}
NAPI_INFO_LOG("@test GetAssetsIPCCall, fileId: %{public}s", fileId.c_str());
return client.Call(context->businessCode, reqBody, respBody);
}
static void PhotoAccessGetAssetsExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessGetAssetsExecute");
auto *context = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_IF_EQUAL(context != nullptr, "context is nullptr");
if (EasterEgg(context)) {
return;
}
string queryUri = CONST_PAH_QUERY_PHOTO;
MediaLibraryNapiUtils::UriAppendKeyValue(queryUri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
Uri uri(queryUri);
int errCode = 0;
auto [accessSandbox, resultSet] = UserFileClient::QueryAccessibleViaSandBox(
uri, context->predicates, context->fetchColumn, errCode, context->userId);
if (accessSandbox) {
if (resultSet == nullptr) {
NAPI_ERR_LOG("QueryAccessibleViaSandBox failed, resultSet is nullptr");
}
} else {
GetAssetsRespBody respBody;
errCode = GetAssetsIPCCall(context, respBody);
if (errCode == E_OK) {
resultSet = respBody.resultSet;
} else if (respBody.resultSet == nullptr && !context->uri.empty() && errCode == E_PERMISSION_DENIED) {
Uri queryWithUri(context->uri);
resultSet = UserFileClient::Query(queryWithUri, context->predicates, context->fetchColumn, errCode,
context->userId);
} else {
NAPI_ERR_LOG("UserDefineIPCClient Call failed, errCode is %{public}d", errCode);
}
}
if (resultSet == nullptr) {
NAPI_ERR_LOG("resultSet is nullptr, errCode is %{public}d", errCode);
context->SaveError(errCode);
return;
}
tracer.Start("MakeFetchResult");
context->fetchFileResult = make_unique<FetchResult<FileAsset>>(move(resultSet));
context->fetchFileResult->uriMap = context->uriMap;
tracer.Finish();
context->fetchFileResult->SetResultNapiType(ResultNapiType::TYPE_PHOTOACCESS_HELPER);
context->fetchFileResult->SetUserId(context->userId);
}
static napi_value PhotoAccessGetAssetsExecuteSync(napi_env env, MediaLibraryAsyncContext& asyncContext)
{
auto context = &asyncContext;
if (context->assetType != TYPE_PHOTO) {
return nullptr;
}
string queryUri = CONST_PAH_QUERY_PHOTO;
MediaLibraryNapiUtils::UriAppendKeyValue(queryUri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
Uri uri(queryUri);
int errCode = 0;
RdbPredicates rdbPredicates = RdbDataShareAdapter::RdbUtils::ToPredicates(context->predicates, "Photos");
NAPI_INFO_LOG("Predicates: %{public}s", rdbPredicates.ToString().c_str());
shared_ptr<DataShare::DataShareResultSet> resultSet = UserFileClient::Query(uri,
context->predicates, context->fetchColumn, errCode, context->userId);
if (resultSet == nullptr && !context->uri.empty() && errCode == E_PERMISSION_DENIED) {
Uri queryWithUri(context->uri);
resultSet = UserFileClient::Query(queryWithUri, context->predicates, context->fetchColumn, errCode);
}
CHECK_NULLPTR_RET(resultSet);
auto fetchResult = make_unique<FetchResult<FileAsset>>(move(resultSet));
fetchResult->SetResultNapiType(ResultNapiType::TYPE_PHOTOACCESS_HELPER);
fetchResult->SetUserId(context->userId);
CHECK_NULLPTR_RET(fetchResult);
std::vector<std::unique_ptr<FileAsset>> fileAssetArray;
auto file = fetchResult->GetFirstObject();
while (file != nullptr) {
fileAssetArray.push_back(move(file));
file = fetchResult->GetNextObject();
}
size_t len = fileAssetArray.size();
napi_value jsFileArray = nullptr;
napi_create_array_with_length(env, len, &jsFileArray);
size_t i = 0;
for (i = 0; i < len; i++) {
fileAssetArray[i]->SetUserId(context->userId);
napi_value jsFileAsset = FileAssetNapi::CreateFileAsset(env, fileAssetArray[i]);
if ((jsFileAsset == nullptr) || (napi_set_element(env, jsFileArray, i, jsFileAsset) != napi_ok)) {
NAPI_ERR_LOG("Failed to get file asset napi object");
break;
}
}
return (i == len) ? jsFileArray : nullptr;
}
napi_value MediaLibraryNapi::PhotoAccessGetPhotoAssets(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessGetPhotoAssets");
NAPI_DEBUG_LOG("MediaLibraryNapi::PhotoAccessGetPhotoAssets start");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->assetType = TYPE_PHOTO;
CHECK_NULLPTR_RET(ParseArgsGetAssets(env, info, asyncContext));
asyncContext->businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::PAH_GET_ASSETS);
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSGetPhotoAssets",
PhotoAccessGetAssetsExecute, GetFileAssetsAsyncCallbackComplete);
}
napi_value MediaLibraryNapi::PhotoAccessGetBurstAssets(napi_env env, napi_callback_info info)
{
NAPI_DEBUG_LOG("MediaLibraryNapi::PhotoAccessGetBurstAssets start");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->assetType = TYPE_PHOTO;
CHECK_NULLPTR_RET(ParseArgsGetBurstAssets(env, info, asyncContext));
asyncContext->businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::GET_BURST_ASSETS);
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSGetPhotoAssets",
PhotoAccessGetAssetsExecute, GetFileAssetsAsyncCallbackComplete);
}
static napi_value PhotoAccessGetFileAssetsExecuteSync(napi_env env, MediaLibraryAsyncContext& asyncContext)
{
auto context = &asyncContext;
if (context->assetType != TYPE_PHOTO) {
return nullptr;
}
string queryUri = CONST_PAH_QUERY_PHOTO;
MediaLibraryNapiUtils::UriAppendKeyValue(queryUri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
Uri uri(queryUri);
shared_ptr<NativeRdb::ResultSet> resultSet = UserFileClient::QueryRdb(uri,
context->predicates, context->fetchColumn);
CHECK_NULLPTR_RET(resultSet);
napi_value jsFileArray = 0;
napi_create_array(env, &jsFileArray);
int count = 0;
while (!resultSet->GoToNextRow()) {
napi_value item = MediaLibraryNapiUtils::GetNextRowObject(env, resultSet);
napi_set_element(env, jsFileArray, count++, item);
}
return jsFileArray;
}
napi_value MediaLibraryNapi::PhotoAccessGetFileAssetsInfo(napi_env env, napi_callback_info info)
{
unique_ptr<MediaLibraryAsyncContext> context = make_unique<MediaLibraryAsyncContext>();
context->assetType = TYPE_PHOTO;
CHECK_NULLPTR_RET(ParseArgsGetAssets(env, info, context));
return PhotoAccessGetFileAssetsExecuteSync(env, *context);
}
napi_value MediaLibraryNapi::PhotoAccessGetPhotoAssetsSync(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessGetPhotoAssetsSync");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->assetType = TYPE_PHOTO;
CHECK_NULLPTR_RET(ParseArgsGetAssets(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return PhotoAccessGetAssetsExecuteSync(env, *asyncContext);
}
napi_value MediaLibraryNapi::JSGetAudioAssets(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("JSGetAudioAssets");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->assetType = TYPE_AUDIO;
CHECK_NULLPTR_RET(ParseArgsGetAssets(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSGetAudioAssets",
JSGetAssetsExecute, GetFileAssetsAsyncCallbackComplete);
}
static napi_value GetPhotoAlbumMap(napi_env env, std::unordered_map<int32_t, unique_ptr<PhotoAlbum>> fileResult)
{
napi_status status;
napi_value mapNapiValue {nullptr};
status = napi_create_map(env, &mapNapiValue);
CHECK_COND_RET(status == napi_ok && mapNapiValue != nullptr, nullptr,
"Failed to create map napi value, napi status: %{public}d", static_cast<int>(status));
NAPI_INFO_LOG("PhotoAlbumMap size: %{public}d", static_cast<int32_t>(fileResult.size()));
for (auto &iter : fileResult) {
napi_value albumId {nullptr};
status = napi_create_int32(env, iter.first, &albumId);
CHECK_COND_RET(status == napi_ok && albumId != nullptr, nullptr,
"Failed to create album id, napi status: %{public}d", static_cast<int>(status));
napi_value albumPhoto = PhotoAlbumNapi::CreatePhotoAlbumNapi(env, iter.second);
status = napi_map_set_property(env, mapNapiValue, albumId, albumPhoto);
CHECK_COND_RET(status == napi_ok, nullptr, "Failed to set albumMap, napi status: %{public}d",
static_cast<int>(status));
}
return mapNapiValue;
}
static void GetPhotoAlbumQueryResult(napi_env env, MediaLibraryAsyncContext *context,
unique_ptr<JSAsyncContextOutput> &jsContext)
{
napi_value fileResult;
if (context->albumIds.empty()) {
fileResult = FetchFileResultNapi::CreateFetchFileResult(env, move(context->fetchPhotoAlbumResult));
} else {
fileResult = GetPhotoAlbumMap(env, move(context->albumMap));
}
if (fileResult == nullptr) {
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->data), JS_INNER_FAIL);
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, ERR_INVALID_OUTPUT,
"Failed to create js object for Fetch Album Result");
return;
}
jsContext->data = fileResult;
jsContext->status = true;
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), JS_INNER_FAIL);
}
static void SetPhotoAlbum(PhotoAlbum* photoAlbumData, shared_ptr<DataShareResultSet> &resultSet)
{
int32_t albumId = get<int32_t>(ResultSetUtils::GetValFromColumn(PhotoAlbumColumns::ALBUM_ID, resultSet,
TYPE_INT32));
photoAlbumData->SetAlbumId(albumId);
photoAlbumData->SetPhotoAlbumType(static_cast<PhotoAlbumType>(
get<int32_t>(ResultSetUtils::GetValFromColumn(PhotoAlbumColumns::ALBUM_TYPE, resultSet, TYPE_INT32))));
photoAlbumData->SetPhotoAlbumSubType(static_cast<PhotoAlbumSubType>(
get<int32_t>(ResultSetUtils::GetValFromColumn(PhotoAlbumColumns::ALBUM_SUBTYPE, resultSet, TYPE_INT32))));
photoAlbumData->SetLPath(get<string>(ResultSetUtils::GetValFromColumn(PhotoAlbumColumns::ALBUM_LPATH, resultSet,
TYPE_STRING)));
photoAlbumData->SetAlbumName(get<string>(ResultSetUtils::GetValFromColumn(PhotoAlbumColumns::ALBUM_NAME,
resultSet, TYPE_STRING)));
photoAlbumData->SetDateAdded(get<int64_t>(ResultSetUtils::GetValFromColumn(
PhotoAlbumColumns::ALBUM_DATE_ADDED, resultSet, TYPE_INT64)));
photoAlbumData->SetDateModified(get<int64_t>(ResultSetUtils::GetValFromColumn(
PhotoAlbumColumns::ALBUM_DATE_MODIFIED, resultSet, TYPE_INT64)));
photoAlbumData->SetResultNapiType(ResultNapiType::TYPE_PHOTOACCESS_HELPER);
string countColumn = PhotoAlbumColumns::ALBUM_COUNT;
string coverColumn = PhotoAlbumColumns::ALBUM_COVER_URI;
string albumUriPrefix = PhotoAlbumColumns::ALBUM_URI_PREFIX;
string coverUriSource = PhotoAlbumColumns::COVER_URI_SOURCE;
string changeTime = PhotoAlbumColumns::CHANGE_TIME;
photoAlbumData->SetAlbumUri(albumUriPrefix + to_string(albumId));
photoAlbumData->SetCount(get<int32_t>(ResultSetUtils::GetValFromColumn(countColumn, resultSet, TYPE_INT32)));
photoAlbumData->SetCoverUri(get<string>(ResultSetUtils::GetValFromColumn(coverColumn, resultSet, TYPE_STRING)));
photoAlbumData->SetCoverUriSource(get<int32_t>(ResultSetUtils::GetValFromColumn(coverUriSource, resultSet, TYPE_INT32)));
photoAlbumData->SetUploadStatus(get<int32_t>(ResultSetUtils::GetValFromColumn(
PhotoAlbumColumns::UPLOAD_STATUS, resultSet, TYPE_INT32)));
photoAlbumData->SetChangeTime(get<int64_t>(ResultSetUtils::GetValFromColumn(changeTime, resultSet, TYPE_INT64)));
int32_t imageCount = get<int32_t>(ResultSetUtils::GetValFromColumn(PhotoAlbumColumns::ALBUM_IMAGE_COUNT,
resultSet, TYPE_INT32));
int32_t videoCount = get<int32_t>(ResultSetUtils::GetValFromColumn(PhotoAlbumColumns::ALBUM_VIDEO_COUNT,
resultSet, TYPE_INT32));
photoAlbumData->SetImageCount(imageCount);
photoAlbumData->SetVideoCount(videoCount);
}
static void BuildAlbumMap(std::unordered_map<int32_t, unique_ptr<PhotoAlbum>> &albumMap,
shared_ptr<DataShareResultSet> resultSet)
{
int32_t count = 0;
auto ret = resultSet->GetRowCount(count);
if (ret != NativeRdb::E_OK) {
NAPI_ERR_LOG("get rdbstore failed");
return;
}
if (count == 0) {
NAPI_ERR_LOG("albumid not find");
return;
}
NAPI_INFO_LOG("build album map size: %{public}d", count);
while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
unique_ptr<PhotoAlbum> albumAssetPtr = make_unique<PhotoAlbum>();
SetPhotoAlbum(albumAssetPtr.get(), resultSet);
albumMap[albumAssetPtr->GetAlbumId()] = std::move(albumAssetPtr);
}
}
static void JSGetPhotoAlbumsExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSGetPhotoAlbumsExecute");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
string queryUri;
if (context->hiddenOnly || context->hiddenAlbumFetchMode == ASSETS_MODE) {
queryUri = (context->resultNapiType == ResultNapiType::TYPE_USERFILE_MGR) ?
CONST_UFM_QUERY_HIDDEN_ALBUM : CONST_PAH_QUERY_HIDDEN_ALBUM;
} else if (context->isAnalysisAlbum) {
queryUri = context->isLocationAlbum == PhotoAlbumSubType::GEOGRAPHY_LOCATION ?
CONST_PAH_QUERY_GEO_PHOTOS : CONST_PAH_QUERY_ANA_PHOTO_ALBUM;
} else {
queryUri = (context->resultNapiType == ResultNapiType::TYPE_USERFILE_MGR) ?
CONST_UFM_QUERY_PHOTO_ALBUM : CONST_PAH_QUERY_PHOTO_ALBUM;
}
Uri uri(queryUri);
int errCode = 0;
auto resultSet = UserFileClient::Query(uri, context->predicates, context->fetchColumn, errCode,
context->userId);
if (resultSet == nullptr) {
NAPI_ERR_LOG("resultSet == nullptr, errCode is %{public}d", errCode);
if (errCode == E_PERMISSION_DENIED || errCode == -E_CHECK_SYSTEMAPP_FAIL) {
context->SaveError(errCode);
} else {
context->SaveError(E_HAS_DB_ERROR);
}
return;
}
if (context->albumIds.empty()) {
context->fetchPhotoAlbumResult = make_unique<FetchResult<PhotoAlbum>>(move(resultSet));
context->fetchPhotoAlbumResult->SetResultNapiType(context->resultNapiType);
context->fetchPhotoAlbumResult->SetHiddenOnly(context->hiddenOnly);
context->fetchPhotoAlbumResult->SetLocationOnly(context->isLocationAlbum ==
PhotoAlbumSubType::GEOGRAPHY_LOCATION);
context->fetchPhotoAlbumResult->SetUserId(context->userId);
} else {
std::unordered_map<int32_t, unique_ptr<PhotoAlbum>> albumMap;
BuildAlbumMap(context->albumMap, resultSet);
}
}
static void JSGetPhotoAlbumsByIdsExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSGetPhotoAlbumsByIdsExecute");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
GetAlbumsByIdsReqBody reqBody;
GetAlbumsByIdsRespBody respBody;
shared_ptr<DataShareResultSet> resultSet;
reqBody.predicates = context->predicates;
reqBody.columns = context->fetchColumn;
uint32_t businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::PAH_QUERY_GET_ALBUMS_BY_IDS);
int errCode = IPC::UserDefineIPCClient().Call(businessCode, reqBody, respBody);
resultSet = respBody.resultSet;
if (resultSet == nullptr) {
NAPI_ERR_LOG("resultSet == nullptr, errCode is %{public}d", errCode);
if (errCode == E_PERMISSION_DENIED || errCode == -E_CHECK_SYSTEMAPP_FAIL) {
context->SaveError(errCode);
} else {
context->SaveError(E_HAS_DB_ERROR);
}
return;
}
if (context->albumIds.empty()) {
context->fetchPhotoAlbumResult = make_unique<FetchResult<PhotoAlbum>>(move(resultSet));
context->fetchPhotoAlbumResult->SetResultNapiType(context->resultNapiType);
context->fetchPhotoAlbumResult->SetHiddenOnly(context->hiddenOnly);
context->fetchPhotoAlbumResult->SetLocationOnly(context->isLocationAlbum ==
PhotoAlbumSubType::GEOGRAPHY_LOCATION);
context->fetchPhotoAlbumResult->SetUserId(context->userId);
} else {
std::unordered_map<int32_t, unique_ptr<PhotoAlbum>> albumMap;
BuildAlbumMap(context->albumMap, resultSet);
}
}
static napi_value JSGetPhotoAlbumsExecuteSync(napi_env env, MediaLibraryAsyncContext& asyncContext)
{
auto context = &asyncContext;
string queryUri;
if (context->hiddenOnly || context->hiddenAlbumFetchMode == ASSETS_MODE) {
queryUri = (context->resultNapiType == ResultNapiType::TYPE_USERFILE_MGR) ?
CONST_UFM_QUERY_HIDDEN_ALBUM : CONST_PAH_QUERY_HIDDEN_ALBUM;
} else if (context->isAnalysisAlbum) {
queryUri = context->isLocationAlbum == PhotoAlbumSubType::GEOGRAPHY_LOCATION ?
CONST_PAH_QUERY_GEO_PHOTOS : CONST_PAH_QUERY_ANA_PHOTO_ALBUM;
} else {
queryUri = (context->resultNapiType == ResultNapiType::TYPE_USERFILE_MGR) ?
CONST_UFM_QUERY_PHOTO_ALBUM : CONST_PAH_QUERY_PHOTO_ALBUM;
}
Uri uri(queryUri);
int errCode = 0;
auto resultSet = UserFileClient::Query(uri, context->predicates, context->fetchColumn, errCode,
context->userId);
CHECK_NULLPTR_RET(resultSet);
auto fetchPhotoAlbumResult = make_unique<FetchResult<PhotoAlbum>>(move(resultSet));
fetchPhotoAlbumResult->SetResultNapiType(context->resultNapiType);
fetchPhotoAlbumResult->SetHiddenOnly(context->hiddenOnly);
fetchPhotoAlbumResult->SetLocationOnly(context->isLocationAlbum == PhotoAlbumSubType::GEOGRAPHY_LOCATION);
fetchPhotoAlbumResult->SetUserId(context->userId);
context->photoAlbumData->SetUserId(context->userId);
if (fetchPhotoAlbumResult->GetCount() <= 0) {
return nullptr;
}
auto photoAlbum = fetchPhotoAlbumResult->GetFirstObject();
CHECK_NULLPTR_RET(photoAlbum);
return PhotoAlbumNapi::CreatePhotoAlbumNapi(env, photoAlbum);
}
static void JSGetPhotoAlbumsCompleteCallback(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSGetPhotoAlbumsCompleteCallback");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), JS_INNER_FAIL);
if (context->error == ERR_DEFAULT && context->albumMap.empty() && context->albumIds.size() > 0) {
napi_status status;
napi_value mapNapiValue {nullptr};
status = napi_create_map(env, &mapNapiValue);
if (status != napi_ok || mapNapiValue == nullptr) {
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->data), JS_INNER_FAIL);
context->HandleError(env, jsContext->error);
} else {
jsContext->data = mapNapiValue;
jsContext->status = true;
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), JS_INNER_FAIL);
}
} else if (context->error != ERR_DEFAULT ||
(context->fetchPhotoAlbumResult == nullptr && context->albumMap.empty())) {
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->data), JS_INNER_FAIL);
context->HandleError(env, jsContext->error);
} else {
GetPhotoAlbumQueryResult(env, context, jsContext);
}
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
napi_value MediaLibraryNapi::JSGetPhotoAlbums(napi_env env, napi_callback_info info)
{
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
CHECK_ARGS(env, MediaLibraryNapiUtils::ParseAlbumFetchOptCallback(env, info, asyncContext),
JS_ERR_PARAMETER_INVALID);
asyncContext->resultNapiType = ResultNapiType::TYPE_USERFILE_MGR;
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSGetPhotoAlbums", JSGetPhotoAlbumsExecute,
JSGetPhotoAlbumsCompleteCallback);
}
napi_value MediaLibraryNapi::UserFileMgrCreatePhotoAsset(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("UserFileMgrCreatePhotoAsset");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_USERFILE_MGR;
asyncContext->assetType = TYPE_PHOTO;
NAPI_ASSERT(env, ParseArgsCreatePhotoAsset(env, info, asyncContext), "Failed to parse js args");
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "UserFileMgrCreatePhotoAsset",
JSCreateAssetExecute, JSCreateAssetCompleteCallback);
}
napi_value MediaLibraryNapi::UserFileMgrCreateAudioAsset(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("UserFileMgrCreateAudioAsset");
napi_value ret = nullptr;
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
CHECK_NULL_PTR_RETURN_UNDEFINED(env, asyncContext, ret, "asyncContext context is null");
asyncContext->resultNapiType = ResultNapiType::TYPE_USERFILE_MGR;
asyncContext->assetType = TYPE_AUDIO;
NAPI_ASSERT(env, ParseArgsCreateAudioAsset(env, info, asyncContext), "Failed to parse js args");
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "UserFileMgrCreateAudioAsset",
JSCreateAssetExecute, JSCreateAssetCompleteCallback);
}
napi_value ParseArgsTrashAsset(napi_env env, napi_callback_info info, unique_ptr<MediaLibraryAsyncContext> &context)
{
string uri;
CHECK_ARGS(env, MediaLibraryNapiUtils::ParseArgsStringCallback(env, info, context, uri),
JS_ERR_PARAMETER_INVALID);
if (uri.empty()) {
NapiError::ThrowError(env, JS_E_URI, "Failed to check empty uri!");
return nullptr;
}
if (uri.find(PhotoColumn::PHOTO_URI_PREFIX) == string::npos &&
uri.find(AudioColumn::AUDIO_URI_PREFIX) == string::npos) {
NapiError::ThrowError(env, JS_E_URI, "Failed to check uri format, not a photo or audio uri");
return nullptr;
}
context->uri = uri;
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
napi_value MediaLibraryNapi::UserFileMgrTrashAsset(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("UserFileMgrTrashAsset");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_USERFILE_MGR;
CHECK_NULLPTR_RET(ParseArgsTrashAsset(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "UserFileMgrTrashAsset", JSTrashAssetExecute,
JSTrashAssetCompleteCallback);
}
napi_value MediaLibraryNapi::UserFileMgrGetPrivateAlbum(napi_env env, napi_callback_info info)
{
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_USERFILE_MGR;
CHECK_NULLPTR_RET(ParseArgsGetPrivateAlbum(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "UserFileMgrGetPrivateAlbum",
JSGetPhotoAlbumsExecute, JSGetPhotoAlbumsCompleteCallback);
}
napi_value MediaLibraryNapi::CreateMediaTypeEnum(napi_env env)
{
return CreateNumberEnumProperty(env, mediaTypesEnum, sMediaTypeEnumRef_);
}
napi_value MediaLibraryNapi::CreateKeyFrameThumbnailTypeEnum(napi_env env)
{
const int32_t startIdx = 1;
return CreateNumberEnumProperty(env, keyFrameThumbnailTypeEnum, sKeyFrameThumbnailTypeRef_, startIdx);
}
napi_value MediaLibraryNapi::CreateMediaTypeUserFileEnum(napi_env env)
{
const int32_t startIdx = 1;
return CreateNumberEnumProperty(env, mediaTypesUserFileEnum, sMediaTypeEnumRef_, startIdx);
}
napi_value MediaLibraryNapi::CreateDirectoryTypeEnum(napi_env env)
{
return CreateNumberEnumProperty(env, directoryEnum, sDirectoryEnumRef_);
}
napi_value MediaLibraryNapi::CreateVirtualAlbumTypeEnum(napi_env env)
{
return CreateNumberEnumProperty(env, virtualAlbumTypeEnum, sVirtualAlbumTypeEnumRef_);
}
napi_value MediaLibraryNapi::CreatePrivateAlbumTypeEnum(napi_env env)
{
return CreateNumberEnumProperty(env, privateAlbumTypeNameEnum, sPrivateAlbumEnumRef_);
}
napi_value MediaLibraryNapi::CreateHiddenPhotosDisplayModeEnum(napi_env env)
{
return CreateNumberEnumProperty(env, HIDDEN_PHOTOS_DISPLAY_MODE_ENUM, sHiddenPhotosDisplayModeEnumRef_);
}
napi_value MediaLibraryNapi::CreateFileKeyEnum(napi_env env)
{
return CreateStringEnumProperty(env, FILE_KEY_ENUM_PROPERTIES, sFileKeyEnumRef_);
}
napi_value MediaLibraryNapi::UserFileMgrCreateFileKeyEnum(napi_env env)
{
return CreateStringEnumProperty(env, USERFILEMGR_FILEKEY_ENUM_PROPERTIES, sUserFileMgrFileKeyEnumRef_);
}
napi_value MediaLibraryNapi::CreateAudioKeyEnum(napi_env env)
{
return CreateStringEnumProperty(env, AUDIOKEY_ENUM_PROPERTIES, sAudioKeyEnumRef_);
}
napi_value MediaLibraryNapi::CreateImageVideoKeyEnum(napi_env env)
{
return CreateStringEnumProperty(env, IMAGEVIDEOKEY_ENUM_PROPERTIES, sImageVideoKeyEnumRef_);
}
napi_value MediaLibraryNapi::CreatePhotoKeysEnum(napi_env env)
{
return CreateStringEnumProperty(env, IMAGEVIDEOKEY_ENUM_PROPERTIES, sPhotoKeysEnumRef_);
}
napi_value MediaLibraryNapi::CreateAlbumKeyEnum(napi_env env)
{
return CreateStringEnumProperty(env, ALBUMKEY_ENUM_PROPERTIES, sAlbumKeyEnumRef_);
}
napi_value MediaLibraryNapi::CreateImageFileTypeEnum(napi_env env)
{
const int32_t startIdx = 1;
return CreateNumberEnumProperty(env, imageFileTypeEnum, sImageFileTypeEnumEnumRef_, startIdx);
}
napi_value MediaLibraryNapi::CreateAlbumTypeEnum(napi_env env)
{
napi_value result = nullptr;
CHECK_ARGS(env, napi_create_object(env, &result), JS_INNER_FAIL);
CHECK_ARGS(env, AddIntegerNamedProperty(env, result, "USER", PhotoAlbumType::USER), JS_INNER_FAIL);
CHECK_ARGS(env, AddIntegerNamedProperty(env, result, "SYSTEM", PhotoAlbumType::SYSTEM), JS_INNER_FAIL);
CHECK_ARGS(env, AddIntegerNamedProperty(env, result, "SMART", PhotoAlbumType::SMART), JS_INNER_FAIL);
CHECK_ARGS(env, AddIntegerNamedProperty(env, result, "SOURCE", PhotoAlbumType::SOURCE), JS_INNER_FAIL);
CHECK_ARGS(env, napi_create_reference(env, result, NAPI_INIT_REF_COUNT, &sAlbumType_), JS_INNER_FAIL);
return result;
}
napi_value MediaLibraryNapi::CreateAlbumSubTypeEnum(napi_env env)
{
napi_value result = nullptr;
CHECK_ARGS(env, napi_create_object(env, &result), JS_INNER_FAIL);
CHECK_ARGS(env, AddIntegerNamedProperty(env, result, "USER_GENERIC", PhotoAlbumSubType::USER_GENERIC),
JS_INNER_FAIL);
CHECK_ARGS(env, AddIntegerNamedProperty(env, result, "SOURCE_GENERIC", PhotoAlbumSubType::SOURCE_GENERIC),
JS_INNER_FAIL);
CHECK_ARGS(env,
AddIntegerNamedProperty(
env, result, "SOURCE_GENERIC_FROM_FILE_MANAGER", PhotoAlbumSubType::SOURCE_GENERIC_FROM_FILE_MANAGER),
JS_INNER_FAIL);
for (size_t i = 0; i < systemAlbumSubType.size(); i++) {
CHECK_ARGS(env, AddIntegerNamedProperty(env, result, systemAlbumSubType[i],
PhotoAlbumSubType::SYSTEM_START + i), JS_INNER_FAIL);
}
CHECK_ARGS(env, AddIntegerNamedProperty(env, result, "CLASSIFY", PhotoAlbumSubType::CLASSIFY),
JS_INNER_FAIL);
for (size_t i = 0; i < analysisAlbumSubType.size(); i++) {
CHECK_ARGS(env, AddIntegerNamedProperty(env, result, analysisAlbumSubType[i],
PhotoAlbumSubType::GEOGRAPHY_LOCATION + i), JS_INNER_FAIL);
}
CHECK_ARGS(env, AddIntegerNamedProperty(env, result, "ANY", PhotoAlbumSubType::ANY), JS_INNER_FAIL);
CHECK_ARGS(env, napi_create_reference(env, result, NAPI_INIT_REF_COUNT, &sAlbumSubType_), JS_INNER_FAIL);
return result;
}
napi_value MediaLibraryNapi::CreateAnalysisTypeEnum(napi_env env)
{
struct AnalysisProperty property[] = {
{ "ANALYSIS_AESTHETICS_SCORE", AnalysisType::ANALYSIS_AESTHETICS_SCORE },
{ "ANALYSIS_LABEL", AnalysisType::ANALYSIS_LABEL },
{ "ANALYSIS_VIDEO_LABEL", AnalysisType::ANALYSIS_VIDEO_LABEL },
{ "ANALYSIS_OCR", AnalysisType::ANALYSIS_OCR },
{ "ANALYSIS_FACE", AnalysisType::ANALYSIS_FACE },
{ "ANALYSIS_OBJECT", AnalysisType::ANALYSIS_OBJECT },
{ "ANALYSIS_RECOMMENDATION", AnalysisType::ANALYSIS_RECOMMENDATION },
{ "ANALYSIS_SEGMENTATION", AnalysisType::ANALYSIS_SEGMENTATION },
{ "ANALYSIS_COMPOSITION", AnalysisType::ANALYSIS_COMPOSITION },
{ "ANALYSIS_SALIENCY", AnalysisType::ANALYSIS_SALIENCY },
{ "ANALYSIS_DETAIL_ADDRESS", AnalysisType::ANALYSIS_DETAIL_ADDRESS },
{ "ANALYSIS_HUMAN_FACE_TAG", AnalysisType::ANALYSIS_HUMAN_FACE_TAG },
{ "ANALYSIS_HEAD_POSITION", AnalysisType::ANALYSIS_HEAD_POSITION },
{ "ANALYSIS_BONE_POSE", AnalysisType::ANALYSIS_BONE_POSE },
{ "ANALYSIS_MULTI_CROP", AnalysisType::ANALYSIS_MULTI_CROP },
{ "ANALYSIS_HIGHLIGHT", AnalysisType::ANALYSIS_HIGHLIGHT },
{ "ANALYSIS_SEARCH_INDEX", AnalysisType::ANALYSIS_SEARCH_INDEX },
{ "ANALYSIS_SELECTED", AnalysisType::ANALYSIS_SELECTED },
{ "ANALYSIS_DUPLICATE_SIMILARITY", AnalysisType::ANALYSIS_DUPLICATE_SIMILARITY },
{ "ANALYSIS_NEGATIVE_EMOTION", AnalysisType::ANALYSIS_NEGATIVE_EMOTION },
{ "ANALYSIS_FACE_AESTHETICS", AnalysisType::ANALYSIS_FACE_AESTHETICS },
{ "ANALYSIS_MAGIC_EMOJI", AnalysisType::ANALYSIS_MAGIC_EMOJI },
{ "ANALYSIS_AI_EDIT", AnalysisType::ANALYSIS_AI_EDIT },
{ "ANALYSIS_VIDEO_AESTHETICS", AnalysisType::ANALYSIS_VIDEO_AESTHETICS },
{ "ANALYSIS_PET_FACE", AnalysisType::ANALYSIS_PET_FACE },
{ "ANALYSIS_PET_TAG", AnalysisType::ANALYSIS_PET_TAG },
};
napi_value result = nullptr;
CHECK_ARGS(env, napi_create_object(env, &result), JS_INNER_FAIL);
for (uint32_t i = 0; i < sizeof(property) / sizeof(property[0]); i++) {
CHECK_ARGS(env, AddIntegerNamedProperty(env, result, property[i].enumName, property[i].enumValue),
JS_INNER_FAIL);
}
CHECK_ARGS(env, napi_create_reference(env, result, NAPI_INIT_REF_COUNT, &sAnalysisType_), JS_INNER_FAIL);
return result;
}
napi_value MediaLibraryNapi::CreateHighlightAlbumInfoTypeEnum(napi_env env)
{
struct AnalysisProperty property[] = {
{ "COVER_INFO", HighlightAlbumInfoType::COVER_INFO },
{ "PLAY_INFO", HighlightAlbumInfoType::PLAY_INFO },
{ "ALBUM_INFO", HighlightAlbumInfoType::ALBUM_INFO },
};
napi_value result = nullptr;
CHECK_ARGS(env, napi_create_object(env, &result), JS_INNER_FAIL);
for (uint32_t i = 0; i < sizeof(property) / sizeof(property[0]); i++) {
CHECK_ARGS(env, AddIntegerNamedProperty(env, result, property[i].enumName, property[i].enumValue),
JS_INNER_FAIL);
}
CHECK_ARGS(env, napi_create_reference(env, result, NAPI_INIT_REF_COUNT, &sHighlightUserActionType_), JS_INNER_FAIL);
return result;
}
napi_value MediaLibraryNapi::CreateHighlightUserActionTypeEnum(napi_env env)
{
struct AnalysisProperty property[] = {
{ "INSERTED_PIC_COUNT", HighlightUserActionType::INSERTED_PIC_COUNT },
{ "REMOVED_PIC_COUNT", HighlightUserActionType::REMOVED_PIC_COUNT },
{ "SHARED_SCREENSHOT_COUNT", HighlightUserActionType::SHARED_SCREENSHOT_COUNT },
{ "SHARED_COVER_COUNT", HighlightUserActionType::SHARED_COVER_COUNT },
{ "RENAMED_COUNT", HighlightUserActionType::RENAMED_COUNT },
{ "CHANGED_COVER_COUNT", HighlightUserActionType::CHANGED_COVER_COUNT },
{ "RENDER_VIEWED_TIMES", HighlightUserActionType::RENDER_VIEWED_TIMES },
{ "RENDER_VIEWED_DURATION", HighlightUserActionType::RENDER_VIEWED_DURATION },
{ "ART_LAYOUT_VIEWED_TIMES", HighlightUserActionType::ART_LAYOUT_VIEWED_TIMES },
{ "ART_LAYOUT_VIEWED_DURATION", HighlightUserActionType::ART_LAYOUT_VIEWED_DURATION },
};
napi_value result = nullptr;
CHECK_ARGS(env, napi_create_object(env, &result), JS_INNER_FAIL);
for (uint32_t i = 0; i < sizeof(property) / sizeof(property[0]); i++) {
CHECK_ARGS(env, AddIntegerNamedProperty(env, result, property[i].enumName, property[i].enumValue),
JS_INNER_FAIL);
}
CHECK_ARGS(env, napi_create_reference(env, result, NAPI_INIT_REF_COUNT, &sHighlightAlbumInfoType_), JS_INNER_FAIL);
return result;
}
napi_value MediaLibraryNapi::CreateHighlightAlbumChangeAttributeEnum(napi_env env)
{
struct AnalysisProperty property[] = {
{ "IS_VIEWED", HighlightAlbumChangeAttribute::IS_VIEWED },
{ "NOTIFICATION_TIME", HighlightAlbumChangeAttribute::NOTIFICATION_TIME },
{ "IS_FAVORITE", HighlightAlbumChangeAttribute::IS_FAVORITE },
};
napi_value result = nullptr;
CHECK_ARGS_WITH_MSG(env, napi_create_object(env, &result), JS_E_INNER_FAIL,
"Failed to create instance");
for (uint32_t i = 0; i < sizeof(property) / sizeof(property[0]); i++) {
CHECK_ARGS_WITH_MSG(env, AddIntegerNamedProperty(env, result, property[i].enumName, property[i].enumValue),
JS_E_INNER_FAIL, "Failed to add property");
}
CHECK_ARGS_WITH_MSG(env, napi_create_reference(env, result, NAPI_INIT_REF_COUNT,
&sHighlightAlbumChangeAttributeEnumRef_), JS_E_INNER_FAIL, "Failed to create reference");
return result;
}
napi_value MediaLibraryNapi::CreateDefaultChangeUriEnum(napi_env env)
{
return CreateStringEnumProperty(env, DEFAULT_URI_ENUM_PROPERTIES, sDefaultChangeUriRef_);
}
napi_value MediaLibraryNapi::CreateAlbumAttributeEnum(napi_env env)
{
return CreateStringEnumProperty(env, ALBUM_ATTRIBUTE_ENUM_PROPERTIES, sAlbumAttributeEnumRef_);
}
napi_value MediaLibraryNapi::CreateAlbumOperationTypeEnum(napi_env env)
{
return CreateStringEnumProperty(env, ALBUM_OPERATION_TYPE_ENUM_PROPERTIES, sAlbumOperationTypeEnumRef_);
}
napi_value MediaLibraryNapi::CreateNotifyTypeEnum(napi_env env)
{
return CreateNumberEnumProperty(env, notifyTypeEnum, sNotifyType_);
}
napi_value MediaLibraryNapi::CreateRequestPhotoTypeEnum(napi_env env)
{
return CreateNumberEnumProperty(env, requestPhotoTypeEnum, sRequestPhotoTypeEnumRef_);
}
napi_value MediaLibraryNapi::CreateDeliveryModeEnum(napi_env env)
{
return CreateNumberEnumProperty(env, deliveryModeEnum, sDeliveryModeEnumRef_);
}
napi_value MediaLibraryNapi::CreateSourceModeEnum(napi_env env)
{
return CreateNumberEnumProperty(env, sourceModeEnum, sSourceModeEnumRef_);
}
napi_value MediaLibraryNapi::CreateAuthorizationModeEnum(napi_env env)
{
return CreateNumberEnumProperty(env, AuthorizationModeEnum, sAuthorizationModeEnumRef_);
}
napi_value MediaLibraryNapi::CreateCompatibleModeEnum(napi_env env)
{
return CreateNumberEnumProperty(env, compatibleModeEnum, sCompatibleModeEnumRef_);
}
napi_value MediaLibraryNapi::CreateAnalysisToolTypeEnum(napi_env env)
{
return CreateNumberEnumProperty(env, analysisToolTypeEnum, sAnalysisToolTypeEnumRef_);
}
napi_value MediaLibraryNapi::CreatePreferredCompatibleModeEnum(napi_env env)
{
return CreateNumberEnumProperty(env, preferredCompatibleModeEnum, sPreferredCompatibleModeEnumRef_);
}
napi_value MediaLibraryNapi::CreateResourceTypeEnum(napi_env env)
{
const int32_t startIdx = 1;
return CreateNumberEnumProperty(env, resourceTypeEnum, sResourceTypeEnumRef_, startIdx);
}
napi_value MediaLibraryNapi::CreateCloudEnhancementTaskStageEnum(napi_env env)
{
return CreateNumberEnumProperty(env, cloudEnhancementTaskStageEnum, sCloudEnhancementTaskStageEnumRef_, -1);
}
napi_value MediaLibraryNapi::CreateCloudEnhancementStateEnum(napi_env env)
{
return CreateNumberEnumProperty(env, cloudEnhancementStateEnum, sCloudEnhancementStateEnumRef_);
}
napi_value MediaLibraryNapi::CreateSupportedImageFormatEnum(napi_env env)
{
return CreateStringEnumProperty(env, SUPPORTED_IMAGE_FORMAT_ENUM_PROPERTIES, sSupportedImageFormatEnumRef_);
}
napi_value MediaLibraryNapi::CreateVideoEnhancementTypeEnum(napi_env env)
{
return CreateNumberEnumProperty(env, videoEnhancementTypeEnum, sVideoEnhancementTypeEnumRef_);
}
napi_value MediaLibraryNapi::CreateHdrModeEnum(napi_env env)
{
return CreateNumberEnumProperty(env, hdrModeEnum, sHdrModeRef_);
}
napi_value MediaLibraryNapi::CreateVideoModeEnum(napi_env env)
{
return CreateNumberEnumProperty(env, videoModeEnum, sVideoModeRef_);
}
napi_value MediaLibraryNapi::CreateMovingPhotoEffectModeEnum(napi_env env)
{
napi_value result = nullptr;
CHECK_ARGS(env, napi_create_object(env, &result), JS_INNER_FAIL);
for (size_t i = 0; i < movingPhotoEffectModeEnum.size(); i++) {
CHECK_ARGS(env, AddIntegerNamedProperty(env, result, movingPhotoEffectModeEnum[i], static_cast<int32_t>(i)),
JS_INNER_FAIL);
}
CHECK_ARGS(env, AddIntegerNamedProperty(env, result, "IMAGE_ONLY",
static_cast<int32_t>(MovingPhotoEffectMode::IMAGE_ONLY)), JS_INNER_FAIL);
CHECK_ARGS(env, napi_create_reference(env, result, NAPI_INIT_REF_COUNT, &sMovingPhotoEffectModeEnumRef_),
JS_INNER_FAIL);
return result;
}
napi_value MediaLibraryNapi::CreateSupportedWatermarkTypeEnum(napi_env env)
{
return CreateNumberEnumProperty(env, watermarkTypeEnum, sSupportedWatermarkTypeEnumRef_);
}
napi_value MediaLibraryNapi::CreateCloudMediaDownloadTypeEnum(napi_env env)
{
return CreateNumberEnumProperty(env, cloudMediaDownloadTypeEnum, sCloudMediaDownloadTypeEnumRef_);
}
napi_value MediaLibraryNapi::CreateCloudMediaRetainTypeEnum(napi_env env)
{
return CreateNumberEnumProperty(env, cloudMediaRetainTypeEnum, sCloudMediaRetainTypeEnumRef_);
}
napi_value MediaLibraryNapi::CreateCloudMediaAssetTaskStatusEnum(napi_env env)
{
return CreateNumberEnumProperty(env, cloudMediaAssetTaskStatusEnum, sCloudMediaAssetTaskStatusEnumRef_);
}
napi_value MediaLibraryNapi::CreateCloudMediaTaskPauseCauseEnum(napi_env env)
{
return CreateNumberEnumProperty(env, cloudMediaTaskPauseCauseEnum, sCloudMediaTaskPauseCauseEnumRef_);
}
napi_value MediaLibraryNapi::CreateNotifyChangeTypeEnum(napi_env env)
{
return CreateNumberEnumProperty(env, notifyChangeTypeEnum, sNotifyChangeTypeEnumRef_);
}
napi_value MediaLibraryNapi::CreateThumbnailChangeStatusEnum(napi_env env)
{
return CreateNumberEnumProperty(env, thumbnailChangeStatusEnum, sThumbnailChangeStatusEnumRef_);
}
napi_value MediaLibraryNapi::CreateStrongAssociationTypeEnum(napi_env env)
{
return CreateNumberEnumProperty(env, strongAssociationTypeEnum, sStrongAssociationTypeEnumRef_);
}
napi_value MediaLibraryNapi::CreateCompositeDisplayModeEnum(napi_env env)
{
return CreateNumberEnumProperty(env, compositeDisplayModeEnum, sCompositeDisplayModeEnumRef_);
}
napi_value MediaLibraryNapi::CreateLivePhoto4dStatusEnum(napi_env env)
{
return CreateNumberEnumProperty(env, livePhoto4dStatusTypeEnum, sLivePhoto4dStatusEnumRef_);
}
napi_value MediaLibraryNapi::CreateAvailabilityStatusEnum(napi_env env)
{
return CreateStringEnumProperty(env, AVAILABILITY_STATUS_ENUM_PROPERTIES, sAvailabilityStatusEnumRef_);
}
static napi_value ParseArgsCreatePhotoAlbum(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
constexpr size_t minArgs = ARGS_ONE;
constexpr size_t maxArgs = ARGS_TWO;
CHECK_ARGS(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs),
JS_ERR_PARAMETER_INVALID);
string albumName;
CHECK_ARGS(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[ARGS_ZERO], albumName),
JS_ERR_PARAMETER_INVALID);
if (MediaFileUtils::CheckAlbumName(albumName) < 0) {
NapiError::ThrowError(env, JS_ERR_PARAMETER_INVALID);
return nullptr;
}
context->valuesBucket.Put(PhotoAlbumColumns::ALBUM_NAME, albumName);
context->valuesBucket.Put(PhotoAlbumColumns::ALBUM_IS_LOCAL, 1);
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
static void GetExistsPhotoAlbum(const string &albumName, MediaLibraryAsyncContext *context)
{
string queryUri = (context->resultNapiType == ResultNapiType::TYPE_USERFILE_MGR) ?
CONST_UFM_CREATE_PHOTO_ALBUM : CONST_PAH_CREATE_PHOTO_ALBUM;
Uri uri(queryUri);
DataSharePredicates predicates;
predicates.EqualTo(PhotoAlbumColumns::ALBUM_NAME, albumName);
vector<string> columns;
int errCode = 0;
auto resultSet = UserFileClient::Query(uri, predicates, columns, errCode, context->userId);
auto fetchResult = make_unique<FetchResult<PhotoAlbum>>(move(resultSet));
fetchResult->SetResultNapiType(context->resultNapiType);
context->photoAlbumData = fetchResult->GetFirstObject();
}
static void GetPhotoAlbumById(const int32_t id, const string &albumName, MediaLibraryAsyncContext *context)
{
auto photoAlbum = make_unique<PhotoAlbum>();
photoAlbum->SetAlbumId(id);
photoAlbum->SetPhotoAlbumType(USER);
photoAlbum->SetPhotoAlbumSubType(USER_GENERIC);
photoAlbum->SetAlbumUri(PhotoAlbumColumns::ALBUM_URI_PREFIX + to_string(id));
photoAlbum->SetAlbumName(albumName);
photoAlbum->SetResultNapiType(context->resultNapiType);
context->photoAlbumData = move(photoAlbum);
}
int32_t CallPhotoAccessCreateAlbum(MediaLibraryAsyncContext *context, const std::string &albumName)
{
CreateAlbumReqBody reqBody;
reqBody.albumName = albumName;
int32_t errCode = IPC::UserDefineIPCClient().Call(context->businessCode, reqBody);
if (errCode < 0) {
NAPI_ERR_LOG("after IPC::UserDefineIPCClient().Call, errCode: %{public}d.", errCode);
}
return errCode;
}
static void JSCreatePhotoAlbumExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSCreatePhotoAlbumExecute");
bool isValid = false;
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
string albumName = context->valuesBucket.Get(PhotoAlbumColumns::ALBUM_NAME, isValid);
if (!isValid) {
context->SaveError(-EINVAL);
return;
}
int32_t ret = -EINVAL;
if (context->businessCode != 0) {
ret = CallPhotoAccessCreateAlbum(context, albumName);
} else {
string createAlbumUri = (context->resultNapiType == ResultNapiType::TYPE_USERFILE_MGR) ?
CONST_UFM_CREATE_PHOTO_ALBUM : CONST_PAH_CREATE_PHOTO_ALBUM;
Uri createPhotoAlbumUri(createAlbumUri);
ret = UserFileClient::Insert(createPhotoAlbumUri, context->valuesBucket);
}
if (ret == -1) {
context->SaveError(-EEXIST);
GetExistsPhotoAlbum(albumName, context);
return;
}
if (ret < 0) {
context->SaveError(ret);
return;
}
GetPhotoAlbumById(ret, albumName, context);
}
static void GetPhotoAlbumCreateResult(napi_env env, MediaLibraryAsyncContext *context,
unique_ptr<JSAsyncContextOutput> &jsContext)
{
if (context->photoAlbumData == nullptr) {
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->data), JS_INNER_FAIL);
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, ERR_INVALID_OUTPUT,
"Obtain photo album asset failed");
return;
}
context->photoAlbumData->SetUserId(context->userId);
napi_value jsPhotoAlbum = PhotoAlbumNapi::CreatePhotoAlbumNapi(env, context->photoAlbumData);
if (jsPhotoAlbum == nullptr) {
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->data), JS_INNER_FAIL);
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, ERR_MEM_ALLOCATION,
"Failed to create js object for PhotoAlbum");
return;
}
jsContext->data = jsPhotoAlbum;
jsContext->status = true;
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), JS_INNER_FAIL);
}
static void HandleExistsError(napi_env env, MediaLibraryAsyncContext *context, napi_value &error)
{
if (context->photoAlbumData == nullptr) {
MediaLibraryNapiUtils::CreateNapiErrorObject(env, error, ERR_INVALID_OUTPUT,
"Obtain photo album asset failed");
return;
}
context->photoAlbumData->SetUserId(context->userId);
napi_value jsPhotoAlbum = PhotoAlbumNapi::CreatePhotoAlbumNapi(env, context->photoAlbumData);
if (jsPhotoAlbum == nullptr) {
MediaLibraryNapiUtils::CreateNapiErrorObject(env, error, ERR_MEM_ALLOCATION,
"Failed to create js object for PhotoAlbum");
return;
}
MediaLibraryNapiUtils::CreateNapiErrorObject(env, error, JS_ERR_FILE_EXIST, "Album has existed");
napi_value propertyName;
CHECK_ARGS_RET_VOID(env, napi_create_string_utf8(env, "data", NAPI_AUTO_LENGTH, &propertyName), JS_INNER_FAIL);
CHECK_ARGS_RET_VOID(env, napi_set_property(env, error, propertyName, jsPhotoAlbum), JS_INNER_FAIL);
}
static void JSCreatePhotoAlbumCompleteCallback(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSCreatePhotoAlbumCompleteCallback");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
auto jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->data), JS_INNER_FAIL);
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), JS_INNER_FAIL);
if (context->error == ERR_DEFAULT) {
GetPhotoAlbumCreateResult(env, context, jsContext);
} else if (context->error == JS_ERR_FILE_EXIST) {
HandleExistsError(env, context, jsContext->error);
} else {
context->HandleError(env, jsContext->error);
}
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
napi_value MediaLibraryNapi::CreatePhotoAlbum(napi_env env, napi_callback_info info)
{
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_USERFILE_MGR;
CHECK_NULLPTR_RET(ParseArgsCreatePhotoAlbum(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "CreatePhotoAlbum", JSCreatePhotoAlbumExecute,
JSCreatePhotoAlbumCompleteCallback);
}
napi_value MediaLibraryNapi::PhotoAccessCreatePhotoAlbum(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessCreatePhotoAlbum");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::PAH_SYSTEM_CREATE_ALBUM);
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
CHECK_NULLPTR_RET(ParseArgsCreatePhotoAlbum(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "CreatePhotoAlbum", JSCreatePhotoAlbumExecute,
JSCreatePhotoAlbumCompleteCallback);
}
static napi_value ParseArgsDeletePhotoAlbums(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
constexpr size_t minArgs = ARGS_ONE;
constexpr size_t maxArgs = ARGS_TWO;
CHECK_ARGS(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs),
JS_ERR_PARAMETER_INVALID);
vector<string> deleteIds;
uint32_t len = 0;
CHECK_ARGS(env, napi_get_array_length(env, context->argv[PARAM0], &len), JS_INNER_FAIL);
for (uint32_t i = 0; i < len; i++) {
napi_value album = nullptr;
CHECK_ARGS(env, napi_get_element(env, context->argv[PARAM0], i, &album), JS_INNER_FAIL);
if (album == nullptr) {
NapiError::ThrowError(env, JS_ERR_PARAMETER_INVALID);
return nullptr;
}
PhotoAlbumNapi *obj = nullptr;
CHECK_ARGS(env, napi_unwrap(env, album, reinterpret_cast<void **>(&obj)), JS_INNER_FAIL);
if (obj == nullptr) {
NapiError::ThrowError(env, JS_ERR_PARAMETER_INVALID);
return nullptr;
}
if (!PhotoAlbum::IsUserPhotoAlbumByType(obj->GetPhotoAlbumType())) {
NapiError::ThrowError(env, JS_ERR_PARAMETER_INVALID);
return nullptr;
}
deleteIds.push_back(to_string(obj->GetAlbumId()));
}
if (deleteIds.empty()) {
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
context->albumIds = deleteIds;
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
static void JSDeletePhotoAlbumsExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSDeletePhotoAlbumsExecute");
auto *context = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "context is null");
CHECK_IF_EQUAL(!context->albumIds.empty(), "albumIds is empty");
int ret = E_OK;
if (context->resultNapiType == ResultNapiType::TYPE_USERFILE_MGR) {
context->predicates.In(PhotoAlbumColumns::ALBUM_ID, context->albumIds);
string deleteAlbumUri = CONST_UFM_DELETE_PHOTO_ALBUM;
Uri uri(deleteAlbumUri);
ret = UserFileClient::Delete(uri, context->predicates);
} else {
DeleteAlbumsReqBody reqBody;
reqBody.albumIds = context->albumIds;
uint32_t businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::PAH_DELETE_PHOTO_ALBUMS);
ret = IPC::UserDefineIPCClient().Call(businessCode, reqBody);
}
if (ret < 0) {
context->SaveError(ret);
} else {
context->retVal = ret;
}
}
static void JSDeletePhotoAlbumsCompleteCallback(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSDeletePhotoAlbumsCompleteCallback");
MediaLibraryAsyncContext *context = static_cast<MediaLibraryAsyncContext*>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), JS_INNER_FAIL);
if (context->error != ERR_DEFAULT) {
context->HandleError(env, jsContext->error);
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->data), JS_INNER_FAIL);
} else {
CHECK_ARGS_RET_VOID(env, napi_create_int32(env, context->retVal, &jsContext->data), JS_INNER_FAIL);
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), JS_INNER_FAIL);
jsContext->status = true;
}
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
napi_value MediaLibraryNapi::DeletePhotoAlbums(napi_env env, napi_callback_info info)
{
MEDIA_INFO_LOG(" enter DeletePhotoAlbums");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_USERFILE_MGR;
CHECK_NULLPTR_RET(ParseArgsDeletePhotoAlbums(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "DeletePhotoAlbums",
JSDeletePhotoAlbumsExecute, JSDeletePhotoAlbumsCompleteCallback);
}
napi_value MediaLibraryNapi::PhotoAccessDeletePhotoAlbums(napi_env env, napi_callback_info info)
{
MEDIA_INFO_LOG(" enter PhotoAccessDeletePhotoAlbums");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
CHECK_NULLPTR_RET(ParseArgsDeletePhotoAlbums(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "DeletePhotoAlbums",
JSDeletePhotoAlbumsExecute, JSDeletePhotoAlbumsCompleteCallback);
}
static napi_value GetAlbumFetchOption(napi_env env, unique_ptr<MediaLibraryAsyncContext> &context, bool hasCallback)
{
if (context->argc < (ARGS_ONE + hasCallback)) {
NAPI_ERR_LOG("No arguments to parse");
return nullptr;
}
uint32_t argIndex = context->argc - ARGS_ONE - hasCallback;
if (argIndex >= NAPI_ARGC_MAX) {
NAPI_ERR_LOG("argIndex ilegal");
return nullptr;
}
napi_value fetchOption = context->argv[argIndex];
CHECK_ARGS(env, MediaLibraryNapiUtils::GetFetchOption(env, fetchOption, ALBUM_FETCH_OPT, context), JS_INNER_FAIL);
if (!context->uri.empty()) {
if (context->uri.find(PhotoAlbumColumns::ANALYSIS_ALBUM_URI_PREFIX) != std::string::npos) {
context->isAnalysisAlbum = PARAM1;
}
}
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
static napi_value GetAlbumIds(napi_env env, unique_ptr<MediaLibraryAsyncContext> &context, bool hasCallback)
{
if (context->argc < (ARGS_ONE + hasCallback)) {
NAPI_ERR_LOG("No arguments to parse");
return nullptr;
}
MediaLibraryNapiUtils::GetStringArrayFromInt32(env, context->argv[PARAM0], context->albumIds);
if (context->albumIds.empty() || context->albumIds.size() > MAX_QUERY_ALBUM_LIMIT) {
NAPI_ERR_LOG("the size of albumid is invalid");
NapiError::ThrowError(env, JS_ERR_PARAMETER_INVALID);
return nullptr;
}
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
NAPI_INFO_LOG("GetAlbumIds: %{public}d", static_cast<int32_t>(context->albumIds.size()));
context->predicates.In(PhotoAlbumColumns::ALBUM_ID, context->albumIds);
return result;
}
static napi_value HandleOneArgAlbum(napi_env env, unique_ptr<MediaLibraryAsyncContext> &context, bool hasCallback)
{
bool hasFetchOpt = false;
CHECK_ARGS(env, MediaLibraryNapiUtils::hasFetchOpt(env, context->argv[PARAM0], hasFetchOpt), JS_INNER_FAIL);
if (hasFetchOpt) {
return GetAlbumFetchOption(env, context, hasCallback);
} else {
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
return GetAlbumIds(env, context, hasCallback);
}
}
static bool ParseLocationAlbumTypes(unique_ptr<MediaLibraryAsyncContext> &context, const int32_t albumSubType)
{
if (albumSubType == PhotoAlbumSubType::GEOGRAPHY_LOCATION) {
context->isLocationAlbum = PhotoAlbumSubType::GEOGRAPHY_LOCATION;
context->fetchColumn.insert(context->fetchColumn.end(),
PhotoAlbumColumns::LOCATION_DEFAULT_FETCH_COLUMNS.begin(),
PhotoAlbumColumns::LOCATION_DEFAULT_FETCH_COLUMNS.end());
MediaLibraryNapiUtils::GetAllLocationPredicates(context->predicates);
return false;
} else if (albumSubType == PhotoAlbumSubType::GEOGRAPHY_CITY) {
context->fetchColumn = PhotoAlbumColumns::CITY_DEFAULT_FETCH_COLUMNS;
context->isLocationAlbum = PhotoAlbumSubType::GEOGRAPHY_CITY;
string onClause = PhotoAlbumColumns::ALBUM_NAME + " = " + CITY_ID;
context->predicates.InnerJoin(GEO_DICTIONARY_TABLE)->On({ onClause });
context->predicates.NotEqualTo(PhotoAlbumColumns::ALBUM_COUNT, to_string(0));
}
return true;
}
static void ApplyTablePrefixToAlbumIdPredicates(DataSharePredicates& predicates)
{
constexpr int32_t fieldIdx = 0;
auto& items = predicates.GetOperationList();
string targetColumn = "AnalysisAlbum.album_id";
std::vector<DataShare::OperationItem> tmpOperations = {};
for (const DataShare::OperationItem& item : items) {
if (item.singleParams.empty()) {
tmpOperations.push_back(item);
continue;
}
if (static_cast<string>(item.GetSingle(fieldIdx)) == PhotoAlbumColumns::ALBUM_ID) {
DataShare::OperationItem tmpItem = item;
tmpItem.singleParams[fieldIdx] = targetColumn;
tmpOperations.push_back(tmpItem);
continue;
}
tmpOperations.push_back(item);
}
predicates = DataSharePredicates(move(tmpOperations));
}
static void AddHighlightAlbumPredicates(DataSharePredicates& predicates, int32_t albumSubType)
{
vector<string> onClause = {
ANALYSIS_ALBUM_TABLE + "." + PhotoAlbumColumns::ALBUM_ID + " = " +
HIGHLIGHT_ALBUM_TABLE + "." + PhotoAlbumColumns::ALBUM_ID,
};
if (albumSubType == PhotoAlbumSubType::HIGHLIGHT_SUGGESTIONS) {
onClause = {
ANALYSIS_ALBUM_TABLE + "." + PhotoAlbumColumns::ALBUM_ID + " = " +
HIGHLIGHT_ALBUM_TABLE + "." + AI_ALBUM_ID,
};
}
predicates.InnerJoin(HIGHLIGHT_ALBUM_TABLE)->On(onClause);
predicates.OrderByDesc(MAX_DATE_ADDED + ", " + GENERATE_TIME);
ApplyTablePrefixToAlbumIdPredicates(predicates);
}
static napi_value ParseAlbumTypes(napi_env env, unique_ptr<MediaLibraryAsyncContext> &context)
{
if (context->argc < ARGS_TWO) {
NAPI_ERR_LOG("No arguments to parse");
return nullptr;
}
int32_t albumType;
CHECK_NULLPTR_RET(MediaLibraryNapiUtils::GetInt32Arg(env, context->argv[PARAM0], albumType));
if (!PhotoAlbum::CheckPhotoAlbumType(static_cast<PhotoAlbumType>(albumType))) {
NapiError::ThrowError(env, JS_ERR_PARAMETER_INVALID);
return nullptr;
}
context->isAnalysisAlbum = (albumType == PhotoAlbumType::SMART) ? 1 : 0;
int32_t albumSubType;
CHECK_NULLPTR_RET(MediaLibraryNapiUtils::GetInt32Arg(env, context->argv[PARAM1], albumSubType));
if (!PhotoAlbum::CheckPhotoAlbumSubType(static_cast<PhotoAlbumSubType>(albumSubType))) {
NapiError::ThrowError(env, JS_ERR_PARAMETER_INVALID);
return nullptr;
}
if (!ParseLocationAlbumTypes(context, albumSubType)) {
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
context->predicates.And()->EqualTo(PhotoAlbumColumns::ALBUM_TYPE, to_string(albumType));
if (albumSubType != ANY) {
context->predicates.And()->EqualTo(PhotoAlbumColumns::ALBUM_SUBTYPE, to_string(albumSubType));
}
if (albumSubType == PhotoAlbumSubType::SHOOTING_MODE || albumSubType == PhotoAlbumSubType::GEOGRAPHY_CITY) {
context->predicates.OrderByDesc(PhotoAlbumColumns::ALBUM_COUNT);
}
if (albumSubType == PhotoAlbumSubType::HIGHLIGHT || albumSubType == PhotoAlbumSubType::HIGHLIGHT_SUGGESTIONS) {
context->isHighlightAlbum = albumSubType;
AddHighlightAlbumPredicates(context->predicates, albumSubType);
}
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
static void RestrictAlbumSubtypeOptions(DataSharePredicates &predicates)
{
if (!MediaLibraryNapiUtils::IsSystemApp()) {
predicates.And()->In(PhotoAlbumColumns::ALBUM_SUBTYPE, vector<string>({
to_string(PhotoAlbumSubType::USER_GENERIC),
to_string(PhotoAlbumSubType::FAVORITE),
to_string(PhotoAlbumSubType::VIDEO),
to_string(PhotoAlbumSubType::IMAGE),
}));
} else {
predicates.And()->NotEqualTo(PhotoAlbumColumns::ALBUM_SUBTYPE, to_string(PhotoAlbumSubType::HIDDEN));
}
}
static napi_value ParseArgsGetPhotoAlbum(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
constexpr size_t minArgs = ARGS_ZERO;
constexpr size_t maxArgs = ARGS_FOUR;
CHECK_ARGS(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs),
JS_ERR_PARAMETER_INVALID);
bool hasCallback = false;
CHECK_ARGS(env, MediaLibraryNapiUtils::HasCallback(env, context->argc, context->argv, hasCallback),
JS_ERR_PARAMETER_INVALID);
if (context->argc == ARGS_THREE) {
napi_valuetype valueType = napi_undefined;
if (napi_typeof(env, context->argv[PARAM2], &valueType) == napi_ok &&
(valueType == napi_undefined || valueType == napi_null)) {
context->argc -= 1;
}
}
switch (context->argc - hasCallback) {
case ARGS_ZERO:
break;
case ARGS_ONE:
CHECK_NULLPTR_RET(HandleOneArgAlbum(env, context, hasCallback));
break;
case ARGS_TWO:
CHECK_NULLPTR_RET(ParseAlbumTypes(env, context));
break;
case ARGS_THREE:
CHECK_NULLPTR_RET(GetAlbumFetchOption(env, context, hasCallback));
CHECK_NULLPTR_RET(ParseAlbumTypes(env, context));
break;
default:
return nullptr;
}
RestrictAlbumSubtypeOptions(context->predicates);
if (context->isLocationAlbum != PhotoAlbumSubType::GEOGRAPHY_LOCATION &&
context->isLocationAlbum != PhotoAlbumSubType::GEOGRAPHY_CITY) {
CHECK_COND(env, CheckAlbumFetchColumns(context->fetchColumn), JS_ERR_PARAMETER_INVALID);
AddDefaultPhotoAlbumColumns(context->fetchColumn);
AddDefaultColumnsForNonAnalysisAlbums(*context);
if (context->isHighlightAlbum) {
context->fetchColumn.erase(std::remove(context->fetchColumn.begin(), context->fetchColumn.end(),
PhotoAlbumColumns::ALBUM_ID), context->fetchColumn.end());
context->fetchColumn.push_back(ANALYSIS_ALBUM_TABLE + "." + PhotoAlbumColumns::ALBUM_ID + " AS " +
PhotoAlbumColumns::ALBUM_ID);
}
}
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
napi_value MediaLibraryNapi::GetPhotoAlbums(napi_env env, napi_callback_info info)
{
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_USERFILE_MGR;
CHECK_NULLPTR_RET(ParseArgsGetPhotoAlbum(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "GetPhotoAlbums", JSGetPhotoAlbumsExecute,
JSGetPhotoAlbumsCompleteCallback);
}
static void ReplaceFetchColumn(std::vector<std::string> &fetchColumn,
const std::string &oldColumn, const std::string &newColumn)
{
auto it = std::find(fetchColumn.begin(), fetchColumn.end(), oldColumn);
if (it != fetchColumn.end()) {
it->assign(newColumn);
}
}
static void AddNoSmartFetchColumns(std::vector<std::string> &fetchColumn)
{
AddDefaultPhotoAlbumColumns(fetchColumn);
fetchColumn.push_back(PhotoAlbumColumns::ALBUM_IMAGE_COUNT);
fetchColumn.push_back(PhotoAlbumColumns::ALBUM_VIDEO_COUNT);
fetchColumn.push_back(PhotoAlbumColumns::ALBUM_LPATH);
fetchColumn.push_back(PhotoAlbumColumns::ALBUM_DATE_ADDED);
}
static void AddPhotoAlbumTypeFilter(DataSharePredicates &predicates, int32_t albumType, int32_t albumSubType)
{
if (albumType != PhotoAlbumType::INVALID) {
predicates.And()->EqualTo(PhotoAlbumColumns::ALBUM_TYPE, to_string(albumType));
}
if (albumSubType != PhotoAlbumSubType::ANY) {
predicates.And()->EqualTo(PhotoAlbumColumns::ALBUM_SUBTYPE, to_string(albumSubType));
if (albumSubType == PhotoAlbumSubType::SHOOTING_MODE || albumSubType == PhotoAlbumSubType::GEOGRAPHY_CITY) {
predicates.OrderByDesc(PhotoAlbumColumns::ALBUM_COUNT);
}
}
RestrictAlbumSubtypeOptions(predicates);
}
static napi_value ParseArgsPahGetAlbums(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
napi_status status = MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, ARGS_ZERO, ARGS_FOUR);
CHECK_ARGS(env, status, JS_ERR_PARAMETER_INVALID);
bool hasCallback = false;
status = MediaLibraryNapiUtils::HasCallback(env, context->argc, context->argv, hasCallback);
CHECK_ARGS(env, status, JS_ERR_PARAMETER_INVALID);
context->photoAlbumType = PhotoAlbumType::INVALID;
context->photoAlbumSubType = PhotoAlbumSubType::ANY;
if (context->argc < ARGS_TWO) {
bool hasFetchOpt = false;
CHECK_ARGS(env, MediaLibraryNapiUtils::hasFetchOpt(env, context->argv[PARAM0], hasFetchOpt), JS_INNER_FAIL);
if (!hasFetchOpt) {
NapiError::ThrowError(env, JS_ERR_PARAMETER_INVALID);
return nullptr;
}
CHECK_NULLPTR_RET(GetAlbumFetchOption(env, context, hasCallback));
CHECK_COND(env, CheckAlbumFetchColumns(context->fetchColumn), JS_ERR_PARAMETER_INVALID);
if (context->isAnalysisAlbum) {
context->photoAlbumType = PhotoAlbumType::SMART;
}
return result;
}
CHECK_NULLPTR_RET(MediaLibraryNapiUtils::GetInt32Arg(env, context->argv[PARAM0], context->photoAlbumType));
if (!PhotoAlbum::CheckPhotoAlbumType(static_cast<PhotoAlbumType>(context->photoAlbumType))) {
NapiError::ThrowError(env, JS_ERR_PARAMETER_INVALID);
return nullptr;
}
CHECK_NULLPTR_RET(MediaLibraryNapiUtils::GetInt32Arg(env, context->argv[PARAM1], context->photoAlbumSubType));
if (!PhotoAlbum::CheckPhotoAlbumSubType(static_cast<PhotoAlbumSubType>(context->photoAlbumSubType))) {
NapiError::ThrowError(env, JS_ERR_PARAMETER_INVALID);
return nullptr;
}
if ((context->argc - hasCallback) == ARGS_THREE) {
CHECK_NULLPTR_RET(GetAlbumFetchOption(env, context, hasCallback));
if (context->photoAlbumSubType != PhotoAlbumSubType::GEOGRAPHY_LOCATION &&
context->photoAlbumSubType != PhotoAlbumSubType::GEOGRAPHY_CITY) {
CHECK_COND(env, CheckAlbumFetchColumns(context->fetchColumn), JS_ERR_PARAMETER_INVALID);
}
}
return result;
}
static std::shared_ptr<DataShareResultSet> CallPahGetAlbums(MediaLibraryAsyncContext *context, int32_t &errCode)
{
if (context->businessCode != 0) {
QueryAlbumsReqBody reqBody;
QueryAlbumsRespBody respBody;
reqBody.albumType = context->photoAlbumType;
reqBody.albumSubType = context->photoAlbumSubType;
reqBody.columns = context->fetchColumn;
reqBody.predicates = context->predicates;
errCode = IPC::UserDefineIPCClient().SetUserId(context->userId).Call(context->businessCode, reqBody, respBody);
if (errCode != 0) {
NAPI_ERR_LOG("after IPC::UserDefineIPCClient().Call, errCode: %{public}d.", errCode);
return nullptr;
}
return respBody.resultSet;
}
if (context->photoAlbumType != PhotoAlbumType::SMART) {
Uri uri(CONST_PAH_QUERY_PHOTO_ALBUM);
AddNoSmartFetchColumns(context->fetchColumn);
AddPhotoAlbumTypeFilter(context->predicates, context->photoAlbumType, context->photoAlbumSubType);
return UserFileClient::Query(uri, context->predicates, context->fetchColumn, errCode, context->userId);
}
if (context->photoAlbumSubType == PhotoAlbumSubType::GEOGRAPHY_LOCATION) {
Uri uri(CONST_PAH_QUERY_GEO_PHOTOS);
MediaLibraryNapiUtils::GetAllLocationPredicates(context->predicates);
const auto &locations = PhotoAlbumColumns::LOCATION_DEFAULT_FETCH_COLUMNS;
context->fetchColumn.insert(context->fetchColumn.end(), locations.begin(), locations.end());
return UserFileClient::Query(uri, context->predicates, context->fetchColumn, errCode, context->userId);
}
if (context->photoAlbumSubType == PhotoAlbumSubType::GEOGRAPHY_CITY) {
context->fetchColumn = PhotoAlbumColumns::CITY_DEFAULT_FETCH_COLUMNS;
std::string onClause = PhotoAlbumColumns::ALBUM_NAME + " = " + CITY_ID;
context->predicates.InnerJoin(GEO_DICTIONARY_TABLE)->On({ onClause });
context->predicates.NotEqualTo(PhotoAlbumColumns::ALBUM_COUNT, to_string(0));
} else {
AddDefaultPhotoAlbumColumns(context->fetchColumn);
if (context->photoAlbumSubType == PhotoAlbumSubType::HIGHLIGHT ||
context->photoAlbumSubType == PhotoAlbumSubType::HIGHLIGHT_SUGGESTIONS) {
AddHighlightAlbumPredicates(context->predicates, context->photoAlbumSubType);
std::string highLightAlbumId = ANALYSIS_ALBUM_TABLE + "." + PhotoAlbumColumns::ALBUM_ID +
" AS " + PhotoAlbumColumns::ALBUM_ID;
ReplaceFetchColumn(context->fetchColumn, PhotoAlbumColumns::ALBUM_ID, highLightAlbumId);
}
}
Uri uri(CONST_PAH_QUERY_ANA_PHOTO_ALBUM);
AddPhotoAlbumTypeFilter(context->predicates, context->photoAlbumType, context->photoAlbumSubType);
return UserFileClient::Query(uri, context->predicates, context->fetchColumn, errCode, context->userId);
}
static void JSPahGetAlbumsExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSPahGetAlbumsExecute");
int errCode = 0;
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
std::shared_ptr<DataShareResultSet> resultSet = CallPahGetAlbums(context, errCode);
if (resultSet == nullptr) {
NAPI_ERR_LOG("resultSet == nullptr, errCode is %{public}d", errCode);
if (errCode == E_PERMISSION_DENIED || errCode == -E_CHECK_SYSTEMAPP_FAIL) {
context->SaveError(errCode);
} else {
context->SaveError(E_HAS_DB_ERROR);
}
return;
}
context->fetchPhotoAlbumResult = make_unique<FetchResult<PhotoAlbum>>(move(resultSet));
context->fetchPhotoAlbumResult->SetResultNapiType(context->resultNapiType);
context->fetchPhotoAlbumResult->SetHiddenOnly(false);
context->fetchPhotoAlbumResult->SetLocationOnly(false);
context->fetchPhotoAlbumResult->SetUserId(context->userId);
}
napi_value MediaLibraryNapi::PahGetAlbums(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PahGetAlbums");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
CHECK_NULLPTR_RET(ParseArgsPahGetAlbums(env, info, asyncContext));
asyncContext->businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::PAH_QUERY_PHOTO_ALBUMS);
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "PahGetAlbums", JSPahGetAlbumsExecute,
JSGetPhotoAlbumsCompleteCallback);
}
napi_value MediaLibraryNapi::PhotoAccessGetPhotoAlbums(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessGetPhotoAlbums");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
CHECK_NULLPTR_RET(ParseArgsGetPhotoAlbum(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "GetPhotoAlbums", JSGetPhotoAlbumsExecute,
JSGetPhotoAlbumsCompleteCallback);
}
napi_value MediaLibraryNapi::PhotoAccessGetPhotoAlbumsByIds(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessGetPhotoAlbumsByIds");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
CHECK_NULLPTR_RET(ParseArgsGetPhotoAlbum(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "GetPhotoAlbums", JSGetPhotoAlbumsByIdsExecute,
JSGetPhotoAlbumsCompleteCallback);
}
static napi_value GetOldAlbumUriMap(napi_env env, MediaLibraryAsyncContext *context)
{
napi_status status;
napi_value mapNapiValue {nullptr};
status = napi_create_map(env, &mapNapiValue);
CHECK_COND_RET(status == napi_ok && mapNapiValue != nullptr, nullptr,
"Failed to create map napi value, napi status: %{public}d", static_cast<int>(status));
for (auto &iter : context->uriAlbumMap) {
napi_value key {nullptr};
napi_value value {nullptr};
status = napi_create_string_utf8(env, iter.first.c_str(), NAPI_AUTO_LENGTH, &key);
CHECK_COND_RET(status == napi_ok && key != nullptr, nullptr,
"Failed to create string key, napi status: %{public}d", static_cast<int>(status));
status = napi_create_string_utf8(env, iter.second.c_str(), NAPI_AUTO_LENGTH, &value);
CHECK_COND_RET(status == napi_ok && value != nullptr, nullptr,
"Failed to create string value, napi status: %{public}d", static_cast<int>(status));
status = napi_map_set_property(env, mapNapiValue, key, value);
CHECK_COND_RET(status == napi_ok, nullptr,
"Failed to set uriMap property, napi status: %{public}d", static_cast<int>(status));
}
return mapNapiValue;
}
static void GetOldAlbumUriQueryResult(napi_env env, MediaLibraryAsyncContext *context,
unique_ptr<JSAsyncContextOutput> &jsContext)
{
napi_value fileResult = GetOldAlbumUriMap(env, context);
if (fileResult == nullptr) {
CHECK_ARGS_RET_VOID_WITH_MEG(env, napi_get_undefined(env, &jsContext->data), JS_E_INNER_FAIL,
"Failed to initialize data field");
MediaLibraryNapiUtils::CreateNapiErrorObject(
env, jsContext->error, ERR_INVALID_OUTPUT,
"Failed to create js object for Fetch Album Result"
);
return;
}
jsContext->data = fileResult;
jsContext->status = true;
CHECK_ARGS_RET_VOID_WITH_MEG(env, napi_get_undefined(env, &jsContext->error), JS_E_INNER_FAIL,
"Failed to initialize error field");
}
static void JSGetAlbumsByOldUrisCompleteCallback(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSGetAlbumsByOldUrisCompleteCallback");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
CHECK_ARGS_RET_VOID_WITH_MEG(env, napi_get_undefined(env, &jsContext->error), JS_E_INNER_FAIL,
"Failed to initialize error field");
if (context->error == ERR_DEFAULT) {
napi_value mapNapiValue {nullptr};
napi_create_map(env, &mapNapiValue);
jsContext->data = mapNapiValue;
jsContext->status = true;
CHECK_ARGS_RET_VOID_WITH_MEG(env, napi_get_undefined(env, &jsContext->error), JS_E_INNER_FAIL,
"Failed to initialize error field");
GetOldAlbumUriQueryResult(env, context, jsContext);
} else {
context->HandleError(env, jsContext->error);
}
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(
env, context->deferred, context->callbackRef, context->work, *jsContext
);
}
delete context;
context = nullptr;
}
static bool IsAllNumeric(const std::string &str) {
return std::all_of(str.begin(), str.end(),
[](unsigned char c){
return std::isdigit(c);
});
}
static std::string GetIdFromAlbumUriHelper(const std::string &uri, const std::string &prefix)
{
size_t lastSlashPos = uri.rfind("/");
if (lastSlashPos == std::string::npos) {
NAPI_ERR_LOG("Id could not be found in the URI");
return "";
}
std::string checkPrefix = uri.substr(0, lastSlashPos + 1);
if (checkPrefix != prefix) {
NAPI_ERR_LOG("The uri format is incorrect: %{public}s", uri.c_str());
return "";
}
std::string id = uri.substr(lastSlashPos + 1);
if (!IsAllNumeric(id)) {
NAPI_ERR_LOG("The id is not numeric: %{public}s", id.c_str());
return "";
}
return id;
}
static std::string GetIdFromAlbumUri(const std::string &uri)
{
size_t paPos = uri.find(TabOldAlbumsColumn::PHOTO_ALBUM_PREFIX);
if (paPos != 0) {
size_t aaPos = uri.find(TabOldAlbumsColumn::ANALYSIS_ALBUM_PREFIX);
if (aaPos != 0) {
NAPI_ERR_LOG("The prefix is incorrect for the URI: %{public}s", uri.c_str());
return "";
}
return GetIdFromAlbumUriHelper(uri, TabOldAlbumsColumn::ANALYSIS_ALBUM_PREFIX);
}
return GetIdFromAlbumUriHelper(uri, TabOldAlbumsColumn::PHOTO_ALBUM_PREFIX);
}
static std::string GetAlbumTypeFromUriHelper(const std::string &uri)
{
size_t pos = uri.find("://");
if (pos == std::string::npos) {
return "";
}
std::string path = uri.substr(pos + 3);
std::istringstream ss(path);
std::string token;
std::vector<std::string> parts;
while (std::getline(ss, token, '/')) {
if (!token.empty()) {
parts.push_back(token);
}
}
if (parts.size() >= 2) {
return parts[1];
}
return "";
}
static std::vector<std::string> ExtractIDAlbumOldUris(const std::vector<std::string> &oldAlbums)
{
std::vector<std::string> result;
for (const std::string &oldAlbum : oldAlbums) {
auto finalParsing = GetIdFromAlbumUri(oldAlbum);
if (!finalParsing.empty()) {
result.emplace_back(finalParsing);
} else {
NAPI_ERR_LOG("SplitPathAndIDAlbumOldUris: Failed to retrieve ID from %{public}s", oldAlbum.c_str());
}
}
return result;
}
static std::vector<std::pair<std::string, std::string>> ExtractUriAndIDFromOldUris(const std::vector<std::string> &oldAlbums)
{
std::vector<std::pair<std::string, std::string>> result;
for (const std::string &oldAlbum : oldAlbums) {
std::string id = GetIdFromAlbumUri(oldAlbum);
result.emplace_back(oldAlbum, id);
}
return result;
}
static std::string GetOldAlbumUriFromResultMapById(const std::map<std::string, std::string>& resultMap, const std::string& oldAlbumUri)
{
auto findResult = std::find_if(resultMap.begin(), resultMap.end(), [&oldAlbumUri](const auto& data) {
return data.first == oldAlbumUri;
});
return findResult != resultMap.end() ? findResult->first : "";
}
static std::map<std::string, std::string> prepareMapping(const std::vector<TabOldAlbumsColumn::RawData>& processEnd, const std::vector<std::pair<std::string, std::string>>& oldAlbumData) {
std::map<std::string, std::string> result{};
for (const auto& data : oldAlbumData) {
result[data.first] = "";
}
for (auto& value : processEnd) {
char albumSubtypeKind = std::to_string(value.album_subtype)[0];
std::string oldAlbumId = std::to_string(value.old_album_id);
std::string oldAlbumUri = albumSubtypeKind == TabOldAlbumsColumn::IS_ANALYSIS_TABLE ?
TabOldAlbumsColumn::MEDIA_URI + '/' + TabOldAlbumsColumn::ALBUM_ANALYSIS_TAB + '/' + oldAlbumId :
TabOldAlbumsColumn::MEDIA_URI + '/' + TabOldAlbumsColumn::ALBUM_PHOTO_TAB + '/' + oldAlbumId;
std::string val = albumSubtypeKind == TabOldAlbumsColumn::IS_ANALYSIS_TABLE ?
TabOldAlbumsColumn::ANALYSIS_ALBUM_PREFIX + std::to_string(value.album_id) :
TabOldAlbumsColumn::PHOTO_ALBUM_PREFIX + std::to_string(value.album_id);
std::string foundUri = GetOldAlbumUriFromResultMapById(result, oldAlbumUri);
if (!foundUri.empty()) {
if (GetAlbumTypeFromUriHelper(foundUri) == GetAlbumTypeFromUriHelper(val)) {
result[foundUri] = val;
}
}
}
return result;
}
static std::vector<TabOldAlbumsColumn::RawData> ProcessOldAlbumResultSet(std::shared_ptr<DataShare::DataShareResultSet> resultSet)
{
std::vector<TabOldAlbumsColumn::RawData> processing;
while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
TabOldAlbumsColumn::RawData row;
row.old_album_id = GetInt32Val(TabOldAlbumsColumn::OLD_ALBUM_ID, resultSet);
row.album_id = GetInt32Val(TabOldAlbumsColumn::ALBUM_ID, resultSet);
row.album_type = GetInt32Val(TabOldAlbumsColumn::ALBUM_TYPE, resultSet);
row.album_subtype = GetInt32Val(TabOldAlbumsColumn::ALBUM_SUBTYPE, resultSet);
row.clone_sequence = GetInt32Val(TabOldAlbumsColumn::CLONE_SEQUENCE, resultSet);
auto it = std::find_if(processing.begin(), processing.end(), [&row](const auto &elem) {
return row.old_album_id == elem.old_album_id;
});
if (it == processing.end()) {
processing.emplace_back(row);
} else {
char itSubtype = (it->album_subtype >= TabOldAlbumsColumn::IsPhotoOrAnalysis && it->album_subtype < TabOldAlbumsColumn::MaxValue)
? TabOldAlbumsColumn::IS_ANALYSIS_TABLE : TabOldAlbumsColumn::IS_PHOTOS_TABLE;
char rowSubtype = (row.album_subtype >= TabOldAlbumsColumn::IsPhotoOrAnalysis && row.album_subtype < TabOldAlbumsColumn::MaxValue)
? TabOldAlbumsColumn::IS_ANALYSIS_TABLE : TabOldAlbumsColumn::IS_PHOTOS_TABLE;
if (itSubtype != rowSubtype) {
processing.emplace_back(row);
} else if (row.clone_sequence > it->clone_sequence) {
*it = row;
}
}
}
return processing;
}
static void PhotoAccessGetAlbumsByOldUrisExecute(napi_env env, void *data)
{
MediaLibraryAsyncContext *context = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
std::vector<std::string> InputAlbumId = ExtractIDAlbumOldUris(context->albumUris);
std::vector<std::pair<std::string, std::string>> oldAlbumData = ExtractUriAndIDFromOldUris(context->albumUris);
DataSharePredicates predicates;
std::vector<std::string> columns= { TabOldAlbumsColumn::OLD_ALBUM_TABLE + '.' + TabOldAlbumsColumn::OLD_ALBUM_ID, TabOldAlbumsColumn::OLD_ALBUM_TABLE + '.' + TabOldAlbumsColumn::ALBUM_ID, TabOldAlbumsColumn::OLD_ALBUM_TABLE + '.' + TabOldAlbumsColumn::ALBUM_TYPE, TabOldAlbumsColumn::OLD_ALBUM_TABLE + '.' + TabOldAlbumsColumn::ALBUM_SUBTYPE, TabOldAlbumsColumn::OLD_ALBUM_TABLE + '.' + TabOldAlbumsColumn::CLONE_SEQUENCE};
predicates.In(TabOldAlbumsColumn::OLD_ALBUM_ID, InputAlbumId);
predicates.OrderByDesc(TabOldAlbumsColumn::CLONE_SEQUENCE);
int errCode = 0;
GetClonedAlbumUrisReqBody reqBody;
reqBody.predicates = predicates;
reqBody.columns = columns;
GetClonedAlbumUrisRespBody respBody;
context->businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::GET_CLONED_ALBUM_URIS);
errCode = IPC::UserDefineIPCClient().SetUserId(context->userId).Call(context->businessCode, reqBody, respBody);
if (errCode != 0) {
context->SaveError(errCode);
NAPI_ERR_LOG("UserDefineIPCClient Call failed, errCode: %{public}d.", errCode);
return;
}
if (respBody.resultSet == nullptr) {
NAPI_ERR_LOG("QueryOldAlbumsTable: ResultSet is nullptr: %{public}d", errCode);
return;
}
std::vector<TabOldAlbumsColumn::RawData> processing = ProcessOldAlbumResultSet(respBody.resultSet);
respBody.resultSet->Close();
context->uriAlbumMap = prepareMapping(processing, oldAlbumData);
}
static napi_value ParseArgsGetAlbumsByOldUris(napi_env env, napi_callback_info info,
std::unique_ptr<MediaLibraryAsyncContext> &context)
{
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL,
"This interface can be called only by system apps");
return nullptr;
}
vector<string> uris;
CHECK_ARGS(env, MediaLibraryNapiUtils::ParseArgsStringArrayCallback(env, info, context, uris),
JS_E_PARAM_INVALID);
if (uris.empty()) {
NapiError::ThrowError(env, JS_E_PARAM_INVALID, "Failed to check empty uri!");
return nullptr;
}
for (const auto &uri : uris) {
if (uri.rfind(MEDIA_URI_PREFIX, 0) != 0) {
NapiError::ThrowError(env, JS_E_PARAM_INVALID, "Failed to check uri format, not a photo uri!");
return nullptr;
}
}
if (uris.size() > MAX_TAB_OLD_ALBUMS_URI_COUNT) {
NapiError::ThrowError(env, JS_E_PARAM_INVALID, "Too many URIs, maximum allowed is 100!");
return nullptr;
}
context->albumUris = uris;
napi_value result = nullptr;
CHECK_ARGS_WITH_MSG(env, napi_get_boolean(env, true, &result), JS_E_INNER_FAIL,
"Failed to create boolean value");
return result;
}
napi_value MediaLibraryNapi::PhotoAcessGetAlbumsByOldUris(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAcessGetAlbumsByOldUris");
std::unique_ptr<MediaLibraryAsyncContext> asyncContext = std::make_unique<MediaLibraryAsyncContext>();
CHECK_NULLPTR_RET(ParseArgsGetAlbumsByOldUris(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(
env,
asyncContext,
"PhotoAcessGetAlbumsByOldUris",
PhotoAccessGetAlbumsByOldUrisExecute,
JSGetAlbumsByOldUrisCompleteCallback
);
}
napi_value MediaLibraryNapi::PhotoAccessGetPhotoAlbumsSync(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessGetPhotoAlbumsSync");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
CHECK_NULLPTR_RET(ParseArgsGetPhotoAlbum(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return JSGetPhotoAlbumsExecuteSync(env, *asyncContext);
}
napi_value MediaLibraryNapi::CreatePositionTypeEnum(napi_env env)
{
const int32_t startIdx = 1;
return CreateNumberEnumProperty(env, positionTypeEnum, sPositionTypeEnumRef_, startIdx);
}
napi_value MediaLibraryNapi::CreatePhotoSubTypeEnum(napi_env env)
{
return CreateNumberEnumPropertyByMap(env, PHOTO_SUB_TYPE_ENUM_PROPERTIES, sPhotoSubType_);
}
napi_value MediaLibraryNapi::CreatePhotoPermissionTypeEnum(napi_env env)
{
return CreateNumberEnumProperty(env, photoPermissionTypeEnum, sPhotoPermissionType_);
}
napi_value MediaLibraryNapi::CreateMediaAssetPermissionStateEnum(napi_env env)
{
return CreateNumberEnumProperty(env, mediaAssetPermissionStateEnum, sMediaAssetPermissionStateEnumRef_);
}
napi_value MediaLibraryNapi::CreateHideSensitiveTypeEnum(napi_env env)
{
return CreateNumberEnumProperty(env, hideSensitiveTypeEnum, sHideSensitiveType_);
}
napi_value MediaLibraryNapi::CreateDynamicRangeTypeEnum(napi_env env)
{
return CreateNumberEnumProperty(env, dynamicRangeTypeEnum, sDynamicRangeType_);
}
napi_value MediaLibraryNapi::CreateAppLinkStateEnum(napi_env env)
{
return CreateNumberEnumProperty(env, appLinkStateEnum, sAppLinkStateRef_);
}
napi_value MediaLibraryNapi::CreateDeepOptimizeStateEnum(napi_env env)
{
return CreateNumberEnumProperty(env, deepOptimizeStateEnum, sDeepOptimizeStateRef_);
}
static bool CheckTitleCompatible(MediaLibraryAsyncContext* context)
{
if (!context->isCreateByComponent) {
return true;
}
bool hasTitleParam = false;
const string title = context->valuesBucket.Get(MediaColumn::MEDIA_TITLE, hasTitleParam);
if (!hasTitleParam) {
return true;
}
return MediaFileUtils::CheckTitleCompatible(title) == E_OK;
}
static int32_t CallPhotoAccessCreateAsset(MediaLibraryAsyncContext* context, std::string &outUri)
{
bool isValid = false;
CreateAssetReqBody reqBody;
reqBody.mediaType = context->valuesBucket.Get(CONST_MEDIA_DATA_DB_MEDIA_TYPE, isValid);
if (context->needSystemApp) {
reqBody.photoSubtype = context->valuesBucket.Get(PhotoColumn::PHOTO_SUBTYPE, isValid);
string displayName = context->valuesBucket.Get(CONST_MEDIA_DATA_DB_NAME, isValid);
string cameraShotKey = context->valuesBucket.Get(PhotoColumn::CAMERA_SHOT_KEY, isValid);
reqBody.displayName = displayName;
reqBody.cameraShotKey = cameraShotKey;
} else {
string title = context->valuesBucket.Get(MediaColumn::MEDIA_TITLE, isValid);
string extension = context->valuesBucket.Get(CONST_ASSET_EXTENTION, isValid);
reqBody.title = title;
reqBody.extension = extension;
}
CreateAssetRespBody respBody;
int32_t errCode = IPC::UserDefineIPCClient().SetUserId(context->userId).Call(context->businessCode, reqBody, respBody);
if (errCode != 0) {
NAPI_ERR_LOG("after IPC::UserDefineIPCClient().Call, errCode: %{public}d.", errCode);
return errCode;
}
outUri = respBody.outUri;
return respBody.fileId;
}
static void PhotoAccessCreateAssetExecute(napi_env env, void *data)
{
OHOS::QOS::SetThreadQos(OHOS::QOS::QosLevel::QOS_USER_INTERACTIVE);
MediaLibraryTracer tracer;
tracer.Start("JSCreateAssetExecute");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
if (!CheckDisplayNameParams(context)) {
context->error = JS_E_DISPLAYNAME;
return;
}
if (!CheckTitleCompatible(context)) {
context->error = JS_E_DISPLAYNAME;
context->errorMsg = "Title is invalid";
return;
}
if ((context->resultNapiType != ResultNapiType::TYPE_PHOTOACCESS_HELPER) && (!CheckRelativePathParams(context))) {
context->error = JS_E_RELATIVEPATH;
return;
}
string outUri;
int index = -EINVAL;
if (context->businessCode != 0) {
index = CallPhotoAccessCreateAsset(context, outUri);
} else {
string uri;
GetCreateUri(context, uri);
Uri createFileUri(uri);
index = UserFileClient::InsertExt(createFileUri, context->valuesBucket, outUri, context->userId);
}
if (index < 0) {
context->SaveError(index);
NAPI_ERR_LOG("InsertExt fail, index: %{public}d.", index);
} else {
if (context->resultNapiType == ResultNapiType::TYPE_PHOTOACCESS_HELPER) {
if (context->isCreateByComponent) {
context->uri = outUri;
} else {
PhotoAccessSetFileAssetByIdV10(index, "", outUri, context);
}
} else {
#ifdef MEDIALIBRARY_COMPATIBILITY
SetFileAssetByIdV9(index, "", context);
#else
getFileAssetById(index, "", context);
#endif
}
}
OHOS::QOS::ResetThreadQos();
}
static void PhotoAccessGrantPhotoUriPermissionExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessGrantPhotoUriPermissionExecute");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
if (context == nullptr) {
NAPI_ERR_LOG("Async context is null");
return;
}
GrantUriPermissionReqBody reqBody;
bool isValid = false;
reqBody.tokenId = context->valuesBucket.Get(AppUriPermissionColumn::TARGET_TOKENID, isValid);
CHECK_IF_EQUAL(isValid, "tokenId is empty");
reqBody.srcTokenId = context->valuesBucket.Get(AppUriPermissionColumn::SOURCE_TOKENID, isValid);
CHECK_IF_EQUAL(isValid, "srcTokenId is empty");
reqBody.fileId = context->valuesBucket.Get(AppUriPermissionColumn::FILE_ID, isValid);
CHECK_IF_EQUAL(isValid, "fileId is empty");
reqBody.permissionType = context->valuesBucket.Get(AppUriPermissionColumn::PERMISSION_TYPE, isValid);
CHECK_IF_EQUAL(isValid, "permissionType is empty");
reqBody.hideSensitiveType = context->valuesBucket.Get(AppUriSensitiveColumn::HIDE_SENSITIVE_TYPE, isValid);
CHECK_IF_EQUAL(isValid, "hideSensitiveType is empty");
reqBody.uriType = context->valuesBucket.Get(AppUriPermissionColumn::URI_TYPE, isValid);
CHECK_IF_EQUAL(isValid, "uriType is empty");
uint32_t businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::PAH_GRANT_PHOTO_URI_PERMISSION);
NAPI_INFO_LOG("before IPC::UserDefineIPCClient().Call");
int32_t result = IPC::UserDefineIPCClient().Call(businessCode, reqBody);
NAPI_INFO_LOG("GrantPhotoUriPermission ret:%{public}d", result);
if (result < 0) {
context->SaveError(result);
NAPI_ERR_LOG("GrantPhotoUriPermission fail, result: %{public}d.", result);
} else {
context->retVal = result;
}
}
static void PhotoAccessGrantPhotoUrisPermissionExecuteEx(MediaLibraryAsyncContext *context)
{
if (context == nullptr) {
NAPI_ERR_LOG("Async context is null");
return;
}
GrantUrisPermissionReqBody reqBody;
bool isValid = false;
std::set<std::string> processedColumn;
for (const auto& valueBucket : context->valuesBucketArray) {
if (processedColumn.find(AppUriPermissionColumn::TARGET_TOKENID) == processedColumn.end()) {
reqBody.tokenId = context->valuesBucket.Get(AppUriPermissionColumn::TARGET_TOKENID, isValid);
CHECK_IF_EQUAL(isValid, "tokenId is empty");
processedColumn.insert(AppUriPermissionColumn::TARGET_TOKENID);
}
if (processedColumn.find(AppUriPermissionColumn::SOURCE_TOKENID) == processedColumn.end()) {
reqBody.srcTokenId = context->valuesBucket.Get(AppUriPermissionColumn::SOURCE_TOKENID, isValid);
CHECK_IF_EQUAL(isValid, "srcTokenId is empty");
processedColumn.insert(AppUriPermissionColumn::SOURCE_TOKENID);
}
if (processedColumn.find(AppUriPermissionColumn::PERMISSION_TYPE) == processedColumn.end()) {
reqBody.permissionType = context->valuesBucket.Get(AppUriPermissionColumn::PERMISSION_TYPE, isValid);
CHECK_IF_EQUAL(isValid, "permissionType is empty");
processedColumn.insert(AppUriPermissionColumn::PERMISSION_TYPE);
}
if (processedColumn.find(AppUriSensitiveColumn::HIDE_SENSITIVE_TYPE) == processedColumn.end()) {
reqBody.hideSensitiveType = context->valuesBucket.Get(AppUriSensitiveColumn::HIDE_SENSITIVE_TYPE, isValid);
CHECK_IF_EQUAL(isValid, "hideSensitiveType is empty");
processedColumn.insert(AppUriSensitiveColumn::HIDE_SENSITIVE_TYPE);
}
if (processedColumn.find(AppUriPermissionColumn::URI_TYPE) == processedColumn.end()) {
reqBody.uriType = context->valuesBucket.Get(AppUriPermissionColumn::URI_TYPE, isValid);
CHECK_IF_EQUAL(isValid, "uriType is empty");
processedColumn.insert(AppUriPermissionColumn::URI_TYPE);
}
reqBody.fileIds.emplace_back(valueBucket.Get(AppUriPermissionColumn::FILE_ID, isValid));
}
NAPI_INFO_LOG("before IPC::UserDefineIPCClient().Call");
uint32_t businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::PAH_GRANT_PHOTO_URIS_PERMISSION);
int32_t result = IPC::UserDefineIPCClient().Call(businessCode, reqBody);
NAPI_INFO_LOG("GrantPhotoUrisPermission ret:%{public}d", result);
if (result < 0) {
context->SaveError(result);
NAPI_ERR_LOG("GrantPhotoUrisPermission fail, result: %{public}d.", result);
} else {
context->retVal = result;
}
}
static void PhotoAccessGrantPhotoUrisPermissionExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessGrantPhotoUrisPermissionExecute");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
if (context == nullptr) {
NAPI_ERR_LOG("Async context is null");
return;
}
if (context->businessCode != 0) {
return PhotoAccessGrantPhotoUrisPermissionExecuteEx(context);
}
string uri = CONST_PAH_CREATE_APP_URI_PERMISSION;
MediaLibraryNapiUtils::UriAppendKeyValue(uri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
Uri createUri(uri);
int result = UserFileClient::BatchInsert(createUri, context->valuesBucketArray);
if (result < 0) {
context->SaveError(result);
NAPI_ERR_LOG("BatchInsert fail, result: %{public}d.", result);
} else {
context->retVal = result;
}
}
static void PhotoAccessCancelPhotoUriPermissionExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessCancelPhotoUriPermissionExecute");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
if (context == nullptr) {
NAPI_ERR_LOG("Async context is null");
return;
}
CancelUriPermissionReqBody reqBody;
bool isValid = false;
reqBody.tokenId = context->valuesBucket.Get(AppUriPermissionColumn::TARGET_TOKENID, isValid);
CHECK_IF_EQUAL(isValid, "tokenId is empty");
reqBody.srcTokenId = context->valuesBucket.Get(AppUriPermissionColumn::SOURCE_TOKENID, isValid);
CHECK_IF_EQUAL(isValid, "srcTokenId is empty");
reqBody.fileId = context->valuesBucket.Get(AppUriPermissionColumn::FILE_ID, isValid);
CHECK_IF_EQUAL(isValid, "fileId is empty");
reqBody.permissionType = context->valuesBucket.Get(AppUriPermissionColumn::PERMISSION_TYPE, isValid);
CHECK_IF_EQUAL(isValid, "permissionType is empty");
reqBody.uriType = context->valuesBucket.Get(AppUriPermissionColumn::URI_TYPE, isValid);
CHECK_IF_EQUAL(isValid, "uriType is empty");
uint32_t businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::PAH_CANCEL_PHOTO_URI_PERMISSION);
NAPI_INFO_LOG("before IPC::UserDefineIPCClient().Call");
int32_t result = IPC::UserDefineIPCClient().Call(businessCode, reqBody);
NAPI_INFO_LOG("CancelPhotoUriPermission ret:%{public}d", result);
if (result < 0) {
context->SaveError(result);
NAPI_ERR_LOG("CancelPhotoUriPermission fail, result: %{public}d.", result);
} else {
context->retVal = result;
}
}
static void PhotoAccessCreatePhotoAssetExecute(napi_env env, void *data)
{
OHOS::QOS::SetThreadQos(OHOS::QOS::QosLevel::QOS_USER_INTERACTIVE);
MediaLibraryTracer tracer;
tracer.Start("JSCreateAssetExecute");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
if (context == nullptr) {
NAPI_ERR_LOG("Async context is null");
return;
}
string outUri;
int index = -EINVAL;
if (context->businessCode != static_cast<uint32_t>(MediaLibraryBusinessCode::MEDIA_BUSINESS_CODE_START)){
index = CallPhotoAccessCreateAsset(context, outUri);
} else {
context->error = JS_E_PARAM_INVALID;
return;
}
if (index < 0) {
context->SaveError(index);
NAPI_ERR_LOG("InsertExt fail, index: %{public}d.", index);
} else {
context->uri = outUri;
}
OHOS::QOS::ResetThreadQos();
}
static napi_value ParseCreatePhotoAssetComponentArgs(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
napi_valuetype valueType;
MediaType mediaType;
int32_t type = 0;
CHECK_COND_WITH_ERR_MESSAGE(env, napi_get_value_int32(env, context->argv[ARGS_ZERO], &type) == napi_ok,
JS_E_PARAM_INVALID, "Failed to get type value");
mediaType = static_cast<MediaType>(type);
CHECK_COND_WITH_ERR_MESSAGE(env, (mediaType == MEDIA_TYPE_IMAGE || mediaType == MEDIA_TYPE_VIDEO),
JS_E_PARAM_INVALID, "invalid file type");
string extension;
CHECK_COND_WITH_ERR_MESSAGE(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[ARGS_ONE], extension) ==
napi_ok, JS_E_PARAM_INVALID, "Failed to get extension");
CHECK_COND_WITH_ERR_MESSAGE(env, mediaType == MediaFileUtils::GetMediaType("." + extension), JS_E_PARAM_INVALID,
"Failed to check extension");
context->valuesBucket.Put(CONST_ASSET_EXTENTION, extension);
if (context->argc >= ARGS_THREE) {
CHECK_COND_WITH_ERR_MESSAGE(env, napi_typeof(env, context->argv[ARGS_TWO], &valueType) == napi_ok,
JS_E_PARAM_INVALID, "Failed to get napi type");
if (valueType == napi_string) {
char buffer[ARG_BUF_SIZE];
size_t res = 0;
CHECK_COND_WITH_ERR_MESSAGE(env,
napi_get_value_string_utf8(env, context->argv[ARGS_TWO], buffer, ARG_BUF_SIZE, &res) == napi_ok,
JS_E_PARAM_INVALID, "failed to get string");
context->valuesBucket.Put(MediaColumn::MEDIA_TITLE, string(buffer));
std::string title = string(buffer);
std::string totalSize = title + "." + extension;
CHECK_COND_WITH_ERR_MESSAGE(env, MediaFileUtils::CheckDisplayName(totalSize, true) == 0, JS_E_PARAM_INVALID,
"Display size is error");
context->valuesBucket.Put(MediaColumn::MEDIA_TITLE, title);
} else {
NAPI_ERR_LOG("Napi type is wrong in create options");
return nullptr;
}
}
context->valuesBucket.Put(CONST_MEDIA_DATA_DB_MEDIA_TYPE, static_cast<int32_t>(mediaType));
napi_value result = nullptr;
NAPI_CALL(env, napi_get_boolean(env, true, &result));
return result;
}
static napi_value ParseArgsPhotoCreateAsset(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
constexpr size_t minArgs = ARGS_TWO;
constexpr size_t maxArgs = ARGS_THREE;
CHECK_COND_WITH_ERR_MESSAGE(env,
MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs) ==
napi_ok, JS_E_PARAM_INVALID,"Failed to get object info");
napi_valuetype valueType;
CHECK_COND_WITH_ERR_MESSAGE(env, napi_typeof(env, context->argv[ARGS_ZERO], &valueType) == napi_ok,
JS_E_PARAM_INVALID, "Failed to get napi type");
if (valueType == napi_number) {
context->isCreateByComponent = true;
context->needSystemApp = false;
return ParseCreatePhotoAssetComponentArgs(env, info, context);
} else {
NAPI_ERR_LOG("JS param type %{public}d is wrong", static_cast<int32_t>(valueType));
return nullptr;
}
}
napi_value MediaLibraryNapi::PhotoAccessCreatePhotoAsset(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessCreatePhotoAsset");
NAPI_INFO_LOG("PhotoAccessCreatePhotoAsset enter");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
asyncContext->assetType = TYPE_PHOTO;
CHECK_COND_WITH_ERR_MESSAGE(env, ParseArgsPhotoCreateAsset(env, info, asyncContext), JS_E_PARAM_INVALID,
"Failed to parse js args");
asyncContext->businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::PAH_PUBLIC_CREATE_ASSET);
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "PhotoAccessCreatePhotoAsset",
PhotoAccessCreatePhotoAssetExecute, JSCreateAssetCompleteCallback);
}
napi_value MediaLibraryNapi::PhotoAccessHelperCreatePhotoAsset(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessHelperCreatePhotoAsset");
HILOG_COMM_INFO("%{public}s:{%{public}s:%{public}d} enter", MLOG_TAG, __FUNCTION__, __LINE__);
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
asyncContext->assetType = TYPE_PHOTO;
NAPI_ASSERT(env, ParseArgsCreatePhotoAsset(env, info, asyncContext), "Failed to parse js args");
asyncContext->businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::PAH_PUBLIC_CREATE_ASSET);
if (asyncContext->needSystemApp) {
asyncContext->businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::PAH_SYSTEM_CREATE_ASSET);
}
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "PhotoAccessHelperCreatePhotoAsset",
PhotoAccessCreateAssetExecute, JSCreateAssetCompleteCallback);
}
napi_value MediaLibraryNapi::PhotoAccessGrantPhotoUriPermission(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessGrantPhotoUriPermission");
NAPI_INFO_LOG("enter");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
asyncContext->assetType = TYPE_PHOTO;
NAPI_ASSERT(env, ParseArgsGrantPhotoUriPermission(env, info, asyncContext), "Failed to parse js args");
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "PhotoAccessGrantPhotoUriPermission",
PhotoAccessGrantPhotoUriPermissionExecute, JSPhotoUriPermissionCallback);
}
napi_value MediaLibraryNapi::PhotoAccessGrantPhotoUrisPermission(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessGrantPhotoUrisPermission");
NAPI_INFO_LOG("enter");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
asyncContext->assetType = TYPE_PHOTO;
asyncContext->businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::PAH_GRANT_PHOTO_URIS_PERMISSION);
NAPI_ASSERT(env, ParseArgsGrantPhotoUrisPermission(env, info, asyncContext), "Failed to parse js args");
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "PhotoAccessGrantPhotoUrisPermission",
PhotoAccessGrantPhotoUrisPermissionExecute, JSPhotoUriPermissionCallback);
}
napi_value MediaLibraryNapi::PhotoAccessCancelPhotoUriPermission(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessCancelPhotoUriPermission");
NAPI_INFO_LOG("enter");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
asyncContext->assetType = TYPE_PHOTO;
NAPI_ASSERT(env, ParseArgsCancelPhotoUriPermission(env, info, asyncContext), "Failed to parse js args");
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "PhotoAccessCancelPhotoUriPermission",
PhotoAccessCancelPhotoUriPermissionExecute, JSPhotoUriPermissionCallback);
}
static bool CheckAlbumUri(napi_env env, OHOS::DataShare::DataShareValuesBucket &valueBucket,
MediaLibraryAsyncContext *context)
{
bool isValid = false;
string ownerAlbumId = valueBucket.Get(PhotoColumn::PHOTO_OWNER_ALBUM_ID, isValid);
if (!isValid || ownerAlbumId.empty()) {
return false;
}
string queryUri = CONST_PAH_QUERY_PHOTO_ALBUM;
Uri uri(queryUri);
DataSharePredicates predicates;
vector selectionArgs = {to_string(PhotoAlbumSubType::USER_GENERIC),
to_string(PhotoAlbumSubType::SOURCE_GENERIC),
to_string(PhotoAlbumSubType::SOURCE_GENERIC_FROM_FILE_MANAGER)};
predicates.In(PhotoAlbumColumns::ALBUM_SUBTYPE, selectionArgs);
predicates.EqualTo(PhotoAlbumColumns::ALBUM_ID, ownerAlbumId);
int errCode = 0;
vector<string> columns;
columns.push_back(CONST_MEDIA_COLUMN_COUNT_1);
shared_ptr<DataShareResultSet> resultSet =
UserFileClient::Query(uri, predicates, columns, errCode, context->userId);
if (resultSet == nullptr) {
NAPI_ERR_LOG("resultSet is null, errCode: %{public}d", errCode);
return false;
}
int err = resultSet->GoToFirstRow();
if (err != NativeRdb::E_OK) {
NAPI_ERR_LOG("Invalid albumuri, Failed GoToFirstRow %{public}d", err);
resultSet->Close();
return false;
}
int32_t count = 0;
resultSet->GetInt(0, count);
if (count == 0) {
NAPI_ERR_LOG("Invalid albumuri!");
resultSet->Close();
return false;
}
resultSet->Close();
return true;
}
static int32_t CallPhotoAccessCreateAssetForApp(MediaLibraryAsyncContext* context,
const DataShareValuesBucket &valuesBucket, std::string &outUri)
{
bool isValid = false;
CreateAssetForAppReqBody reqBody;
reqBody.tokenId = static_cast<int64_t>(context->tokenId);
reqBody.mediaType = valuesBucket.Get(CONST_MEDIA_DATA_DB_MEDIA_TYPE, isValid);
reqBody.photoSubtype = valuesBucket.Get(PhotoColumn::PHOTO_SUBTYPE, isValid);
string extension = valuesBucket.Get(CONST_ASSET_EXTENTION, isValid);
string title = valuesBucket.Get(MediaColumn::MEDIA_TITLE, isValid);
string appId = valuesBucket.Get(CONST_MEDIA_DATA_DB_OWNER_APPID, isValid);
string bundleName = valuesBucket.Get(CONST_MEDIA_DATA_DB_OWNER_PACKAGE, isValid);
string packageName = valuesBucket.Get(CONST_MEDIA_DATA_DB_PACKAGE_NAME, isValid);
string ownerAlbumId = valuesBucket.Get(PhotoColumn::PHOTO_OWNER_ALBUM_ID, isValid);
reqBody.title = title;
reqBody.extension = extension;
reqBody.bundleName = bundleName;
reqBody.packageName = packageName;
reqBody.appId = appId;
reqBody.ownerAlbumId = ownerAlbumId;
CreateAssetForAppRespBody respBody;
int32_t errCode = IPC::UserDefineIPCClient().SetUserId(context->userId).Call(context->businessCode, reqBody, respBody);
if (errCode != 0) {
NAPI_ERR_LOG("after IPC::UserDefineIPCClient().Call, errCode: %{public}d.", errCode);
return errCode;
}
outUri = respBody.outUri;
return respBody.fileId;
}
static int32_t CallPhotoAccessCreateAssetsWithAlbum(MediaLibraryAsyncContext* context,
const DataShareValuesBucket &valuesBucket, std::string &outUri)
{
bool isValid = false;
CreateAssetsWithAlbumReqBody reqBody;
reqBody.mediaType = valuesBucket.Get(CONST_MEDIA_DATA_DB_MEDIA_TYPE, isValid);
string extension = valuesBucket.Get(CONST_ASSET_EXTENTION, isValid);
string title = valuesBucket.Get(MediaColumn::MEDIA_TITLE, isValid);
string ownerAlbumId = valuesBucket.Get(PhotoColumn::PHOTO_OWNER_ALBUM_ID, isValid);
bool isRealTimeThumb = context->isRealTimeThumb;
reqBody.title = title;
reqBody.extension = extension;
reqBody.isRealTimeThumb = isRealTimeThumb;
if (context->isContainsAlbumUri) {
reqBody.ownerAlbumId = ownerAlbumId;
}
CreateAssetsWithAlbumRespBody respBody;
int32_t errCode = IPC::UserDefineIPCClient().SetUserId(context->userId).Call(context->businessCode, reqBody, respBody);
if (errCode != 0) {
NAPI_ERR_LOG("after IPC::UserDefineIPCClient().Call, errCode: %{public}d.", errCode);
return errCode;
}
outUri = respBody.outUri;
return respBody.fileId;
}
static void PhotoAccessAgentCreateAssetsExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSCreateAssetExecute");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
string uri;
GetCreateUri(context, uri);
if (context->isContainsAlbumUri) {
bool isValid = CheckAlbumUri(env, context->valuesBucketArray[0], context);
if (!isValid) {
context->error = JS_ERR_PARAMETER_INVALID;
return;
}
}
if (context->tokenId != 0) {
NAPI_INFO_LOG("tokenId: %{public}d", context->tokenId);
MediaLibraryNapiUtils::UriAppendKeyValue(uri, TOKEN_ID, to_string(context->tokenId));
}
Uri createFileUri(uri);
for (const auto& valuesBucket : context->valuesBucketArray) {
bool inValid = false;
string title = valuesBucket.Get(MediaColumn::MEDIA_TITLE, inValid);
if (!context->isContainsAlbumUri && !title.empty() && MediaFileUtils::CheckTitleCompatible(title) != E_OK) {
NAPI_ERR_LOG("Title contains invalid characters: %{private}s, skipping", title.c_str());
context->uriArray.push_back(to_string(E_INVALID_DISPLAY_NAME));
continue;
}
string outUri;
int index = -EINVAL;
if (context->businessCode != 0) {
index = CallPhotoAccessCreateAssetForApp(context, valuesBucket, outUri);
} else {
index = UserFileClient::InsertExt(createFileUri, valuesBucket, outUri, context->userId);
}
if (index < 0) {
if (index == E_PERMISSION_DENIED || index == -E_CHECK_SYSTEMAPP_FAIL) {
context->SaveError(index);
NAPI_ERR_LOG("PERMISSION_DENIED, index: %{public}d.", index);
return;
}
if (index == E_HAS_DB_ERROR) {
index = OHOS_INVALID_PARAM_CODE;
}
context->uriArray.push_back(to_string(index));
NAPI_ERR_LOG("InsertExt fail, index: %{public}d title: %{public}s.", index, title.c_str());
} else {
context->uriArray.push_back(move(outUri));
}
}
}
static void JSStartAssetAnalysisExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSStartAssetAnalysisExecute");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
if (!context->isFullAnalysis && context->uris.empty()) {
NAPI_INFO_LOG("asset uris are empty");
return;
}
context->taskId = ForegroundAnalysisMeta::GetIncTaskId();
uint32_t businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::START_ASSET_ANALYSIS);
StartAssetAnalysisReqBody reqBody;
StartAssetAnalysisRespBody respBody;
std::vector<std::string> fileIds;
for (const auto &uri : context->uris) {
std::string fileId = MediaLibraryNapiUtils::GetFileIdFromUriString(uri);
if (!fileId.empty()) {
fileIds.push_back(fileId);
}
}
if (!fileIds.empty()) {
reqBody.predicates.In(PhotoColumn::PHOTOS_TABLE + "." + PhotoColumn::MEDIA_ID, fileIds);
}
int errCode = IPC::UserDefineIPCClient().SetUserId(context->userId).Call(businessCode, reqBody, respBody);
if (respBody.resultSet != nullptr) {
respBody.resultSet->Close();
}
if (errCode != E_OK) {
if (errCode == E_PERMISSION_DENIED) {
context->error = OHOS_PERMISSION_DENIED_CODE;
} else {
context->SaveError(errCode);
}
NAPI_ERR_LOG("Start assets analysis failed! errCode is = %{public}d", errCode);
}
}
static napi_value ParseArgsCreateAgentCreateAssetsWithMode(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
BundleInfo bundleInfo;
CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[ARGS_ZERO],
bundleInfo.bundleName) == napi_ok, "Failed to get bundleName");
CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[ARGS_ONE],
bundleInfo.packageName) == napi_ok, "Failed to get appName");
CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[ARGS_TWO],
bundleInfo.appId) == napi_ok, "Failed to get appId");
napi_value result = nullptr;
NAPI_CALL(env, napi_get_boolean(env, true, &result));
vector<napi_value> napiValues;
CHECK_NULLPTR_RET(MediaLibraryNapiUtils::GetNapiValueArray(env, context->argv[ARGS_FIVE], napiValues));
if (napiValues.empty()) {
return result;
}
for (const auto& napiValue : napiValues) {
CHECK_COND_WITH_MESSAGE(env, ParseCreateConfig(env, napiValue, bundleInfo, *context) == napi_ok,
"Parse asset create config failed");
}
CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::GetParamCallback(env, context)
== napi_ok, "Failed to get callback");
return result;
}
static napi_value ParseArgsAgentCreateAssetsWithMode(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
constexpr size_t minArgs = ARGS_SIX;
constexpr size_t maxArgs = ARGS_SIX;
NAPI_ASSERT(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs) ==
napi_ok, "Failed to get object info");
context->isCreateByComponent = false;
context->isCreateByAgent = true;
context->needSystemApp = true;
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
return ParseArgsCreateAgentCreateAssetsWithMode(env, info, context);
}
napi_value MediaLibraryNapi::PhotoAccessHelperAgentCreateAssetsWithMode(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessHelperAgentCreateAssetsWithMode");
NAPI_INFO_LOG("enter");
int32_t authorizationMode = -1;
int32_t tokenId = -1;
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::PAH_SYSTEM_CREATE_ASSET_FOR_APP_WITH_MODE);
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
asyncContext->assetType = TYPE_PHOTO;
NAPI_ASSERT(env, ParseArgsAgentCreateAssetsWithMode(env, info, asyncContext), "Failed to parse js args");
CHECK_NULLPTR_RET(MediaLibraryNapiUtils::GetInt32Arg(env, asyncContext->argv[ARGS_THREE], tokenId));
CHECK_NULLPTR_RET(MediaLibraryNapiUtils::GetInt32Arg(env, asyncContext->argv[ARGS_FOUR], authorizationMode));
CHECK_COND_WITH_MESSAGE(env, authorizationMode == SaveType::SHORT_IMAGE_PERM, "authorizationMode is error");
asyncContext->tokenId = static_cast<uint32_t>(tokenId);
int ret = Security::AccessToken::AccessTokenKit::GrantPermissionForSpecifiedTime(
tokenId, PERM_SHORT_TERM_WRITE_IMAGEVIDEO, SHORT_TERM_PERMISSION_DURATION_300S);
if (ret != E_SUCCESS) {
NapiError::ThrowError(env, OHOS_PERMISSION_DENIED_CODE, "This app have no short permission");
return nullptr;
}
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "PhotoAccessHelperAgentCreateAssetsWithMode",
PhotoAccessAgentCreateAssetsExecute, JSCreateAssetCompleteCallback);
}
napi_value MediaLibraryNapi::PhotoAccessStartAssetAnalysis(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessStartAssetAnalysis");
NAPI_INFO_LOG("enter");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
asyncContext->assetType = TYPE_PHOTO;
CHECK_COND_WITH_MESSAGE(env, ParseArgsStartAssetAnalysis(env, info, asyncContext) != nullptr,
"Failed to parse js args");
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "PhotoAccessStartAssetAnalysis",
JSStartAssetAnalysisExecute, JSStartAssetAnalysisCallback);
}
static void JSActiveAnalysisCompleteCallback(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSActiveAnalysisCompleteCallback");
auto *context = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->data), JS_INNER_FAIL);
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), JS_INNER_FAIL);
if (status == napi_ok && context->error == ERR_DEFAULT && context->retVal == E_OK) {
CHECK_ARGS_RET_VOID(env, napi_create_int32(env, context->taskId, &jsContext->data), JS_INNER_FAIL);
jsContext->status = true;
} else {
CHECK_ARGS_RET_VOID(env, napi_create_int32(env, -1, &jsContext->data), JS_INNER_FAIL);
int32_t rawErrorCode = context->error != ERR_DEFAULT ? context->error :
MediaLibraryNapiUtils::TransErrorCode("PhotoAccessStartActiveAnalysis", context->retVal);
int32_t jsErrorCode = NormalizeActiveAnalysisPromiseErrorCode(rawErrorCode);
std::string errorMessage = GetActiveAnalysisPromiseErrorMessage(jsErrorCode);
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, jsErrorCode, errorMessage);
}
NAPI_INFO_LOG("JSActiveAnalysisCompleteCallback finish, status: %{public}d, error: %{public}d, retVal: "
"%{public}d, taskId: %{public}d", static_cast<int32_t>(status), context->error, context->retVal,
context->taskId);
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
static void ReleaseStartActiveAnalysisCallback(MediaLibraryAsyncContext *context, uint64_t callbackRegistryId)
{
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
ActiveAnalysisJsCallbackRegistry::Unregister(callbackRegistryId);
CHECK_NULL_PTR_RETURN_VOID(context->activeAnalysisCallbackHolder.get(), "Active analysis callback holder is null");
context->activeAnalysisCallbackHolder->Release();
}
static bool PrepareStartActiveAnalysisCallback(MediaLibraryAsyncContext *context, StartActiveAnalysisReqBody &reqBody,
uint64_t &callbackRegistryId)
{
sptr<ActiveAnalysisJsCallbackStub> callbackStub =
sptr<ActiveAnalysisJsCallbackStub>(new (std::nothrow) ActiveAnalysisJsCallbackStub(
context->activeAnalysisCallbackHolder));
if (callbackStub == nullptr) {
NAPI_ERR_LOG("Failed to create active analysis callback stub");
context->activeAnalysisCallbackHolder->Release();
context->retVal = MEDIA_LIBRARY_INTERNAL_SYSTEM_ERROR;
return false;
}
reqBody.callbackRemote = callbackStub->AsObject();
NAPI_INFO_LOG("Prepared active analysis callback remote, callbackStubValid: %{public}d, "
"callbackRemoteValid: %{public}d", callbackStub != nullptr, reqBody.callbackRemote != nullptr);
callbackRegistryId = ActiveAnalysisJsCallbackRegistry::Register(
context->activeAnalysisCallbackHolder, callbackStub, reqBody.callbackRemote);
if (callbackRegistryId != 0) {
return true;
}
NAPI_ERR_LOG("Failed to register active analysis callback lifecycle record");
context->activeAnalysisCallbackHolder->Release();
context->retVal = MEDIA_LIBRARY_INTERNAL_SYSTEM_ERROR;
return false;
}
static StartActiveAnalysisReqBody BuildStartActiveAnalysisReqBody(MediaLibraryAsyncContext *context)
{
StartActiveAnalysisReqBody reqBody;
reqBody.analysisTypes = context->activeAnalysisTypes;
reqBody.fileIds = context->activeAnalysisFileIds;
reqBody.param = context->activeAnalysisParam;
NAPI_INFO_LOG("Start active analysis execute, typeSize: %{public}zu, fileIdSize: %{public}zu, paramSize:"
" %{public}zu, holderValid: %{public}d", reqBody.analysisTypes.size(), reqBody.fileIds.size(),
reqBody.param.size(), context->activeAnalysisCallbackHolder != nullptr);
return reqBody;
}
static bool HandleStartActiveAnalysisResponse(MediaLibraryAsyncContext *context, uint64_t callbackRegistryId,
int32_t ret, const StartActiveAnalysisRespBody &respBody)
{
if (ret != E_OK) {
ReleaseStartActiveAnalysisCallback(context, callbackRegistryId);
if (ret == E_INVALID_ARGUMENTS) {
context->error = JS_E_PARAM_INVALID;
return false;
}
context->retVal = ret;
if (ret < 0) {
context->SaveError(ret);
}
return false;
}
context->retVal = NormalizeActiveAnalysisErrorCode(respBody.result);
if (context->retVal != E_OK) {
ReleaseStartActiveAnalysisCallback(context, callbackRegistryId);
return false;
}
if (respBody.saRemote == nullptr) {
NAPI_ERR_LOG("Active analysis saRemote is nullptr");
context->retVal = MEDIA_LIBRARY_INTERNAL_SYSTEM_ERROR;
ReleaseStartActiveAnalysisCallback(context, callbackRegistryId);
return false;
}
NAPI_INFO_LOG("Bind active analysis saRemote after successful start, saRemoteValid: %{public}d",
respBody.saRemote != nullptr);
switch (context->activeAnalysisCallbackHolder->BindSaRemote(respBody.saRemote)) {
case ActiveAnalysisSaBindResult::BOUND:
return true;
case ActiveAnalysisSaBindResult::ALREADY_COMPLETED:
NAPI_WARN_LOG("Skip treating active analysis start as failure because result arrived before SA binding");
return true;
case ActiveAnalysisSaBindResult::INVALID_REMOTE:
NAPI_ERR_LOG("Active analysis saRemote is invalid during bind");
break;
case ActiveAnalysisSaBindResult::ADD_DEATH_RECIPIENT_FAILED:
NAPI_ERR_LOG("Failed to bind active analysis saRemote death recipient");
break;
case ActiveAnalysisSaBindResult::HOLDER_RELEASED:
NAPI_ERR_LOG("Active analysis callback holder released before SA binding completed");
break;
default:
NAPI_ERR_LOG("Unknown active analysis SA bind result");
break;
}
context->retVal = MEDIA_LIBRARY_INTERNAL_SYSTEM_ERROR;
ReleaseStartActiveAnalysisCallback(context, callbackRegistryId);
return false;
}
static StopActiveAnalysisReqBody BuildStopActiveAnalysisReqBody(MediaLibraryAsyncContext *context)
{
StopActiveAnalysisReqBody reqBody;
reqBody.analysisTypes = context->activeAnalysisTypes;
reqBody.fileIds = context->activeAnalysisFileIds;
reqBody.param = context->activeAnalysisParam;
return reqBody;
}
static int32_t NormalizeStopActiveAnalysisErrorCode(int32_t code)
{
if (code == E_INVALID_ARGUMENTS || code == JS_E_PARAM_INVALID) {
return MEDIA_LIBRARY_INVALID_PARAMETER_ERROR;
}
if (code == E_PERMISSION_DENIED || code == JS_ERR_PERMISSION_DENIED) {
return OHOS_PERMISSION_DENIED_CODE;
}
if (code == -E_CHECK_SYSTEMAPP_FAIL) {
return E_CHECK_SYSTEMAPP_FAIL;
}
if (code == OHOS_PERMISSION_DENIED_CODE || code == E_CHECK_SYSTEMAPP_FAIL ||
code == MEDIA_LIBRARY_INVALID_PARAMETER_ERROR || code == MEDIA_LIBRARY_INTERNAL_SYSTEM_ERROR) {
return code;
}
return NormalizeActiveAnalysisPromiseErrorCode(
MediaLibraryNapiUtils::TransErrorCode("PhotoAccessStopActiveAnalysis", code));
}
static void ThrowStopActiveAnalysisError(napi_env env, int32_t code)
{
int32_t jsErrorCode = NormalizeStopActiveAnalysisErrorCode(code);
NapiError::ThrowError(env, jsErrorCode, GetActiveAnalysisPromiseErrorMessage(jsErrorCode));
}
static void JSStartActiveAnalysisExecute(napi_env env, void *data)
{
(void)env;
MediaLibraryTracer tracer;
tracer.Start("JSStartActiveAnalysisExecute");
auto *context = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
CHECK_NULL_PTR_RETURN_VOID(context->activeAnalysisCallbackHolder.get(), "Active analysis callback holder is null");
context->taskId = ForegroundAnalysisMeta::GetIncTaskId();
StartActiveAnalysisReqBody reqBody = BuildStartActiveAnalysisReqBody(context);
uint64_t callbackRegistryId = 0;
if (!PrepareStartActiveAnalysisCallback(context, reqBody, callbackRegistryId)) {
return;
}
StartActiveAnalysisRespBody respBody;
int32_t ret = IPC::UserDefineIPCClient().SetUserId(context->userId).Call(
static_cast<uint32_t>(MediaLibraryBusinessCode::START_ACTIVE_ANALYSIS), reqBody, respBody);
NAPI_INFO_LOG("Active analysis IPC returned, ret: %{public}d, resp.result: %{public}d, "
"saRemoteValid: %{public}d", ret, respBody.result, respBody.saRemote != nullptr);
(void)HandleStartActiveAnalysisResponse(context, callbackRegistryId, ret, respBody);
}
napi_value MediaLibraryNapi::PhotoAccessStartActiveAnalysis(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessStartActiveAnalysis");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
asyncContext->assetType = TYPE_PHOTO;
CHECK_NULLPTR_RET(ParseArgsStartActiveAnalysis(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "PhotoAccessStartActiveAnalysis",
JSStartActiveAnalysisExecute, JSActiveAnalysisCompleteCallback);
}
napi_value MediaLibraryNapi::PhotoAccessStopActiveAnalysis(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessStopActiveAnalysis");
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_undefined(env, &result), JS_INNER_FAIL);
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
asyncContext->assetType = TYPE_PHOTO;
CHECK_NULLPTR_RET(ParseArgsStopActiveAnalysis(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
StopActiveAnalysisReqBody reqBody = BuildStopActiveAnalysisReqBody(asyncContext.get());
StopActiveAnalysisRespBody respBody;
int32_t ret = IPC::UserDefineIPCClient().SetUserId(asyncContext->userId).Call(
static_cast<uint32_t>(MediaLibraryBusinessCode::STOP_ACTIVE_ANALYSIS), reqBody, respBody);
if (ret != E_OK) {
ThrowStopActiveAnalysisError(env, ret);
return result;
}
int32_t stopResult = NormalizeActiveAnalysisErrorCode(respBody.result);
if (stopResult != E_OK) {
ThrowStopActiveAnalysisError(env, stopResult);
}
return result;
}
static void PhotoAccessQueryExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessQueryExecute");
auto *context = static_cast<ResultSetAsyncContext *>(data);
if (context == nullptr) {
NAPI_ERR_LOG("Execute context is nullptr");
return;
}
context->queryRet = UserFileClient::QueryByStep(context->uri);
if (context->queryRet == nullptr) {
context->error = OHOS_PERMISSION_DENIED_CODE;
context->errorMsg = "Permission denied";
return;
}
int count = -1;
context->queryRet->GetRowCount(count);
if (count == -1) {
context->queryRet = nullptr;
context->error = JS_E_PARAM_INVALID;
context->errorMsg = "Invalid sql";
return;
}
return;
}
static void PhotoAccessQueryCompleteCallback(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessQueryCompleteCallback");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
status = napi_get_undefined(env, &jsContext->data);
if (status != napi_ok) {
NAPI_ERR_LOG("Napi env error");
return;
}
napi_get_undefined(env, &jsContext->error);
auto *context = static_cast<ResultSetAsyncContext *>(data);
if (context != nullptr) {
if (context->error == ERR_DEFAULT && context->queryRet != nullptr) {
jsContext->data = ResultSetNapi::CreateResultSetNapi(env, context->queryRet, *jsContext);
if (jsContext->data == nullptr) {
NAPI_ERR_LOG("CreateResultSetNapi failed");
} else {
jsContext->status = true;
context->queryRet = nullptr;
}
} else {
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, context->error, context->errorMsg);
}
} else {
NAPI_ERR_LOG("Callback context is nullptr");
return;
}
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef, context->work,
*jsContext);
}
delete context;
return;
}
napi_value MediaLibraryNapi::PhotoAccessQuery(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessQuery");
CHECK_COND_WITH_ERR_MESSAGE(env, MediaLibraryNapiUtils::IsSystemApp(), UFM_SYSCAP_BASE, "Not system app");
unique_ptr<ResultSetAsyncContext> asyncContext = make_unique<ResultSetAsyncContext>();
CHECK_COND_WITH_ERR_MESSAGE(
env, MediaLibraryNapiUtils::ParseArgsStringCallback(env, info, asyncContext, asyncContext->uri) == napi_ok,
JS_E_PARAM_INVALID, "Failed to get resourceUrl from arguments or wrong param");
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "PhotoAccessQuery", PhotoAccessQueryExecute,
PhotoAccessQueryCompleteCallback);
}
napi_value MediaLibraryNapi::PhotoAccessHelperAgentCreateAssets(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessHelperAgentCreateAssets");
NAPI_INFO_LOG("enter");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::PAH_SYSTEM_CREATE_ASSET_FOR_APP);
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
asyncContext->assetType = TYPE_PHOTO;
asyncContext->needSystemApp = true;
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
NAPI_ASSERT(env, ParseArgsAgentCreateAssets(env, info, asyncContext), "Failed to parse js args");
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "PhotoAccessHelperAgentCreateAssets",
PhotoAccessAgentCreateAssetsExecute, JSCreateAssetCompleteCallback);
}
napi_value MediaLibraryNapi::CreateAssetsForAppWithAlbum(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("CreateAssetsForAppWithAlbum");
NAPI_INFO_LOG("enter");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->businessCode =
static_cast<uint32_t>(MediaLibraryBusinessCode::PAH_SYSTEM_CREATE_ASSET_FOR_APP_WITH_ALBUM);
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
asyncContext->assetType = TYPE_PHOTO;
asyncContext->needSystemApp = true;
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
NAPI_ASSERT(env, ParseArgsCreatePhotoAssetForAppWithAlbum(env, info, asyncContext), "Failed to parse js args");
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "CreateAssetsForAppWithAlbum",
PhotoAccessAgentCreateAssetsExecute, JSCreateAssetCompleteCallback);
}
static void PhotoAccessCreateAssetsWithAlbumExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessCreateAssetsWithAlbumExecute");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
string uri;
GetCreateUri(context, uri);
Uri createFileUri(uri);
CHECK_IF_EQUAL(!context->valuesBucketArray.empty(),
"valuesBucketArray is empty, no assets to create");
if (context->isContainsAlbumUri) {
bool isValid = CheckAlbumUri(env, context->valuesBucketArray[0], context);
if (!isValid) {
context->isContainsAlbumUri = false;
}
}
for (const auto& valuesBucket : context->valuesBucketArray) {
bool inValid = false;
string title = valuesBucket.Get(MediaColumn::MEDIA_TITLE, inValid);
string outUri;
int index = -EINVAL;
if (context->businessCode != 0) {
index = CallPhotoAccessCreateAssetsWithAlbum(context, valuesBucket, outUri);
}
if (index > 0) {
context->uriArray.push_back(move(outUri));
continue;
}
if (index == E_PERMISSION_DENIED || index == -E_CHECK_SYSTEMAPP_FAIL) {
context->SaveError(index);
NAPI_ERR_LOG("PERMISSION_DENIED, index: %{public}d.", index);
return;
}
context->uriArray.push_back("");
NAPI_ERR_LOG("InsertExt fail, index: %{public}d title: %{public}s.", index, title.c_str());
}
}
napi_value MediaLibraryNapi::CreateAssetsWithAlbum(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("CreateAssetsWithAlbum");
NAPI_INFO_LOG("enter");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->businessCode =
static_cast<uint32_t>(MediaLibraryBusinessCode::PAH_SYSTEM_CREATE_ASSET_WITH_ALBUM);
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
asyncContext->assetType = TYPE_PHOTO;
asyncContext->needSystemApp = true;
asyncContext->isCreateByAgent = true;
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
NAPI_ASSERT(env, ParseArgsCreatePhotoAssetWithAlbum(env, info, asyncContext), "Failed to parse js args");
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext,
"CreateAssetsWithAlbum", PhotoAccessCreateAssetsWithAlbumExecute,
JSCreateAssetCompleteCallback);
}
bool MediaLibraryNapi::isSucceedSetting(napi_env env, napi_value &members, napi_value jsResult, std::string &inputKey,
FileAssetNapi* obj)
{
if (jsResult == nullptr) {
return false;
}
if (napi_set_named_property(env, members, inputKey.c_str(), jsResult) != napi_ok) {
napi_throw_error(env, nullptr, "Failed to set property");
return false;
}
return true;
}
static void checkKeyInEnum(napi_env env, std::string inputKey) {
static const std::unordered_set<std::string> enumValues = []() {
std::unordered_set<std::string> values;
values.reserve(IMAGEVIDEOKEY_ENUM_PROPERTIES.size());
for (const auto& pair : IMAGEVIDEOKEY_ENUM_PROPERTIES) {
values.insert(pair.second);
}
return values;
} ();
if (enumValues.find(inputKey) != enumValues.end()) {
NapiError::ThrowError(env, JS_E_PARAM_INVALID);
} else {
NapiError::ThrowError(env, JS_E_INPUT_INVALID);
}
}
napi_value MediaLibraryNapi::ProcessSingleAsset(napi_env env, napi_value asset, std::vector<std::string>& inputKeys)
{
FileAssetNapi *obj = nullptr;
napi_status status = napi_unwrap(env, asset, reinterpret_cast<void **>(&obj));
if (obj == nullptr || status != napi_ok) {
NapiError::ThrowError(env, JS_ERR_PARAMETER_INVALID, "Parse args in array failed");
}
napi_value members;
if (napi_create_object(env, &members) != napi_ok) {
napi_throw_error(env, nullptr, "Failed to create members");
return nullptr;
}
for (auto &inputKey : inputKeys) {
napi_value jsResult = nullptr;
napi_get_undefined(env, &jsResult);
if (DATE_TRANSITION_MAP.count(inputKey) != 0) {
jsResult = FileAssetNapi::HandleDateTransitionKey(env, DATE_TRANSITION_MAP.at(inputKey), obj->fileAssetPtr);
if (!isSucceedSetting(env, members, jsResult, inputKey, obj)) {
return nullptr;
}
continue;
}
if (obj->fileAssetPtr->GetMemberMap().count(inputKey) == 0) {
checkKeyInEnum(env, inputKey);
return nullptr;
}
if (FileAssetNapi::IsSpecialKey(inputKey)) {
jsResult = FileAssetNapi::HandleGettingSpecialKey(env, inputKey, obj->fileAssetPtr);
} else if (inputKey == PhotoColumn::PHOTO_DETAIL_TIME) {
jsResult = FileAssetNapi::HandleGettingDetailTimeKey(env, obj->fileAssetPtr);
} else {
auto m = obj->fileAssetPtr->GetMemberMap().at(inputKey);
if (m.index() == MEMBER_TYPE_STRING) {
napi_create_string_utf8(env, get<string>(m).c_str(), NAPI_AUTO_LENGTH, &jsResult);
} else if (m.index() == MEMBER_TYPE_INT32) {
napi_create_int32(env, get<int32_t>(m), &jsResult);
} else if (m.index() == MEMBER_TYPE_INT64) {
napi_create_int64(env, FileAssetNapi::GetCompatDate(inputKey, get<int64_t>(m)), &jsResult);
} else {
NapiError::ThrowError(env, JS_ERR_PARAMETER_INVALID);
return nullptr;
}
}
if (!isSucceedSetting(env, members, jsResult, inputKey, obj)) {
napi_throw_error(env, nullptr, "Failed to set property");
return nullptr;
}
}
return members;
}
napi_value MediaLibraryNapi::PhotoAccessHelperGetAssetMemberBatch(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessHelperGetAssetMemberBatch");
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
size_t argc = 2;
napi_value args[2];
if (napi_get_cb_info(env, info, &argc, args, NULL, NULL) != napi_ok) {
NapiError::ThrowError(env, JS_ERR_PARAMETER_INVALID, "Parse args failed");
return nullptr;
}
std::vector<napi_value> fileAssetArray;
CHECK_NULLPTR_RET(MediaLibraryNapiUtils::GetNapiValueArray(env, args[0], fileAssetArray));
std::vector<std::string> inputKeys;
if (MediaLibraryNapiUtils::GetStringArray(env, args[1], inputKeys) != napi_ok || inputKeys.empty() ||
fileAssetArray.empty()) {
NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "invalid input parameter");
return nullptr;
}
for (auto& inputKey: inputKeys) {
if (FileAssetNapi::CheckSystemApiKeys(env, inputKey) < 0) {
return nullptr;
}
}
napi_value resultArray;
if (napi_create_array_with_length(env, fileAssetArray.size(), &resultArray) != napi_ok) {
napi_throw_error(env, nullptr, "Failed to create array");
return nullptr;
}
for (size_t i = 0; i < fileAssetArray.size(); i++) {
napi_value membersObj = MediaLibraryNapi::ProcessSingleAsset(env, fileAssetArray[i], inputKeys);
if (!membersObj) {
return nullptr;
}
if (napi_set_element(env, resultArray, i, membersObj) != napi_ok) {
return nullptr;
}
}
if (resultArray == nullptr) {
NapiError::ThrowError(env, JS_E_FILE_KEY);
return nullptr;
}
return resultArray;
}
napi_value MediaLibraryNapi::CreateAssetsHasPermission(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessHelperAgentCreateAssets");
NAPI_INFO_LOG("enter");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::PAH_PUBLIC_CREATE_ASSET_FOR_APP);
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
asyncContext->assetType = TYPE_PHOTO;
NAPI_ASSERT(env, ParseArgsAgentCreateAssets(env, info, asyncContext), "Failed to parse js args");
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "PhotoAccessHelperAgentCreateAssets",
PhotoAccessAgentCreateAssetsExecute, JSCreateAssetCompleteCallback);
}
napi_value MediaLibraryNapi::PhotoAccessHelperOnCallback(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessHelperOnCallback");
int64_t startTime = MediaFileUtils::UTCTimeMilliSeconds();
napi_value undefinedResult = nullptr;
napi_get_undefined(env, &undefinedResult);
std::string errCode = "0";
size_t argc = ARGS_THREE;
napi_value argv[ARGS_THREE] = {nullptr};
napi_value thisVar = nullptr;
GET_JS_ARGS(env, info, argc, argv, thisVar);
if (argc == ARGS_TWO) {
return JSOnCallback(env, info);
}
NAPI_ASSERT(env, argc == ARGS_THREE, "requires 3 parameters");
MediaLibraryNapi *obj = nullptr;
napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void**>(&obj));
if (status == napi_ok && obj != nullptr) {
napi_valuetype valueType = napi_undefined;
if (napi_typeof(env, argv[PARAM0], &valueType) != napi_ok || valueType != napi_string ||
napi_typeof(env, argv[PARAM1], &valueType) != napi_ok || valueType != napi_boolean ||
napi_typeof(env, argv[PARAM2], &valueType) != napi_ok || valueType != napi_function) {
NapiError::ThrowError(env, JS_ERR_PARAMETER_INVALID);
errCode = std::to_string(JS_ERR_PARAMETER_INVALID);
CreateRecordKitAsyncWork(env, obj, startTime, MediaLibraryAsyncContext::REGISTER_CHANGE, errCode);
return undefinedResult;
}
char buffer[ARG_BUF_SIZE];
size_t res = 0;
if (napi_get_value_string_utf8(env, argv[PARAM0], buffer, ARG_BUF_SIZE, &res) != napi_ok) {
NapiError::ThrowError(env, JS_ERR_PARAMETER_INVALID);
errCode = std::to_string(JS_ERR_PARAMETER_INVALID);
CreateRecordKitAsyncWork(env, obj, startTime, MediaLibraryAsyncContext::REGISTER_CHANGE, errCode);
return undefinedResult;
}
string uri = string(buffer);
bool isDerived = false;
if (napi_get_value_bool(env, argv[PARAM1], &isDerived) != napi_ok) {
NapiError::ThrowError(env, JS_ERR_PARAMETER_INVALID);
errCode = std::to_string(JS_ERR_PARAMETER_INVALID);
CreateRecordKitAsyncWork(env, obj, startTime, MediaLibraryAsyncContext::REGISTER_CHANGE, errCode);
return undefinedResult;
}
const int32_t refCount = 1;
napi_ref cbOnRef = nullptr;
napi_create_reference(env, argv[PARAM2], refCount, &cbOnRef);
tracer.Start("RegisterNotifyChange");
if (CheckRef(env, cbOnRef, *g_listObj, false, uri)) {
obj->RegisterNotifyChange(env, uri, isDerived, cbOnRef, *g_listObj);
} else {
NapiError::ThrowError(env, JS_ERR_PARAMETER_INVALID);
errCode = std::to_string(JS_ERR_PARAMETER_INVALID);
CreateRecordKitAsyncWork(env, obj, startTime, MediaLibraryAsyncContext::REGISTER_CHANGE, errCode);
napi_delete_reference(env, cbOnRef);
cbOnRef = nullptr;
return undefinedResult;
}
tracer.Finish();
}
CreateRecordKitAsyncWork(env, obj, startTime, MediaLibraryAsyncContext::REGISTER_CHANGE, errCode);
return undefinedResult;
}
std::string MediaLibraryNapiUtils::GetSingleIdFromNapiAssets(
napi_env env, const napi_value &napiAsset)
{
FileAssetNapi *obj = nullptr;
auto err = napi_unwrap(env, napiAsset, reinterpret_cast<void **>(&obj));
if (err != napi_ok) {
NapiError::ThrowError(env, JS_E_PARAM_INVALID, "napi_unwrap failed to get asset object");
return "";
}
if (obj == nullptr) {
NapiError::ThrowError(env, JS_E_PARAM_INVALID, "asset napi object is nullptr");
return "";
}
if (obj->IsHidden() || obj->IsTrash()) {
NAPI_ERR_LOG("Skip invalid asset (hidden or trash), assetId: %{public}d", obj->GetFileId());
NapiError::ThrowError(env, JS_E_PARAM_INVALID, "Check Whether It Is A Hidden Or Recycled Asset");
return "";
}
if ((obj->GetMediaType() != MEDIA_TYPE_IMAGE && obj->GetMediaType() != MEDIA_TYPE_VIDEO)) {
NAPI_ERR_LOG("Skip invalid asset, mediaType: %{public}d", obj->GetMediaType());
NapiError::ThrowError(env, JS_E_PARAM_INVALID,
"Check Whether It Is Not A MEDIA_TYPE_IMAGE Or MEDIA_TYPE_VIDEO");
return "";
}
std::string fileId = to_string(obj->GetFileId());
if (obj->GetFileId() == 0) {
NAPI_ERR_LOG("Get invalid asset ID from asset object");
NapiError::ThrowError(env, JS_E_PARAM_INVALID,"Ordinary assets invalid");
} else {
NAPI_INFO_LOG("Successfully extracted asset Id: %{private}s", fileId.c_str());
}
return fileId;
}
std::string MediaLibraryNapiUtils::GetSingleIdFromNapiPhotoAlbum(
napi_env env, const napi_value &napiPhotoAlbum)
{
PhotoAlbumNapi *obj = nullptr;
auto err = napi_unwrap(env, napiPhotoAlbum, reinterpret_cast<void **>(&obj));
if (err != napi_ok) {
NapiError::ThrowError(env, JS_E_PARAM_INVALID, "napi_unwrap failed to get album object");
return "";
}
if (obj == nullptr) {
NapiError::ThrowError(env, JS_E_PARAM_INVALID, "album napi object is nullptr");
return "";
}
PhotoAlbumSubType albumSubType = obj->GetPhotoAlbumSubType();
if (albumSubType == PhotoAlbumSubType::TRASH || albumSubType == PhotoAlbumSubType::HIDDEN) {
NAPI_ERR_LOG("Skip invalid album (trash or hidden), albumId: %{public}d, subType: %{public}d",
obj->GetAlbumId(), static_cast<int32_t>(albumSubType));
NapiError::ThrowError(env, JS_E_PARAM_INVALID, "Check Whether It Is A Hidden Or Recycled Hide");
return "";
}
std::string albumId = to_string(obj->GetAlbumId());
if (obj->GetAlbumId() == 0) {
NAPI_ERR_LOG("Get invalid album Id from photo album object");
NapiError::ThrowError(env, JS_E_PARAM_INVALID,"Ordinary Album invalid");
} else {
NAPI_INFO_LOG("Successfully extracted album Id: %{private}s", albumId.c_str());
}
return albumId;
}
int32_t MediaLibraryNapi::AddSingleClientObserver(napi_env env, napi_ref ref,
std::shared_ptr<MediaOnNotifyNewObserver> &observer,
const Notification::NotifyUriType uriType, const std::string &fileIdOrAlbumId)
{
auto& uriMap = observer->singleClientObservers_[uriType];
auto uriIter = uriMap.find(fileIdOrAlbumId);
if (uriIter == uriMap.end()) {
return HandleNewUriRegistration(env, ref, observer, uriType, fileIdOrAlbumId);
}
CheckSinglePhotoPermissionReqBody reqBody;
reqBody.registerType = static_cast<int32_t>(uriType);
reqBody.fileId = fileIdOrAlbumId;
uint32_t businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::CHECK_SINGLE_PHOTO_CHANGE_PERMISSION);
int32_t ret = IPC::UserDefineIPCClient().Call(businessCode, reqBody);
CHECK_AND_RETURN_RET_LOG(ret == E_OK, ret, "Failed to check permission, ret: %{public}d", ret);
return HandleExistingUriCheck(env, ref, uriIter->second, uriType, fileIdOrAlbumId);
}
int32_t MediaLibraryNapi::HandleNewUriRegistration(napi_env env, napi_ref ref,
std::shared_ptr<MediaOnNotifyNewObserver> &observer,
const Notification::NotifyUriType uriType, const std::string &fileIdOrAlbumId)
{
Notification::NotifyUriType registerUriType = Notification::NotifyUriType::INVALID;
std::string registerUri = "";
if (MediaLibraryNotifyUtils::GetSingleNotifyTypeAndUri(uriType, registerUriType, registerUri) != E_OK) {
NAPI_ERR_LOG("Failed to get registerUriType registerUri");
return JS_E_PARAM_INVALID;
}
std::vector<std::shared_ptr<ClientObserver>> newObservers;
auto clientObserver = std::make_shared<ClientObserver>(uriType, ref);
if (clientObserver == nullptr) {
NAPI_ERR_LOG("Failed to create ClientObserver");
return JS_E_PARAM_INVALID;
}
auto ret = UserFileClient::RegisterObserverExtProvider(Uri(registerUri + URI_SEPARATOR + fileIdOrAlbumId),
static_cast<std::shared_ptr<DataShare::DataShareObserver>>(observer), false);
if (ret != E_OK) {
NAPI_ERR_LOG("failed to register observer, ret: %{public}d, uri: %{private}s", ret, fileIdOrAlbumId.c_str());
return ret;
}
std::lock_guard<std::mutex> lock(ChangeListenerNapi::trashMutex_);
newObservers.push_back(clientObserver);
observer->singleClientObservers_[uriType][fileIdOrAlbumId] = newObservers;
return E_OK;
}
int32_t MediaLibraryNapi::HandleExistingUriCheck(napi_env env, napi_ref ref,
std::vector<std::shared_ptr<ClientObserver>> &existingObservers,
const Notification::NotifyUriType uriType, const std::string &fileIdOrAlbumId)
{
napi_value callback = nullptr;
napi_status status = napi_get_reference_value(env, ref, &callback);
if (status != napi_ok) {
NAPI_ERR_LOG("napi_get_reference_value failed, status: %{public}d", status);
return OHOS_PERMISSION_DENIED_CODE;
}
bool hasRegister = false;
for (auto &obs : existingObservers) {
napi_value onCallback = nullptr;
status = napi_get_reference_value(env, obs->ref_, &onCallback);
if (status == napi_ok) {
napi_strict_equals(env, callback, onCallback, &hasRegister);
}
if (hasRegister) {
NAPI_ERR_LOG("clientObserver hasRegister");
return JS_E_PARAM_INVALID;
}
}
if (!hasRegister) {
auto clientObserver = std::make_shared<ClientObserver>(uriType, ref);
if (clientObserver == nullptr) {
NAPI_ERR_LOG("Failed to create ClientObserver");
return JS_E_PARAM_INVALID;
}
std::lock_guard<std::mutex> lock(ChangeListenerNapi::trashMutex_);
existingObservers.push_back(clientObserver);
}
return E_OK;
}
int32_t MediaLibraryNapi::RegisterObserverExecute(napi_env env, napi_ref ref,
ChangeListenerNapi &listObj, const Notification::NotifyUriType uriType, const std::string &fileIdOrAlbumId)
{
Notification::NotifyUriType registerUriType = Notification::NotifyUriType::INVALID;
std::string registerUri = "";
if (MediaLibraryNotifyUtils::GetSingleNotifyTypeAndUri(uriType, registerUriType, registerUri) != E_OK) {
NAPI_ERR_LOG("Failed to get registerUriType registerUri");
return JS_E_PARAM_INVALID;
}
for (auto it = listObj.newObservers_.begin(); it != listObj.newObservers_.end(); it++) {
Notification::NotifyUriType observerUri = (*it)->uriType_;
if (observerUri == registerUriType) {
NAPI_INFO_LOG("Found existing observer for registerUriType: %{public}d, adding client observer with uriType: %{public}d",
registerUriType, uriType);
return AddSingleClientObserver(env, ref, *it, uriType, fileIdOrAlbumId);
}
}
shared_ptr<MediaOnNotifyNewObserver> observer =
make_shared<MediaOnNotifyNewObserver>(registerUriType, registerUri, env);
Uri notifyUri(registerUri);
int32_t ret = UserFileClient::RegisterObserverExtProvider(notifyUri,
static_cast<shared_ptr<DataShare::DataShareObserver>>(observer), false);
if (ret != E_OK) {
NAPI_ERR_LOG("failed to register observer, ret: %{public}d, uri: %{private}s", ret, registerUri.c_str());
return ret;
}
ret = AddSingleClientObserver(env, ref, observer, uriType, fileIdOrAlbumId);
if (ret != E_OK) {
int32_t retUnreg = UserFileClient::UnregisterObserverExtProvider(notifyUri,
static_cast<shared_ptr<DataShare::DataShareObserver>>(observer));
if (retUnreg != E_OK) {
NAPI_ERR_LOG("failed to Unregister observer, retUnreg: %{public}d, uri: %{private}s", retUnreg, registerUri.c_str());
return ret;
}
NAPI_ERR_LOG("Failed to add client observer, ret: %{public}d", ret);
return ret;
}
listObj.newObservers_.push_back(observer);
RegisterUnregisterHandlerFunctions::SyncUpdateNormalListener(listObj, registerUriType, observer);
NAPI_INFO_LOG("success to register observer, uriType: %{public}d,"
"registerUriType: %{public}d, fileIdOrAlbumId: %{public}s", uriType, registerUriType, fileIdOrAlbumId.c_str());
return ret;
}
napi_value MediaLibraryNapi::SinglePhotoAccessRegisterCallback(napi_env env, napi_callback_info info)
{
NAPI_INFO_LOG("enter SinglePhotoAccessRegisterCallback");
MediaLibraryTracer tracer;
tracer.Start("SinglePhotoAccessRegisterCallback");
napi_value undefinedResult = nullptr;
napi_get_undefined(env, &undefinedResult);
unique_ptr<MediaLibraryAsyncContext> context = make_unique<MediaLibraryAsyncContext>();
if (RegisterUnregisterHandlerFunctions::CheckRegisterCallbackArgs(env, info, context) == nullptr) {
NAPI_ERR_LOG("Parameter validation failed");
return undefinedResult;
}
int32_t ret = E_OK;
std::string fileId;
fileId = MediaLibraryNapiUtils::GetSingleIdFromNapiAssets(env, context->argv[PARAM0]);
if (fileId.empty()) {
NapiError::ThrowError(env, JS_E_PARAM_INVALID, "Failed to get fileId from photo asset");
return undefinedResult;
}
NAPI_INFO_LOG("SinglePhotoAccessRegisterCallback got fileId: %{public}s", fileId.c_str());
string type = RegisterNotifyType::SINGLE_PHOTO_CHANGE;
Notification::NotifyUriType uriType = Notification::NotifyUriType::INVALID;
if (MediaLibraryNotifyUtils::GetSingleRegisterNotifyType(type, uriType) != E_OK) {
NapiError::ThrowError(env, JS_E_PARAM_INVALID, "The scenario parameter verification fails.");
return undefinedResult;
}
if (!RegisterUnregisterHandlerFunctions::CheckSingleRegisterCount(*g_listObj, uriType)) {
NapiError::ThrowError(env, JS_E_PARAM_INVALID, "Registration has reached the limit.");
return undefinedResult;
}
const int32_t refCount = 1;
napi_ref cbOnRef = nullptr;
if (napi_create_reference(env, context->argv[PARAM1], refCount, &cbOnRef) != napi_ok) {
NapiError::ThrowError(env, JS_E_PARAM_INVALID);
return undefinedResult;
}
ret = RegisterObserverExecute(env, cbOnRef, *g_listObj, uriType, fileId);
if (ret != E_OK) {
NapiError::ThrowError(env, MediaLibraryNotifyUtils::ConvertToJsError(ret));
napi_delete_reference(env, cbOnRef);
return undefinedResult;
}
NAPI_INFO_LOG("SinglePhotoAccessRegisterCallback success for fileId: %{public}s", fileId.c_str());
tracer.Finish();
return undefinedResult;
}
napi_value MediaLibraryNapi::SinglePhotoAlbumRegisterCallback(napi_env env, napi_callback_info info)
{
NAPI_INFO_LOG("enter SinglePhotoAlbumRegisterCallback");
MediaLibraryTracer tracer;
tracer.Start("SinglePhotoAlbumRegisterCallback");
napi_value undefinedResult = nullptr;
napi_get_undefined(env, &undefinedResult);
unique_ptr<MediaLibraryAsyncContext> context = make_unique<MediaLibraryAsyncContext>();
if (RegisterUnregisterHandlerFunctions::CheckRegisterCallbackArgs(env, info, context) == nullptr) {
NAPI_ERR_LOG("Parameter validation failed");
return undefinedResult;
}
int32_t ret = E_OK;
std::string albumUri;
albumUri = MediaLibraryNapiUtils::GetSingleIdFromNapiPhotoAlbum(env, context->argv[PARAM0]);
if (albumUri.empty()) {
NapiError::ThrowError(env, JS_E_PARAM_INVALID, "Failed to get URI from photo album");
return undefinedResult;
}
NAPI_INFO_LOG("SinglePhotoAlbumRegisterCallback got albumUri: %{public}s", albumUri.c_str());
string type = RegisterNotifyType::SINGLE_PHOTO_ALBUM_CHANGE;
Notification::NotifyUriType uriType = Notification::NotifyUriType::INVALID;
if (MediaLibraryNotifyUtils::GetSingleRegisterNotifyType(type, uriType) != E_OK) {
NapiError::ThrowError(env, JS_E_PARAM_INVALID, "The scenario parameter verification fails.");
return undefinedResult;
}
if (!RegisterUnregisterHandlerFunctions::CheckSingleRegisterCount(*g_listObj, uriType)) {
NapiError::ThrowError(env, JS_E_PARAM_INVALID, "Registration has reached the limit.");
return undefinedResult;
}
const int32_t refCount = 1;
napi_ref cbOnRef = nullptr;
if (napi_create_reference(env, context->argv[PARAM1], refCount, &cbOnRef) != napi_ok) {
NapiError::ThrowError(env, JS_E_PARAM_INVALID);
return undefinedResult;
}
ret = RegisterObserverExecute(env, cbOnRef, *g_listObj, uriType, albumUri);
if (ret != E_OK) {
NapiError::ThrowError(env, MediaLibraryNotifyUtils::ConvertToJsError(ret));
napi_delete_reference(env, cbOnRef);
return undefinedResult;
}
NAPI_INFO_LOG("SinglePhotoAlbumRegisterCallback success for album uri: %{public}s", albumUri.c_str());
tracer.Finish();
return undefinedResult;
}
bool MediaLibraryNapi::RegisterAnalysisAccessCallbackInternal(napi_env env, napi_callback_info info,
Notification::NotifyUriType uriType)
{
size_t argc = ARGS_ONE;
napi_value argv[ARGS_ONE] = { nullptr };
napi_value thisVar = nullptr;
GET_JS_ARGS(env, info, argc, argv, thisVar);
if (argc != ARGS_ONE) {
NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "requires only 1 parameter.");
return false;
}
napi_valuetype valueType = napi_undefined;
if (napi_typeof(env, argv[PARAM0], &valueType) != napi_ok ||
valueType != napi_function) {
NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE);
return false;
}
napi_ref cbOnRef = nullptr;
napi_status status = napi_create_reference(env, argv[PARAM0], 1, &cbOnRef);
if (status != napi_ok || cbOnRef == nullptr) {
NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE);
return false;
}
int32_t ret = RegisterObserverExecute(env, cbOnRef, *g_listObj, uriType);
if (ret != E_OK) {
napi_delete_reference(env, cbOnRef);
NapiError::ThrowError(env, MediaLibraryNotifyUtils::ConvertToJsError(ret));
return false;
}
return true;
}
napi_value MediaLibraryNapi::AnalysisPhotoAccessRegisterCallback(napi_env env, napi_callback_info info)
{
NAPI_INFO_LOG("enter AnalysisPhotoAccessRegisterCallback");
MediaLibraryTracer tracer;
tracer.Start("AnalysisPhotoAccessRegisterCallback");
napi_value undefinedResult = nullptr;
napi_get_undefined(env, &undefinedResult);
CHECK_AND_RETURN_RET(RegisterAnalysisAccessCallbackInternal(env, info,
Notification::NotifyUriType::ANALYSIS_PHOTO_URI), undefinedResult);
NAPI_INFO_LOG("AnalysisPhotoAccessRegisterCallback success");
return undefinedResult;
}
napi_value MediaLibraryNapi::AnalysisAlbumAccessRegisterCallback(napi_env env, napi_callback_info info)
{
NAPI_INFO_LOG("enter AnalysisAlbumAccessRegisterCallback");
MediaLibraryTracer tracer;
tracer.Start("AnalysisAlbumAccessRegisterCallback");
napi_value undefinedResult = nullptr;
napi_get_undefined(env, &undefinedResult);
CHECK_AND_RETURN_RET(RegisterAnalysisAccessCallbackInternal(env, info,
Notification::NotifyUriType::ANALYSIS_ALBUM_URI), undefinedResult);
NAPI_INFO_LOG("AnalysisAlbumAccessRegisterCallback success");
return undefinedResult;
}
int32_t MediaLibraryNapi::AddClientObserver(napi_env env, napi_ref ref,
std::map<Notification::NotifyUriType, std::vector<std::shared_ptr<ClientObserver>>> &clientObservers,
const Notification::NotifyUriType uriType)
{
auto iter = clientObservers.find(uriType);
if (iter == clientObservers.end()) {
shared_ptr<ClientObserver> clientObserver = make_shared<ClientObserver>(uriType, ref);
clientObservers[uriType].push_back(clientObserver);
return E_OK;
}
napi_value callback = nullptr;
napi_status status = napi_get_reference_value(env, ref, &callback);
if (status != napi_ok) {
NAPI_ERR_LOG("Create reference fail, status: %{public}d", status);
return OHOS_INVALID_PARAM_CODE;
}
bool hasRegister = false;
auto observers = iter->second;
for (auto &observer : observers) {
napi_value onCallback = nullptr;
status = napi_get_reference_value(env, observer->ref_, &onCallback);
if (status != napi_ok) {
NAPI_ERR_LOG("Create reference fail, status: %{public}d", status);
return OHOS_INVALID_PARAM_CODE;
}
napi_strict_equals(env, callback, onCallback, &hasRegister);
if (hasRegister) {
NAPI_INFO_LOG("clientObserver hasRegister");
return JS_E_PARAM_INVALID;
}
}
if (!hasRegister) {
shared_ptr<ClientObserver> clientObserver = make_shared<ClientObserver>(uriType, ref);
clientObservers[uriType].push_back(clientObserver);
}
return E_OK;
}
int32_t MediaLibraryNapi::RegisterObserverExecute(napi_env env, napi_ref ref, ChangeListenerNapi &listObj,
const Notification::NotifyUriType uriType)
{
Notification::NotifyUriType registerUriType = Notification::NotifyUriType::INVALID;
std::string registerUri = "";
if (MediaLibraryNotifyUtils::GetNotifyTypeAndUri(uriType, registerUriType, registerUri) != E_OK) {
return JS_E_PARAM_INVALID;
}
for (auto it = listObj.newObservers_.begin(); it != listObj.newObservers_.end(); it++) {
Notification::NotifyUriType observerUri = (*it)->uriType_;
if (observerUri == registerUriType) {
auto& clientObservers = (*it)->clientObservers_;
return AddClientObserver(env, ref, clientObservers, uriType);
}
}
shared_ptr<MediaOnNotifyNewObserver> observer =
make_shared<MediaOnNotifyNewObserver>(registerUriType, registerUri, env);
Uri notifyUri(registerUri);
int32_t ret = UserFileClient::RegisterObserverExtProvider(notifyUri,
static_cast<shared_ptr<DataShare::DataShareObserver>>(observer), false);
if (ret != E_OK) {
NAPI_ERR_LOG("failed to register observer, ret: %{public}d, uri: %{public}s", ret, registerUri.c_str());
return ret;
}
shared_ptr<ClientObserver> clientObserver = make_shared<ClientObserver>(uriType, ref);
observer->clientObservers_[uriType].push_back(clientObserver);
RegisterUnregisterHandlerFunctions::SyncUpdateSingleListener(listObj, registerUriType, observer);
listObj.newObservers_.push_back(observer);
NAPI_INFO_LOG("success to register observer, ret: %{public}d, uri: %{public}s", ret, registerUri.c_str());
return ret;
}
napi_value MediaLibraryNapi::PhotoAccessRegisterCallback(napi_env env, napi_callback_info info)
{
NAPI_INFO_LOG("enter PhotoAccessRegisterCallback");
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessRegisterCallback");
napi_value undefinedResult = nullptr;
napi_get_undefined(env, &undefinedResult);
size_t argc = ARGS_TWO;
napi_value argv[ARGS_TWO] = {nullptr};
napi_value thisVar = nullptr;
GET_JS_ARGS(env, info, argc, argv, thisVar);
if (argc != ARGS_TWO) {
NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "requires 2 parameters.");
return undefinedResult;
}
napi_valuetype valueType = napi_undefined;
if (napi_typeof(env, argv[PARAM0], &valueType) != napi_ok || valueType != napi_string ||
napi_typeof(env, argv[PARAM1], &valueType) != napi_ok || valueType != napi_function) {
NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE);
return undefinedResult;
}
char buffer[ARG_BUF_SIZE];
size_t res = 0;
if (napi_get_value_string_utf8(env, argv[PARAM0], buffer, ARG_BUF_SIZE, &res) != napi_ok) {
NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE);
return undefinedResult;
}
string type = string(buffer);
Notification::NotifyUriType uriType = Notification::NotifyUriType::INVALID;
if (MediaLibraryNotifyUtils::GetRegisterNotifyType(type, uriType) != E_OK) {
NapiError::ThrowError(env, JS_E_PARAM_INVALID, "The scenario parameter verification fails.");
return undefinedResult;
}
const int32_t refCount = 1;
napi_ref cbOnRef = nullptr;
napi_create_reference(env, argv[PARAM1], refCount, &cbOnRef);
int32_t ret = RegisterObserverExecute(env, cbOnRef, *g_listObj, uriType);
if (ret == E_OK) {
NAPI_INFO_LOG("PhotoAccessRegisterCallback success");
} else {
NapiError::ThrowError(env, MediaLibraryNotifyUtils::ConvertToJsError(ret));
napi_delete_reference(env, cbOnRef);
return undefinedResult;
}
return undefinedResult;
}
int32_t MediaLibraryNapi::OverrideExistingAvailabilityObserver(napi_env env, napi_ref ref,
MediaOnNotifyNewObserver& observer, Notification::NotifyUriType registerUriType)
{
NAPI_INFO_LOG("Found existing availability observer, overriding...");
auto& clientObservers = observer.clientObservers_;
auto iter = clientObservers.find(registerUriType);
if (iter != clientObservers.end()) {
for (auto& oldObs : iter->second) {
if (oldObs != nullptr && oldObs->ref_ != nullptr) {
NAPI_INFO_LOG("Delete old napi_ref to avoid leak");
napi_delete_reference(env, oldObs->ref_);
}
}
iter->second.clear();
}
auto clientObserver = make_shared<ClientObserver>(registerUriType, ref);
clientObservers[registerUriType].push_back(clientObserver);
NAPI_INFO_LOG("Availability callback overridden successfully");
return E_OK;
}
int32_t MediaLibraryNapi::CreateAndRegisterNewAvailabilityObserver(napi_env env, napi_ref ref,
Notification::NotifyUriType registerUriType, std::string& registerUri, ChangeListenerNapi& listObj)
{
auto observer = make_shared<MediaOnNotifyNewObserver>(registerUriType, registerUri, env);
Uri notifyUri(registerUri);
int32_t ret = UserFileClient::RegisterObserverExtProvider(notifyUri,
static_cast<shared_ptr<DataShare::DataShareObserver>>(observer), false);
if (ret != E_OK) {
NAPI_ERR_LOG("RegisterObserverExtProvider failed, ret: %{public}d", ret);
return ret;
}
auto clientObserver = make_shared<ClientObserver>(registerUriType, ref);
observer->clientObservers_[registerUriType].push_back(clientObserver);
listObj.newObservers_.push_back(observer);
NAPI_INFO_LOG("Register availability observer success");
return E_OK;
}
int32_t MediaLibraryNapi::RegisterAvailabilityObserverExecute(napi_env env, napi_ref ref, ChangeListenerNapi &listObj)
{
Notification::NotifyUriType registerUriType = Notification::NotifyUriType::AVAILABILITY_URI;
std::string registerUri = RegisterNotifyType::MEDIALIBRARY_AVAILABILITY_CHANGE;
for (auto it = listObj.newObservers_.begin(); it != listObj.newObservers_.end(); ++it) {
if ((*it)->uriType_ == registerUriType) {
return OverrideExistingAvailabilityObserver(env, ref, **it, registerUriType);
}
}
return CreateAndRegisterNewAvailabilityObserver(env, ref, registerUriType, registerUri, listObj);
}
void MediaLibraryNapi::AvailabilityRegisterExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("AvailabilityRegisterExecute");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
uint32_t businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::CHECK_DB_AVAILABILITY);
CheckDbAvailabilityReqBody reqBody;
reqBody.isOnlyCheckPermission = false;
int32_t result = IPC::UserDefineIPCClient().Call(businessCode, reqBody);
NAPI_INFO_LOG("CheckDbAvailability ret:%{public}d", result);
tracer.Finish();
}
static void AvailabilityRegisterComplete(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("AvailabilityRegisterComplete");
auto *context = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "AvailabilityRegisterComplete context is null");
auto jsContext = std::make_unique<JSAsyncContextOutput>();
jsContext->status = true;
napi_get_undefined(env, &jsContext->data);
napi_get_undefined(env, &jsContext->error);
NAPI_INFO_LOG("Availability register complete success");
tracer.Finish();
}
napi_value MediaLibraryNapi::AvailabilityRegisterCallback(napi_env env, napi_callback_info info)
{
NAPI_INFO_LOG("enter AvailabilityRegisterCallback");
MediaLibraryTracer tracer;
tracer.Start("AvailabilityRegisterCallback");
size_t argc = ARGS_ONE;
napi_value argv[ARGS_ONE] = {nullptr};
napi_value thisVar = nullptr;
GET_JS_ARGS(env, info, argc, argv, thisVar);
if (argc != ARGS_ONE) {
NapiError::ThrowErrorWithIntCode(env, JS_E_PARAM_INVALID, "requires one parameter.");
return nullptr;
}
napi_valuetype valueType = napi_undefined;
if (napi_typeof(env, argv[PARAM0], &valueType) != napi_ok || valueType != napi_function) {
NapiError::ThrowErrorWithIntCode(env, JS_E_PARAM_INVALID, "callback must be a function.");
return nullptr;
}
if (g_listObj == nullptr) {
NAPI_ERR_LOG("g_listObj is nullptr");
NapiError::ThrowErrorWithIntCode(env, JS_E_INNER_FAIL , "global list object is null.");
return nullptr;
}
const int32_t refCount = 1;
napi_ref cbRef = nullptr;
if (napi_create_reference(env, argv[PARAM0], refCount, &cbRef) != napi_ok) {
NapiError::ThrowErrorWithIntCode(env, JS_E_INNER_FAIL , "create reference failed.");
return nullptr;
}
int32_t ret = RegisterAvailabilityObserverExecute(env, cbRef, *g_listObj);
if (ret != E_OK) {
napi_delete_reference(env, cbRef);
NapiError::ThrowErrorWithIntCode(env, MediaLibraryNotifyUtils::ConvertToJsError(ret));
return nullptr;
}
NAPI_INFO_LOG("registercallck success");
std::unique_ptr<MediaLibraryAsyncContext> context = std::make_unique<MediaLibraryAsyncContext>();
context->callbackRef = cbRef;
context->error = E_OK;
context->work = nullptr;
return MediaLibraryNapiUtils::NapiCreateAsyncWork(
env, context, "AvailabilityRegisterCallback",
AvailabilityRegisterExecute, AvailabilityRegisterComplete
);
}
napi_value MediaLibraryNapi::AvailabilityUnregisterCallback(napi_env env, napi_callback_info info)
{
NAPI_INFO_LOG("enter AvailabilityUnregisterCallback");
MediaLibraryTracer tracer;
tracer.Start("AvailabilityUnregisterCallback");
napi_value undefinedResult = nullptr;
napi_get_undefined(env, &undefinedResult);
if (g_listObj == nullptr) {
NAPI_INFO_LOG("g_listObj is nullptr");
return undefinedResult;
}
size_t argc = ARGS_ONE;
napi_value args[ARGS_ONE] = { nullptr };
napi_value thisVar = nullptr;
GET_JS_ARGS(env, info, argc, args, thisVar);
if (argc > ARGS_ONE) {
NapiError::ThrowErrorWithIntCode(env, JS_E_PARAM_INVALID, "At most 1 optional callback parameter is allowed.");
return undefinedResult;
}
if (argc == ARGS_ONE) {
napi_valuetype valueType = napi_undefined;
if (napi_typeof(env, args[PARAM0], &valueType) != napi_ok || valueType != napi_function) {
NapiError::ThrowErrorWithIntCode(env, JS_E_PARAM_INVALID, "Optional callback must be a function.");
return undefinedResult;
}
}
uint32_t businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::CHECK_DB_AVAILABILITY);
CheckDbAvailabilityReqBody reqBody;
reqBody.isOnlyCheckPermission = true;
int32_t result = IPC::UserDefineIPCClient().Call(businessCode, reqBody);
NAPI_INFO_LOG("CheckDbAvailability ret:%{public}d", result);
if (result < 0) {
NAPI_ERR_LOG("CheckDbAvailability fail, result: %{public}d.", result);
NapiError::ThrowErrorWithIntCode(env, OHOS_PERMISSION_DENIED_CODE, "No parameters required.");
return undefinedResult;
}
int32_t ret = UnregisterAvailabilityObserverExecute(env, *g_listObj);
if (ret != E_OK) {
NapiError::ThrowErrorWithIntCode(env, MediaLibraryNotifyUtils::ConvertToJsError(ret));
return undefinedResult;
}
NAPI_INFO_LOG("AvailabilityUnregisterCallback success");
tracer.Finish();
return undefinedResult;
}
int32_t MediaLibraryNapi::UnregisterAvailabilityObserverExecute(napi_env env, ChangeListenerNapi &listObj)
{
auto registerUriType = Notification::NotifyUriType::AVAILABILITY_URI;
std::string registerUri = RegisterNotifyType::MEDIALIBRARY_AVAILABILITY_CHANGE;
std::shared_ptr<MediaOnNotifyNewObserver> targetObserver = nullptr;
for (auto& obs : listObj.newObservers_) {
if (obs && obs->uriType_ == registerUriType) {
targetObserver = obs;
break;
}
}
if (targetObserver == nullptr) {
NAPI_INFO_LOG("No availability observer");
return E_OK;
}
auto& clientObserversMap = targetObserver->clientObservers_;
if (clientObserversMap.count(registerUriType)) {
auto& observers = clientObserversMap[registerUriType];
for (auto& co : observers) {
if (co && co->ref_) {
napi_delete_reference(env, co->ref_);
co->ref_ = nullptr;
}
}
clientObserversMap.erase(registerUriType);
}
int32_t ret = UserFileClient::UnregisterObserverExtProvider(Uri(registerUri), targetObserver);
NAPI_INFO_LOG("UnregisterObserverExtProvider ret: %{public}d", ret);
auto it = std::find(listObj.newObservers_.begin(), listObj.newObservers_.end(), targetObserver);
if (it != listObj.newObservers_.end()) {
listObj.newObservers_.erase(it);
}
NAPI_INFO_LOG("Availability unregister clean success");
return ret;
}
int32_t MediaLibraryNapi::RemoveClientObserver(napi_env env, napi_ref ref,
map<Notification::NotifyUriType, vector<shared_ptr<ClientObserver>>> &clientObservers,
const Notification::NotifyUriType uriType)
{
if (clientObservers.find(uriType) == clientObservers.end()) {
NAPI_ERR_LOG("invalid register uriType");
return JS_E_PARAM_INVALID;
}
if (ref == nullptr) {
NAPI_ERR_LOG("remove all client observers of uriType");
clientObservers.erase(uriType);
return E_OK;
}
napi_value offCallback = nullptr;
napi_status status = napi_get_reference_value(env, ref, &offCallback);
if (status != napi_ok) {
NAPI_ERR_LOG("Create reference fail, status: %{public}d", status);
return OHOS_INVALID_PARAM_CODE;
}
bool hasRegister = false;
for (auto iter = clientObservers[uriType].begin(); iter != clientObservers[uriType].end(); iter++) {
napi_value onCallback = nullptr;
status = napi_get_reference_value(env, (*iter)->ref_, &onCallback);
if (status != napi_ok) {
NAPI_ERR_LOG("Create reference fail, status: %{public}d", status);
return OHOS_INVALID_PARAM_CODE;
}
napi_strict_equals(env, offCallback, onCallback, &hasRegister);
if (!hasRegister) {
continue;
}
clientObservers[uriType].erase(iter);
if (clientObservers[uriType].empty()) {
clientObservers.erase(uriType);
}
return E_OK;
}
NAPI_ERR_LOG("failed to find observer");
return JS_E_PARAM_INVALID;
}
int32_t MediaLibraryNapi::UnregisterObserverExecute(napi_env env,
const Notification::NotifyUriType uriType, napi_ref ref, ChangeListenerNapi &listObj)
{
if (listObj.newObservers_.size() == 0) {
NAPI_ERR_LOG("listObj.newObservers_ size 0");
return JS_E_PARAM_INVALID;
}
Notification::NotifyUriType registerUriType = Notification::NotifyUriType::INVALID;
std::string registerUri = "";
if (MediaLibraryNotifyUtils::GetNotifyTypeAndUri(uriType, registerUriType, registerUri) != E_OK) {
return JS_E_PARAM_INVALID;
}
int32_t ret = JS_E_PARAM_INVALID;
for (auto it = listObj.newObservers_.begin(); it != listObj.newObservers_.end(); it++) {
Notification::NotifyUriType observerUri = (*it)->uriType_;
if (observerUri != registerUriType) {
continue;
}
auto& clientObservers = (*it)->clientObservers_;
ret = RemoveClientObserver(env, ref, clientObservers, uriType);
if (ret == E_OK && clientObservers.empty()) {
ret = UserFileClient::UnregisterObserverExtProvider(Uri(registerUri),
static_cast<shared_ptr<DataShare::DataShareObserver>>(*it));
if (ret != E_OK) {
NAPI_ERR_LOG("failed to unregister observer, ret: %{public}d, uri: %{public}s",
ret, registerUri.c_str());
return ret;
}
std::vector<shared_ptr<MediaOnNotifyNewObserver>>::iterator tmp = it;
listObj.newObservers_.erase(tmp);
NAPI_INFO_LOG("success to unregister observer, ret: %{public}d, uri: %{public}s", ret, registerUri.c_str());
}
return ret;
}
return ret;
}
static napi_value CheckUnregisterCallbackArgs(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
napi_value thisVar = nullptr;
context->argc = ARGS_TWO;
GET_JS_ARGS(env, info, context->argc, context->argv, thisVar);
if (context->argc < ARGS_ONE || context->argc > ARGS_TWO) {
NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "requires one or two parameters.");
return nullptr;
}
if (thisVar == nullptr || context->argv[PARAM0] == nullptr) {
NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE);
return nullptr;
}
napi_valuetype valueType = napi_undefined;
if (napi_typeof(env, context->argv[PARAM0], &valueType) != napi_ok || valueType != napi_string) {
NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE);
return nullptr;
}
return thisVar;
}
napi_value MediaLibraryNapi::PhotoAccessUnregisterCallback(napi_env env, napi_callback_info info)
{
NAPI_INFO_LOG("ento PhotoAccessUnregisterCallback");
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessUnregisterCallback");
napi_value undefinedResult = nullptr;
napi_get_undefined(env, &undefinedResult);
if (g_listObj == nullptr) {
return undefinedResult;
}
unique_ptr<MediaLibraryAsyncContext> context = make_unique<MediaLibraryAsyncContext>();
if (CheckUnregisterCallbackArgs(env, info, context) == nullptr) {
return undefinedResult;
}
char buffer[ARG_BUF_SIZE];
size_t res = 0;
if (napi_get_value_string_utf8(env, context->argv[PARAM0], buffer, ARG_BUF_SIZE, &res) != napi_ok) {
NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE);
return undefinedResult;
}
string type = string(buffer);
Notification::NotifyUriType uriType = Notification::NotifyUriType::INVALID;
if (MediaLibraryNotifyUtils::GetRegisterNotifyType(type, uriType) != E_OK) {
NapiError::ThrowError(env, JS_E_PARAM_INVALID);
return undefinedResult;
}
napi_valuetype valueType = napi_undefined;
napi_ref cbOffRef = nullptr;
if (context->argc == ARGS_TWO) {
if (napi_typeof(env, context->argv[PARAM1], &valueType) != napi_ok || valueType != napi_function) {
NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE);
return undefinedResult;
}
const int32_t refCount = 1;
napi_create_reference(env, context->argv[PARAM1], refCount, &cbOffRef);
}
int32_t ret = UnregisterObserverExecute(env, uriType, cbOffRef, *g_listObj);
if (ret != E_OK) {
NapiError::ThrowError(env, MediaLibraryNotifyUtils::ConvertToJsError(ret));
}
if (cbOffRef != nullptr) {
napi_delete_reference(env, cbOffRef);
}
return undefinedResult;
}
napi_value MediaLibraryNapi::SinglePhotoAccessUnregisterCallback(napi_env env, napi_callback_info info)
{
NAPI_INFO_LOG("enter SinglePhotoAccessUnregisterCallback");
MediaLibraryTracer tracer;
tracer.Start("SinglePhotoAccessUnregisterCallback");
napi_value undefinedResult = nullptr;
napi_get_undefined(env, &undefinedResult);
if (g_listObj == nullptr) {
return undefinedResult;
}
unique_ptr<MediaLibraryAsyncContext> context = make_unique<MediaLibraryAsyncContext>();
if (RegisterUnregisterHandlerFunctions::CheckSingleUnregisterCallbackArgs(env, info, context) == nullptr) {
return undefinedResult;
}
napi_ref cbOffRef = nullptr;
Notification::NotifyUriType uriType = Notification::NotifyUriType::SINGLE_PHOTO_URI;
UnregisterContext singleContext(env, uriType, "", *g_listObj);
singleContext.cbRef = cbOffRef;
int32_t ret = RegisterUnregisterHandlerFunctions::HandleSingleIdScenario(singleContext, context);
if (ret != E_OK) {
NapiError::ThrowError(env, MediaLibraryNotifyUtils::ConvertToJsError(ret));
}
if (singleContext.cbRef != nullptr) {
napi_delete_reference(env, singleContext.cbRef);
}
return undefinedResult;
}
napi_value MediaLibraryNapi::SinglePhotoAlbumUnregisterCallback(napi_env env, napi_callback_info info)
{
NAPI_INFO_LOG("enter SinglePhotoAlbumUnregisterCallback");
MediaLibraryTracer tracer;
tracer.Start("SinglePhotoAlbumUnregisterCallback");
napi_value undefinedResult = nullptr;
napi_get_undefined(env, &undefinedResult);
if (g_listObj == nullptr) {
return undefinedResult;
}
unique_ptr<MediaLibraryAsyncContext> context = make_unique<MediaLibraryAsyncContext>();
if (RegisterUnregisterHandlerFunctions::CheckSingleUnregisterCallbackArgs(env, info, context) == nullptr) {
return undefinedResult;
}
napi_ref cbOffRef = nullptr;
Notification::NotifyUriType uriType = Notification::NotifyUriType::SINGLE_PHOTO_ALBUM_URI;
UnregisterContext singleContext(env, uriType, "", *g_listObj);
singleContext.cbRef = cbOffRef;
int32_t ret = RegisterUnregisterHandlerFunctions::HandleSingleIdScenario(singleContext, context);
if (ret != E_OK) {
NapiError::ThrowError(env, MediaLibraryNotifyUtils::ConvertToJsError(ret));
}
if (singleContext.cbRef != nullptr) {
napi_delete_reference(env, singleContext.cbRef);
}
return undefinedResult;
}
static napi_value CheckUnregisterAnalysisCallbackArg(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
napi_value thisVar = nullptr;
context->argc = ARGS_ONE;
GET_JS_ARGS(env, info, context->argc, context->argv, thisVar);
if (context->argc > ARGS_ONE) {
NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "requires no or one parameter.");
return nullptr;
}
napi_valuetype valueType = napi_undefined;
if (context->argc == ARGS_ONE && napi_typeof(env, context->argv[PARAM0], &valueType) == napi_ok
&& valueType != napi_function) {
NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE);
return nullptr;
}
return thisVar;
}
void MediaLibraryNapi::UnregisterAnalysisAccessCallbackInternal(napi_env env, napi_callback_info info,
Notification::NotifyUriType uriType)
{
if (g_listObj == nullptr) {
return;
}
unique_ptr<MediaLibraryAsyncContext> context =
make_unique<MediaLibraryAsyncContext>();
if (CheckUnregisterAnalysisCallbackArg(env, info, context) == nullptr) {
return;
}
napi_ref cbOffRef = nullptr;
if (context->argc == ARGS_ONE) {
const int32_t refCount = 1;
napi_create_reference(env, context->argv[PARAM0], refCount, &cbOffRef);
}
int32_t ret = UnregisterObserverExecute(env, uriType, cbOffRef, *g_listObj);
if (ret != E_OK) {
NapiError::ThrowError(env, MediaLibraryNotifyUtils::ConvertToJsError(ret));
}
if (cbOffRef != nullptr) {
napi_delete_reference(env, cbOffRef);
}
}
napi_value MediaLibraryNapi::AnalysisPhotoAccessUnregisterCallback(napi_env env, napi_callback_info info)
{
NAPI_INFO_LOG("enter AnalysisPhotoAccessUnregisterCallback");
MediaLibraryTracer tracer;
tracer.Start("AnalysisPhotoAccessUnregisterCallback");
napi_value undefinedResult = nullptr;
napi_get_undefined(env, &undefinedResult);
UnregisterAnalysisAccessCallbackInternal(
env, info, Notification::NotifyUriType::ANALYSIS_PHOTO_URI);
return undefinedResult;
}
napi_value MediaLibraryNapi::AnalysisAlbumAccessUnregisterCallback(
napi_env env, napi_callback_info info)
{
NAPI_INFO_LOG("enter AnalysisAlbumAccessUnregisterCallback");
MediaLibraryTracer tracer;
tracer.Start("AnalysisAlbumAccessUnregisterCallback");
napi_value undefinedResult = nullptr;
napi_get_undefined(env, &undefinedResult);
UnregisterAnalysisAccessCallbackInternal(
env, info, Notification::NotifyUriType::ANALYSIS_ALBUM_URI);
return undefinedResult;
}
napi_value MediaLibraryNapi::PhotoAccessHelperOffCallback(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessHelperOffCallback");
napi_value undefinedResult = nullptr;
napi_get_undefined(env, &undefinedResult);
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
napi_value thisVar = UserFileMgrOffCheckArgs(env, info, asyncContext);
MediaLibraryNapi *obj = nullptr;
napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void**>(&obj));
if (status != napi_ok || obj == nullptr || g_listObj == nullptr) {
return undefinedResult;
}
size_t res = 0;
char buffer[ARG_BUF_SIZE];
if (napi_get_value_string_utf8(env, asyncContext->argv[PARAM0], buffer, ARG_BUF_SIZE, &res) != napi_ok) {
NapiError::ThrowError(env, JS_ERR_PARAMETER_INVALID);
return undefinedResult;
}
string uri = string(buffer);
napi_valuetype valueType = napi_undefined;
if (ListenerTypeMaps.find(uri) != ListenerTypeMaps.end()) {
if (asyncContext->argc == ARGS_TWO) {
if (napi_typeof(env, asyncContext->argv[PARAM1], &valueType) != napi_ok || valueType != napi_function) {
return undefinedResult;
}
const int32_t refCount = 1;
napi_create_reference(env, asyncContext->argv[PARAM1], refCount, &g_listObj->cbOffRef_);
}
obj->UnregisterChange(env, uri, *g_listObj);
return undefinedResult;
}
napi_ref cbOffRef = nullptr;
if (asyncContext->argc == ARGS_TWO) {
if (napi_typeof(env, asyncContext->argv[PARAM1], &valueType) != napi_ok || valueType != napi_function) {
NapiError::ThrowError(env, JS_ERR_PARAMETER_INVALID);
return undefinedResult;
}
const int32_t refCount = 1;
napi_create_reference(env, asyncContext->argv[PARAM1], refCount, &cbOffRef);
}
tracer.Start("UnRegisterNotifyChange");
obj->UnRegisterNotifyChange(env, uri, cbOffRef, *g_listObj);
return undefinedResult;
}
napi_value ParseArgsPHAccessHelperTrash(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
vector<string> uris;
CHECK_ARGS(env, MediaLibraryNapiUtils::ParseArgsStringArrayCallback(env, info, context, uris),
JS_ERR_PARAMETER_INVALID);
if (uris.empty()) {
NapiError::ThrowError(env, JS_E_URI, "Failed to check empty uri!");
return nullptr;
}
for (const auto &uri : uris) {
if (uri.find(PhotoColumn::PHOTO_URI_PREFIX) == string::npos) {
NapiError::ThrowError(env, JS_E_URI, "Failed to check uri format, not a photo uri!");
return nullptr;
}
}
context->uris = uris;
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
static void PhotoAccessHelperTrashExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessHelperTrashExecute");
auto *context = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "context is null");
CHECK_IF_EQUAL(!context->uris.empty(), "uris is empty");
TrashPhotosReqBody reqBody;
reqBody.uris = context->uris;
uint32_t businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::PAH_SYS_TRASH_PHOTOS);
int32_t changedRows = IPC::UserDefineIPCClient().Call(businessCode, reqBody);
if (changedRows < 0) {
context->SaveError(changedRows);
NAPI_ERR_LOG("Media asset delete failed, err: %{public}d", changedRows);
}
}
napi_value MediaLibraryNapi::PhotoAccessHelperTrashAsset(napi_env env, napi_callback_info info)
{
NAPI_INFO_LOG("enter");
napi_value ret = nullptr;
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
CHECK_NULL_PTR_RETURN_UNDEFINED(env, asyncContext, ret, "asyncContext context is null");
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
CHECK_NULLPTR_RET(ParseArgsPHAccessHelperTrash(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "PhotoAccessHelperTrashAsset",
PhotoAccessHelperTrashExecute, JSTrashAssetCompleteCallback);
}
napi_value ParseArgsSetHidden(napi_env env, napi_callback_info info, unique_ptr<MediaLibraryAsyncContext> &context)
{
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
constexpr size_t minArgs = ARGS_ONE;
constexpr size_t maxArgs = ARGS_THREE;
CHECK_ARGS(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs),
JS_ERR_PARAMETER_INVALID);
vector<napi_value> napiValues;
CHECK_NULLPTR_RET(MediaLibraryNapiUtils::GetNapiValueArray(env, context->argv[PARAM0], napiValues));
if (napiValues.empty()) {
return result;
}
napi_valuetype valueType = napi_undefined;
vector<string> uris;
CHECK_ARGS(env, napi_typeof(env, napiValues.front(), &valueType), JS_ERR_PARAMETER_INVALID);
if (valueType == napi_string) {
CHECK_NULLPTR_RET(MediaLibraryNapiUtils::GetStringArray(env, napiValues, uris));
} else if (valueType == napi_object) {
CHECK_NULLPTR_RET(MediaLibraryNapiUtils::GetUriArrayFromAssets(env, napiValues, uris));
}
if (uris.empty()) {
return result;
}
bool hiddenState = false;
CHECK_ARGS(env, MediaLibraryNapiUtils::GetParamBool(env, context->argv[PARAM1], hiddenState),
JS_ERR_PARAMETER_INVALID);
context->predicates.In(MediaColumn::MEDIA_ID, uris);
context->valuesBucket.Put(MediaColumn::MEDIA_HIDDEN, static_cast<int32_t>(hiddenState));
return result;
}
static void SetHiddenExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("SetHiddenExecute");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
string hideUri = CONST_PAH_HIDE_PHOTOS;
MediaLibraryNapiUtils::UriAppendKeyValue(hideUri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
Uri uri(hideUri);
int32_t changedRows = UserFileClient::Update(uri, context->predicates, context->valuesBucket);
if (changedRows < 0) {
context->SaveError(changedRows);
NAPI_ERR_LOG("Media asset delete failed, err: %{public}d", changedRows);
}
}
static void SetHiddenCompleteCallback(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("SetHiddenCompleteCallback");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
if (context->error == ERR_DEFAULT) {
jsContext->status = true;
napi_get_undefined(env, &jsContext->error);
napi_get_undefined(env, &jsContext->data);
} else {
napi_get_undefined(env, &jsContext->data);
context->HandleError(env, jsContext->error);
}
if (context->work != nullptr) {
tracer.Finish();
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
napi_value MediaLibraryNapi::SetHidden(napi_env env, napi_callback_info info)
{
auto asyncContext = make_unique<MediaLibraryAsyncContext>();
CHECK_NULLPTR_RET(ParseArgsSetHidden(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "SetHidden",
SetHiddenExecute, SetHiddenCompleteCallback);
}
napi_value ParseArgsPahGetHiddenAlbums(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
napi_status status = MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, ARGS_ONE, ARGS_THREE);
CHECK_ARGS(env, status, OHOS_INVALID_PARAM_CODE);
int32_t fetchMode = 0;
status = MediaLibraryNapiUtils::GetInt32(env, context->argv[PARAM0], fetchMode);
CHECK_ARGS(env, status, OHOS_INVALID_PARAM_CODE);
if (fetchMode != HiddenPhotosDisplayMode::ASSETS_MODE && fetchMode != HiddenPhotosDisplayMode::ALBUMS_MODE) {
NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "Invalid fetch mode: " + to_string(fetchMode));
return nullptr;
}
bool hasCallback = false;
status = MediaLibraryNapiUtils::HasCallback(env, context->argc, context->argv, hasCallback);
CHECK_ARGS(env, status, OHOS_INVALID_PARAM_CODE);
if ((context->argc - hasCallback) == ARGS_TWO) {
status = MediaLibraryNapiUtils::GetFetchOption(env, context->argv[PARAM1], ALBUM_FETCH_OPT, context);
CHECK_ARGS(env, status, OHOS_INVALID_PARAM_CODE);
}
CHECK_COND(env, CheckAlbumFetchColumns(context->fetchColumn), JS_ERR_PARAMETER_INVALID);
context->hiddenAlbumFetchMode = fetchMode;
return result;
}
static std::shared_ptr<DataShareResultSet> CallPahGetHiddenAlbums(MediaLibraryAsyncContext *context, int32_t &errCode)
{
if (context->businessCode != 0) {
QueryAlbumsReqBody reqBody;
QueryAlbumsRespBody respBody;
reqBody.columns = context->fetchColumn;
reqBody.predicates = context->predicates;
reqBody.hiddenAlbumFetchMode = context->hiddenAlbumFetchMode;
errCode = IPC::UserDefineIPCClient().SetUserId(context->userId).Call(context->businessCode, reqBody, respBody);
if (errCode != 0) {
NAPI_ERR_LOG("after IPC::UserDefineIPCClient().Call, errCode: %{public}d.", errCode);
return nullptr;
}
return respBody.resultSet;
}
Uri uri(CONST_PAH_QUERY_HIDDEN_ALBUM);
AddDefaultPhotoAlbumColumns(context->fetchColumn);
if (context->hiddenAlbumFetchMode == ALBUMS_MODE) {
context->fetchColumn.push_back(PhotoAlbumColumns::HIDDEN_COUNT);
context->fetchColumn.push_back(PhotoAlbumColumns::HIDDEN_COVER);
context->predicates.EqualTo(PhotoAlbumColumns::CONTAINS_HIDDEN, to_string(1));
} else {
context->predicates.EqualTo(PhotoAlbumColumns::ALBUM_SUBTYPE, PhotoAlbumSubType::HIDDEN);
}
return UserFileClient::Query(uri, context->predicates, context->fetchColumn, errCode, context->userId);
}
static void JSPahGetHiddenAlbumsExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSPahGetHiddenAlbumsExecute");
int32_t errCode = 0;
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
std::shared_ptr<DataShareResultSet> resultSet = CallPahGetHiddenAlbums(context, errCode);
if (resultSet == nullptr) {
NAPI_ERR_LOG("resultSet == nullptr, errCode is %{public}d", errCode);
if (errCode == E_PERMISSION_DENIED || errCode == -E_CHECK_SYSTEMAPP_FAIL) {
context->SaveError(errCode);
} else {
context->SaveError(E_HAS_DB_ERROR);
}
return;
}
context->fetchPhotoAlbumResult = make_unique<FetchResult<PhotoAlbum>>(move(resultSet));
context->fetchPhotoAlbumResult->SetResultNapiType(context->resultNapiType);
context->fetchPhotoAlbumResult->SetHiddenOnly(context->hiddenAlbumFetchMode == ALBUMS_MODE);
context->fetchPhotoAlbumResult->SetLocationOnly(false);
context->fetchPhotoAlbumResult->SetUserId(context->userId);
}
napi_value MediaLibraryNapi::PahGetHiddenAlbums(napi_env env, napi_callback_info info)
{
auto asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
CHECK_NULLPTR_RET(ParseArgsPahGetHiddenAlbums(env, info, asyncContext));
asyncContext->businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::PAH_QUERY_HIDDEN_ALBUMS);
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "PahGetHiddenAlbums",
JSPahGetHiddenAlbumsExecute, JSGetPhotoAlbumsCompleteCallback);
}
napi_value MediaLibraryNapi::JSApplyChanges(napi_env env, napi_callback_info info)
{
size_t argc = ARGS_TWO;
napi_value argv[ARGS_TWO] = { 0 };
napi_value thisVar = nullptr;
napi_valuetype valueType;
MediaLibraryNapi* mediaLibraryNapi;
CHECK_ARGS(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr), JS_INNER_FAIL);
CHECK_ARGS(env, napi_unwrap(env, thisVar, reinterpret_cast<void**>(&mediaLibraryNapi)), JS_INNER_FAIL);
CHECK_COND_WITH_MESSAGE(env, mediaLibraryNapi != nullptr, "Failed to get object info");
CHECK_COND_WITH_MESSAGE(env, argc >= ARGS_ONE && argc <= ARGS_TWO, "Number of args is invalid");
CHECK_ARGS(env, napi_typeof(env, argv[PARAM0], &valueType), JS_INNER_FAIL);
CHECK_COND_WITH_MESSAGE(env, valueType == napi_object, "Invalid argument type");
MediaChangeRequestNapi* obj;
CHECK_ARGS(env, napi_unwrap(env, argv[PARAM0], reinterpret_cast<void**>(&obj)), JS_INNER_FAIL);
CHECK_COND_WITH_MESSAGE(env, obj != nullptr, "MediaChangeRequestNapi object is null");
return obj->ApplyChanges(env, info);
}
static napi_value initRequest(OHOS::AAFwk::Want &request, shared_ptr<DeleteCallback> &callback,
napi_env env, napi_value args[], size_t argsLen)
{
if (argsLen < ARGS_THREE) {
return nullptr;
}
napi_value result = nullptr;
napi_create_object(env, &result);
request.SetElementName(DELETE_UI_PACKAGE_NAME, DELETE_UI_EXT_ABILITY_NAME);
request.SetParam(DELETE_UI_EXTENSION_TYPE, DELETE_UI_REQUEST_TYPE);
size_t nameRes = 0;
char nameBuffer[ARG_BUF_SIZE];
if (napi_get_value_string_utf8(env, args[ARGS_ONE], nameBuffer, ARG_BUF_SIZE, &nameRes) != napi_ok) {
NapiError::ThrowError(env, JS_ERR_PARAMETER_INVALID);
return nullptr;
}
string appName = string(nameBuffer);
request.SetParam(DELETE_UI_APPNAME, appName);
vector<string> uris;
uint32_t len = 0;
CHECK_ARGS(env, napi_get_array_length(env, args[ARGS_TWO], &len), JS_ERR_PARAMETER_INVALID);
char uriBuffer[ARG_BUF_SIZE];
for (uint32_t i = 0; i < len; i++) {
napi_value uri = nullptr;
CHECK_ARGS(env, napi_get_element(env, args[ARGS_TWO], i, &uri), JS_ERR_PARAMETER_INVALID);
if (uri == nullptr) {
NapiError::ThrowError(env, JS_ERR_PARAMETER_INVALID);
return nullptr;
}
size_t uriRes = 0;
CHECK_ARGS(env, napi_get_value_string_utf8(env, uri, uriBuffer, ARG_BUF_SIZE, &uriRes),
JS_ERR_PARAMETER_INVALID);
uris.push_back(string(uriBuffer));
}
request.SetParam(DELETE_UI_URIS, uris);
callback->SetUris(uris);
callback->SetFunc(args[ARGS_THREE]);
return result;
}
napi_value MediaLibraryNapi::CreateDeleteRequest(napi_env env, napi_callback_info info)
{
#ifdef HAS_ACE_ENGINE_PART
size_t argc = ARGS_FOUR;
napi_value args[ARGS_FOUR] = {nullptr};
napi_value thisVar = nullptr;
napi_value result = nullptr;
napi_create_object(env, &result);
CHECK_ARGS(env, napi_get_cb_info(env, info, &argc, args, &thisVar, nullptr), JS_ERR_PARAMETER_INVALID);
auto context = OHOS::AbilityRuntime::GetStageModeContext(env, args[ARGS_ZERO]);
NAPI_ASSERT(env, context != nullptr, "context == nullptr");
std::shared_ptr<OHOS::AbilityRuntime::AbilityContext> abilityContext =
OHOS::AbilityRuntime::Context::ConvertTo<OHOS::AbilityRuntime::AbilityContext>(context);
NAPI_ASSERT(env, abilityContext != nullptr, "abilityContext == nullptr");
auto uiContent = abilityContext->GetUIContent();
NAPI_ASSERT(env, uiContent != nullptr, "uiContent == nullptr");
auto callback = std::make_shared<DeleteCallback>(env, uiContent);
OHOS::Ace::ModalUIExtensionCallbacks extensionCallback = {
([callback](auto arg) { callback->OnRelease(arg); }),
([callback](auto arg1, auto arg2) { callback->OnResult(arg1, arg2); }),
([callback](auto arg) { callback->OnReceive(arg); }),
([callback](auto arg1, auto arg2, auto arg3) { callback->OnError(arg1, arg2, arg3); }),
};
OHOS::Ace::ModalUIExtensionConfig config;
config.isProhibitBack = true;
OHOS::AAFwk::Want request;
napi_value initRequestResult = initRequest(request, callback, env, args, sizeof(args));
NAPI_ASSERT(env, initRequestResult != nullptr, "initRequest fail");
int32_t sessionId = uiContent->CreateModalUIExtension(request, extensionCallback, config);
NAPI_ASSERT(env, sessionId != DEFAULT_SESSION_ID, "CreateModalUIExtension fail");
callback->SetSessionId(sessionId);
return result;
#else
NapiError::ThrowError(env, JS_INNER_FAIL, "ace_engine is not support");
return nullptr;
#endif
}
#ifdef HAS_ACE_ENGINE_PART
static bool ParseString(const napi_env &env, const napi_value &value, std::string &result)
{
size_t size = 0;
CHECK_COND_RET(napi_get_value_string_utf8(env, value, nullptr, 0, &size) == napi_ok, false,
"Failed to get string length.");
result.reserve(size + 1);
result.resize(size);
CHECK_COND_RET(napi_get_value_string_utf8(env, value, result.data(), size + 1, &size) == napi_ok, false,
"Failed to get string value.");
return true;
}
static bool ParseAndSetFileUriArray(const napi_env &env, OHOS::AAFwk::Want &want, const napi_value &value)
{
uint32_t len = 0;
CHECK_COND_RET(napi_get_array_length(env, value, &len) == napi_ok, false, "Failed to get array length.");
if (len > CONFIRM_BOX_ARRAY_MAX_LENGTH) {
NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "Array size over 100.");
return false;
}
vector<string> srcFileUris;
for (uint32_t i = 0; i < len; ++i) {
napi_value element = nullptr;
CHECK_COND_RET(napi_get_element(env, value, i, &element) == napi_ok, false, "Failed to get array element.");
if (element == nullptr) {
NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "Failed to get array element.");
return false;
}
string srcFileUri;
if (!ParseString(env, element, srcFileUri)) {
return false;
}
NAPI_INFO_LOG("srcFileUri is %{public}s.", srcFileUri.c_str());
std::string prefix = "https://";
std::string realUriPath = "";
if (srcFileUri.find(prefix) == 0) {
realUriPath = srcFileUri.c_str();
} else {
AppFileService::ModuleFileUri::FileUri fileUri(srcFileUri);
realUriPath = fileUri.ToString();
}
NAPI_INFO_LOG("realUriPath is %{public}s.", realUriPath.c_str());
srcFileUris.emplace_back(realUriPath);
}
want.SetParam(CONFIRM_BOX_SRC_FILE_URIS, srcFileUris);
return true;
}
static bool IsNeedParseProperty(const napi_env &env, const napi_value &value, const string &key, napi_value &property,
napi_valuetype &needType)
{
bool hasProp = false;
napi_valuetype valueType = napi_undefined;
CHECK_COND_RET(napi_has_named_property(env, value, key.c_str(), &hasProp) == napi_ok, false,
"Failed to check named property.");
if (hasProp) {
CHECK_COND_RET(napi_get_named_property(env, value, key.c_str(), &property) == napi_ok, false,
"Failed to get named property.");
CHECK_COND_RET(napi_typeof(env, property, &valueType) == napi_ok, false, "Failed to get value type.");
return ((valueType != napi_undefined) && (valueType != napi_null) && (valueType == needType));
}
return hasProp;
}
static bool ParseConfigObject(const napi_env &env, const napi_value &value, PhotoCreationConfig &config)
{
napi_value property = nullptr;
napi_valuetype type = napi_undefined;
type = napi_string;
if (IsNeedParseProperty(env, value, TITLE, property, type)) {
NAPI_INFO_LOG("With title.");
if (!ParseString(env, property, config.title)) {
return false;
}
}
CHECK_COND_RET(IsNeedParseProperty(env, value, EXTENSION, property, type), false, "Lack param fileNameExtension.");
if (!ParseString(env, property, config.fileNameExtension)) {
return false;
}
type = napi_number;
CHECK_COND_RET(IsNeedParseProperty(env, value, PHOTO_TYPE, property, type), false, "Lack param photoType.");
CHECK_COND_RET(napi_get_value_int32(env, property, &(config.photoType)) == napi_ok, false,
"Failed to get number type.");
CHECK_COND_RET(((config.photoType == static_cast<int32_t>(MediaType::MEDIA_TYPE_IMAGE)) || (
(config.photoType) == static_cast<int32_t>(MediaType::MEDIA_TYPE_VIDEO))), false,
"Param photoType is not valid.");
if (IsNeedParseProperty(env, value, PHOTO_SUB_TYPE, property, type)) {
NAPI_INFO_LOG("With subtype.");
CHECK_COND_RET(napi_get_value_int32(env, property, &(config.subtype)) == napi_ok, false,
"Failed to get number type.");
CHECK_COND_RET(((config.subtype == static_cast<int32_t>(PhotoSubType::DEFAULT)) || (
(config.subtype) == static_cast<int32_t>(PhotoSubType::MOVING_PHOTO))), false,
"Param subtype is not valid.");
}
return true;
}
static bool ParseAndSetConfigArray(const napi_env &env, OHOS::AAFwk::Want &want, const napi_value &value)
{
uint32_t len = 0;
CHECK_COND_RET(napi_get_array_length(env, value, &len) == napi_ok, false, "Failed to get array length.");
if (len > CONFIRM_BOX_ARRAY_MAX_LENGTH) {
NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "Array size over 100.");
return false;
}
vector<string> titleList;
vector<string> extensionList;
vector<int32_t> photoTypeList;
vector<int32_t> photoSubTypeList;
for (uint32_t i = 0; i < len; ++i) {
napi_value element = nullptr;
CHECK_COND_RET(napi_get_element(env, value, i, &element) == napi_ok, false, "Failed to get array element.");
if (element == nullptr) {
NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "Failed to get array element.");
return false;
}
PhotoCreationConfig config;
if (!ParseConfigObject(env, element, config)) {
return false;
}
titleList.emplace_back(config.title);
extensionList.emplace_back(config.fileNameExtension);
photoTypeList.emplace_back(config.photoType);
photoSubTypeList.emplace_back(config.subtype);
}
want.SetParam(CONFIRM_BOX_TITLE_ARRAY, titleList);
want.SetParam(CONFIRM_BOX_EXTENSION_ARRAY, extensionList);
want.SetParam(CONFIRM_BOX_PHOTO_TYPE_ARRAY, photoTypeList);
want.SetParam(CONFIRM_BOX_PHOTO_SUB_TYPE_ARRAY, photoSubTypeList);
return true;
}
static bool InitConfirmRequest(OHOS::AAFwk::Want &want, shared_ptr<ConfirmCallback> &callback,
napi_env env, napi_value args[], size_t argsLen, bool isImageFullyDisplayed = false)
{
if (argsLen < ARGS_EIGHT) {
return false;
}
want.SetElementName(CONFIRM_BOX_PACKAGE_NAME, CONFIRM_BOX_EXT_ABILITY_NAME);
want.SetParam(CONFIRM_BOX_EXTENSION_TYPE, CONFIRM_BOX_REQUEST_TYPE);
want.AddFlags(Want::FLAG_AUTH_READ_URI_PERMISSION);
want.SetParam(CONFIRM_BOX_IMAGE_FULLY_DISPLAYED, isImageFullyDisplayed);
if (!ParseAndSetFileUriArray(env, want, args[PARAM1])) {
return false;
}
if (!ParseAndSetConfigArray(env, want, args[PARAM2])) {
return false;
}
string bundleName;
if (!ParseString(env, args[PARAM3], bundleName)) {
return false;
}
want.SetParam(CONFIRM_BOX_BUNDLE_NAME, bundleName);
string appName;
if (!ParseString(env, args[PARAM4], appName)) {
return false;
}
want.SetParam(CONFIRM_BOX_APP_NAME, appName);
string appId;
if (!ParseString(env, args[PARAM5], appId)) {
return false;
}
want.SetParam(CONFIRM_BOX_APP_ID, appId);
callback->SetFunc(args[PARAM6]);
return true;
}
#endif
static bool IsNullValue(napi_env env, napi_value value)
{
napi_valuetype type;
napi_typeof(env, value, &type);
return type == napi_null || type == napi_undefined;
}
napi_value MediaLibraryNapi::ShowAssetsCreationDialog(napi_env env, napi_callback_info info)
{
#ifdef HAS_ACE_ENGINE_PART
size_t argc = ARGS_EIGHT;
napi_value args[ARGS_EIGHT] = {nullptr};
napi_value thisVar = nullptr;
napi_value result = nullptr;
napi_create_object(env, &result);
CHECK_ARGS(env, napi_get_cb_info(env, info, &argc, args, &thisVar, nullptr), OHOS_INVALID_PARAM_CODE);
bool isImageFullyDisplayed = false;
if (argc >= ARGS_EIGHT && !IsNullValue(env, args[ARGS_SEVEN])) {
napi_get_value_bool(env, args[ARGS_SEVEN], &isImageFullyDisplayed);
}
auto context = OHOS::AbilityRuntime::GetStageModeContext(env, args[ARGS_ZERO]);
NAPI_ASSERT(env, context != nullptr, "Context is null.");
std::shared_ptr<OHOS::AbilityRuntime::AbilityContext> abilityContext =
OHOS::AbilityRuntime::Context::ConvertTo<OHOS::AbilityRuntime::AbilityContext>(context);
NAPI_ASSERT(env, abilityContext != nullptr, "AbilityContext is null.");
auto uiContent = abilityContext->GetUIContent();
NAPI_ASSERT(env, uiContent != nullptr, "UiContent is null.");
OHOS::AAFwk::Want want;
auto callback = std::make_shared<ConfirmCallback>(env, uiContent);
NAPI_ASSERT(env, InitConfirmRequest(want, callback, env, args, sizeof(args), isImageFullyDisplayed), "Parse input fail.");
OHOS::Ace::ModalUIExtensionCallbacks extensionCallback = {
[callback](int32_t releaseCode) {
callback->OnRelease(releaseCode);
},
[callback](int32_t resultCode, const AAFwk::Want &result) {
callback->OnResult(resultCode, result);
},
[callback](const AAFwk::WantParams &receive) {
callback->OnReceive(receive);
},
[callback](int32_t code, const std::string &name, const std::string &message) {
callback->OnError(code, name, name);
},
};
OHOS::Ace::ModalUIExtensionConfig config;
config.isProhibitBack = true;
int32_t sessionId = uiContent->CreateModalUIExtension(want, extensionCallback, config);
NAPI_ASSERT(env, sessionId != DEFAULT_SESSION_ID, "CreateModalUIExtension fail");
NAPI_INFO_LOG("SessionId is %{public}d.", sessionId);
callback->SetSessionId(sessionId);
return result;
#else
NapiError::ThrowError(env, JS_INNER_FAIL, "ace_engine is not support");
return nullptr;
#endif
}
napi_value MediaLibraryNapi::CheckShortTermPermission(napi_env env, napi_callback_info info)
{
AccessTokenID tokenCaller = IPCSkeleton::GetSelfTokenID();
int res = AccessTokenKit::VerifyAccessToken(tokenCaller, PERM_SHORT_TERM_WRITE_IMAGEVIDEO);
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, res == PermissionState::PERMISSION_GRANTED, &result), JS_INNER_FAIL);
return result;
}
static bool InitShortTermRequest(OHOS::AAFwk::Want &want, shared_ptr<ShortTermCallback> &callback,
napi_env env, napi_value args[], size_t argsLen)
{
if (argsLen < ARGS_SIX) {
return false;
}
want.SetElementName(CONFIRM_BOX_PACKAGE_NAME, CONFIRM_BOX_EXT_ABILITY_NAME);
want.SetParam(CONFIRM_BOX_EXTENSION_TYPE, CONFIRM_BOX_REQUEST_TYPE);
if (args[PARAM1] == nullptr) {
return false;
}
PhotoCreationConfig config;
napi_value element = args[PARAM1];
if (!ParseConfigObject(env, element, config)) {
return false;
}
want.SetParam(SHORT_TERM_TAG, true);
want.SetParam(SHORT_TERM_TITLE, config.title);
want.SetParam(SHORT_TERM_EXTENSION, config.fileNameExtension);
want.SetParam(SHORT_TERM_PHOTO_TYPE, config.photoType);
want.SetParam(SHORT_TERM_PHOTO_SUB_TYPE, config.subtype);
string bundleName;
if (!ParseString(env, args[PARAM2], bundleName)) {
return false;
}
want.SetParam(CONFIRM_BOX_BUNDLE_NAME, bundleName);
string appName;
if (!ParseString(env, args[PARAM3], appName)) {
return false;
}
want.SetParam(CONFIRM_BOX_APP_NAME, appName);
string appId;
if (!ParseString(env, args[PARAM4], appId)) {
return false;
}
want.SetParam(CONFIRM_BOX_APP_ID, appId);
callback->SetFunc(args[PARAM5]);
return true;
}
napi_value MediaLibraryNapi::CreateAssetWithShortTermPermission(napi_env env, napi_callback_info info)
{
NAPI_INFO_LOG("CreateAssetWithShortTermPermission enter");
size_t argc = ARGS_SIX;
napi_value args[ARGS_SIX] = {nullptr};
napi_value thisVar = nullptr;
napi_value result = nullptr;
napi_create_object(env, &result);
CHECK_ARGS(env, napi_get_cb_info(env, info, &argc, args, &thisVar, nullptr), JS_ERR_PARAMETER_INVALID);
auto context = OHOS::AbilityRuntime::GetStageModeContext(env, args[ARGS_ZERO]);
NAPI_ASSERT(env, context != nullptr, "context == nullptr");
shared_ptr<OHOS::AbilityRuntime::AbilityContext> abilityContext =
OHOS::AbilityRuntime::Context::ConvertTo<OHOS::AbilityRuntime::AbilityContext>(context);
NAPI_ASSERT(env, abilityContext != nullptr, "abilityContext == nullptr");
auto uiContent = abilityContext->GetUIContent();
NAPI_ASSERT(env, uiContent != nullptr, "uiContent == nullptr");
OHOS::AAFwk::Want want;
shared_ptr<ShortTermCallback> callback = make_shared<ShortTermCallback>(env, uiContent);
NAPI_ASSERT(env, InitShortTermRequest(want, callback, env, args, sizeof(args)), "parse short term param fail");
OHOS::Ace::ModalUIExtensionCallbacks extensionCallback = {
([callback](auto arg) { callback->OnRelease(arg); }),
([callback](auto arg1, auto arg2) { callback->OnResult(arg1, arg2); }),
([callback](auto arg) { callback->OnReceive(arg); }),
([callback](auto arg1, auto arg2, auto arg3) { callback->OnError(arg1, arg2, arg3); }),
};
OHOS::Ace::ModalUIExtensionConfig config;
config.isProhibitBack = true;
int32_t sessionId = uiContent->CreateModalUIExtension(want, extensionCallback, config);
NAPI_ASSERT(env, sessionId != DEFAULT_SESSION_ID, "CreateModalUIExtension fail");
callback->SetSessionId(sessionId);
return result;
}
static bool InitRequestPhotoUrisReadPermissionRequest(OHOS::AAFwk::Want &want,
shared_ptr<RequestPhotoUrisReadPermissionCallback> &callback, napi_env env, napi_value args[], size_t argsLen)
{
NAPI_INFO_LOG("InitRequestPhotoUrisReadPermission enter.");
if (argsLen < ARGS_FOUR) {
return false;
}
std::string targetType = "photoPicker";
want.SetParam(ABILITY_WANT_PARAMS_UIEXTENSIONTARGETTYPE, targetType);
std::string requestPhotoUrisTag = "requestPhotoUrisPage";
want.SetParam(TARGET_PAGE, requestPhotoUrisTag);
if (!ParseAndSetFileUriArray(env, want, args[PARAM1])) {
NAPI_ERR_LOG("FileUriArray check failed.");
return false;
}
string appName;
if (!ParseString(env, args[PARAM2], appName)) {
NAPI_ERR_LOG("appName check failed.");
return false;
}
want.SetParam(CONFIRM_BOX_APP_NAME, appName);
callback->SetFunc(args[PARAM3]);
return true;
}
napi_value MediaLibraryNapi::RequestPhotoUrisReadPermission(napi_env env, napi_callback_info info)
{
NAPI_INFO_LOG("RequestPhotoUrisReadPermission enter");
size_t argc = ARGS_FOUR;
napi_value args[ARGS_FOUR] = {nullptr};
napi_value thisVar = nullptr;
napi_value result = nullptr;
napi_create_object(env, &result);
CHECK_ARGS(env, napi_get_cb_info(env, info, &argc, args, &thisVar, nullptr), JS_ERR_PARAMETER_INVALID);
Ace::UIContent *uiContent = nullptr;
auto context = OHOS::AbilityRuntime::GetStageModeContext(env, args[ARGS_ZERO]);
NAPI_ASSERT(env, context != nullptr, "Context is null.");
shared_ptr<OHOS::AbilityRuntime::AbilityContext> abilityContext =
OHOS::AbilityRuntime::Context::ConvertTo<OHOS::AbilityRuntime::AbilityContext>(context);
if (abilityContext == nullptr) {
auto uiExtensionContext = AbilityRuntime::Context::ConvertTo<AbilityRuntime::UIExtensionContext>(context);
if (uiExtensionContext == nullptr) {
NAPI_ERR_LOG("Fail to convert to abilityContext or uiExtensionContext");
return nullptr;
}
uiContent = uiExtensionContext->GetUIContent();
} else {
uiContent = abilityContext->GetUIContent();
}
NAPI_ASSERT(env, uiContent != nullptr, "UiContent is null.");
OHOS::AAFwk::Want want;
shared_ptr<RequestPhotoUrisReadPermissionCallback> callback =
make_shared<RequestPhotoUrisReadPermissionCallback>(env, uiContent);
NAPI_ASSERT(env, InitRequestPhotoUrisReadPermissionRequest(want, callback, env, args, sizeof(args)),
"Parse RequestPhotoUrisReadPermission input fail.");
OHOS::Ace::ModalUIExtensionCallbacks extensionCallback = {
([callback](auto arg) { callback->OnRelease(arg); }),
([callback](auto arg1, auto arg2) { callback->OnResult(arg1, arg2); }),
([callback](auto arg) { callback->OnReceive(arg); }),
([callback](auto arg1, auto arg2, auto arg3) { callback->OnError(arg1, arg2, arg3); }),
};
OHOS::Ace::ModalUIExtensionConfig config;
config.isProhibitBack = true;
NAPI_INFO_LOG("RequestPhotoUrisReadPermission regist callback and config success.");
int32_t sessionId = uiContent->CreateModalUIExtension(want, extensionCallback, config);
NAPI_ASSERT(env, sessionId != DEFAULT_SESSION_ID, "CreateModalUIExtension fail");
callback->SetSessionId(sessionId);
return result;
}
static bool InitRequestPhotoAllUrisReadPermissionRequest(OHOS::AAFwk::Want &want,
shared_ptr<RequestPhotoUrisReadPermissionCallback> &callback, napi_env env, napi_value args[], size_t argsLen)
{
NAPI_INFO_LOG("InitRequestPhotoUrisReadPermission enter.");
if (argsLen < ARGS_FOUR) {
return false;
}
std::string targetType = "photoPicker";
want.SetParam(ABILITY_WANT_PARAMS_UIEXTENSIONTARGETTYPE, targetType);
std::string requestPhotoUrisTag = "requestPhotoUrisPage";
want.SetParam(TARGET_PAGE, requestPhotoUrisTag);
if (!ParseAndSetFileUriArray(env, want, args[PARAM1])) {
NAPI_ERR_LOG("FileUriArray check failed.");
return false;
}
string appName;
bool uriReadPermissonFlag = true;
if (!ParseString(env, args[PARAM2], appName)) {
NAPI_ERR_LOG("appName check failed.");
return false;
}
want.SetParam(CONFIRM_BOX_APP_NAME, appName);
want.SetParam(REQUEST_PHOTO_URIS_READPERMISSIONEX, uriReadPermissonFlag);
callback->SetFunc(args[PARAM3]);
return true;
}
napi_value MediaLibraryNapi::RequestPhotoUrisReadPermissionEx(napi_env env, napi_callback_info info)
{
NAPI_INFO_LOG("RequestPhotoUrisReadPermissionEx enter");
size_t argc = ARGS_FOUR;
napi_value args[ARGS_FOUR] = {nullptr};
napi_value thisVar = nullptr;
napi_value result = nullptr;
napi_create_object(env, &result);
CHECK_ARGS(env, napi_get_cb_info(env, info, &argc, args, &thisVar, nullptr), JS_ERR_PARAMETER_INVALID);
Ace::UIContent *uiContent = nullptr;
auto context = OHOS::AbilityRuntime::GetStageModeContext(env, args[ARGS_ZERO]);
NAPI_ASSERT(env, context != nullptr, "Context is null.");
shared_ptr<OHOS::AbilityRuntime::AbilityContext> abilityContext =
OHOS::AbilityRuntime::Context::ConvertTo<OHOS::AbilityRuntime::AbilityContext>(context);
if (abilityContext == nullptr) {
auto uiExtensionContext = AbilityRuntime::Context::ConvertTo<AbilityRuntime::UIExtensionContext>(context);
if (uiExtensionContext == nullptr) {
NAPI_ERR_LOG("Fail to convert to abilityContext or uiExtensionContext");
return nullptr;
}
uiContent = uiExtensionContext->GetUIContent();
} else {
uiContent = abilityContext->GetUIContent();
}
NAPI_ASSERT(env, uiContent != nullptr, "UiContent is null.");
OHOS::AAFwk::Want want;
shared_ptr<RequestPhotoUrisReadPermissionCallback> callback =
make_shared<RequestPhotoUrisReadPermissionCallback>(env, uiContent);
NAPI_ASSERT(env, InitRequestPhotoAllUrisReadPermissionRequest(want, callback, env, args, sizeof(args)),
"Parse RequestPhotoUrisReadPermissionEx input fail.");
OHOS::Ace::ModalUIExtensionCallbacks extensionCallback = {
([callback](auto arg) { callback->OnRelease(arg); }),
([callback](auto arg1, auto arg2) { callback->OnResultEx(arg1, arg2); }),
([callback](auto arg) { callback->OnReceive(arg); }),
([callback](auto arg1, auto arg2, auto arg3) { callback->OnErrorEx(arg1, arg2, arg3); }),
};
OHOS::Ace::ModalUIExtensionConfig config;
config.isProhibitBack = true;
NAPI_INFO_LOG("RequestPhotoUrisReadPermissionEx regist callback and config success.");
int32_t sessionId = uiContent->CreateModalUIExtension(want, extensionCallback, config);
NAPI_ASSERT(env, sessionId != DEFAULT_SESSION_ID, "CreateModalUIExtension fail");
callback->SetSessionId(sessionId);
return result;
}
static void StartPhotoPickerExecute(napi_env env, void *data)
{
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
CHECK_NULL_PTR_RETURN_VOID(context->pickerCallBack, "pickerCallBack is null");
while (!context->pickerCallBack->ready) {
std::this_thread::sleep_for(std::chrono::milliseconds(SLEEP_TIME));
}
}
static void getPhotoPickerContextRecoveryInfoExtend(napi_env env, napi_status status, MediaLibraryAsyncContext* context, napi_value recoverInfo) {
napi_value gridLevel = nullptr;
napi_create_int32(env, context->pickerCallBack->gridLevel, &gridLevel);
status = napi_set_named_property(env, recoverInfo, "gridLevel", gridLevel);
if (status != napi_ok) {
NAPI_ERR_LOG("napi_set_named_property gridLevel failed");
}
napi_value version = nullptr;
std::string recoverSceneVersion = "1.0";
napi_create_string_utf8(env, recoverSceneVersion.c_str(), NAPI_AUTO_LENGTH, &version);
status = napi_set_named_property(env, recoverInfo, "version", version);
if (status != napi_ok) {
NAPI_ERR_LOG("napi_set_named_property version failed");
}
napi_value sortRule = nullptr;
const string &jsSortRule = context->pickerCallBack->sortRule;
napi_create_string_utf8(env, jsSortRule.c_str(), NAPI_AUTO_LENGTH, &sortRule);
status = napi_set_named_property(env, recoverInfo, "sortRule", sortRule);
if (status != napi_ok) {
NAPI_ERR_LOG("napi_set_named_property sortRule failed");
}
napi_value fileSize = nullptr;
napi_create_int32(env, context->pickerCallBack->fileSize, &fileSize);
status = napi_set_named_property(env, recoverInfo, "fileSize", fileSize);
if (status != napi_ok) {
NAPI_ERR_LOG("napi_set_named_property fileSize failed");
}
}
static void getPhotoPickerContextRecoveryInfo(napi_env env, napi_status status, MediaLibraryAsyncContext* context, napi_value result)
{
NAPI_INFO_LOG("getPhotoPickerContextRecoveryInfo start");
napi_value recoverInfo = nullptr;
napi_create_object(env, &recoverInfo);
napi_value albumUri = nullptr;
const string &jsAlbumUri = context->pickerCallBack->albumUri;
napi_create_string_utf8(env, jsAlbumUri.c_str(), NAPI_AUTO_LENGTH, &albumUri);
status = napi_set_named_property(env, recoverInfo, "albumUri", albumUri);
if (status != napi_ok) {
NAPI_ERR_LOG("napi_set_named_property albumUri failed");
}
napi_value time = nullptr;
napi_create_int64(env, context->pickerCallBack->time, &time);
status = napi_set_named_property(env, recoverInfo, "time", time);
if (status != napi_ok) {
NAPI_ERR_LOG("napi_set_named_property time failed");
}
napi_value displayName = nullptr;
const string &jsDisplayName = context->pickerCallBack->displayName;
napi_create_string_utf8(env, jsDisplayName.c_str(), NAPI_AUTO_LENGTH, &displayName);
status = napi_set_named_property(env, recoverInfo, "displayName", displayName);
if (status != napi_ok) {
NAPI_ERR_LOG("napi_set_named_property displayName failed");
}
napi_value recommendationType = nullptr;
napi_create_int32(env, context->pickerCallBack->recommendationType, &recommendationType);
status = napi_set_named_property(env, recoverInfo, "recommendationType", recommendationType);
if (status != napi_ok) {
NAPI_ERR_LOG("napi_set_named_property recommendationType failed");
}
napi_value selectedRecommendationType = nullptr;
napi_create_int32(env, context->pickerCallBack->selectedRecommendationType, &selectedRecommendationType);
status = napi_set_named_property(env, recoverInfo, "selectedRecommendationType", selectedRecommendationType);
if (status != napi_ok) {
NAPI_ERR_LOG("napi_set_named_property selectedRecommendationType failed");
}
getPhotoPickerContextRecoveryInfoExtend(env, status, context, recoverInfo);
status = napi_set_named_property(env, result, "contextRecoveryInfo", recoverInfo);
if (status != napi_ok) {
NAPI_ERR_LOG("napi_set_named_property contextRecoveryInfo failed");
}
}
static void getPhotoPickerSelectUris(napi_env env, napi_value result, MediaLibraryAsyncContext* context)
{
const vector<string> &uris = context->pickerCallBack->uris;
napi_value jsUris = nullptr;
napi_create_array_with_length(env, uris.size(), &jsUris);
napi_value jsUri = nullptr;
for (size_t i = 0; i < uris.size(); i++) {
CHECK_ARGS_RET_VOID(env, napi_create_string_utf8(env, uris[i].c_str(),
NAPI_AUTO_LENGTH, &jsUri), JS_INNER_FAIL);
if ((jsUri == nullptr) || (napi_set_element(env, jsUris, i, jsUri) != napi_ok)) {
NAPI_ERR_LOG("failed to set uri array");
break;
}
}
if (napi_set_named_property(env, result, "uris", jsUris) != napi_ok) {
NAPI_ERR_LOG("napi_set_named_property uris failed");
}
}
static void getPhotoPickerMovingPhotoBadgeStates(napi_env env, napi_value result, MediaLibraryAsyncContext* context)
{
const vector<int32_t> &movingPhotoBadgeStates = context->pickerCallBack->movingPhotoBadgeStates;
napi_value jsMovingPhotoBadgeStates = nullptr;
napi_create_array_with_length(env, movingPhotoBadgeStates.size(), &jsMovingPhotoBadgeStates);
napi_value jsMovingPhotoBadgeState = nullptr;
for (size_t i = 0; i < movingPhotoBadgeStates.size(); i++) {
CHECK_ARGS_RET_VOID(env, napi_create_int32(env, movingPhotoBadgeStates[i], &jsMovingPhotoBadgeState), JS_INNER_FAIL);
if ((jsMovingPhotoBadgeState == nullptr) || (napi_set_element(env, jsMovingPhotoBadgeStates, i, jsMovingPhotoBadgeState) != napi_ok)) {
NAPI_ERR_LOG("failed to set jsMovingPhotoBadgeState array");
break;
}
}
if (napi_set_named_property(env, result, "movingPhotoBadgeStates", jsMovingPhotoBadgeStates) != napi_ok) {
NAPI_ERR_LOG("napi_set_named_property movingPhotoBadgeStates failed");
}
}
static void StartPhotoPickerAsyncCallbackComplete(napi_env env, napi_status status, void *data)
{
NAPI_INFO_LOG("StartPhotoPickerAsyncCallbackComplete start");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
NAPI_ERR_LOG("pickerCallBack");
if (context->pickerCallBack == nullptr) {
NAPI_ERR_LOG("pickerCallBack is null");
CHECK_NULL_PTR_RETURN_VOID(context->pickerCallBack, "pickerCallBack is null");
}
auto jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->data), JS_ERR_PARAMETER_INVALID);
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), JS_ERR_PARAMETER_INVALID);
napi_value result = nullptr;
napi_create_object(env, &result);
napi_value resultCode = nullptr;
napi_create_int32(env, context->pickerCallBack->resultCode, &resultCode);
status = napi_set_named_property(env, result, "resultCode", resultCode);
if (status != napi_ok) {
NAPI_ERR_LOG("napi_set_named_property resultCode failed");
}
getPhotoPickerSelectUris(env, result, context);
getPhotoPickerMovingPhotoBadgeStates(env, result, context);
napi_value gridLevel = nullptr;
napi_create_int32(env, context->pickerCallBack->gridLevel, &gridLevel);
status = napi_set_named_property(env, result, "gridLevel", gridLevel);
if (status != napi_ok) {
NAPI_ERR_LOG("napi_set_named_property gridLevel failed");
}
napi_value isOrigin = nullptr;
napi_get_boolean(env, context->pickerCallBack->isOrigin, &isOrigin);
status = napi_set_named_property(env, result, "isOrigin", isOrigin);
if (status != napi_ok) {
NAPI_ERR_LOG("napi_set_named_property isOrigin failed");
}
getPhotoPickerContextRecoveryInfo(env, status, context, result);
if (result != nullptr) {
jsContext->data = result;
jsContext->status = true;
} else {
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, ERR_MEM_ALLOCATION,
"failed to create js object");
}
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
Ace::UIContent *GetSubWindowUIContent(napi_env env, unique_ptr<MediaLibraryAsyncContext> &AsyncContext)
{
bool present = false;
napi_status status = napi_has_named_property(env, AsyncContext->argv[ARGS_ONE], "parameters", &present);
if (status != napi_ok || !present) {
return nullptr;
}
napi_value paramValue;
status = napi_get_named_property(env, AsyncContext->argv[ARGS_ONE], "parameters", ¶mValue);
CHECK_COND_RET(status == napi_ok, nullptr, "failed to get named property of parameters");
present = false;
status = napi_has_named_property(env, paramValue, "subWindowName", &present);
if (status != napi_ok || !present) {
return nullptr;
}
napi_value subWindowName;
status = napi_get_named_property(env, paramValue, "subWindowName", &subWindowName);
CHECK_COND_RET(status == napi_ok, nullptr, "failed to get named property of subWindowName");
char buffer[ARG_BUF_SIZE];
size_t res = 0;
status = napi_get_value_string_utf8(env, subWindowName, buffer, ARG_BUF_SIZE, &res);
if (status != napi_ok) {
NAPI_ERR_LOG("failed to get the value of subWindow name");
return nullptr;
}
auto currentWindow = Rosen::Window::Find(string(buffer));
if (currentWindow == nullptr) {
NAPI_ERR_LOG("GetSubWindowUIContent failed to find context by subWindow name");
return nullptr;
}
return currentWindow->GetUIContent();
}
static bool IsPcPicker(napi_env env, unique_ptr<MediaLibraryAsyncContext> &AsyncContext)
{
bool present = false;
napi_status status = napi_has_named_property(env, AsyncContext->argv[ARGS_ONE], "parameters", &present);
if (status != napi_ok || !present) {
return false;
}
napi_value paramValue;
status = napi_get_named_property(env, AsyncContext->argv[ARGS_ONE], "parameters", ¶mValue);
CHECK_COND_RET(status == napi_ok, false, "failed to get named property of parameters");
present = false;
status = napi_has_named_property(env, paramValue, "isPc", &present);
if (status != napi_ok || !present) {
return false;
}
napi_value isPc;
status = napi_get_named_property(env, paramValue, "isPc", &isPc);
CHECK_COND_RET(status == napi_ok, false, "failed to get named property of isPc");
bool isPcPicker;
napi_get_value_bool(env, isPc, &isPcPicker);
return isPcPicker;
}
Ace::UIContent *GetUIContent(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &AsyncContext)
{
NAPI_INFO_LOG("GetUIContent start");
if (!IsPcPicker(env, AsyncContext)) {
NAPI_INFO_LOG("GetUIContent is not from PcPicker");
Ace::UIContent *uiContent = GetSubWindowUIContent(env, AsyncContext);
if (uiContent != nullptr) {
NAPI_INFO_LOG("GetSubWindowUIContent success");
return uiContent;
}
}
bool isStageMode = false;
napi_status status = AbilityRuntime::IsStageContext(env, AsyncContext->argv[ARGS_ZERO], isStageMode);
if (status != napi_ok || !isStageMode) {
NAPI_ERR_LOG("is not StageMode context, status: %{public}d, isStageMode: %{public}d",
status, static_cast<int32_t>(isStageMode));
return nullptr;
}
auto context = AbilityRuntime::GetStageModeContext(env, AsyncContext->argv[ARGS_ZERO]);
if (context == nullptr) {
NAPI_ERR_LOG("Failed to get native stage context instance");
return nullptr;
}
auto abilityContext = AbilityRuntime::Context::ConvertTo<AbilityRuntime::AbilityContext>(context);
if (abilityContext == nullptr) {
auto uiExtensionContext = AbilityRuntime::Context::ConvertTo<AbilityRuntime::UIExtensionContext>(context);
if (uiExtensionContext == nullptr) {
NAPI_ERR_LOG("Fail to convert to abilityContext or uiExtensionContext");
return nullptr;
}
return uiExtensionContext->GetUIContent();
}
return abilityContext->GetUIContent();
}
static napi_value StartPickerExtension(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &AsyncContext)
{
NAPI_INFO_LOG("StartPickerExtension start");
Ace::UIContent *uiContent = GetUIContent(env, info, AsyncContext);
if (uiContent == nullptr) {
NAPI_ERR_LOG("get uiContent failed");
return nullptr;
}
AAFwk::Want request;
AppExecFwk::UnwrapWant(env, AsyncContext->argv[ARGS_ONE], request);
std::string targetType = "photoPicker";
request.SetParam(ABILITY_WANT_PARAMS_UIEXTENSIONTARGETTYPE, targetType);
AsyncContext->pickerCallBack = make_shared<PickerCallBack>();
auto callback = std::make_shared<ModalUICallback>(uiContent, AsyncContext->pickerCallBack.get());
Ace::ModalUIExtensionCallbacks extensionCallback = {
([callback](auto arg) { callback->OnRelease(arg); }),
([callback](auto arg1, auto arg2) { callback->OnResultForModal(arg1, arg2); }),
([callback](auto arg) { callback->OnReceive(arg); }),
([callback](auto arg1, auto arg2, auto arg3) { callback->OnError(arg1, arg2, arg3); }),
std::bind(&ModalUICallback::OnDestroy, callback),
};
Ace::ModalUIExtensionConfig config;
config.isWindowModeFollowHost = true;
config.isProhibitBack = true;
int sessionId = uiContent->CreateModalUIExtension(request, extensionCallback, config);
if (sessionId == 0) {
NAPI_ERR_LOG("create modalUIExtension failed");
return nullptr;
}
callback->SetSessionId(sessionId);
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
template <class AsyncContext>
static napi_status AsyncContextSetStaticObjectInfo(napi_env env, napi_callback_info info,
AsyncContext &asyncContext, const size_t minArgs, const size_t maxArgs)
{
NAPI_INFO_LOG("AsyncContextSetStaticObjectInfo start");
napi_value thisVar = nullptr;
asyncContext->argc = maxArgs;
CHECK_STATUS_RET(napi_get_cb_info(env, info, &asyncContext->argc, &(asyncContext->argv[ARGS_ZERO]), &thisVar,
nullptr), "Failed to get cb info");
CHECK_COND_RET(((asyncContext->argc >= minArgs) && (asyncContext->argc <= maxArgs)), napi_invalid_arg,
"Number of args is invalid");
if (minArgs > 0) {
CHECK_COND_RET(asyncContext->argv[ARGS_ZERO] != nullptr, napi_invalid_arg, "Argument list is empty");
}
CHECK_STATUS_RET(MediaLibraryNapiUtils::GetParamCallback(env, asyncContext), "Failed to get callback param!");
return napi_ok;
}
static napi_value ParseArgsStartPhotoPicker(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
NAPI_INFO_LOG("ParseArgsStartPhotoPicker start");
constexpr size_t minArgs = ARGS_TWO;
constexpr size_t maxArgs = ARGS_THREE;
CHECK_ARGS(env, AsyncContextSetStaticObjectInfo(env, info, context, minArgs, maxArgs),
JS_ERR_PARAMETER_INVALID);
NAPI_CALL(env, MediaLibraryNapiUtils::GetParamCallback(env, context));
CHECK_NULLPTR_RET(StartPickerExtension(env, info, context));
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
static void PhotoAccessGetSupportedPhotoFormatsExec(MediaLibraryAsyncContext *context)
{
CHECK_IF_EQUAL(context != nullptr, "context is nullptr");
if (context->photoType == MEDIA_TYPE_IMAGE || context->photoType == MEDIA_TYPE_VIDEO) {
context->mediaTypeNames = MediaFileUtils::GetAllTypes(context->photoType);
} else {
context->SaveError(E_FAIL);
}
}
static void GetSupportedPhotoFormatsAsyncCallbadkComplete(napi_env env, napi_status status, void *data)
{
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
CHECK_IF_EQUAL(context != nullptr, "context is nullptr");
auto jsContext = make_unique<JSAsyncContextOutput>();
CHECK_IF_EQUAL(jsContext != nullptr, "jsContext is nullptr");
napi_value mediaValue;
napi_value resultTypes;
napi_create_array(env, &resultTypes);
int count = 0;
jsContext->status = false;
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->data), JS_ERR_PARAMETER_INVALID);
if (context->error != ERR_DEFAULT) {
context->HandleError(env, jsContext->error);
} else {
for (const auto &type_out : context->mediaTypeNames) {
CHECK_ARGS_RET_VOID(
env, napi_create_string_utf8(env, type_out.c_str(), NAPI_AUTO_LENGTH, &mediaValue),
JS_INNER_FAIL);
CHECK_ARGS_RET_VOID(
env, napi_set_element(env, resultTypes, count++, mediaValue), JS_INNER_FAIL);
}
jsContext->status = true;
jsContext->data = resultTypes;
}
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
napi_value MediaLibraryNapi::PhotoAccessGetSupportedPhotoFormats(napi_env env, napi_callback_info info)
{
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
CHECK_ARGS(env, MediaLibraryNapiUtils::ParseArgsNumberCallback(env, info, asyncContext, asyncContext->photoType),
JS_ERR_PARAMETER_INVALID);
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSGetSupportedPhotoFormats",
[](napi_env env, void *data) {
auto context = static_cast<MediaLibraryAsyncContext*>(data);
PhotoAccessGetSupportedPhotoFormatsExec(context);
},
reinterpret_cast<CompleteCallback>(GetSupportedPhotoFormatsAsyncCallbadkComplete));
}
napi_value MediaLibraryNapi::StartPhotoPicker(napi_env env, napi_callback_info info)
{
NAPI_INFO_LOG("StartPhotoPicker start");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
auto pickerCallBack = make_shared<PickerCallBack>();
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
ParseArgsStartPhotoPicker(env, info, asyncContext);
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "StrartPhotoPicker",
StartPhotoPickerExecute, StartPhotoPickerAsyncCallbackComplete);
}
napi_value MediaLibraryNapi::PhotoAccessHelperSetForceHideSensitiveType(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessHelperSetForceHideSensitiveType");
NAPI_INFO_LOG("enter");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
asyncContext->assetType = TYPE_PHOTO;
NAPI_ASSERT(env, ParseArgsGrantPhotoUrisForForceSensitive(env, info, asyncContext), "Failed to parse js args");
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "PhotoAccessGrantPhotoUrisPermission",
PhotoAccessGrantPhotoUrisPermissionExecute, JSPhotoUriPermissionCallback);
}
napi_value MediaLibraryNapi::PhotoAccessGetSharedPhotoAssets(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessGetSharedPhotoAssets");
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system app");
return nullptr;
}
if (access(CONST_MEDIA_DB_DIR, E_OK) != 0) {
NapiError::ThrowError(env, OHOS_PERMISSION_DENIED_CODE, "Have no permission");
return nullptr;
}
unique_ptr<MediaLibraryAsyncContext> asyncContext =
make_unique<MediaLibraryAsyncContext>();
asyncContext->assetType = TYPE_PHOTO;
CHECK_NULLPTR_RET(ParseArgsGetAssets(env, info, asyncContext));
MediaLibraryAsyncContext* context =
static_cast<MediaLibraryAsyncContext*>((asyncContext.get()));
string queryUri = CONST_PAH_QUERY_PHOTO;
MediaLibraryNapiUtils::UriAppendKeyValue(queryUri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
Uri uri(queryUri);
shared_ptr<NativeRdb::ResultSet> resultSet = UserFileClient::QueryRdb(uri,
context->predicates, context->fetchColumn);
CHECK_NULLPTR_RET(resultSet);
napi_value jsFileArray = 0;
napi_create_array(env, &jsFileArray);
int count = 0;
int err = resultSet->GoToFirstRow();
if (err != napi_ok) {
NAPI_ERR_LOG("Failed GoToFirstRow %{public}d", err);
return jsFileArray;
}
do {
napi_value item = MediaLibraryNapiUtils::GetNextRowObject(env, resultSet, true);
napi_set_element(env, jsFileArray, count++, item);
} while (!resultSet->GoToNextRow());
resultSet->Close();
return jsFileArray;
}
static bool InitDefaultAlbumNameRequest(OHOS::AAFwk::Want &want, shared_ptr<DefaultAlbumNameCallback> &callback,
napi_env env, napi_value args[], size_t argsLen)
{
if (argsLen < ARGS_TWO) {
return false;
}
want.SetElementName(CONFIRM_BOX_PACKAGE_NAME, CONFIRM_BOX_EXT_DEFAULT_ALBUM_NAME_ABILITY_NAME);
want.SetParam(CONFIRM_BOX_EXTENSION_TYPE, CONFIRM_BOX_REQUEST_TYPE);
callback->SetFunc(args[PARAM1]);
return true;
}
napi_value MediaLibraryNapi::GetPhotoPickerComponentDefaultAlbumName(napi_env env, napi_callback_info info)
{
NAPI_INFO_LOG("GetPhotoPickerComponentDefaultAlbumName enter");
size_t argc = ARGS_TWO;
napi_value args[ARGS_TWO] = {nullptr};
napi_value thisVar = nullptr;
napi_value result = nullptr;
napi_create_object(env, &result);
CHECK_ARGS(env, napi_get_cb_info(env, info, &argc, args, &thisVar, nullptr), JS_ERR_PARAMETER_INVALID);
auto context = OHOS::AbilityRuntime::GetStageModeContext(env, args[ARGS_ZERO]);
NAPI_ASSERT(env, context != nullptr, "context == nullptr");
shared_ptr<OHOS::AbilityRuntime::AbilityContext> abilityContext =
OHOS::AbilityRuntime::Context::ConvertTo<OHOS::AbilityRuntime::AbilityContext>(context);
NAPI_ASSERT(env, abilityContext != nullptr, "abilityContext == nullptr");
auto uiContent = abilityContext->GetUIContent();
NAPI_ASSERT(env, uiContent != nullptr, "uiContent == nullptr");
OHOS::AAFwk::Want want;
shared_ptr<DefaultAlbumNameCallback> callback = make_shared<DefaultAlbumNameCallback>(env, uiContent);
NAPI_ASSERT(env, InitDefaultAlbumNameRequest(want, callback, env, args, sizeof(args)),
"parse default album name param fail");
OHOS::Ace::ModalUIExtensionCallbacks extensionCallback = {
([callback](auto arg) { callback->OnRelease(arg); }),
([callback](auto arg1, auto arg2) { callback->OnResult(arg1, arg2); }),
([callback](auto arg) { callback->OnReceive(arg); }),
([callback](auto arg1, auto arg2, auto arg3) { callback->OnError(arg1, arg2, arg3); }),
};
OHOS::Ace::ModalUIExtensionConfig config;
config.isProhibitBack = true;
int32_t sessionId = uiContent->CreateModalUIExtension(want, extensionCallback, config);
NAPI_ASSERT(env, sessionId != DEFAULT_SESSION_ID, "CreateModalUIExtension fail");
callback->SetSessionId(sessionId);
return result;
}
static napi_value CheckOrderStyle(napi_env env, unique_ptr<MediaLibraryAsyncContext> &context,
OrderStyleType &orderStyle)
{
CHECK_COND_RET(context->argc >= ARGS_ONE, nullptr, "No arguments to check order style");
int32_t albumOrderStyle;
CHECK_NULLPTR_RET(MediaLibraryNapiUtils::GetInt32Arg(env, context->argv[PARAM0], albumOrderStyle));
double doubleArg;
CHECK_COND_RET(MediaLibraryNapiUtils::GetDouble(env, context->argv[PARAM0], doubleArg) == napi_ok, nullptr,
"Failed to get orderStyle");
CHECK_ARGS_WITH_MEG(env, doubleArg == static_cast<double>(albumOrderStyle), JS_E_PARAM_INVALID,
"orderStyle is not an integer");
if (!PhotoAlbum::CheckOrderStyleType(static_cast<OrderStyleType>(albumOrderStyle))) {
NapiError::ThrowError(env, JS_E_PARAM_INVALID, "orderStyle is an invalid parameter");
return nullptr;
}
napi_value result = nullptr;
CHECK_ARGS_WITH_MSG(env, napi_get_boolean(env, true, &result), JS_E_INNER_FAIL,
"Failed to create boolean value");
orderStyle = static_cast<OrderStyleType>(albumOrderStyle);
return result;
}
static napi_value ParseArgsGetPhotoAlbumsWithoutSubtype(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system app");
return nullptr;
}
constexpr size_t minArgs = ARGS_ZERO;
constexpr size_t maxArgs = ARGS_ONE;
CHECK_ARGS(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs),
JS_E_PARAM_INVALID);
if (context->argc == maxArgs) {
napi_valuetype valueType = napi_undefined;
if (napi_typeof(env, context->argv[PARAM1], &valueType) == napi_ok &&
(valueType == napi_undefined || valueType == napi_null)) {
context->argc -= 1;
}
}
if (context->argc == maxArgs) {
auto status =
MediaLibraryNapiUtils::GetFetchOption(env, context->argv[maxArgs - 1], ALBUM_FETCH_OPT, context);
CHECK_ARGS_WITH_MSG(env, status, JS_E_INNER_FAIL,"Failed to get fetch option");
}
RestrictAlbumSubtypeOptions(context->predicates);
CHECK_COND_WITH_MSG(env, CheckAlbumFetchColumns(context->fetchColumn), JS_E_INNER_FAIL, "Invalid fetch columns");
AddNoSmartFetchColumns(context->fetchColumn);
napi_value result = nullptr;
CHECK_ARGS_WITH_MSG(env, napi_get_boolean(env, true, &result), JS_E_INNER_FAIL, "Failed to create boolean value");
return result;
}
static void JSGetPhotoAlbumsWithoutSubtypeExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSGetPhotoAlbumsWithoutSubtypeExecute");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
GetPhotoAlbumObjectReqBody reqBody;
GetPhotoAlbumObjectRespBody respBody;
shared_ptr<DataShareResultSet> resultSet;
reqBody.predicates = context->predicates;
reqBody.columns = context->fetchColumn;
uint32_t businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::PAH_GET_PHOTO_ALBUMS);
int32_t errCode = IPC::UserDefineIPCClient().Call(businessCode, reqBody, respBody);
resultSet = respBody.resultSet;
if (resultSet == nullptr) {
NAPI_ERR_LOG("resultSet == nullptr, errCode is %{public}d", errCode);
if (errCode == E_PERMISSION_DENIED || errCode == -E_CHECK_SYSTEMAPP_FAIL) {
context->SaveError(errCode);
} else {
context->SaveError(E_INNER_FAIL);
}
return;
}
context->fetchPhotoAlbumResult = make_unique<FetchResult<PhotoAlbum>>(move(resultSet));
context->fetchPhotoAlbumResult->SetResultNapiType(context->resultNapiType);
context->fetchPhotoAlbumResult->SetUserId(context->userId);
}
static void GetNapiPhotoAlbumResult(napi_env env, MediaLibraryAsyncContext *context,
unique_ptr<JSAsyncContextOutput> &jsContext)
{
napi_value fileResult = FetchFileResultNapi::CreateFetchFileResult(env, move(context->fetchPhotoAlbumResult));
if (fileResult == nullptr) {
CHECK_ARGS_RET_VOID_WITH_MEG(env, napi_get_undefined(env, &jsContext->data), JS_E_INNER_FAIL,
"Failed to initialize data field");
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, ERR_INVALID_OUTPUT,
"Failed to create js object for Fetch Photo Album Result");
return;
}
jsContext->data = fileResult;
jsContext->status = true;
CHECK_ARGS_RET_VOID_WITH_MEG(env, napi_get_undefined(env, &jsContext->error), JS_E_INNER_FAIL,
"Failed to initialize error field");
}
static void GetPhotoAlbumsWithoutSubtypeCompleteCallback(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("GetPhotoAlbumsWithoutSubtypeCompleteCallback");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
CHECK_ARGS_RET_VOID_WITH_MEG(env, napi_get_undefined(env, &jsContext->error), JS_E_INNER_FAIL,
"Failed to initialize error field");
if (context->error != ERR_DEFAULT || context->fetchPhotoAlbumResult == nullptr) {
CHECK_ARGS_RET_VOID_WITH_MEG(env, napi_get_undefined(env, &jsContext->data), JS_E_INNER_FAIL,
"Failed to initialize data field");
context->HandleError(env, jsContext->error);
} else {
GetNapiPhotoAlbumResult(env, context, jsContext);
}
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
napi_value MediaLibraryNapi::PhotoAccessGetPhotoAlbumsWithoutSubtype(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessGetPhotoAlbumsWithoutSubtype");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
CHECK_NULLPTR_RET(ParseArgsGetPhotoAlbumsWithoutSubtype(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "GetPhotoAlbumsWithoutSubtype",
JSGetPhotoAlbumsWithoutSubtypeExecute, GetPhotoAlbumsWithoutSubtypeCompleteCallback);
}
static napi_value AddDefaultAlbumOrderColumns(napi_env env, unique_ptr<MediaLibraryAsyncContext> &context,
vector<string> &fetchColumns, const OrderStyleType &orderStyle)
{
auto validFetchColumns = (orderStyle == OrderStyleType::MIX) ?
PhotoAlbumColumns::DEFAULT_FETCH_ORDER_COLUMNS_STYLE1 : PhotoAlbumColumns::DEFAULT_FETCH_ORDER_COLUMNS_STYLE2;
for (const auto &column : fetchColumns) {
if (!AlbumOrder::IsAlbumOrderColumn(orderStyle, column)) {
NAPI_ERR_LOG("AlbumOrder does not contain this column: %{public}s, orderStyle: %{public}d",
column.c_str(), static_cast<int32_t>(orderStyle));
return nullptr;
}
}
fetchColumns.assign(validFetchColumns.begin(), validFetchColumns.end());
napi_value result = nullptr;
CHECK_ARGS_WITH_MSG(env, napi_get_boolean(env, true, &result), JS_E_INNER_FAIL,
"Failed to create boolean value.");
return result;
}
static napi_value ParseArgsGetPhotoAlbumOrder(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system app");
return nullptr;
}
constexpr size_t minArgs = ARGS_ONE;
constexpr size_t maxArgs = ARGS_TWO;
CHECK_ARGS(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs),
JS_E_PARAM_INVALID);
if (context->argc == maxArgs) {
napi_valuetype valueType = napi_undefined;
if (napi_typeof(env, context->argv[PARAM1], &valueType) == napi_ok &&
(valueType == napi_undefined || valueType == napi_null)) {
context->argc -= 1;
}
}
OrderStyleType orderStyle;
CHECK_ARGS_WITH_MEG(env, CheckOrderStyle(env, context, orderStyle), JS_E_PARAM_INVALID,
"Failed to check orderStyle");
if (context->argc == maxArgs) {
auto status =
MediaLibraryNapiUtils::GetFetchOption(env, context->argv[maxArgs - 1], ALBUM_FETCH_OPT, context);
CHECK_ARGS_WITH_MSG(env, status, JS_E_INNER_FAIL, "Failed to get fetch option");
}
RestrictAlbumSubtypeOptions(context->predicates);
CHECK_ARGS_WITH_MEG(env, AddDefaultAlbumOrderColumns(env, context, context->fetchColumn, orderStyle),
JS_E_PARAM_INVALID, "Failed to add default columns");
napi_value result = nullptr;
CHECK_ARGS_WITH_MSG(env, napi_get_boolean(env, true, &result), JS_E_INNER_FAIL,
"Failed to create boolean value");
return result;
}
static void JSGetPhotoAlbumOrderExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSGetPhotoAlbumOrderExecute");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
GetPhotoAlbumObjectReqBody reqBody;
GetPhotoAlbumObjectRespBody respBody;
shared_ptr<DataShareResultSet> resultSet;
reqBody.predicates = context->predicates;
reqBody.columns = context->fetchColumn;
uint32_t businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::PAH_GET_PHOTO_ALBUM_ORDER);
int32_t errCode = IPC::UserDefineIPCClient().Call(businessCode, reqBody, respBody);
resultSet = respBody.resultSet;
if (resultSet == nullptr) {
NAPI_ERR_LOG("resultSet == nullptr, errCode is %{public}d", errCode);
if (errCode == E_PERMISSION_DENIED || errCode == -E_CHECK_SYSTEMAPP_FAIL) {
context->SaveError(errCode);
} else {
context->SaveError(E_INNER_FAIL);
}
return;
}
context->fetchAlbumOrderResult = make_unique<FetchResult<AlbumOrder>>(move(resultSet));
}
static void GetNapiAlbumOrderResult(napi_env env, MediaLibraryAsyncContext *context,
unique_ptr<JSAsyncContextOutput> &jsContext)
{
if (context->fetchAlbumOrderResult == nullptr) {
NAPI_ERR_LOG("No fetch album order result found!");
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, ERR_INVALID_OUTPUT,
"Failed to obtain Fetch AlbumOrder Result");
return;
}
napi_value fileResult = FetchFileResultNapi::CreateFetchFileResult(env, move(context->fetchAlbumOrderResult));
if (fileResult == nullptr) {
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, ERR_INVALID_OUTPUT,
"Failed to create js object for Fetch File Result");
} else {
jsContext->data = fileResult;
jsContext->status = true;
napi_get_undefined(env, &jsContext->error);
}
}
static void GetAlbumOrderCallbackComplete(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("GetAlbumOrderCallbackComplete");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
CHECK_ARGS_RET_VOID_WITH_MEG(env, napi_get_undefined(env, &jsContext->error), JS_E_INNER_FAIL,
"Failed to initialize error field");
if (context->error != ERR_DEFAULT) {
context->HandleError(env, jsContext->error);
} else {
GetNapiAlbumOrderResult(env, context, jsContext);
}
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
napi_value MediaLibraryNapi::PhotoAccessGetPhotoAlbumOrder(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessGetPhotoAlbumOrder");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
CHECK_NULLPTR_RET(ParseArgsGetPhotoAlbumOrder(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "GetPhotoAlbumOrder",
JSGetPhotoAlbumOrderExecute, GetAlbumOrderCallbackComplete);
}
static napi_status HandleAlbumOrderParam(AlbumOrderNapi *obj, DataShareValuesBucket &valuesBucket,
OrderStyleType orderStyle, AlbumOrderParam param)
{
int32_t index = static_cast<int32_t>(orderStyle);
auto iter = PhotoAlbumColumns::ORDER_COLUMN_STYLE_MAP.find(param);
if (iter != PhotoAlbumColumns::ORDER_COLUMN_STYLE_MAP.end()) {
size_t len = iter->second.size();
CHECK_COND_RET(index >= 0 && static_cast<size_t>(index) < len, napi_invalid_arg,
"The orderStyle has exceeded the length of the column array, unable to find the real column name");
string columnName = iter->second[index];
switch (param) {
case AlbumOrderParam::ALBUM_ORDER:
valuesBucket.Put(columnName, obj->GetAlbumOrder());
break;
case AlbumOrderParam::ORDER_SECTION:
valuesBucket.Put(columnName, obj->GetOrderSection());
break;
case AlbumOrderParam::ORDER_TYPE:
valuesBucket.Put(columnName, obj->GetOrderType());
break;
case AlbumOrderParam::ORDER_STATUS:
valuesBucket.Put(columnName, obj->GetOrderStatus());
break;
default:
break;
}
}
return napi_ok;
}
static napi_status TransAlbumOrderToBuckets(AlbumOrderNapi *obj, DataShare::DataShareValuesBucket &values,
OrderStyleType orderStyle)
{
for (int i = static_cast<int32_t>(AlbumOrderParam::ALBUM_ORDER);
i <= static_cast<int32_t>(AlbumOrderParam::ORDER_STATUS); i++) {
AlbumOrderParam param = static_cast<AlbumOrderParam>(i);
CHECK_COND_RET(HandleAlbumOrderParam(obj, values, orderStyle, param) == napi_ok, napi_invalid_arg,
"orderStyle cannot be mapped to the actual rdb columnName");
}
return napi_ok;
}
static napi_value ParseAlbumOrderArray(napi_env env, napi_value arg, OrderStyleType orderStyle,
vector<DataShare::DataShareValuesBucket> &dataShareValuesBuckets)
{
bool isArray = false;
CHECK_ARGS_WITH_MSG(env, napi_is_array(env, arg, &isArray), JS_E_INNER_FAIL, "Failed to check array type");
CHECK_ARGS_WITH_MEG(env, isArray == true, JS_E_PARAM_INVALID, "Failed to check array type");
uint32_t len = 0;
CHECK_ARGS_WITH_MSG(env, napi_get_array_length(env, arg, &len), JS_E_INNER_FAIL, "Failed to get array length");
CHECK_ARGS_WITH_MEG(env, len > 0, JS_E_PARAM_INVALID, "Failed to check array length");
for (uint32_t i = 0; i < len; i++) {
napi_value orderData = nullptr;
CHECK_ARGS_WITH_MSG(env, napi_get_element(env, arg, i, &orderData), JS_E_INNER_FAIL,
"Failed to get array element");
CHECK_ARGS_WITH_MEG(env, orderData != nullptr, JS_E_PARAM_INVALID, "Failed to get album order element");
AlbumOrderNapi *obj = nullptr;
DataShareValuesBucket valuesBucket;
CHECK_ARGS_WITH_MSG(env, napi_unwrap(env, orderData, reinterpret_cast<void **>(&obj)),
JS_E_INNER_FAIL, "Failed to unwrap native object");
CHECK_ARGS_WITH_MEG(env, obj != nullptr, JS_E_PARAM_INVALID, "Failed to get albumOrder napi object");
if (obj->GetAlbumId() <= 0) {
NAPI_ERR_LOG("Skip invalid album order, albumId: %{public}d", obj->GetAlbumId());
continue;
}
valuesBucket.Put(PhotoAlbumColumns::ALBUM_ID, obj->GetAlbumId());
CHECK_ARGS_WITH_MEG(env, TransAlbumOrderToBuckets(obj, valuesBucket, orderStyle) == napi_ok,
JS_E_PARAM_INVALID, "Transform albumOrder to datasharvaluebucket failed");
dataShareValuesBuckets.emplace_back(valuesBucket);
}
napi_value result = nullptr;
CHECK_ARGS_WITH_MSG(env, napi_get_boolean(env, true, &result), JS_E_INNER_FAIL,
"Failed to create boolean value");
return result;
}
static napi_value ParseArgsSetPhotoAlbumOrder(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system app");
return nullptr;
}
napi_value result = nullptr;
constexpr size_t minArgs = ARGS_TWO;
constexpr size_t maxArgs = ARGS_TWO;
CHECK_ARGS(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs),
JS_E_PARAM_INVALID);
OrderStyleType orderStyle;
CHECK_ARGS_WITH_MEG(env, CheckOrderStyle(env, context, orderStyle), JS_E_PARAM_INVALID,
"Failed to check orderStyle");
context->orderStyle = static_cast<int32_t>(orderStyle);
CHECK_NULLPTR_RET(ParseAlbumOrderArray(env, context->argv[PARAM1], orderStyle, context->valuesBucketArray));
CHECK_ARGS_WITH_MEG(env, !context->valuesBucketArray.empty(), JS_E_PARAM_INVALID,
"Albumorder array is empty");
CHECK_ARGS_WITH_MEG(env, context->valuesBucketArray.size() <= MAX_SET_ORDER_ARRAY_SIZE, JS_E_PARAM_INVALID,
"Exceeded the maximum batch input quantity, can not update album order");
CHECK_ARGS_WITH_MSG(env, napi_get_boolean(env, true, &result), JS_E_INNER_FAIL,
"Failed to create boolean value");
return result;
}
static void TransValueBucketToReqBody(const std::vector<OHOS::DataShare::DataShareValuesBucket> &valuesBucketArray,
const int32_t &orderStyle, SetPhotoAlbumOrderReqBody &reqBody)
{
reqBody.albumOrderColumn = PhotoAlbumColumns::ALBUM_ORDER_COLUMNS[orderStyle];
reqBody.orderSectionColumn = PhotoAlbumColumns::ALBUM_ORDER_SECTION_COLUMNS[orderStyle];
reqBody.orderTypeColumn = PhotoAlbumColumns::ALBUM_ORDER_TYPE_COLUMNS[orderStyle];
reqBody.orderStatusColumn = PhotoAlbumColumns::ALBUM_ORDER_STATUS_COLUMNS[orderStyle];
bool isValid = false;
for (const auto& valueBucket : valuesBucketArray) {
reqBody.albumIds.emplace_back(valueBucket.Get(PhotoAlbumColumns::ALBUM_ID, isValid));
reqBody.albumOrders.emplace_back(valueBucket.Get(reqBody.albumOrderColumn, isValid));
reqBody.orderSection.emplace_back(valueBucket.Get(reqBody.orderSectionColumn, isValid));
reqBody.orderType.emplace_back(valueBucket.Get(reqBody.orderTypeColumn, isValid));
reqBody.orderStatus.emplace_back(valueBucket.Get(reqBody.orderStatusColumn, isValid));
}
}
static void JSSetPhotoAlbumOrderExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSSetPhotoAlbumOrderExecute");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
SetPhotoAlbumOrderReqBody reqBody;
TransValueBucketToReqBody(context->valuesBucketArray, context->orderStyle, reqBody);
uint32_t businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::PAH_SET_PHOTO_ALBUM_ORDER);
int32_t ret = IPC::UserDefineIPCClient().Call(businessCode, reqBody);
if (ret < 0) {
context->SaveError(ret);
NAPI_ERR_LOG("set photo album order failed, err: %{public}d", ret);
}
}
static void JSSetAlbumOrderCompleteCallback(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSSetAlbumOrderCompleteCallback");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
if (context->error == ERR_DEFAULT) {
jsContext->status = true;
napi_get_undefined(env, &jsContext->error);
napi_get_undefined(env, &jsContext->data);
} else {
napi_get_undefined(env, &jsContext->data);
context->HandleError(env, jsContext->error);
}
if (context->work != nullptr) {
tracer.Finish();
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
napi_value MediaLibraryNapi::PhotoAccessSetPhotoAlbumOrder(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessSetPhotoAlbumOrder");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
CHECK_NULLPTR_RET(ParseArgsSetPhotoAlbumOrder(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "SetPhotoAlbumOrder",
JSSetPhotoAlbumOrderExecute, JSSetAlbumOrderCompleteCallback);
}
static napi_value ParseArgsCanSupportedCompatibleDuplicate(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
constexpr size_t minArgs = ARGS_ONE;
constexpr size_t maxArgs = ARGS_ONE;
CHECK_ARGS(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs),
JS_ERR_PARAMETER_INVALID);
string bundleName;
CHECK_ARGS(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[ARGS_ZERO], bundleName),
JS_ERR_PARAMETER_INVALID);
context->bundleName = bundleName;
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
static void JSCanSupportedCompatibleDuplicateExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSSetPhotoAlbumOrderExecute");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
HeifTranscodingCheckReqBody reqBody;
HeifTranscodingCheckRespBody respBody;
reqBody.bundleName = context->bundleName;
uint32_t businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::HEIF_TRANSCODING_CHECK);
int32_t ret = IPC::UserDefineIPCClient().Call(businessCode, reqBody, respBody);
context->canSupportedCompatibleDuplicate = respBody.canSupportedCompatibleDuplicate;
if (ret < 0) {
context->SaveError(ret);
NAPI_ERR_LOG("CanSupportedCompatibleDuplicateExecute failed, err: %{public}d", ret);
}
}
static void JSCanSupportedCompatibleDuplicateCompleteCallback(napi_env env, napi_status status, void *data)
{
auto *context = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->data), JS_INNER_FAIL);
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), JS_INNER_FAIL);
if (context->error == ERR_DEFAULT) {
CHECK_ARGS_RET_VOID(env,
napi_get_boolean(env, context->canSupportedCompatibleDuplicate, &jsContext->data), JS_INNER_FAIL);
jsContext->status = true;
} else {
context->HandleError(env, jsContext->error);
}
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
napi_value MediaLibraryNapi::CanSupportedCompatibleDuplicate(napi_env env, napi_callback_info info)
{
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
CHECK_NULLPTR_RET(ParseArgsCanSupportedCompatibleDuplicate(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "CanSupportedCompatibleDuplicate",
JSCanSupportedCompatibleDuplicateExecute, JSCanSupportedCompatibleDuplicateCompleteCallback);
}
int32_t MediaLibraryNapi::GetUserId()
{
return userId_;
}
void MediaLibraryNapi::SetUserId(const int32_t &userId)
{
if (userId_ != userId) {
ChangeListenerNapi::InvalidateTrashAlbumUri();
}
userId_ = userId;
}
static bool CheckBetaIssueId(const std::string &betaIssueId)
{
return betaIssueId.length() == BETA_ISSUE_ID_LENGTH && MediaLibraryNapiUtils::IsNumber(betaIssueId);
}
static bool CheckBetaScenario(const std::string &betaScenario)
{
return !betaScenario.empty() && BETACLUB_FAULT_TREE_CODES.count(betaScenario) != 0;
}
static bool CheckFileDescriptor(int32_t fileFd)
{
return fileFd >= 0 && fileFd <= MAX_FILE_FD;
}
static int32_t CheckDebugDatabasePermission(napi_env env)
{
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "API only can be called by system app");
return E_ERROR;
}
if (!MediaLibraryNapiUtils::IsBetaVersion()) {
NapiError::ThrowError(env, JS_E_OPR_TYPE_NOT_SUPPORT, "Caller not beta version");
return E_ERROR;
}
return E_OK;
}
static napi_value ParseArgsAcquireDebugDatabase(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
CHECK_COND_RET(CheckDebugDatabasePermission(env) == E_OK, nullptr, "Permission verification failed");
constexpr size_t minArgs = ARGS_TWO;
constexpr size_t maxArgs = ARGS_THREE;
CHECK_ARGS(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs),
JS_E_PARAM_INVALID);
std::string betaIssueId;
std::string betaScenario;
CHECK_ARGS_WITH_MEG(env,
MediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[ARGS_ZERO], betaIssueId) == napi_ok,
JS_E_PARAM_INVALID, "Failed to parse betaIssueId");
CHECK_ARGS_WITH_MEG(env, CheckBetaIssueId(betaIssueId), JS_E_PARAM_INVALID, "betaIssueId is invalid");
CHECK_ARGS_WITH_MEG(env,
MediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[ARGS_ONE], betaScenario) == napi_ok,
JS_E_PARAM_INVALID, "Failed to parse betaScenario");
CHECK_ARGS_WITH_MEG(env, CheckBetaScenario(betaScenario), JS_E_PARAM_INVALID, "betaScenario is invalid");
context->valuesBucket.Put(CONST_MEDIA_DATA_BETA_ISSUE_ID, betaIssueId);
context->valuesBucket.Put(CONST_MEDIA_DATA_BETA_SCENARIO, betaScenario);
napi_value result = nullptr;
CHECK_ARGS_WITH_MSG(env, napi_get_boolean(env, true, &result), JS_E_INNER_FAIL,
"Fail to get boolean value");
return result;
}
static void JSAcquireDebugDatabaseExecute(napi_env env, void* data)
{
MediaLibraryTracer tracer;
tracer.Start("JSAcquireDebugDatabaseExecute");
bool isValid = false;
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
CHECK_IF_EQUAL(context != nullptr, "AsyncContext is nullptr");
std::string betaIssueId = context->valuesBucket.Get(CONST_MEDIA_DATA_BETA_ISSUE_ID, isValid);
CHECK_IF_EQUAL(isValid, "JSAcquireDebugDatabaseExecute betaIssueId is empty");
std::string betaScenario = context->valuesBucket.Get(CONST_MEDIA_DATA_BETA_SCENARIO, isValid);
CHECK_IF_EQUAL(isValid, "JSAcquireDebugDatabaseExecute betaScenario is empty");
AcquireDebugDatabaseReqBody reqBody;
reqBody.betaIssueId = betaIssueId;
reqBody.betaScenario = betaScenario;
AcquireDebugDatabaseRespBody respBody;
int32_t errCode = IPC::UserDefineIPCClient().Call(context->businessCode, reqBody, respBody);
if (errCode != E_SUCCESS) {
context->SaveError(errCode);
return;
}
string uri = string(CONST_ML_FILE_URI_PREFIX) + "/" + CONST_MEDIA_FILEOPRN_OPEN_DEBUG_DB + "/" + betaIssueId;
Uri openFileUri(uri);
int32_t fileFd = UserFileClient::OpenFile(openFileUri, "r");
if (fileFd < 0) {
context->SaveError(fileFd);
return;
}
context->debugDatabaseMap["FILE_FD"] = std::to_string(fileFd);
context->debugDatabaseMap["FILE_NAME"] = respBody.fileName;
context->debugDatabaseMap["FILE_SIZE"] = respBody.fileSize;
}
static napi_value AcquireDebugDatabaseResultMap(napi_env env, unordered_map<string, string> &debugDatabaseMap)
{
napi_status status;
napi_value mapNapiValue {nullptr};
status = napi_create_map(env, &mapNapiValue);
CHECK_COND_RET(status == napi_ok && mapNapiValue != nullptr, nullptr,
"Failed to create map napi value, napi status: %{public}d", static_cast<int32_t>(status));
for (auto &iter : debugDatabaseMap) {
napi_value key, value;
CHECK_COND_RET(napi_create_string_utf8(env, iter.first.c_str(), NAPI_AUTO_LENGTH, &key) == napi_ok, nullptr,
"Failed to create ResultMap key: %{public}s", iter.first.c_str());
CHECK_COND_RET(napi_create_string_utf8(env, iter.second.c_str(), NAPI_AUTO_LENGTH, &value) == napi_ok, nullptr,
"Failed to create ResultMap value: %{public}s", iter.second.c_str());
status = napi_map_set_property(env, mapNapiValue, key, value);
CHECK_COND_RET(status == napi_ok, nullptr, "Failed to set debugDatabaseMap, napi status: %{public}d",
static_cast<int32_t>(status));
}
return mapNapiValue;
}
static void JSAcquireDebugDatabaseCallbackComplete(napi_env env, napi_status status, void* data)
{
MediaLibraryTracer tracer;
tracer.Start("JSAcquireDebugDatabaseCallbackComplete");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "AsyncContext is nullptr");
auto jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
CHECK_ARGS_RET_VOID_WITH_MEG(env, napi_get_undefined(env, &jsContext->data), JS_E_INNER_FAIL,
"Failed to get undefined value");
CHECK_ARGS_RET_VOID_WITH_MEG(env, napi_get_undefined(env, &jsContext->error), JS_E_INNER_FAIL,
"Failed to get undefined value");
if (context->error != ERR_DEFAULT) {
context->HandleError(env, jsContext->error);
} else {
napi_value resultMap = AcquireDebugDatabaseResultMap(env, context->debugDatabaseMap);
if (resultMap == nullptr) {
CHECK_ARGS_RET_VOID_WITH_MEG(env, napi_get_undefined(env, &jsContext->data), JS_E_INNER_FAIL,
"Failed to get undefined value");
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, JS_E_INNER_FAIL,
"Failed to create js object for AcquireDebugDatabase resultMap");
return;
}
jsContext->data = resultMap;
jsContext->status = true;
CHECK_ARGS_RET_VOID_WITH_MEG(env, napi_get_undefined(env, &jsContext->error), JS_E_INNER_FAIL,
"Failed to get undefined value");
}
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
napi_value MediaLibraryNapi::PhotoAccessAcquireDebugDatabase(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessAcquireDebugDatabase");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
CHECK_NULLPTR_RET(ParseArgsAcquireDebugDatabase(env, info, asyncContext));
asyncContext->businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::ACQUIRE_DEBUG_DATABASE);
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "AcquireDebugDatabase",
JSAcquireDebugDatabaseExecute, JSAcquireDebugDatabaseCallbackComplete);
}
static napi_value ParseArgsReleaseDebugDatabase(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
CHECK_COND_RET(CheckDebugDatabasePermission(env) == E_OK, nullptr, "Permission verification failed");
constexpr size_t minArgs = ARGS_TWO;
constexpr size_t maxArgs = ARGS_THREE;
CHECK_ARGS(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs),
JS_E_PARAM_INVALID);
std::string betaIssueId;
int32_t fileFd;
CHECK_ARGS_WITH_MEG(env,
MediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[ARGS_ZERO], betaIssueId) == napi_ok,
JS_E_PARAM_INVALID, "Failed to parse betaIssueId");
CHECK_ARGS_WITH_MEG(env, CheckBetaIssueId(betaIssueId), JS_E_PARAM_INVALID, "betaIssueId is invalid");
CHECK_ARGS(env, MediaLibraryNapiUtils::GetInt32(env, context->argv[ARGS_ONE], fileFd), JS_E_PARAM_INVALID);
CHECK_ARGS_WITH_MEG(env, CheckFileDescriptor(fileFd), JS_E_PARAM_INVALID, "fileFd is invalid");
context->valuesBucket.Put(CONST_MEDIA_DATA_BETA_ISSUE_ID, betaIssueId);
context->valuesBucket.Put(CONST_MEDIA_DATA_BETA_DEBUG_DB_FD, fileFd);
napi_value result = nullptr;
CHECK_ARGS_WITH_MSG(env, napi_get_boolean(env, true, &result), JS_E_INNER_FAIL,
"Failed to get boolean value");
return result;
}
static void JSReleaseDebugDatabaseExecute(napi_env env, void* data)
{
MediaLibraryTracer tracer;
tracer.Start("JSReleaseDebugDatabaseExecute");
bool isValid = false;
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
CHECK_IF_EQUAL(context != nullptr, "asyncContext is nullptr");
std::string betaIssueId = context->valuesBucket.Get(CONST_MEDIA_DATA_BETA_ISSUE_ID, isValid);
CHECK_IF_EQUAL(isValid, "JSReleaseDebugDatabaseExecute betaIssueId is empty");
int32_t fileFd = context->valuesBucket.Get(CONST_MEDIA_DATA_BETA_DEBUG_DB_FD, isValid);
CHECK_IF_EQUAL(isValid, "JSReleaseDebugDatabaseExecute fileFd is empty");
int32_t errCode = close(fileFd);
CHECK_AND_PRINT_LOG(errCode == 0, "Failed to close fileFd, errCode: %{public}d", errCode);
ReleaseDebugDatabaseReqBody reqBody;
reqBody.betaIssueId = betaIssueId;
errCode = IPC::UserDefineIPCClient().Call(context->businessCode, reqBody);
if (errCode != E_OK) {
context->SaveError(errCode);
}
}
static void JSReleaseDebugDatabaseCallbackComplete(napi_env env, napi_status status, void* data)
{
MediaLibraryTracer tracer;
tracer.Start("JSReleaseDebugDatabaseCallbackComplete");
MediaLibraryAsyncContext *context = static_cast<MediaLibraryAsyncContext*>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "asyncContext is nullptr");
auto jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->data), JS_INNER_FAIL);
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), JS_INNER_FAIL);
if (context->error != ERR_DEFAULT) {
context->HandleError(env, jsContext->error);
} else {
jsContext->status = true;
}
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
napi_value MediaLibraryNapi::PhotoAccessReleaseDebugDatabase(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessReleaseDatabaseDFX");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
CHECK_NULLPTR_RET(ParseArgsReleaseDebugDatabase(env, info, asyncContext));
asyncContext->businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::RELEASE_DEBUG_DATABASE);
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "ReleaseDebugDatabase",
JSReleaseDebugDatabaseExecute, JSReleaseDebugDatabaseCallbackComplete);
}
static napi_value HandleOneArgGetAlbumByLpath(napi_env env, unique_ptr<MediaLibraryAsyncContext> &context)
{
string lpath;
CHECK_ARGS(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[ARGS_ZERO], lpath),
JS_E_PARAM_INVALID);
if (lpath.empty() || lpath.length() > MAX_LPATH_BUNDLENAME_LENGTH) {
NAPI_ERR_LOG("lpath is invalid");
NapiError::ThrowError(env, JS_E_PARAM_INVALID);
return nullptr;
}
NAPI_DEBUG_LOG("lpath: %{public}s", lpath.c_str());
context->predicates.EqualTo(PhotoAlbumColumns::ALBUM_LPATH, lpath);
if (context->photoAlbumData == nullptr) {
context->photoAlbumData = std::make_unique<PhotoAlbum>();
NAPI_DEBUG_LOG("Created new PhotoAlbum object");
}
context->photoAlbumData->SetLPath(lpath);
napi_value result = nullptr;
CHECK_ARGS_WITH_MSG(env, napi_get_boolean(env, true, &result), JS_E_INNER_FAIL,
"Failed to create boolean value");
return result;
}
static napi_value ParseArgsGetAlbumIdByLpath(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
napi_value result = nullptr;
CHECK_ARGS_WITH_MSG(env, napi_get_boolean(env, true, &result), JS_E_INNER_FAIL,
"Failed to create boolean value");
constexpr size_t minArgs = ARGS_ONE;
constexpr size_t maxArgs = ARGS_ONE;
napi_status status = MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs);
CHECK_ARGS(env, status, JS_E_PARAM_INVALID);
switch (context->argc) {
case ARGS_ONE:
CHECK_NULLPTR_RET(HandleOneArgGetAlbumByLpath(env, context));
break;
default:
NapiError::ThrowError(env, JS_E_PARAM_INVALID);
return nullptr;
}
CHECK_NULLPTR_RET(context->photoAlbumData);
string lpath = context->photoAlbumData->GetLPath();
static const std::unordered_set<std::string> MEDIA_DIRS = {
"/DCIM/Camera",
"/Pictures/Screenshots",
"/Pictures/Screenrecords"
};
bool isValidLpath = MEDIA_DIRS.find(lpath) == MEDIA_DIRS.end();
if (isValidLpath) {
NAPI_ERR_LOG("lpath is Invalid");
NapiError::ThrowError(env, JS_E_PARAM_INVALID);
return nullptr;
}
context->fetchColumn.clear();
context->fetchColumn.push_back(PhotoAlbumColumns::ALBUM_ID);
return result;
}
static void GetAlbumIdByLpathOrBundleNameExecute(napi_env env, void *data)
{
NAPI_DEBUG_LOG("enter GetAlbumIdByLpathOrBundleNameExecute");
MediaLibraryTracer tracer;
tracer.Start("GetAlbumIdByLpathOrBundleNameExecute");
auto *context = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "context is null");
CHECK_NULL_PTR_RETURN_VOID(context->photoAlbumData, "photoAlbumData is null");
GetAlbumIdByLpathReqBody reqBody;
GetAlbumIdByLpathRespBody respBody;
reqBody.predicates = context->predicates;
reqBody.columns = context->fetchColumn;
int32_t ret = IPC::UserDefineIPCClient().Call(context->businessCode, reqBody, respBody);
if (ret != 0) {
MEDIA_ERR_LOG("UserDefineIPCClient().Call is failed");
context->SaveError(ret);
return;
}
int32_t albumId = respBody.albumId;
context->photoAlbumData->SetAlbumId(albumId);
}
static void GetAlbumIdByLpathOrBundleNameCompleteCallback(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("GetAlbumIdByLpathOrBundleNameCompleteCallback");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "context is nullptr");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
CHECK_ARGS_RET_VOID_WITH_MEG(env, napi_get_undefined(env, &jsContext->error), JS_E_INNER_FAIL,
"Failed to initialize error Field");
CHECK_ARGS_RET_VOID_WITH_MEG(env, napi_get_undefined(env, &jsContext->data), JS_E_INNER_FAIL,
"Failed to initialize data Field");
if (status != napi_ok || context->error != ERR_DEFAULT) {
NAPI_ERR_LOG("GetAlbumIdByLpath failed, status: %{public}d, error: %{public}d", status, context->error);
context->HandleError(env, jsContext->error);
} else {
int albumId = context->photoAlbumData->GetAlbumId();
napi_status napiStatus = napi_create_int32(env, albumId, &jsContext->data);
if (napiStatus == napi_ok) {
jsContext->status = true;
} else {
NAPI_ERR_LOG("Failed to create albumId value");
context->HandleError(env, jsContext->error);
}
}
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
NAPI_DEBUG_LOG("MediaLibraryNapi::GetAlbumIdByLpathOrBundleNameCompleteCallback end");
delete context;
}
napi_value MediaLibraryNapi::PhotoAccessGetAlbumIdByLpath(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessGetAlbumIdByLpath");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
CHECK_NULLPTR_RET(ParseArgsGetAlbumIdByLpath(env, info, asyncContext));
asyncContext->businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::PAH_GET_ALBUM_BY_LPATH);
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "GetAlbumIdByLpath",
GetAlbumIdByLpathOrBundleNameExecute, GetAlbumIdByLpathOrBundleNameCompleteCallback);
}
static napi_value HandleOneArgGetAlbumIdByBundleName(napi_env env,
unique_ptr<MediaLibraryAsyncContext> &context)
{
string bundle_name;
CHECK_ARGS(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[ARGS_ZERO], bundle_name),
JS_E_PARAM_INVALID);
if (bundle_name.empty() || bundle_name.length() > MAX_LPATH_BUNDLENAME_LENGTH) {
NAPI_ERR_LOG("bundle_name is invalid");
NapiError::ThrowError(env, JS_E_PARAM_INVALID);
return nullptr;
}
NAPI_DEBUG_LOG("bundle_name: %{public}s", bundle_name.c_str());
context->predicates.EqualTo(PhotoAlbumColumns::ALBUM_BUNDLE_NAME, bundle_name);
context->predicates.EqualTo(PhotoAlbumColumns::ALBUM_PRIORITY, to_string(1));
if (context->photoAlbumData == nullptr) {
context->photoAlbumData = std::make_unique<PhotoAlbum>();
}
context->photoAlbumData->SetAlbumName(bundle_name);
napi_value result = nullptr;
CHECK_ARGS_WITH_MSG(env, napi_get_boolean(env, true, &result), JS_E_INNER_FAIL,
"Failed to create boolean value");
return result;
}
static napi_value ParseArgsGetAlbumIdByBundleName(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
napi_value result = nullptr;
CHECK_ARGS_WITH_MSG(env, napi_get_boolean(env, true, &result), JS_E_INNER_FAIL,
"Failed to create boolean value");
constexpr size_t minArgs = ARGS_ONE;
constexpr size_t maxArgs = ARGS_ONE;
napi_status status = MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs);
CHECK_ARGS(env, status, JS_E_PARAM_INVALID);
switch (context->argc) {
case ARGS_ONE:
CHECK_NULLPTR_RET(HandleOneArgGetAlbumIdByBundleName(env, context));
break;
default:
NapiError::ThrowError(env, JS_E_PARAM_INVALID);
return nullptr;
}
context->fetchColumn.clear();
context->fetchColumn.push_back(PhotoAlbumColumns::ALBUM_ID);
return result;
}
napi_value MediaLibraryNapi::PhotoAccessGetAlbumIdByBundleName(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessGetAlbumIdByBundleName");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
CHECK_NULLPTR_RET(ParseArgsGetAlbumIdByBundleName(env, info, asyncContext));
asyncContext->businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::PAH_GET_ALBUM_BY_BUNDLENAME);
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "GetAlbumIdByBundleName",
GetAlbumIdByLpathOrBundleNameExecute, GetAlbumIdByLpathOrBundleNameCompleteCallback);
}
static napi_value ParseArgsQueryMediaDataReady(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
napi_value result = nullptr;
CHECK_ARGS_WITH_MSG(env, napi_get_boolean(env, true, &result), JS_E_INNER_FAIL,
"Failed to create boolean value");
constexpr size_t minArgs = ARGS_ONE;
constexpr size_t maxArgs = ARGS_ONE;
napi_status status = MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs);
CHECK_ARGS(env, status, JS_E_PARAM_INVALID);
CHECK_ARGS(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[ARGS_ZERO], context->strParam),
JS_E_PARAM_INVALID);
return result;
}
static void QueryMediaDataReadyCompleteCallback(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("QueryMediaDataReadyCompleteCallback");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "context is nullptr");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
CHECK_ARGS_RET_VOID_WITH_MEG(env, napi_get_undefined(env, &jsContext->error), JS_E_INNER_FAIL,
"Failed to initialize error field");
CHECK_ARGS_RET_VOID_WITH_MEG(env, napi_get_undefined(env, &jsContext->data), JS_E_INNER_FAIL,
"Failed to initialize data field");
if (status != napi_ok || context->error != ERR_DEFAULT) {
NAPI_ERR_LOG("QueryMediaDataReady failed, status: %{public}d, error: %{public}d", status, context->error);
context->HandleError(env, jsContext->error);
} else {
napi_status napiStatus = napi_get_boolean(env, context->boolResult, &jsContext->data);
if (napiStatus == napi_ok) {
jsContext->status = true;
} else {
NAPI_ERR_LOG("Failed to create result value");
context->error = JS_E_INNER_FAIL;
context->HandleError(env, jsContext->error);
}
}
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
static void QueryMediaDataReadyExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("QueryMediaDataReadyExecute");
auto *context = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "context is null");
QueryMediaDataStatusReqBody reqBody;
QueryMediaDataStatusRespBody respBody;
reqBody.dataKey = context->strParam;
int32_t ret = IPC::UserDefineIPCClient().Call(context->businessCode, reqBody, respBody);
if (ret != 0) {
NAPI_ERR_LOG("UserDefineIPCClient().Call failed, ret: %{public}d", ret);
if (ret == E_INVALID_ARGUMENTS) {
context->error = JS_E_PARAM_INVALID;
return;
}
context->SaveError(ret);
return;
}
context->boolResult = respBody.result;
}
napi_value MediaLibraryNapi::QueryMediaDataReady(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("QueryMediaDataReady");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
CHECK_NULLPTR_RET(ParseArgsQueryMediaDataReady(env, info, asyncContext));
asyncContext->businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::QUERY_MEDIA_DATA_STATUS);
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "QueryMediaDataReady",
QueryMediaDataReadyExecute, QueryMediaDataReadyCompleteCallback);
}
static napi_value ParseArgsSetPreferredCompatibleMode(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system app");
return nullptr;
}
constexpr size_t minArgs = ARGS_TWO;
constexpr size_t maxArgs = ARGS_TWO;
CHECK_ARGS(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs),
JS_E_PARAM_INVALID);
string bundleName;
CHECK_ARGS(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[ARGS_ZERO], bundleName),
JS_E_PARAM_INVALID);
CHECK_COND(env, !bundleName.empty(), JS_E_PARAM_INVALID);
int32_t preferredCompatibleMode = 0;
CHECK_ARGS(env, napi_get_value_int32(env, context->argv[ARGS_ONE], &preferredCompatibleMode), JS_E_PARAM_INVALID);
CHECK_COND(env, preferredCompatibleMode >= static_cast<int32_t>(PreferredCompatibleMode::DEFAULT) &&
preferredCompatibleMode <= static_cast<int32_t>(PreferredCompatibleMode::COMPATIBLE), JS_E_PARAM_INVALID);
context->bundleName = bundleName;
context->preferredCompatibleMode = preferredCompatibleMode;
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
static void JSSetPreferredCompatibleModeExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSSetPreferredCompatibleModeExecute");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
SetPreferredCompatibleModeReqBody reqBody;
reqBody.bundleName = context->bundleName;
reqBody.preferredCompatibleMode = context->preferredCompatibleMode;
int32_t ret = IPC::UserDefineIPCClient().Call(
static_cast<uint32_t>(MediaLibraryBusinessCode::SET_PREFERRED_COMPATIBLE_MODE), reqBody);
if (ret != 0) {
NAPI_ERR_LOG("UserDefineIPCClient().Call failed, ret: %{public}d", ret);
context->SaveError(JS_E_INNER_FAIL);
return;
}
context->retVal = E_OK;
}
static void JSSetPreferredCompatibleModeCompleteCallback(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSSetPreferredCompatibleModeCompleteCallback");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
if (context->error == ERR_DEFAULT) {
jsContext->status = true;
napi_get_undefined(env, &jsContext->error);
napi_get_undefined(env, &jsContext->data);
} else {
napi_get_undefined(env, &jsContext->data);
context->HandleError(env, jsContext->error);
}
if (context->work != nullptr) {
tracer.Finish();
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
napi_value MediaLibraryNapi::PhotoAccessHelperSetPreferredCompatibleMode(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessHelperSetPreferredCompatibleMode");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
CHECK_NULLPTR_RET(ParseArgsSetPreferredCompatibleMode(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "SetPreferredCompatibleMode",
JSSetPreferredCompatibleModeExecute, JSSetPreferredCompatibleModeCompleteCallback);
}
static napi_value ParseArgsGetPreferredCompatibleMode(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system app");
return nullptr;
}
constexpr size_t minArgs = ARGS_ONE;
constexpr size_t maxArgs = ARGS_ONE;
CHECK_ARGS(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs),
JS_E_PARAM_INVALID);
string bundleName;
CHECK_ARGS(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[ARGS_ZERO], bundleName),
JS_E_PARAM_INVALID);
CHECK_COND(env, !bundleName.empty(), JS_E_PARAM_INVALID);
context->bundleName = bundleName;
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
static void JSGetPreferredCompatibleModeExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSGetPreferredCompatibleModeExecute");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
GetPreferredCompatibleModeReqBody reqBody;
GetPreferredCompatibleModeRespBody respBody;
reqBody.bundleName = context->bundleName;
int32_t ret = IPC::UserDefineIPCClient().Call(
static_cast<uint32_t>(MediaLibraryBusinessCode::GET_PREFERRED_COMPATIBLE_MODE), reqBody, respBody);
if (ret != 0) {
NAPI_ERR_LOG("UserDefineIPCClient().Call failed, ret: %{public}d", ret);
context->SaveError(JS_E_INNER_FAIL);
return;
}
context->preferredCompatibleMode = respBody.preferredCompatibleMode;
context->retVal = E_OK;
}
static void JSGetPreferredCompatibleModeCompleteCallback(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSGetPreferredCompatibleModeCompleteCallback");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
if (context->error == ERR_DEFAULT) {
jsContext->status = true;
napi_get_undefined(env, &jsContext->error);
napi_create_int32(env, context->preferredCompatibleMode, &jsContext->data);
} else {
napi_get_undefined(env, &jsContext->data);
context->HandleError(env, jsContext->error);
}
if (context->work != nullptr) {
tracer.Finish();
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
napi_value MediaLibraryNapi::PhotoAccessHelperGetPreferredCompatibleMode(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessHelperGetPreferredCompatibleMode");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
CHECK_NULLPTR_RET(ParseArgsGetPreferredCompatibleMode(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "GetPreferredCompatibleMode",
JSGetPreferredCompatibleModeExecute, JSGetPreferredCompatibleModeCompleteCallback);
}
static bool IsSupportedCompatibleMimeType(const std::string &mimeType)
{
return mimeType == SUPPORTED_COMPATIBLE_HEIC_MIME_TYPE ||
mimeType == SUPPORTED_COMPATIBLE_JPEG_MIME_TYPE;
}
static napi_value NormalizeSupportedMimeTypes(napi_env env, const std::vector<std::string> &supportedMimeTypes,
std::vector<std::string> &normalizedMimeTypes)
{
std::map<std::string, bool> mimeTypeMap;
for (const auto &mimeType : supportedMimeTypes) {
CHECK_ARGS_WITH_MEG(env, IsSupportedCompatibleMimeType(mimeType), JS_E_PARAM_INVALID,
"supportedMimeType is invalid");
mimeTypeMap[mimeType] = true;
}
CHECK_ARGS_WITH_MEG(env, mimeTypeMap.size() <= MAX_SUPPORTED_COMPATIBLE_MIME_TYPES,
JS_E_PARAM_INVALID, "supportedMimeTypes exceeds max size");
normalizedMimeTypes.clear();
normalizedMimeTypes.reserve(mimeTypeMap.size());
for (const auto &pair : mimeTypeMap) {
normalizedMimeTypes.emplace_back(pair.first);
}
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
static napi_value ParseSupportedMimeTypesFromConfig(napi_env env, napi_value configObj,
std::vector<std::string> &supportedMimeTypes)
{
supportedMimeTypes.clear();
bool hasSupportedMimeType = false;
CHECK_ARGS(env, napi_has_named_property(env, configObj, "supportedMimeType", &hasSupportedMimeType),
JS_E_PARAM_INVALID);
if (!hasSupportedMimeType) {
napi_value result = nullptr;
NAPI_INFO_LOG("ParseSupportedMimeTypesFromConfig: supportedMimeType property not found, use default value");
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
napi_value supportedMimeTypesValue = nullptr;
CHECK_ARGS(env, napi_get_named_property(env, configObj, "supportedMimeType", &supportedMimeTypesValue),
JS_INNER_FAIL);
napi_valuetype supportedMimeTypesType = napi_undefined;
CHECK_ARGS(env, napi_typeof(env, supportedMimeTypesValue, &supportedMimeTypesType), JS_E_PARAM_INVALID);
if (supportedMimeTypesType == napi_undefined) {
NAPI_INFO_LOG("ParseSupportedMimeTypesFromConfig: supportedMimeType is undefined, use default value");
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
bool isArray = false;
CHECK_ARGS_WITH_MSG(env, napi_is_array(env, supportedMimeTypesValue, &isArray), JS_E_INNER_FAIL,
"Failed to check array type");
CHECK_ARGS_WITH_MEG(env, isArray == true, JS_E_PARAM_INVALID, "Failed to check array type");
uint32_t len = 0;
CHECK_ARGS_WITH_MSG(env, napi_get_array_length(env, supportedMimeTypesValue, &len), JS_E_INNER_FAIL,
"Failed to get array length");
if (len == 0) {
NAPI_INFO_LOG("ParseSupportedMimeTypesFromConfig: supportedMimeType is empty, use default value");
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
CHECK_ARGS(env, MediaLibraryNapiUtils::GetStringArray(env, supportedMimeTypesValue, supportedMimeTypes),
JS_E_PARAM_INVALID);
std::vector<std::string> normalizedMimeTypes;
CHECK_NULLPTR_RET(NormalizeSupportedMimeTypes(env, supportedMimeTypes, normalizedMimeTypes));
supportedMimeTypes = std::move(normalizedMimeTypes);
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
static napi_value ParseArgsSetFileCompatibleConfig(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
constexpr size_t minArgs = ARGS_ONE;
constexpr size_t maxArgs = ARGS_TWO;
CHECK_ARGS(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs),
JS_E_PARAM_INVALID);
napi_value configObj;
napi_valuetype valueType = napi_undefined;
if (context->argc == 2) {
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system app");
return nullptr;
}
string bundleName;
CHECK_ARGS(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[ARGS_ZERO], bundleName),
JS_E_PARAM_INVALID);
CHECK_COND(env, !bundleName.empty(), JS_E_PARAM_INVALID);
context->bundleName = bundleName;
configObj = context->argv[ARGS_ONE];
} else {
configObj = context->argv[ARGS_ZERO];
}
CHECK_ARGS(env, napi_typeof(env, configObj, &valueType), JS_E_PARAM_INVALID);
if (valueType != napi_object) {
NapiError::ThrowError(env, JS_E_PARAM_INVALID, "Config must be an object");
return nullptr;
}
bool supportedHighResolution = false;
napi_value supportedHighResolutionValue = nullptr;
if (napi_get_named_property(env, configObj, "supportedHighResolution", &supportedHighResolutionValue) == napi_ok) {
CHECK_ARGS(env, napi_get_value_bool(env, supportedHighResolutionValue, &supportedHighResolution),
JS_E_PARAM_INVALID);
}
vector<string> supportedMimeTypes;
CHECK_NULLPTR_RET(ParseSupportedMimeTypesFromConfig(env, configObj, supportedMimeTypes));
context->supportedHighResolution = supportedHighResolution;
context->supportedMimeTypes = supportedMimeTypes;
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
static void JSSetFileCompatibleConfigExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSSetFileCompatibleConfigExecute");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
SetCompatibleInfoReqBody reqBody;
reqBody.bundleName = context->bundleName;
reqBody.supportedHighResolution = context->supportedHighResolution;
reqBody.supportedMimeTypes = context->supportedMimeTypes;
int32_t ret = IPC::UserDefineIPCClient().Call(
static_cast<uint32_t>(MediaLibraryBusinessCode::SET_COMPATIBLE_INFO), reqBody);
if (ret != 0) {
NAPI_ERR_LOG("UserDefineIPCClient().Call failed, ret: %{public}d", ret);
context->SaveError(ret);
return;
}
context->retVal = E_OK;
}
static void JSSetFileCompatibleConfigCompleteCallback(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSSetFileCompatibleConfigCompleteCallback");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
if (context->error == ERR_DEFAULT) {
jsContext->status = true;
napi_get_undefined(env, &jsContext->error);
napi_get_undefined(env, &jsContext->data);
} else {
napi_get_undefined(env, &jsContext->data);
context->HandleError(env, jsContext->error);
}
if (context->work != nullptr) {
tracer.Finish();
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
napi_value MediaLibraryNapi::PhotoAccessHelperSetFileCompatibleConfig(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessHelperSetFileCompatibleConfig");
NAPI_INFO_LOG("PhotoAccessHelperSetFileCompatibleConfig");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
CHECK_NULLPTR_RET(ParseArgsSetFileCompatibleConfig(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "SetFileCompatibleConfig",
JSSetFileCompatibleConfigExecute, JSSetFileCompatibleConfigCompleteCallback);
}
static napi_value ParseArgsGetAssetCompatibleConfig(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system app");
return nullptr;
}
constexpr size_t minArgs = ARGS_ONE;
constexpr size_t maxArgs = ARGS_ONE;
CHECK_ARGS(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs),
JS_E_PARAM_INVALID);
string bundleName;
CHECK_ARGS(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[ARGS_ZERO], bundleName),
JS_E_PARAM_INVALID);
CHECK_COND(env, !bundleName.empty(), JS_E_PARAM_INVALID);
context->bundleName = bundleName;
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
static void JSGetAssetCompatibleConfigExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSGetAssetCompatibleConfigExecute");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
GetCompatibleInfoReqBody reqBody;
GetCompatibleInfoRespBody respBody;
reqBody.bundleName = context->bundleName;
int32_t ret = IPC::UserDefineIPCClient().Call(
static_cast<uint32_t>(MediaLibraryBusinessCode::GET_COMPATIBLE_INFO), reqBody, respBody);
if (ret != 0) {
NAPI_ERR_LOG("UserDefineIPCClient().Call failed, ret: %{public}d", ret);
context->SaveError(ret);
return;
}
context->supportedHighResolution = respBody.supportedHighResolution;
context->supportedMimeTypes = respBody.supportedMimeTypes;
context->retVal = E_OK;
}
static void JSGetAssetCompatibleConfigCompleteCallback(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSGetAssetCompatibleConfigCompleteCallback");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
if (context->error == ERR_DEFAULT) {
jsContext->status = true;
napi_get_undefined(env, &jsContext->error);
napi_value configObj = nullptr;
napi_create_object(env, &configObj);
bool supportedHighResolution = context->supportedHighResolution;
napi_value supportedHighResolutionValue = nullptr;
napi_get_boolean(env, supportedHighResolution, &supportedHighResolutionValue);
napi_set_named_property(env, configObj, "supportedHighResolution", supportedHighResolutionValue);
napi_value supportedMimeTypeValue = nullptr;
napi_create_array_with_length(env, context->supportedMimeTypes.size(), &supportedMimeTypeValue);
for (size_t i = 0; i < context->supportedMimeTypes.size(); i++) {
napi_value mimeTypeValue = nullptr;
napi_create_string_utf8(env, context->supportedMimeTypes[i].c_str(), NAPI_AUTO_LENGTH, &mimeTypeValue);
napi_set_element(env, supportedMimeTypeValue, i, mimeTypeValue);
}
napi_set_named_property(env, configObj, "supportedMimeType", supportedMimeTypeValue);
jsContext->data = configObj;
} else {
napi_get_undefined(env, &jsContext->data);
context->HandleError(env, jsContext->error);
}
if (context->work != nullptr) {
tracer.Finish();
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
napi_value MediaLibraryNapi::PhotoAccessHelperGetAssetCompatibleConfig(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessHelperGetAssetCompatibleConfig");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
CHECK_NULLPTR_RET(ParseArgsGetAssetCompatibleConfig(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "GetAssetCompatibleConfig",
JSGetAssetCompatibleConfigExecute, JSGetAssetCompatibleConfigCompleteCallback);
}
static napi_value ParseArgsGetAssetCompatibleUris(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
constexpr size_t minArgs = ARGS_TWO;
constexpr size_t maxArgs = ARGS_THREE;
CHECK_ARGS(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs),
JS_E_PARAM_INVALID);
CHECK_ARGS(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[ARGS_ZERO],
context->bundleName), JS_E_PARAM_INVALID);
CHECK_COND_WITH_ERR_MESSAGE(env, !context->bundleName.empty(), JS_E_PARAM_INVALID, "invalid bundleName");
bool isArray = false;
CHECK_ARGS(env, napi_is_array(env, context->argv[ARGS_ONE], &isArray), JS_E_PARAM_INVALID);
CHECK_COND_WITH_ERR_MESSAGE(env, isArray, JS_E_PARAM_INVALID, "assets must be an array");
uint32_t len = 0;
CHECK_ARGS(env, napi_get_array_length(env, context->argv[ARGS_ONE], &len), JS_E_PARAM_INVALID);
for (uint32_t i = 0; i < len; i++) {
napi_value item = nullptr;
CHECK_ARGS(env, napi_get_element(env, context->argv[ARGS_ONE], i, &item), JS_E_PARAM_INVALID);
FileAssetNapi *obj = nullptr;
CHECK_ARGS(env, napi_unwrap(env, item, reinterpret_cast<void **>(&obj)), JS_E_PARAM_INVALID);
CHECK_NULLPTR_RET(obj);
PhotoAssetInfo info;
info.width = obj->GetWidth();
info.height = obj->GetHeight();
info.uri = obj->GetFileUri();
info.fileId = obj->GetFileId();
context->photoAssetInfos.push_back(info);
}
if (context->argc >= ARGS_THREE) {
napi_valuetype valueType = napi_undefined;
napi_typeof(env, context->argv[ARGS_TWO], &valueType);
if (valueType == napi_number) {
CHECK_ARGS(env, MediaLibraryNapiUtils::GetInt32(env, context->argv[ARGS_TWO],
context->compatibleFlags), JS_E_PARAM_INVALID);
constexpr uint32_t VALID_FLAGS_MASK = 0x3;
uint32_t flags = static_cast<uint32_t>(context->compatibleFlags);
CHECK_COND_WITH_ERR_MESSAGE(env, (flags & ~VALID_FLAGS_MASK) == 0,
JS_E_PARAM_INVALID, "invalid compatibleFlags");
} else if (valueType == napi_undefined) {
context->compatibleFlags = THIRD_ENUM;
} else {
NapiError::ThrowError(env, JS_E_PARAM_INVALID, "invalid compatibleFlags");
}
} else {
context->compatibleFlags = THIRD_ENUM;
}
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_E_INNER_FAIL);
return result;
}
static void GetAssetCompatibleUrisExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("GetAssetCompatibleUrisExecute");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
GetTranscodeCheckInfoReqBody reqBody;
GetTranscodeCheckInfoRespBody respBody;
reqBody.bundleName = context->bundleName;
int32_t ret = IPC::UserDefineIPCClient().Call(
static_cast<uint32_t>(MediaLibraryBusinessCode::GET_TRANSCODE_CHECK_INFO), reqBody, respBody);
if (ret != 0) {
NAPI_ERR_LOG("UserDefineIPCClient().Call failed, ret: %{public}d", ret);
context->SaveError(ret);
return;
}
context->supportedHighResolution = respBody.supportedHighResolution;
context->supportedMimeTypes = respBody.supportedMimeTypes;
context->preferredCompatibleMode = respBody.preferredCompatibleMode;
NAPI_ERR_LOG("bundleName %{public}s, supportedHighResolution %{public}d, preferredCompatibleMode %{public}d",
respBody.bundleName.c_str(), respBody.supportedHighResolution, respBody.preferredCompatibleMode);
context->retVal = E_OK;
}
static void HandleCheckTranscodeUri(MediaLibraryAsyncContext *context,
bool checkHighPixel, bool checkHeif, vector<string> &result)
{
for (auto &item : context->photoAssetInfos) {
bool isHighPixel = IsHighPixel(item.width, item.height);
size_t atDot = item.uri.find('.');
if (atDot == std::string::npos) {
continue;
}
std::string ext = item.uri.substr(atDot + 1);
bool isHeifFile = (ext == "heif" || ext == "heic");
if (context->compatibleFlags == THIRD_ENUM) {
CompatibleInfo compatibleinfo;
compatibleinfo.bundleName = context->bundleName;
compatibleinfo.highResolution = context->supportedHighResolution;
compatibleinfo.encodings = context->supportedMimeTypes;
compatibleinfo.preferredCompatibleMode =
static_cast<PreferredCompatibleMode>(context->preferredCompatibleMode);
TranscodeMode res =
PreferredCompatibleModeCheckUtils::CheckTranscodeMode(compatibleinfo, isHighPixel, isHeifFile);
if (res == TranscodeMode::CURRENT) {
continue;
} else if (res == TranscodeMode::COMPATIBLE) {
result.push_back(item.uri);
continue;
}
}
if (isHighPixel && checkHighPixel && !context->supportedHighResolution) {
result.push_back(item.uri);
continue;
}
if (isHeifFile && checkHeif &&
find(context->supportedMimeTypes.begin(), context->supportedMimeTypes.end(), "image/heic") ==
context->supportedMimeTypes.end()) {
result.push_back(item.uri);
continue;
}
}
}
static vector<string> CheckTranscodeUri(MediaLibraryAsyncContext *context)
{
vector<string> result;
uint32_t flags = static_cast<uint32_t>(context->compatibleFlags);
if (flags == 0) {
return result;
}
bool checkHighPixel = (context->compatibleFlags & 0x1) != 0;
bool checkHeif = (context->compatibleFlags & 0x2) != 0;
if (context->preferredCompatibleMode ==
static_cast<int32_t>(TranscodeMode::CURRENT)) {
return result;
}
if (context->preferredCompatibleMode ==
static_cast<int32_t>(TranscodeMode::COMPATIBLE)) {
for (auto &item : context->photoAssetInfos) {
bool isHighPixel = item.width * item.height >= HIGH_PIXEL_SIZE;
size_t atDot = item.uri.find('.');
if (atDot == std::string::npos) {
continue;
}
std::string ext = item.uri.substr(atDot + 1);
bool isHeifFile = (ext == "heif" || ext == "heic");
if (checkHighPixel && isHighPixel) {
result.push_back(item.uri);
continue;
}
if (checkHeif && isHeifFile) {
result.push_back(item.uri);
continue;
}
}
return result;
}
HandleCheckTranscodeUri(context, checkHighPixel, checkHeif, result);
return result;
}
static void GetAssetCompatibleUrisCallback(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("GetAssetCompatibleUrisComplete");
MediaLibraryAsyncContext *context = static_cast<MediaLibraryAsyncContext*>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), JS_E_INNER_FAIL);
if (context->error != ERR_DEFAULT) {
context->HandleError(env, jsContext->error);
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->data), JS_E_INNER_FAIL);
} else {
napi_value jsArray = nullptr;
CHECK_ARGS_RET_VOID(env, napi_create_array(env, &jsArray), JS_E_INNER_FAIL);
vector<string> res = CheckTranscodeUri(context);
for (size_t i = 0; i < res.size(); i++) {
napi_value jsString = nullptr;
CHECK_ARGS_RET_VOID(env, napi_create_string_utf8(env, res[i].c_str(),
NAPI_AUTO_LENGTH, &jsString), JS_E_INNER_FAIL);
CHECK_ARGS_RET_VOID(env, napi_set_element(env, jsArray, i, jsString), JS_E_INNER_FAIL);
}
jsContext->data = jsArray;
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), JS_E_INNER_FAIL);
jsContext->status = true;
}
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
napi_value MediaLibraryNapi::GetAssetCompatibleUris(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("GetAssetCompatibleUris");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
NAPI_ASSERT(env, ParseArgsGetAssetCompatibleUris(env, info, asyncContext), "Failed to parse js args");
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "GetAssetCompatibleUris",
GetAssetCompatibleUrisExecute, GetAssetCompatibleUrisCallback);
}
static napi_value ParseConvertToAsset(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
constexpr size_t minArgs = ARGS_ONE;
constexpr size_t maxArgs = ARGS_ONE;
CHECK_ARGS(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs),
JS_ERR_PARAMETER_INVALID);
string path;
CHECK_ARGS(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[ARGS_ZERO], path),
JS_ERR_PARAMETER_INVALID);
context->path = path;
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
static void ConvertToAssetExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("ConvertToAssetExecute");
auto *context = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "context is null");
ConvertToAssetReqBody reqBody;
ConvertToAssetRespBody respBody;
reqBody.path = context->path;
NAPI_INFO_LOG("ConvertToAssetExecute path: %{public}s", reqBody.path.c_str());
int32_t ret = IPC::UserDefineIPCClient().Call(context->businessCode, reqBody, respBody);
if (ret < 0 || respBody.resultSet == nullptr) {
NAPI_ERR_LOG("UserDefineIPCClient().Call failed, ret: %{public}d", ret);
context->SaveError(ret);
return;
}
auto fetchResult = make_unique<FetchResult<FileAsset>>(move(respBody.resultSet));
if (fetchResult == nullptr) {
context->SaveError(E_FAIL);
NAPI_ERR_LOG("fetchResult is null");
return;
}
context->fileAsset = fetchResult->GetFirstObject();
}
static void JSConvertToAssetCompleteCallback(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSConvertToAssetCompleteCallback");
NAPI_INFO_LOG("JSConvertToAssetCompleteCallback");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
if (context->error == ERR_DEFAULT) {
NAPI_INFO_LOG("JSConvertToAssetCompleteCallback sucess");
if (context->fileAsset == nullptr) {
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, ERR_INVALID_OUTPUT,
"Obtain file asset failed");
napi_get_undefined(env, &jsContext->data);
} else {
napi_value jsFileAsset = nullptr;
std::unique_ptr<FileAsset> fileAsset = std::move(context->fileAsset);
std::string fileAssetUri = MediaFileUtils::GetFileAssetUri(fileAsset->GetPath(), fileAsset->GetDisplayName(),
fileAsset->GetId());
fileAsset->SetUri(MediaFileUtils::Encode(fileAssetUri));
CHECK_NULL_PTR_RETURN_VOID(fileAsset, "fileAsset is null.");
NAPI_INFO_LOG("JSConvertToAssetCompleteCallback sucess path:%{public}s, id:%{public}d, displayName:%{public}s", fileAsset->GetPath().c_str(), fileAsset->GetId(), fileAsset->GetDisplayName().c_str());
fileAsset->SetResultNapiType(ResultNapiType::TYPE_PHOTOACCESS_HELPER);
std::shared_ptr<FileAsset> sharedFileAsset = std::move(fileAsset);
jsFileAsset = FileAssetNapi::CreatePhotoAsset(env, sharedFileAsset);
if (jsFileAsset == nullptr) {
NAPI_ERR_LOG("Failed to clone file napi object");
napi_get_undefined(env, &jsContext->data);
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, JS_INNER_FAIL, "System inner fail");
} else {
NAPI_DEBUG_LOG("JSCreateAssetCompleteCallback jsFileAsset != nullptr");
jsContext->data = jsFileAsset;
napi_get_undefined(env, &jsContext->error);
jsContext->status = true;
}
}
} else {
NAPI_INFO_LOG("JSConvertToAssetCompleteCallback error");
context->HandleError(env, jsContext->error);
NAPI_CALL_RETURN_VOID(env, napi_get_undefined(env, &jsContext->data));
}
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
HILOG_COMM_INFO("%{public}s:{%{public}s:%{public}d} End convert to asset.",
MLOG_TAG, __FUNCTION__, __LINE__);
delete context;
}
napi_value MediaLibraryNapi::JSConvertToAsset(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("JSConvertToAsset");
NAPI_INFO_LOG("JSConvertToAsset");
HILOG_COMM_INFO("%{public}s:{%{public}s:%{public}d} enter", MLOG_TAG, __FUNCTION__, __LINE__);
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
asyncContext->assetType = TYPE_PHOTO;
NAPI_ASSERT(env, ParseConvertToAsset(env, info, asyncContext), "Failed to parse js args");
asyncContext->businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::CONVERT_TO_ASSET);
CHECK_COND_WITH_ERR_MESSAGE(env, !asyncContext->path.empty(), JS_E_PARAM_INVALID, "path is empty");
std::string targetPath = TARGET_DIR + asyncContext->path;
CHECK_COND_WITH_ERR_MESSAGE(env, PhotoFileUtils::CheckFileManagerRealPath(targetPath),
JS_E_PARAM_INVALID, "DirPath is error");
asyncContext->path = targetPath;
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSConvertToAsset",
ConvertToAssetExecute, JSConvertToAssetCompleteCallback);
}
bool CheckFileManagerRealPath(const std::string &path)
{
MEDIA_INFO_LOG("CheckFileManagerRealPath path: %{public}s", MediaFileUtils::DesensitizePath(path).c_str());
const std::string specialChar = "//";
CHECK_AND_RETURN_RET(MediaStringUtils::StartsWith(path, DOCS_DIR) || path.find(specialChar) != std::string::npos, false);
size_t pos = path.find("/", DOCS_DIR.length());
std::string subDirName = (pos == std::string::npos) ?
path.substr(DOCS_DIR.length()) : path.substr(DOCS_DIR.length(), pos - DOCS_DIR.length());
return FILE_MANAGER_EXCLUDED_DIR_NAMES.count(subDirName) == 0;
}
bool CheckGalleryRealPath(const std::string &uri)
{
MEDIA_DEBUG_LOG("CheckGalleryRealPath uri: %{public}s", MediaFileUtils::DesensitizeUri(uri).c_str());
CHECK_AND_RETURN_RET(!uri.empty(), false);
CHECK_AND_RETURN_RET(MediaStringUtils::StartsWith(uri, "file://media/"), false);
return true;
}
static void ProgressCallback(napi_env env, napi_value cb, void *context, void *data)
{
auto *progressData = static_cast<ProgressData *>(data);
if (progressData == nullptr) {
return;
}
napi_value undefined = nullptr;
napi_get_undefined(env, &undefined);
napi_value progressObj;
napi_create_object(env, &progressObj);
napi_value processedValue;
napi_create_int64(env, progressData->processed, &processedValue);
napi_set_named_property(env, progressObj, "processed", processedValue);
napi_value remainValue;
napi_create_int64(env, progressData->remain, &remainValue);
napi_set_named_property(env, progressObj, "remain", remainValue);
if (cb != nullptr) {
napi_call_function(env, undefined, cb, 1, &progressObj, nullptr);
}
delete progressData;
}
static void GenerateStringArrayValue(napi_env &env, const std::vector<std::string> &result, const size_t len,
napi_value &value)
{
if (len == 0) {
return;
}
NapiScopeHandler scopeHandler(env);
CHECK_IF_EQUAL(scopeHandler.IsValid(), "scopeHandler is nullptr");
CHECK_ARGS_RET_VOID(env, napi_create_array_with_length(env, len, &value), JS_INNER_FAIL);
for (size_t i = 0; i < len; ++i) {
napi_value element = nullptr;
CHECK_ARGS_RET_VOID(env, napi_create_string_utf8(env, result[i].c_str(), NAPI_AUTO_LENGTH, &element),
JS_INNER_FAIL);
if ((element == nullptr) || (napi_set_element(env, value, i, element) != napi_ok)) {
NAPI_ERR_LOG("Failed to set element %{public}s to array.", result[i].c_str());
break;
}
}
}
static void ResultCallback(napi_env env, napi_value cb, void *context, void *data)
{
auto *resultData = static_cast<ResultInfo *>(data);
if (resultData == nullptr) {
return;
}
napi_value undefined = nullptr;
napi_get_undefined(env, &undefined);
napi_value resultObj;
napi_create_object(env, &resultObj);
napi_value codeValue;
napi_create_int32(env, resultData->code, &codeValue);
napi_set_named_property(env, resultObj, "code", codeValue);
size_t len = resultData->result.size();
if (len > 0) {
napi_value resultValue = nullptr;
GenerateStringArrayValue(env, resultData->result, len, resultValue);
CHECK_ARGS_RET_VOID(env,
napi_set_named_property(env, resultObj, "result", resultValue),
JS_INNER_FAIL);
} else {
NAPI_ERR_LOG("authorizedUris size is zero.");
}
if (cb != nullptr) {
napi_call_function(env, undefined, cb, 1, &resultObj, nullptr);
}
delete resultData;
}
static napi_value ParseAssetsArray(napi_env env, napi_value arg, std::vector<std::string> &assetArray)
{
CHECK_ARGS(env, MediaLibraryNapiUtils::GetStringArray(env, arg, assetArray), JS_E_PARAM_INVALID);
constexpr size_t sizeOfArray = 0;
if (assetArray.size() > MAX_ASSETS_NUMBER || assetArray.size() == sizeOfArray) {
NapiError::ThrowError(env, JS_E_PARAM_INVALID, "size of assets is 0 or over 20.");
return nullptr;
}
for (const auto &asset : assetArray) {
CHECK_COND_WITH_ERR_MESSAGE(env, !asset.empty(), JS_E_PARAM_INVALID, "asset is empty");
CHECK_COND_WITH_ERR_MESSAGE(env, CheckGalleryRealPath(asset),
JS_E_PARAM_INVALID, "asset is error");
}
RETURN_NAPI_TRUE(env);
}
static napi_value ParseTargetDir(napi_env env, napi_value arg, std::string &targetDir)
{
CHECK_ARGS(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, arg, targetDir), JS_E_PARAM_INVALID);
CHECK_COND_WITH_ERR_MESSAGE(env, !targetDir.empty(), JS_E_PARAM_INVALID, "targetDir is empty");
CHECK_COND_WITH_ERR_MESSAGE(env, CheckFileManagerRealPath(targetDir),
JS_E_PARAM_INVALID, "targetDir is error");
targetDir = FILE_MANAGEMENT_PREFIX + targetDir;
RETURN_NAPI_TRUE(env);
}
static napi_status ParseBatchOperationOptions(napi_env env, napi_value option, napi_threadsafe_function &onSizeProgress,
napi_threadsafe_function &onCountProgress, napi_threadsafe_function &onResultProcess)
{
napi_value sizeProgressHandler = nullptr;
napi_valuetype valueType;
napi_status status = napi_get_named_property(env, option, "sizeProgressListener", &sizeProgressHandler);
if (status == napi_ok && sizeProgressHandler != nullptr) {
status = napi_typeof(env, sizeProgressHandler, &valueType);
if (status == napi_ok && valueType == napi_function) {
napi_value resource;
napi_create_string_utf8(env, "MoveAssetsToDirSizeProgress", NAPI_AUTO_LENGTH, &resource);
status = napi_create_threadsafe_function(env, sizeProgressHandler, nullptr,
resource, 0, 1, nullptr, nullptr, nullptr, ProgressCallback, &onSizeProgress);
CHECK_AND_RETURN_RET_LOG(status == napi_ok, status, "Failed to create onSizeProgress threadsafe function");
}
}
napi_value countProgressHandler = nullptr;
status = napi_get_named_property(env, option, "countProgressListener", &countProgressHandler);
if (status == napi_ok && countProgressHandler != nullptr) {
status = napi_typeof(env, countProgressHandler, &valueType);
if (status == napi_ok && valueType == napi_function) {
napi_value resource;
napi_create_string_utf8(env, "MoveAssetsToDirCountProgress", NAPI_AUTO_LENGTH, &resource);
status = napi_create_threadsafe_function(env, countProgressHandler, nullptr,
resource, 0, 1, nullptr, nullptr, nullptr, ProgressCallback, &onCountProgress);
CHECK_AND_RETURN_RET_LOG(status == napi_ok, status, "Failed to create onCountProgress threadsafe function");
}
}
napi_value onResultHandler = nullptr;
status = napi_get_named_property(env, option, "resultListener", &onResultHandler);
if (status == napi_ok && onResultHandler != nullptr) {
status = napi_typeof(env, onResultHandler, &valueType);
if (status == napi_ok && valueType == napi_function) {
napi_value resource;
napi_create_string_utf8(env, "MoveAssetsToDirResultProcess", NAPI_AUTO_LENGTH, &resource);
status = napi_create_threadsafe_function(env, onResultHandler, nullptr,
resource, 0, 1, nullptr, nullptr, nullptr, ResultCallback, &onResultProcess);
CHECK_AND_RETURN_RET_LOG(status == napi_ok, status, "Failed to create onResult threadsafe function");
}
}
return napi_ok;
}
template<typename RespBodyType>
static void OnMoveTaskFinished(napi_env env, void *data, RespBodyType respBody)
{
std::unordered_map<int32_t, int32_t> errorCodeMap = { {E_PATH_NOT_SUPPORT, 1},
{E_FILE_NOT_EXIST, 2},
{E_RENAME, 3},
{E_MEDIA_TYPE_INVAILD, 4},
{E_CANCEL_TASK, 5},
{E_TARGET_ALBUM_INVAILD, 6},
{E_FILE_HIDDEN_OR_TRASHED, 7},
{E_FILE_IS_PENDING_STATU, 8}};
auto *context = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "context is null");
if (context->onResultProcess != nullptr) {
int32_t code = (errorCodeMap.count(respBody.errCode) > 0) ? errorCodeMap[respBody.errCode] : 0;
auto *resultData = new ResultInfo{code, respBody.resultList};
napi_call_threadsafe_function(context->onResultProcess, resultData, napi_tsfn_nonblocking);
}
if (context->onSizeProgress != nullptr) {
auto *sizeProgressData = new ProgressData{respBody.processedSize, respBody.remainSize};
napi_call_threadsafe_function(context->onSizeProgress, sizeProgressData, napi_tsfn_nonblocking);
}
if (context->onCountProgress != nullptr) {
auto *countProgressData = new ProgressData{respBody.processedCount, respBody.remainCount};
napi_call_threadsafe_function(context->onCountProgress, countProgressData, napi_tsfn_nonblocking);
}
{
std::lock_guard<std::mutex> lock(g_moveAssetsProgressMutex);
g_moveAssetsProgressMap.erase(context->requestId);
}
TaskSignalNapi::UnregisterTaskCancelFlag(context->requestId);
}
static napi_value ParseArgsMoveAssetsToDir(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_E_INNER_FAIL);
constexpr size_t minArgs = ARGS_TWO;
constexpr size_t maxArgs = ARGS_THREE;
napi_status status = MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs);
CHECK_ARGS(env, status, JS_E_PARAM_INVALID);
std::vector<std::string> assetArray;
CHECK_NULLPTR_RET(ParseAssetsArray(env, context->argv[ARGS_ZERO], assetArray));
context->uriArray = assetArray;
std::string targetDir;
CHECK_NULLPTR_RET(ParseTargetDir(env, context->argv[ARGS_ONE], targetDir));
context->targetDir = targetDir;
if (context->argc == ARGS_THREE) {
napi_value option = context->argv[ARGS_TWO];
napi_valuetype valueType;
status = napi_typeof(env, option, &valueType);
CHECK_COND(env, status == napi_ok && valueType == napi_object, JS_E_PARAM_INVALID);
status = ParseBatchOperationOptions(env, option, context->onSizeProgress, context->onCountProgress,
context->onResultProcess);
CHECK_ARGS(env, status, JS_E_PARAM_INVALID);
TaskSignalNapi *taskSignalNative = nullptr;
napi_value taskSignal = nullptr;
status = napi_get_named_property(env, option, "taskSignal", &taskSignal);
if (status == napi_ok && taskSignal != nullptr) {
napi_valuetype valueType;
status = napi_typeof(env, taskSignal, &valueType);
if (status == napi_ok && valueType == napi_object) {
status = napi_unwrap(env, taskSignal, reinterpret_cast<void **>(&taskSignalNative));
CHECK_AND_RETURN_RET_LOG(status == napi_ok && taskSignalNative != nullptr, nullptr,
"Failed to unwrap TaskSignalNapi");
status = napi_create_reference(env, taskSignal, 1, &context->taskSignalRef);
CHECK_AND_RETURN_RET_LOG(status == napi_ok, nullptr, "Failed to create taskSignal reference");
}
}
napi_value modeValue = nullptr;
if (napi_get_named_property(env, option, "mode", &modeValue) == napi_ok) {
CHECK_AND_RETURN_RET_LOG(MediaLibraryNapiUtils::GetInt32(env, modeValue, context->mode) == napi_ok,
nullptr, "fail to parse mode");
}
context->requestId = MediaLibraryNapi::AssignRequestId();
if (taskSignalNative != nullptr) {
taskSignalNative->SetRequestId(context->requestId);
}
auto cancelFlag = std::make_shared<std::atomic<bool>>(false);
TaskSignalNapi::RegisterTaskCancelFlag(context->requestId, cancelFlag);
}
return result;
}
static void MoveAssetsToDirExecute(napi_env env, void *data)
{
NAPI_DEBUG_LOG("enter MoveAssetsToDirExecute");
MediaLibraryTracer tracer;
tracer.Start("MoveAssetsToDirExecute");
const std::string PROGRESS_TYPE = "MoveAssetsToDir";
auto *context = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "context is null");
if (context->onSizeProgress != nullptr || context->onCountProgress != nullptr ||
context->onResultProcess != nullptr) {
auto observer = make_shared<ProgressCallbackObserver>(context->requestId,
context->onSizeProgress, context->onCountProgress);
std::string uriStr = MEDIA_PROGRESS_REGISTER_PREFIX + PROGRESS_TYPE + "/" + std::to_string(context->requestId);
Uri uri(uriStr);
int32_t ret = UserFileClient::RegisterObserverExtProvider(uri,
static_cast<shared_ptr<DataShare::DataShareObserver>>(observer), false);
if (ret != 0) {
context->SaveError(E_INNER_FAIL);
MEDIA_ERR_LOG("Failed to register observer, err: %{public}d", ret);
}
{
std::lock_guard<std::mutex> lock(g_moveAssetsProgressMutex);
g_moveAssetsProgressMap[context->requestId] =
Notification::NotifyForUserDefineType::MOVE_ASSETS_TO_DIR_PROGRESS;
}
}
ChangeRequestMoveAssetsToDirReqBody reqBody;
ChangeRequestMoveAssetsToDirRespBody respBody;
reqBody.assets = context->uriArray;
reqBody.targetDir = context->targetDir;
reqBody.requestId = context->requestId;
reqBody.mode = context->mode;
int32_t ret = IPC::UserDefineIPCClient().Call(
static_cast<uint32_t>(MediaLibraryBusinessCode::MOVE_ASSETS_TO_DIR),
reqBody, respBody);
context->uriArray = respBody.resultList;
OnMoveTaskFinished(env, data, respBody);
if (ret < 0) {
MEDIA_ERR_LOG("Failed to move assets to dir, err: %{public}d", ret);
context->SaveMoveError(ret);
return;
}
NAPI_INFO_LOG("Move %{public}d assets to dir successfully", static_cast<int>(reqBody.assets.size()));
tracer.Finish();
}
static void MoveAssetsToDirCompleteCallback(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("MoveAssetsToDirCompleteCallback");
auto *context = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "context is nullptr");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), JS_E_INNER_FAIL);
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->data), JS_E_INNER_FAIL);
if (status != napi_ok || context->error != ERR_DEFAULT) {
NAPI_ERR_LOG("MoveAssetsToDir failed, status: %{public}d, error: %{public}d", status, context->error);
context->HandleError(env, jsContext->error);
} else {
jsContext->status = true;
JSCreateUriArrayInCallback(env, context, jsContext);
}
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
if (context->onSizeProgress != nullptr) {
napi_release_threadsafe_function(context->onSizeProgress, napi_tsfn_release);
}
if (context->onCountProgress != nullptr) {
napi_release_threadsafe_function(context->onCountProgress, napi_tsfn_release);
}
if (context->onResultProcess != nullptr) {
napi_release_threadsafe_function(context->onResultProcess, napi_tsfn_release);
}
if (context->taskSignalRef != nullptr) {
napi_delete_reference(env, context->taskSignalRef);
}
NAPI_DEBUG_LOG("MediaLibraryNapi::MoveAssetsToDirCompleteCallback end");
delete context;
}
napi_value MediaLibraryNapi::MoveAssetsToDir(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("MoveAssetsToDir");
NAPI_INFO_LOG("MoveAssetsToDir in");
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
CHECK_NULLPTR_RET(ParseArgsMoveAssetsToDir(env, info, asyncContext));
asyncContext->businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::MOVE_ASSETS_TO_DIR);
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "MoveAssetsToDir",
MoveAssetsToDirExecute, MoveAssetsToDirCompleteCallback);
}
bool CheckFileManagementPath(const std::string &path)
{
CHECK_AND_RETURN_RET_LOG(path.size() >= DOCS_DIR.size(), false, "invaild args");
return path.find(DOCS_DIR) != std::string::npos && path.substr(0, DOCS_DIR.length()) == DOCS_DIR;
}
static napi_value ParseAssetsDirArray(napi_env env, napi_value arg, std::vector<std::string> &assetPathArray)
{
CHECK_ARGS(env, MediaLibraryNapiUtils::GetStringArray(env, arg, assetPathArray), JS_E_PARAM_INVALID);
constexpr size_t sizeOfArray = 0;
if (assetPathArray.size() > MAX_ASSETS_NUMBER || assetPathArray.size() == sizeOfArray) {
NapiError::ThrowError(env, JS_E_PARAM_INVALID, "size of assets is 0 or over 20.");
return nullptr;
}
for (auto &asset : assetPathArray) {
CHECK_COND_WITH_ERR_MESSAGE(env, !asset.empty(), JS_E_PARAM_INVALID, "asset is empty");
CHECK_COND_WITH_ERR_MESSAGE(env, CheckFileManagementPath(asset),
JS_E_PARAM_INVALID, "asset is error");
asset = FILE_MANAGEMENT_PREFIX + asset;
}
RETURN_NAPI_TRUE(env);
}
static napi_value ParsePhotoAlbum(napi_env env, napi_value arg, shared_ptr<PhotoAlbum>& photoAlbum)
{
napi_valuetype valueType;
PhotoAlbumNapi* photoAlbumNapi;
CHECK_ARGS(env, napi_typeof(env, arg, &valueType), JS_INNER_FAIL);
CHECK_COND_WITH_MESSAGE(env, valueType == napi_object, "Invalid argument type");
CHECK_ARGS(env, napi_unwrap(env, arg, reinterpret_cast<void**>(&photoAlbumNapi)), JS_INNER_FAIL);
CHECK_COND_WITH_MESSAGE(env, photoAlbumNapi != nullptr, "Failed to get PhotoAlbumNapi object");
auto photoAlbumPtr = photoAlbumNapi->GetPhotoAlbumInstance();
CHECK_COND_WITH_MESSAGE(env, photoAlbumPtr != nullptr, "photoAlbum is null");
CHECK_COND_WITH_MESSAGE(env,
photoAlbumPtr->GetResultNapiType() == ResultNapiType::TYPE_PHOTOACCESS_HELPER &&
PhotoAlbum::CheckPhotoAlbumType(photoAlbumPtr->GetPhotoAlbumType()) &&
PhotoAlbum::CheckPhotoAlbumSubType(photoAlbumPtr->GetPhotoAlbumSubType()),
"Unsupported type of photoAlbum");
photoAlbum = photoAlbumPtr;
RETURN_NAPI_TRUE(env);
}
static napi_value ParseArgsMoveAssetsByPath(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_E_INNER_FAIL);
napi_status status = MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, ARGS_TWO, ARGS_THREE);
CHECK_ARGS(env, status, JS_E_PARAM_INVALID);
std::vector<std::string> assetArray;
CHECK_NULLPTR_RET(ParseAssetsDirArray(env, context->argv[ARGS_ZERO], assetArray));
context->uriArray.swap(assetArray);
shared_ptr<PhotoAlbum> targetAlbum = nullptr;
CHECK_COND_WITH_MESSAGE(
env, ParsePhotoAlbum(env, context->argv[PARAM1], targetAlbum), "Failed to parse targetAlbum");
context->albumIds.push_back(std::to_string(targetAlbum->GetAlbumId()));
if (context->argc == ARGS_THREE) {
napi_value option = context->argv[ARGS_TWO];
napi_valuetype valueType;
status = napi_typeof(env, option, &valueType);
CHECK_COND(env, status == napi_ok && valueType == napi_object, JS_E_PARAM_INVALID);
status = ParseBatchOperationOptions(env, option, context->onSizeProgress, context->onCountProgress,
context->onResultProcess);
CHECK_ARGS(env, status, JS_E_PARAM_INVALID);
TaskSignalNapi *taskSignalNative = nullptr;
napi_value taskSignal = nullptr;
status = napi_get_named_property(env, option, "taskSignal", &taskSignal);
if (status == napi_ok && taskSignal != nullptr) {
napi_valuetype valueType;
status = napi_typeof(env, taskSignal, &valueType);
if (status == napi_ok && valueType == napi_object) {
status = napi_unwrap(env, taskSignal, reinterpret_cast<void **>(&taskSignalNative));
CHECK_AND_RETURN_RET_LOG(status == napi_ok && taskSignalNative != nullptr, nullptr,
"Failed to unwrap TaskSignalNapi");
status = napi_create_reference(env, taskSignal, 1, &context->taskSignalRef);
CHECK_AND_RETURN_RET_LOG(status == napi_ok, nullptr, "Failed to create taskSignal reference");
}
}
napi_value modeValue = nullptr;
if (napi_get_named_property(env, option, "mode", &modeValue) == napi_ok) {
CHECK_AND_RETURN_RET_LOG(MediaLibraryNapiUtils::GetInt32(env, modeValue, context->mode) == napi_ok,
nullptr, "fail to parse mode");
}
context->requestId = MediaLibraryNapi::AssignRequestId();
if (taskSignalNative != nullptr) {
taskSignalNative->SetRequestId(context->requestId);
}
auto cancelFlag = std::make_shared<std::atomic<bool>>(false);
TaskSignalNapi::RegisterTaskCancelFlag(context->requestId, cancelFlag);
}
return result;
}
static void MoveAssetsByPathExecute(napi_env env, void *data)
{
NAPI_DEBUG_LOG("enter MoveAssetsByPathExecute");
MediaLibraryTracer tracer;
tracer.Start("MoveAssetsByPathExecute");
const std::string PROGRESS_TYPE = "MoveAssetsByPath";
auto *context = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "context is null");
if (context->onSizeProgress != nullptr || context->onCountProgress != nullptr ||
context->onResultProcess != nullptr) {
auto observer = make_shared<ProgressCallbackObserver>(context->requestId,
context->onSizeProgress, context->onCountProgress);
std::string uriStr = MEDIA_PROGRESS_REGISTER_PREFIX + PROGRESS_TYPE + "/" + std::to_string(context->requestId);
Uri uri(uriStr);
int32_t ret = UserFileClient::RegisterObserverExtProvider(uri,
static_cast<shared_ptr<DataShare::DataShareObserver>>(observer), false);
if (ret != 0) {
context->SaveError(E_INNER_FAIL);
MEDIA_ERR_LOG("Failed to register observer, err: %{public}d", ret);
}
{
std::lock_guard<std::mutex> lock(g_moveAssetsProgressMutex);
g_moveAssetsProgressMap[context->requestId] =
Notification::NotifyForUserDefineType::MOVE_ASSETS_TO_DIR_PROGRESS;
}
}
ChangeRequestMoveAssetsByPathReqBody reqBody;
ChangeRequestMoveAssetsByPathRespBody respBody;
reqBody.assetPaths = context->uriArray;
reqBody.mode = context->mode;
CHECK_AND_RETURN_RET_LOG(context->albumIds.size() != 0, ," albumIds is invaild");
reqBody.targetAlbumId = context->albumIds[0];
reqBody.requestId = context->requestId;
int32_t ret = IPC::UserDefineIPCClient().Call(
static_cast<uint32_t>(MediaLibraryBusinessCode::MOVE_ASSETS_BY_PATH), reqBody, respBody);
context->uriArray = respBody.resultList;
OnMoveTaskFinished(env, data, respBody);
if (ret < 0) {
MEDIA_ERR_LOG("Failed to move assets by path, err: %{public}d", ret);
context->SaveMoveError(ret);
return;
}
NAPI_INFO_LOG("Move %{public}d assets to dir successfully", static_cast<int>(reqBody.assetPaths.size()));
tracer.Finish();
}
static void MoveAssetsByPathCompleteCallback(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("MoveAssetsByPathCompleteCallback");
auto *context = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "context is nullptr");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), JS_E_INNER_FAIL);
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->data), JS_E_INNER_FAIL);
if (status != napi_ok || context->error != ERR_DEFAULT) {
NAPI_ERR_LOG("MoveAssetsByPath failed, status: %{public}d, error: %{public}d", status, context->error);
context->HandleError(env, jsContext->error);
} else {
JSCreateUriArrayInCallback(env, context, jsContext);
jsContext->status = true;
}
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
if (context->onSizeProgress != nullptr) {
napi_release_threadsafe_function(context->onSizeProgress, napi_tsfn_release);
}
if (context->onCountProgress != nullptr) {
napi_release_threadsafe_function(context->onCountProgress, napi_tsfn_release);
}
if (context->onResultProcess != nullptr) {
napi_release_threadsafe_function(context->onResultProcess, napi_tsfn_release);
}
if (context->taskSignalRef != nullptr) {
napi_delete_reference(env, context->taskSignalRef);
}
NAPI_DEBUG_LOG("MediaLibraryNapi::MoveAssetsByPathCompleteCallback end");
delete context;
}
napi_value MediaLibraryNapi::MoveAssetsByPath(napi_env env, napi_callback_info info)
{
NAPI_INFO_LOG("MoveAssetsByPath in");
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
MediaLibraryTracer tracer;
tracer.Start("MoveAssetsByPath");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
CHECK_NULLPTR_RET(ParseArgsMoveAssetsByPath(env, info, asyncContext));
asyncContext->businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::MOVE_ASSETS_BY_PATH);
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "MoveAssetsByPath",
MoveAssetsByPathExecute, MoveAssetsByPathCompleteCallback);
}
static napi_value ParseArgsStartDeepOptimizeSpace(
napi_env env, napi_callback_info info, unique_ptr<MediaLibraryAsyncContext> &context)
{
constexpr size_t minArgs = ARGS_ZERO;
constexpr size_t maxArgs = ARGS_ONE;
CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs,
maxArgs) == napi_ok, "Failed to get object info");
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
context->hasDeepOptimizeSpaceCallback = (context->argc >= ARGS_ONE);
if (context->hasDeepOptimizeSpaceCallback) {
CHECK_COND_WITH_ERR_MESSAGE(env, MediaLibraryNapiUtils::CheckJSArgsTypeAsFunc(env, context->argv[ARGS_ZERO]),
OHOS_INVALID_PARAM_CODE, "callback invalid");
CHECK_COND_WITH_ERR_MESSAGE(env,
DeepOptimizeSpaceJsCallbackHolder::Create(env, context->argv[ARGS_ZERO],
context->deepOptimizeSpaceCallbackHolder) == napi_ok, OHOS_INVALID_PARAM_CODE,
"Failed to create deep optimize space callback");
if (context->callbackRef != nullptr) {
napi_delete_reference(env, context->callbackRef);
context->callbackRef = nullptr;
}
}
if (context->objectInfo) {
context->userId = context->objectInfo->GetUserId();
} else {
NAPI_ERR_LOG("objectInfo is nullptr.");
}
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), OHOS_INVALID_PARAM_CODE);
return result;
}
static void ReleaseStartDeepOptimizeSpaceCallback(MediaLibraryAsyncContext *context, uint64_t callbackRegistryId)
{
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
if (callbackRegistryId == 0) {
NAPI_INFO_LOG("No callback registry, skip release");
return;
}
DeepOptimizeSpaceJsCallbackRegistry::Unregister(callbackRegistryId);
CHECK_NULL_PTR_RETURN_VOID(context->deepOptimizeSpaceCallbackHolder.get(),
"Deep optimize space callback holder is null");
context->deepOptimizeSpaceCallbackHolder->Release();
}
static bool PrepareStartDeepOptimizeSpaceCallback(MediaLibraryAsyncContext *context,
StartDeepOptimizeSpaceReqBody &reqBody, uint64_t &callbackRegistryId)
{
sptr<DeepOptimizeSpaceCallbackStub> clientStub = nullptr;
if (context->hasDeepOptimizeSpaceCallback) {
if (context->deepOptimizeSpaceCallbackHolder.get() == nullptr) {
NAPI_ERR_LOG("Deep optimize space callback holder is null");
context->retVal = MEDIA_LIBRARY_INTERNAL_SYSTEM_ERROR;
return false;
}
clientStub = sptr<DeepOptimizeSpaceCallbackStub>(new (std::nothrow) DeepOptimizeSpaceJsCallbackStub(
context->deepOptimizeSpaceCallbackHolder));
if (clientStub == nullptr) {
NAPI_ERR_LOG("Failed to create deep optimize space client stub");
context->deepOptimizeSpaceCallbackHolder->Release();
context->retVal = MEDIA_LIBRARY_INTERNAL_SYSTEM_ERROR;
return false;
}
reqBody.callbackRemote = clientStub->AsObject();
NAPI_INFO_LOG("Created callback stub, hasCallback: true");
} else {
clientStub = sptr<DeepOptimizeSpaceCallbackStub>(new (std::nothrow) DeepOptimizeSpaceDummyJsCallbackStub());
if (clientStub == nullptr) {
NAPI_ERR_LOG("Failed to create dummy client stub");
context->retVal = MEDIA_LIBRARY_INTERNAL_SYSTEM_ERROR;
return false;
}
reqBody.callbackRemote = nullptr;
NAPI_INFO_LOG("Created dummy stub for death monitoring, hasCallback: false");
}
reqBody.clientRemote = clientStub->AsObject();
NAPI_INFO_LOG("Prepared deep optimize space client remote, clientStubValid: %{public}d, "
"clientRemoteValid: %{public}d", clientStub != nullptr, reqBody.clientRemote != nullptr);
if (context->hasDeepOptimizeSpaceCallback) {
callbackRegistryId = DeepOptimizeSpaceJsCallbackRegistry::Register(
context->deepOptimizeSpaceCallbackHolder, clientStub, reqBody.callbackRemote);
if (callbackRegistryId == 0) {
NAPI_ERR_LOG("Failed to register deep optimize space callback lifecycle record");
context->deepOptimizeSpaceCallbackHolder->Release();
context->retVal = MEDIA_LIBRARY_INTERNAL_SYSTEM_ERROR;
return false;
}
} else {
callbackRegistryId = 0;
}
return true;
}
static void JSStartDeepOptimizeSpaceExecute(napi_env env, void *data)
{
(void)env;
MediaLibraryTracer tracer;
tracer.Start("JSStartDeepOptimizeSpaceExecute");
auto *context = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
StartDeepOptimizeSpaceReqBody reqBody;
uint64_t callbackRegistryId = 0;
if (!PrepareStartDeepOptimizeSpaceCallback(context, reqBody, callbackRegistryId)) {
return;
}
int32_t ret = IPC::UserDefineIPCClient().SetUserId(context->userId).Call(
static_cast<uint32_t>(MediaLibraryBusinessCode::START_DEEP_OPTIMIZE_SPACE), reqBody);
NAPI_INFO_LOG("Deep optimize space IPC returned, ret: %{public}d, hasCallback: %{public}d",
ret, context->hasDeepOptimizeSpaceCallback);
if (ret != E_OK) {
ReleaseStartDeepOptimizeSpaceCallback(context, callbackRegistryId);
if (ret == E_ERR) {
context->error = JS_E_INNER_FAIL;
return;
}
context->SaveError(ret);
return;
}
context->retVal = E_OK;
}
static void JSStartDeepOptimizeSpaceCompleteCallback(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSStartDeepOptimizeSpaceCompleteCallback");
auto *context = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->data), OHOS_INVALID_PARAM_CODE);
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), OHOS_INVALID_PARAM_CODE);
if (status == napi_ok && context->error == ERR_DEFAULT && context->retVal == E_OK) {
jsContext->status = true;
} else {
if (context->error == JS_E_OPR_TYPE_NOT_SUPPORT) {
context->errorMsg = "Unsupported operation type, Possible causes: "
"1. Restarted repeatedly; 2. system is busy. Please try again later";
} else if (context->error == JS_E_INNER_FAIL) {
context->errorMsg = "Internal system error. It is recommended to retry and check the logs. Possible causes:"
"1. Database corrupted; 2. The file system is abnormal; 3. The IPC request timed out";
}
context->HandleError(env, jsContext->error);
}
NAPI_INFO_LOG("JSStartDeepOptimizeSpaceCompleteCallback finish, status: %{public}d, error: %{public}d, retVal: "
"%{public}d", static_cast<int32_t>(status), context->error, context->retVal);
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
napi_value MediaLibraryNapi::PhotoAccessStartDeepOptimizeSpace(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessStartDeepOptimizeSpace");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
asyncContext->assetType = TYPE_PHOTO;
CHECK_NULLPTR_RET(ParseArgsStartDeepOptimizeSpace(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "PhotoAccessStartDeepOptimizeSpace",
JSStartDeepOptimizeSpaceExecute, JSStartDeepOptimizeSpaceCompleteCallback);
}
static napi_value ParseArgsStopDeepOptimizeSpace(
napi_env env, napi_callback_info info, unique_ptr<MediaLibraryAsyncContext> &context)
{
constexpr size_t minArgs = ARGS_ZERO;
constexpr size_t maxArgs = ARGS_ZERO;
CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs,
maxArgs) == napi_ok, "Failed to get object info");
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
if (context->objectInfo) {
context->userId = context->objectInfo->GetUserId();
} else {
NAPI_ERR_LOG("objectInfo is nullptr.");
}
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), OHOS_INVALID_PARAM_CODE);
return result;
}
static void JSStopDeepOptimizeSpaceExecute(napi_env env, void *data)
{
(void)env;
MediaLibraryTracer tracer;
tracer.Start("JSStopDeepOptimizeSpaceExecute");
auto *context = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
int32_t ret = IPC::UserDefineIPCClient().SetUserId(context->userId).Call(
static_cast<uint32_t>(MediaLibraryBusinessCode::STOP_DEEP_OPTIMIZE_SPACE));
NAPI_INFO_LOG("Stop deep optimize space IPC returned, ret: %{public}d", ret);
if (ret != E_OK) {
if (ret == E_ERR) {
context->error = JS_E_INNER_FAIL;
return;
}
context->SaveError(ret);
return;
}
context->retVal = E_OK;
}
static void JSStopDeepOptimizeSpaceCompleteCallback(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSStopDeepOptimizeSpaceCompleteCallback");
auto *context = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->data), OHOS_INVALID_PARAM_CODE);
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), OHOS_INVALID_PARAM_CODE);
if (status == napi_ok && context->error == ERR_DEFAULT && context->retVal == E_OK) {
jsContext->status = true;
} else {
if (context->error == JS_E_INNER_FAIL) {
context->errorMsg = "Internal system error. It is recommended to retry and check the logs. Possible causes:"
"1. Database corrupted; 2. The file system is abnormal; 3. The IPC request timed out";
}
context->HandleError(env, jsContext->error);
}
NAPI_INFO_LOG("JSStopDeepOptimizeSpaceCompleteCallback finish, status: %{public}d, error: %{public}d, retVal: "
"%{public}d", static_cast<int32_t>(status), context->error, context->retVal);
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
napi_value MediaLibraryNapi::PhotoAccessStopDeepOptimizeSpace(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessStopDeepOptimizeSpace");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
asyncContext->assetType = TYPE_PHOTO;
CHECK_NULLPTR_RET(ParseArgsStopDeepOptimizeSpace(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "PhotoAccessStopDeepOptimizeSpace",
JSStopDeepOptimizeSpaceExecute, JSStopDeepOptimizeSpaceCompleteCallback);
}
static napi_status GetInt32Property(napi_env env, const napi_value arg, const string &propName, int32_t &value)
{
bool present = false;
napi_value property = nullptr;
CHECK_STATUS_RET(napi_has_named_property(env, arg, propName.c_str(), &present),
"Failed to check property name");
if (!present) {
NAPI_ERR_LOG("No %{public}s specified", propName.c_str());
NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "No " + propName + " specified");
return napi_invalid_arg;
}
CHECK_STATUS_RET(napi_get_named_property(env, arg, propName.c_str(), &property), "Failed to get property");
CHECK_STATUS_RET(napi_get_value_int32(env, property, &value), "Failed to parse int32 argument value");
return napi_ok;
}
static napi_status GetStringProperty(napi_env env, const napi_value arg, const string &propName, string& value,
bool optional = false)
{
bool present = false;
napi_value property = nullptr;
CHECK_STATUS_RET(napi_has_named_property(env, arg, propName.c_str(), &present),
"Failed to check property name");
if (!present) {
if (optional) {
NAPI_INFO_LOG("%{public}s is optional.", propName.c_str());
value = "";
return napi_ok;
}
NAPI_ERR_LOG("No %{public}s specified", propName.c_str());
NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "No " + propName + " specified");
return napi_invalid_arg;
}
CHECK_STATUS_RET(napi_get_named_property(env, arg, propName.c_str(), &property), "Failed to get property");
size_t length = 0;
CHECK_STATUS_RET(napi_get_value_string_utf8(env, property, nullptr, 0, &length), "Failed to get string length");
unique_ptr<char[]> buffer = make_unique<char[]>(length + 1);
CHECK_STATUS_RET(napi_get_value_string_utf8(env, property, buffer.get(), length + 1, nullptr),
"Failed to parse string argument value");
value = string(buffer.get());
return napi_ok;
}
static bool CheckDefaultCoverOrderInfo(napi_env env, const DefaultCoverOrderInfo &info)
{
bool cond = PhotoAlbum::IsSystemAlbum(static_cast<PhotoAlbumType>(info.albumType)) &&
((static_cast<PhotoAlbumSubType>(info.albumSubType) >= PhotoAlbumSubType::SYSTEM_START) &&
(static_cast<PhotoAlbumSubType>(info.albumSubType) <= PhotoAlbumSubType::SYSTEM_END));
if (!cond && info.lpath.empty()) {
NAPI_ERR_LOG("Only system album is allowed without lpath.");
NapiError::ThrowError(env, JS_E_PARAM_INVALID, "Only system album is allowed without lpath.");
return false;
}
bool isValidCombination = false;
for (const auto& combo : VALID_ORDER_COMBINATIONS) {
if (combo.first == info.orderKey && combo.second == info.orderSubKey) {
isValidCombination = true;
break;
}
}
if (!isValidCombination) {
NAPI_ERR_LOG("The orderKey and orderSubKey are not in the specified range.");
NapiError::ThrowError(env, JS_E_PARAM_INVALID, "The orderKey and orderSubKey are not in the specified range.");
return false;
}
if (info.orderType != OrderType::DESC && info.orderType != OrderType::ASC) {
NAPI_ERR_LOG("The orderType must be either descending or ascending.");
NapiError::ThrowError(env, JS_E_PARAM_INVALID, "orderType must be DESC or ASC.");
return false;
}
return true;
}
static napi_value ParseCoverOrderInfoArray(napi_env env, napi_value arg,
std::vector<DefaultCoverOrderInfo>& coverOrderInfos)
{
vector<napi_value> napiValues;
napi_valuetype valueType = napi_undefined;
CHECK_NULLPTR_RET(MediaLibraryNapiUtils::GetNapiValueArray(env, arg, napiValues));
CHECK_COND_WITH_MESSAGE(env, !napiValues.empty(), "array is empty");
CHECK_ARGS(env, napi_typeof(env, napiValues.front(), &valueType), JS_E_INNER_FAIL);
CHECK_COND_WITH_MESSAGE(env, valueType == napi_object, "Invalid argument type");
for (const auto &napiValue : napiValues) {
DefaultCoverOrderInfo info;
CHECK_COND_WITH_MESSAGE(env, GetInt32Property(env, napiValue, "albumType", info.albumType) == napi_ok,
"Failed to parse albumType");
CHECK_COND_WITH_MESSAGE(env, GetInt32Property(env, napiValue, "albumSubtype", info.albumSubType) == napi_ok,
"Failed to parse albumSubtype");
CHECK_COND_WITH_MESSAGE(env, GetStringProperty(env, napiValue, "lpath", info.lpath, true) == napi_ok,
"Failed to parse lpath");
CHECK_COND_WITH_MESSAGE(env, GetStringProperty(env, napiValue, "orderKey", info.orderKey) == napi_ok,
"Failed to parse orderKey");
CHECK_COND_WITH_MESSAGE(env, GetStringProperty(env, napiValue, "orderSubKey", info.orderSubKey) == napi_ok,
"Failed to parse orderSubKey");
CHECK_COND_WITH_MESSAGE(env, GetInt32Property(env, napiValue, "orderType", info.orderType) == napi_ok,
"Failed to parse orderType");
if (!CheckDefaultCoverOrderInfo(env, info)) {
NAPI_ERR_LOG("Failed to check default cover order info.");
return nullptr;
}
coverOrderInfos.push_back(info);
}
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_E_INNER_FAIL);
return result;
}
static napi_value ParseArgsModifyAlbumDefaultCoverOrder(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NAPI_ERR_LOG("This interface can be called only by system apps.");
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
constexpr size_t minArgs = 3;
constexpr size_t maxArgs = 3;
CHECK_ARGS(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, minArgs, maxArgs),
OHOS_INVALID_PARAM_CODE);
CHECK_NULLPTR_RET(ParseCoverOrderInfoArray(env, context->argv[PARAM0], context->coverOrderInfos));
CHECK_COND_WITH_MESSAGE(env,
MediaLibraryNapiUtils::GetParamBool(env, context->argv[PARAM1], context->disableModification) == napi_ok,
"Failed to get disableModification");
CHECK_COND_WITH_MESSAGE(env,
MediaLibraryNapiUtils::GetParamBool(env, context->argv[PARAM2], context->isAsyncRefreshAlbum) == napi_ok,
"Failed to get isAsyncRefreshAlbum");
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_E_INNER_FAIL);
return result;
}
static void ModifyAlbumDefaultCoverOrderExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("ModifyAlbumDefaultCoverOrderExecute");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "context is nullptr");
CHECK_IF_EQUAL(!context->coverOrderInfos.empty(), "coverOrderInfos is empty");
ModifyAlbumDefaultCoverOrderReqBody reqBody;
reqBody.coverOrderInfos = context->coverOrderInfos;
reqBody.disable = context->disableModification;
reqBody.isAsyncRefreshAlbum = context->isAsyncRefreshAlbum;
int32_t ret = IPC::UserDefineIPCClient().Call(context->businessCode, reqBody);
CHECK_AND_RETURN(ret < 0);
if (ret == E_PERMISSION_DENIED || ret == -E_CHECK_SYSTEMAPP_FAIL) {
context->SaveError(ret);
} else {
context->error = JS_E_INNER_FAIL;
}
NAPI_ERR_LOG("Failed to modify album default cover order, err: %{public}d", ret);
return;
}
static void ModifyAlbumDefaultCoverOrderCompleteCallback(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("ModifyAlbumDefaultCoverOrderCompleteCallback");
MediaLibraryAsyncContext *asyncContext = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_NULL_PTR_RETURN_VOID(asyncContext, "Async context is null");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
if (asyncContext->error == ERR_DEFAULT) {
jsContext->status = true;
napi_get_undefined(env, &jsContext->data);
napi_get_undefined(env, &jsContext->error);
} else {
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, asyncContext->error,
"Failed to modify album default cover order");
napi_get_undefined(env, &jsContext->data);
}
if (asyncContext->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env,
asyncContext->deferred, asyncContext->callbackRef, asyncContext->work, *jsContext);
}
delete asyncContext;
}
napi_value MediaLibraryNapi::PhotoAccessModifyAlbumDefaultCoverOrder(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessModifyAlbumDefaultCoverOrder");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
if (!ParseArgsModifyAlbumDefaultCoverOrder(env, info, asyncContext)) {
NAPI_ERR_LOG("Failed to parse args");
return nullptr;
}
asyncContext->businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::PAH_MODIFY_ALBUM_DEFAULT_COVER_ORDER);
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "ModifyAlbumDefaultCoverOrder",
ModifyAlbumDefaultCoverOrderExecute, ModifyAlbumDefaultCoverOrderCompleteCallback);
}
static void ModifyHiddenAlbumDefaultCoverOrderExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("ModifyHiddenAlbumDefaultCoverOrderExecute");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "context is nullptr");
CHECK_IF_EQUAL(!context->coverOrderInfos.empty(), "coverOrderInfos is empty");
ModifyAlbumDefaultCoverOrderReqBody reqBody;
reqBody.coverOrderInfos = context->coverOrderInfos;
reqBody.disable = context->disableModification;
reqBody.isAsyncRefreshAlbum = context->isAsyncRefreshAlbum;
int32_t ret = IPC::UserDefineIPCClient().Call(context->businessCode, reqBody);
CHECK_AND_RETURN(ret < 0);
if (ret == E_PERMISSION_DENIED || ret == -E_CHECK_SYSTEMAPP_FAIL) {
context->SaveError(ret);
} else {
context->error = JS_E_INNER_FAIL;
}
NAPI_ERR_LOG("Failed to modify hidden album default cover order, err: %{public}d", ret);
return;
}
static void ModifyHiddenAlbumDefaultCoverOrderCompleteCallback(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("ModifyHiddenAlbumDefaultCoverOrderCompleteCallback");
MediaLibraryAsyncContext *asyncContext = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_NULL_PTR_RETURN_VOID(asyncContext, "Async context is null");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
if (asyncContext->error == ERR_DEFAULT) {
jsContext->status = true;
napi_get_undefined(env, &jsContext->data);
napi_get_undefined(env, &jsContext->error);
} else {
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, asyncContext->error,
"Failed to modify hidden album default cover order");
napi_get_undefined(env, &jsContext->data);
}
if (asyncContext->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env,
asyncContext->deferred, asyncContext->callbackRef, asyncContext->work, *jsContext);
}
delete asyncContext;
}
napi_value MediaLibraryNapi::PhotoAccessModifyHiddenAlbumDefaultCoverOrder(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessModifyHiddenAlbumDefaultCoverOrder");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
if (!ParseArgsModifyAlbumDefaultCoverOrder(env, info, asyncContext)) {
NAPI_ERR_LOG("Failed to parse args");
return nullptr;
}
asyncContext->businessCode =
static_cast<uint32_t>(MediaLibraryBusinessCode::PAH_MODIFY_HIDDEN_ALBUM_DEFAULT_COVER_ORDER);
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "ModifyHiddenAlbumDefaultCoverOrder",
ModifyHiddenAlbumDefaultCoverOrderExecute, ModifyHiddenAlbumDefaultCoverOrderCompleteCallback);
}
namespace {
const std::string ANALYSIS_TOOL_CONFIG_TYPE = "type";
const std::string ANALYSIS_TOOL_CONFIG_PARAM = "param";
const std::string ANALYSIS_TOOL_STOP_TASK_ID = "taskId";
constexpr size_t UUID_STR_LENGTH = 37;
constexpr int32_t ANALYSIS_TOOL_TYPE_BEGIN = 0;
constexpr int32_t ANALYSIS_TOOL_TYPE_END = 14;
constexpr size_t MAX_ANALYSIS_TOOL_PARAM_LENGTH = 16 * 1024;
static std::string GenerateAnalysisToolTaskId()
{
uuid_t uuid;
uuid_generate(uuid);
char str[UUID_STR_LENGTH] = {};
uuid_unparse(uuid, str);
return str;
}
}
static napi_value ParseArgsInvokeAnalysisTool(
napi_env env, napi_callback_info info, unique_ptr<MediaLibraryAsyncContext> &context)
{
constexpr size_t minArgs = ARGS_TWO;
constexpr size_t maxArgs = ARGS_TWO;
napi_value thisVar = nullptr;
context->argc = maxArgs;
CHECK_COND_WITH_ERR_MESSAGE(env, napi_get_cb_info(env, info, &context->argc, &(context->argv[ARGS_ZERO]),
&thisVar, nullptr) == napi_ok, JS_E_PARAM_INVALID, "Failed to get cb info");
CHECK_COND_WITH_ERR_MESSAGE(env, context->argc >= minArgs && context->argc <= maxArgs, JS_E_PARAM_INVALID,
"Number of args is invalid");
CHECK_COND_WITH_ERR_MESSAGE(env,
napi_unwrap(env, thisVar, reinterpret_cast<void **>(&context->objectInfo)) == napi_ok
&& context->objectInfo != nullptr, JS_E_PARAM_INVALID, "Failed to get object info");
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL,"This interface can be called only by system apps");
return nullptr;
}
napi_value configArg = context->argv[ARGS_ZERO];
napi_valuetype configType = napi_undefined;
CHECK_COND_WITH_ERR_MESSAGE(env, napi_typeof(env, configArg, &configType) == napi_ok,
JS_E_PARAM_INVALID, "config type check failed");
CHECK_COND_WITH_ERR_MESSAGE(env, configType == napi_object, JS_E_PARAM_INVALID, "config must be object");
napi_value typeValue = MediaLibraryNapiUtils::GetPropertyValueByName(env, configArg, ANALYSIS_TOOL_CONFIG_TYPE.c_str());
CHECK_COND_WITH_ERR_MESSAGE(env, typeValue != nullptr, JS_E_PARAM_INVALID, "type is required");
CHECK_COND_WITH_ERR_MESSAGE(env,
MediaLibraryNapiUtils::GetInt32(env, typeValue, context->analysisToolType) == napi_ok,
JS_E_PARAM_INVALID, "type invalid");
CHECK_COND_WITH_ERR_MESSAGE(env,
context->analysisToolType >= ANALYSIS_TOOL_TYPE_BEGIN && context->analysisToolType <= ANALYSIS_TOOL_TYPE_END,
JS_E_PARAM_INVALID, "type out of range");
if (MediaLibraryNapiUtils::IsExistsByPropertyName(env, configArg, ANALYSIS_TOOL_CONFIG_PARAM.c_str())) {
napi_value paramValue = MediaLibraryNapiUtils::GetPropertyValueByName(env, configArg,
ANALYSIS_TOOL_CONFIG_PARAM.c_str());
CHECK_COND_WITH_ERR_MESSAGE(env, paramValue != nullptr, JS_E_PARAM_INVALID, "param invalid");
CHECK_COND_WITH_ERR_MESSAGE(env,
MediaLibraryNapiUtils::GetParamStringStrict(env, paramValue,
MAX_ANALYSIS_TOOL_PARAM_LENGTH, context->analysisToolParam) == napi_ok,
JS_E_PARAM_INVALID, "param invalid or too long");
}
CHECK_COND_WITH_ERR_MESSAGE(env, MediaLibraryNapiUtils::CheckJSArgsTypeAsFunc(env, context->argv[ARGS_ONE]),
JS_E_PARAM_INVALID, "callback invalid");
CHECK_COND_WITH_ERR_MESSAGE(env,
ActiveAnalysisJsCallbackHolder::Create(env, context->argv[ARGS_ONE], context->activeAnalysisCallbackHolder)
== napi_ok, JS_E_INNER_FAIL, "Failed to create analysiis tool callback");
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
return result;
}
static napi_value ParseArgsCancelAnalysisTool(
napi_env env, napi_callback_info info, unique_ptr<MediaLibraryAsyncContext> &context)
{
constexpr size_t minArgs = ARGS_ONE;
constexpr size_t maxArgs = ARGS_ONE;
napi_value thisVar = nullptr;
context->argc = maxArgs;
CHECK_COND_WITH_ERR_MESSAGE(env, napi_get_cb_info(env, info, &context->argc,&(context->argv[ARGS_ZERO]),
&thisVar, nullptr) == napi_ok, JS_E_PARAM_INVALID, "Failed to get cb info");
CHECK_COND_WITH_ERR_MESSAGE(env, context->argc >= minArgs && context->argc <= maxArgs, JS_E_PARAM_INVALID,
"Number of args is invalid");
CHECK_COND_WITH_ERR_MESSAGE(env,
napi_unwrap(env, thisVar, reinterpret_cast<void **>(&context->objectInfo)) == napi_ok,
JS_E_PARAM_INVALID, "Failed to unwrap thisVar");
CHECK_COND_WITH_ERR_MESSAGE(env, context->objectInfo != nullptr, JS_E_PARAM_INVALID,
"Failed to get object info");
if(!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
CHECK_COND_WITH_ERR_MESSAGE(env, CheckNapiCallerPermission(PERM_WRITE_IMAGEVIDEO),
OHOS_PERMISSION_DENIED_CODE, "Have no write imagevideo permission");
napi_value configArg = context->argv[ARGS_ZERO];
napi_valuetype configType = napi_undefined;
CHECK_COND_WITH_ERR_MESSAGE(env, napi_typeof(env, configArg, &configType) == napi_ok,
JS_E_PARAM_INVALID, "config type check failed");
CHECK_COND_WITH_ERR_MESSAGE(env, configType == napi_object, JS_E_PARAM_INVALID, "config must be object");
napi_value taskIdValue = MediaLibraryNapiUtils::GetPropertyValueByName(env, configArg,
ANALYSIS_TOOL_STOP_TASK_ID.c_str());
CHECK_COND_WITH_ERR_MESSAGE(env, taskIdValue != nullptr,JS_E_PARAM_INVALID, "taskid is required");
CHECK_COND_WITH_ERR_MESSAGE(env,
MediaLibraryNapiUtils::GetParamStringPathMax(env, taskIdValue, context->analysisToolTaskId) == napi_ok,
OHOS_INVALID_PARAM_CODE, "taskId invalid");
CHECK_COND_WITH_ERR_MESSAGE(env, !context->analysisToolTaskId.empty(), JS_E_PARAM_INVALID, "taskid is empty");
if (MediaLibraryNapiUtils::IsExistsByPropertyName(env, configArg, ANALYSIS_TOOL_CONFIG_PARAM.c_str())) {
napi_value paramValue = MediaLibraryNapiUtils::GetPropertyValueByName(env, configArg,
ANALYSIS_TOOL_CONFIG_PARAM.c_str());
CHECK_COND_WITH_ERR_MESSAGE(env, paramValue != nullptr, JS_E_PARAM_INVALID, "param invalid");
CHECK_COND_WITH_ERR_MESSAGE(env,
MediaLibraryNapiUtils::GetParamStringStrict(env, paramValue,
MAX_ANALYSIS_TOOL_PARAM_LENGTH, context->analysisToolParam) == napi_ok,
JS_E_PARAM_INVALID, "param invalid or too long");
}
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_boolean(env, true, &result),JS_INNER_FAIL);
return result;
}
static void ReleaseInvokeAnalysisToolCallback(MediaLibraryAsyncContext *context, uint64_t callbackRegistryId)
{
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
ActiveAnalysisJsCallbackRegistry::Unregister(callbackRegistryId);
CHECK_NULL_PTR_RETURN_VOID(context->activeAnalysisCallbackHolder.get(), "Active analysis callback holder is null");
context->activeAnalysisCallbackHolder->Release();
}
static bool PrepareInvokeAnalysisToolCallback(MediaLibraryAsyncContext *context,
InvokeAnalysisToolReqBody &reqBody, uint64_t &callbackRegistryId)
{
context->activeAnalysisCallbackHolder->MarkAsToolCallback();
sptr<ActiveAnalysisJsCallbackStub> callbackStub =
sptr<ActiveAnalysisJsCallbackStub>(new (std::nothrow)ActiveAnalysisJsCallbackStub(
context->activeAnalysisCallbackHolder));
if (callbackStub == nullptr) {
NAPI_ERR_LOG("Failed to create analysis tool callback stub");
context->activeAnalysisCallbackHolder->Release();
context->retVal = MEDIA_LIBRARY_INTERNAL_SYSTEM_ERROR;
return false;
}
reqBody.callbackRemote = callbackStub->AsObject();
NAPI_INFO_LOG("Prepared analysis tool callback remote, calllbackStubValid: %{public}d, "
"callbackRemoteValid: %{public}d", callbackStub != nullptr, (reqBody.callbackRemote != nullptr));
callbackRegistryId = ActiveAnalysisJsCallbackRegistry::Register(
context->activeAnalysisCallbackHolder, callbackStub, reqBody.callbackRemote);
if(callbackRegistryId != 0) {
return true;
}
NAPI_ERR_LOG("Failed to register analysis tool callback lifecycle record");
context->activeAnalysisCallbackHolder->Release();
context->retVal = MEDIA_LIBRARY_INTERNAL_SYSTEM_ERROR;
return false;
}
static InvokeAnalysisToolReqBody BuildInvokeAnalysisToolReqBody(MediaLibraryAsyncContext *context)
{
InvokeAnalysisToolReqBody reqBody;
reqBody.type = context->analysisToolType;
reqBody.param = context->analysisToolParam;
reqBody.taskId = context->analysisToolTaskId;
NAPI_INFO_LOG("Invoke analysis tool execute, type: %{public}d, paramSize: %{public}zu,"
"taskId: %{public}s, holderValid: %{public}d", reqBody.type, reqBody.param.size(),
reqBody.taskId.c_str(), context->activeAnalysisCallbackHolder != nullptr);
return reqBody;
}
static bool HandleInvokeAnalysisToolResponse(MediaLibraryAsyncContext *context, uint64_t callbackRegistryId,
int32_t ret, const InvokeAnalysisToolRespBody &respBody)
{
if (ret != E_OK) {
ReleaseInvokeAnalysisToolCallback(context, callbackRegistryId);
if (ret == E_INVALID_ARGUMENTS) {
context->error = JS_E_PARAM_INVALID;
context->SaveError(JS_E_PARAM_INVALID);
return false;
}
context->retVal = ret;
context->SaveError(ret);
return false;
}
context->retVal = NormalizeActiveAnalysisErrorCode(respBody.result);
if (context->retVal != E_OK) {
ReleaseInvokeAnalysisToolCallback(context, callbackRegistryId);
context->SaveError(respBody.result);
return false;
}
if (respBody.saRemote == nullptr) {
NAPI_ERR_LOG("Invoke analysis tool saRemote is nullptr");
context->retVal = MEDIA_LIBRARY_INTERNAL_SYSTEM_ERROR;
ReleaseInvokeAnalysisToolCallback(context, callbackRegistryId);
return false;
}
switch (context->activeAnalysisCallbackHolder->BindSaRemote(respBody.saRemote)) {
case ActiveAnalysisSaBindResult::BOUND:
NAPI_INFO_LOG("Invoke analysis tool bound sa remote death recipient");
return true;
case ActiveAnalysisSaBindResult::ALREADY_COMPLETED:
NAPI_WARN_LOG("Invoke analysis tool result arrived before SA binding");
return true;
case ActiveAnalysisSaBindResult::INVALID_REMOTE:
NAPI_ERR_LOG("Invoke analysis tool saRemote is invalid during bind");
break;
case ActiveAnalysisSaBindResult::ADD_DEATH_RECIPIENT_FAILED:
NAPI_ERR_LOG("Failed to add invoke analysis tool sa death recipient");
break;
case ActiveAnalysisSaBindResult::HOLDER_RELEASED:
NAPI_ERR_LOG("Invoke analysis tool callback holder released before SA binding");
break;
default:
NAPI_ERR_LOG("Unknown invoke analysis tool SA bind result");
break;
}
context->retVal = MEDIA_LIBRARY_INTERNAL_SYSTEM_ERROR;
ReleaseInvokeAnalysisToolCallback(context, callbackRegistryId);
return false;
}
static CancelAnalysisToolReqBody BuildCancelAnalysisToolReqBody(MediaLibraryAsyncContext *context)
{
CancelAnalysisToolReqBody reqBody;
reqBody.taskId = context->analysisToolTaskId;
reqBody.param = context->analysisToolParam;
return reqBody;
}
static int32_t NormalizeAnalysisToolErrorCode(int32_t code)
{
if (code == 0) {
return E_OK;
}
if (code == E_INVALID_ARGUMENTS || code == JS_E_PARAM_INVALID) {
return MEDIA_LIBRARY_INVALID_PARAMETER_ERROR;
}
if (code == E_PERMISSION_DENIED || code == JS_ERR_PERMISSION_DENIED) {
return OHOS_PERMISSION_DENIED_CODE;
}
if (code == -E_CHECK_SYSTEMAPP_FAIL) {
return E_CHECK_SYSTEMAPP_FAIL;
}
if (code == OHOS_PERMISSION_DENIED_CODE || code == E_CHECK_SYSTEMAPP_FAIL ||
code == MEDIA_LIBRARY_INVALID_PARAMETER_ERROR || code == MEDIA_LIBRARY_INTERNAL_SYSTEM_ERROR) {
return code;
}
return NormalizeActiveAnalysisPromiseErrorCode(
MediaLibraryNapiUtils::TransErrorCode("PhotoAcceessCancelAnalysisTool", code));
}
static void ThrowAnalysisToolError(napi_env env, int32_t normalizedCode)
{
NapiError::ThrowError(env, normalizedCode, GetActiveAnalysisPromiseErrorMessage(normalizedCode));
}
static void JSInvokeAnalysisToolExecute(napi_env env,void *data)
{
(void)env;
MediaLibraryTracer tracer;
tracer.Start("JSInvokeAnalysisToolExecute");
NAPI_INFO_LOG("JSInvokeAnalysisToolExecute start");
auto *context = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context isnull");
CHECK_NULL_PTR_RETURN_VOID(context->activeAnalysisCallbackHolder.get(), "Analysis tool callback holder is null");
context->analysisToolTaskId = GenerateAnalysisToolTaskId();
InvokeAnalysisToolReqBody reqBody = BuildInvokeAnalysisToolReqBody(context);
uint64_t callbackRegistryId = 0;
if (!PrepareInvokeAnalysisToolCallback(context, reqBody, callbackRegistryId)) {
return;
}
InvokeAnalysisToolRespBody respBody;
int32_t ret = IPC::UserDefineIPCClient().SetUserId(context->userId).Call(
static_cast<uint32_t>(MediaLibraryBusinessCode::INVOKE_ANALYSIS_TOOL), reqBody, respBody);
NAPI_INFO_LOG("Invoke analysis tool IPC returned, ret: %{public}d, resp.result: %{public}d,"
"taskId: %{public}s", ret, respBody.result, respBody.taskId.c_str());
if (!HandleInvokeAnalysisToolResponse(context, callbackRegistryId, ret, respBody)) {
return;
}
}
static void JSInvokeAnalysisToolCompleteCallback(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSInvokeAnalysisToolCompleteCallback");
auto *context = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->data), JS_INNER_FAIL);
CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), JS_INNER_FAIL);
if (status == napi_ok && context->error == ERR_DEFAULT && context->retVal == E_OK) {
CHECK_ARGS_RET_VOID(env,
napi_create_string_utf8(env, context->analysisToolTaskId.c_str(), NAPI_AUTO_LENGTH, &jsContext->data),
JS_INNER_FAIL);
jsContext->status = true;
} else {
CHECK_ARGS_RET_VOID(env,
napi_create_string_utf8(env, "", NAPI_AUTO_LENGTH,&jsContext->data), JS_INNER_FAIL);
int32_t rawErrorCode = context->error != ERR_DEFAULT ? context->error : MediaLibraryNapiUtils::TransErrorCode("PhotoAccessInvokeAnalysisTool",context->retVal);
int32_t jsErrorCode = NormalizeActiveAnalysisPromiseErrorCode(rawErrorCode);
std::string errorMessage = GetActiveAnalysisPromiseErrorMessage(jsErrorCode);
MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, jsErrorCode, errorMessage);
}
NAPI_INFO_LOG("JSInvokeAnalysisToolCompleteCallback finish,status: %{public}d, error: %{public}d, "
"retVal: %{public}d, taskId: %{public}s", static_cast<int32_t>(status), context->error,
context->retVal, context->analysisToolTaskId.c_str());
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
napi_value MediaLibraryNapi::PhotoAccessInvokeAnalysisTool(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessInvokeAnalysisTool");
NAPI_INFO_LOG("PhotoAccessInvokeAnalysisTool start");
if (!CheckNapiCallerPermission(CONTROL_IMAGEVIDEO_ANALYSIS_PERMISSION)) {
NapiError::ThrowError(env, OHOS_PERMISSION_DENIED_CODE,
"Permission denied: " + CONTROL_IMAGEVIDEO_ANALYSIS_PERMISSION + " required.");
return nullptr;
}
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
CHECK_NULLPTR_RET(ParseArgsInvokeAnalysisTool(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "PhotoAccessInvokeAnalysisTool",
JSInvokeAnalysisToolExecute, JSInvokeAnalysisToolCompleteCallback);
}
napi_value MediaLibraryNapi::PhotoAccessCancelAnalysisTool(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessCancelAnalysisTool");
NAPI_INFO_LOG("PhotoAccessCancelAnalysisTool start");
napi_value result = nullptr;
CHECK_ARGS(env, napi_get_undefined(env, &result), JS_INNER_FAIL);
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
if (!CheckNapiCallerPermission(CONTROL_IMAGEVIDEO_ANALYSIS_PERMISSION)) {
NapiError::ThrowError(env, OHOS_PERMISSION_DENIED_CODE,
"Permission denied: " + CONTROL_IMAGEVIDEO_ANALYSIS_PERMISSION + " required.");
return nullptr;
}
CHECK_NULLPTR_RET(ParseArgsCancelAnalysisTool(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
CancelAnalysisToolReqBody reqBody = BuildCancelAnalysisToolReqBody(asyncContext.get());
CancelAnalysisToolRespBody respBody;
int32_t ret = IPC::UserDefineIPCClient().SetUserId(asyncContext->userId).Call(
static_cast<uint32_t>(MediaLibraryBusinessCode::CANCEL_ANALYSIS_TOOL), reqBody, respBody);
NAPI_INFO_LOG("PhotoAccessCancelAnalysisTool ret:%{public}d", ret);
if (ret != E_OK) {
ThrowAnalysisToolError(env, ret);
return result;
}
int32_t cancelResult = NormalizeAnalysisToolErrorCode(respBody.result);
NAPI_INFO_LOG("PhotoAccessCancelAnalysisTool cancelResult:%{public}d", cancelResult);
if (cancelResult != E_OK) {
ThrowAnalysisToolError(env, cancelResult);
}
return result;
}
static void CanPerformDeepOptimizeSpaceExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("CanPerformDeepOptimizeSpaceExecute");
auto *context = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "context is null");
IPC::MediaEmptyObjVo reqBody;
QueryDeepOptimizeSpaceRespBody respBody;
int32_t ret = IPC::UserDefineIPCClient().Call(
static_cast<uint32_t>(MediaLibraryBusinessCode::QUERY_DEEP_OPTIMIZE_SPACE), reqBody, respBody);
if (ret != 0) {
NAPI_ERR_LOG("UserDefineIPCClient().Call failed, ret: %{public}d", ret);
if (ret == E_PERMISSION_DENIED) {
context->error = OHOS_PERMISSION_DENIED_CODE;
return;
} else {
context->error = JS_E_INNER_FAIL;
}
return;
}
context->canDeepOptimize = respBody.result;
}
static void CanPerformDeepOptimizeSpaceCompleteCallback(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("CanPerformDeepOptimizeSpaceCompleteCallback");
auto *context = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "context is null");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
CHECK_ARGS_RET_VOID_WITH_MEG(env, napi_get_undefined(env, &jsContext->error), JS_E_INNER_FAIL,
"Failed to initialize error field");
CHECK_ARGS_RET_VOID_WITH_MEG(env, napi_get_undefined(env, &jsContext->data), JS_E_INNER_FAIL,
"Failed to initialize data field");
if (status != napi_ok || context->error != ERR_DEFAULT) {
NAPI_ERR_LOG("CanPerformDeepOptimizeSpace failed, status: %{public}d, error: %{public}d",
status, context->error);
context->HandleError(env, jsContext->error);
} else {
napi_status napiStatus = napi_get_boolean(env, context->canDeepOptimize, &jsContext->data);
if (napiStatus == napi_ok) {
jsContext->status = true;
} else {
NAPI_ERR_LOG("Failed to create result value");
context->error = JS_E_INNER_FAIL;
context->HandleError(env, jsContext->error);
}
}
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
napi_value MediaLibraryNapi::CanPerformDeepOptimizeSpace(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("CanPerformDeepOptimizeSpace");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
if(!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "CanPerformDeepOptimizeSpace",
CanPerformDeepOptimizeSpaceExecute, CanPerformDeepOptimizeSpaceCompleteCallback);
}
static void GetDeepOptimizeSpaceExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("GetDeepOptimizeSpaceExecute");
auto *context = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "context is null");
IPC::MediaEmptyObjVo reqBody;
QueryDeepOptimizableSpaceRespBody respBody;
int32_t ret = IPC::UserDefineIPCClient().Call(
static_cast<uint32_t>(MediaLibraryBusinessCode::GET_DEEP_OPTIMIZE_SPACE), reqBody, respBody);
if (ret != 0) {
NAPI_ERR_LOG("UserDefineIPCClient().Call failed, ret: %{public}d", ret);
if (ret == E_PERMISSION_DENIED) {
context->error = OHOS_PERMISSION_DENIED_CODE;
return;
} else {
context->error = JS_E_INNER_FAIL;
}
return;
}
context->lcdOptimizableSize = respBody.space;
}
static void GetDeepOptimizeSpaceCompleteCallback(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("GetDeepOptimizeSpaceCompleteCallback");
auto *context = static_cast<MediaLibraryAsyncContext *>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "context is null");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
CHECK_ARGS_RET_VOID_WITH_MEG(env, napi_get_undefined(env, &jsContext->error), JS_E_INNER_FAIL,
"Failed to initialize error field");
CHECK_ARGS_RET_VOID_WITH_MEG(env, napi_get_undefined(env, &jsContext->data), JS_E_INNER_FAIL,
"Failed to initialize data field");
if (status != napi_ok || context->error != ERR_DEFAULT) {
NAPI_ERR_LOG("GetDeepOptimizeSpace failed, status: %{public}d, error: %{public}d",
status, context->error);
context->HandleError(env, jsContext->error);
} else {
napi_status napiStatus = napi_create_int64(env, context->lcdOptimizableSize, &jsContext->data);
if (napiStatus == napi_ok) {
jsContext->status = true;
} else {
NAPI_ERR_LOG("Failed to create result value");
context->error = JS_E_INNER_FAIL;
context->HandleError(env, jsContext->error);
}
}
tracer.Finish();
if (context->work != nullptr) {
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
napi_value MediaLibraryNapi::GetDeepOptimizeSpace(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("GetDeepOptimizeSpace");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
if(!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "GetDeepOptimizeSpace",
GetDeepOptimizeSpaceExecute, GetDeepOptimizeSpaceCompleteCallback);
}
static bool UnwrapAssetElement(napi_env env, napi_value element, shared_ptr<FileAsset> &outAsset)
{
napi_valuetype valueType = napi_undefined;
if (!(napi_typeof(env, element, &valueType) == napi_ok && valueType == napi_object)) {
NapiError::ThrowErrorWithIntCode(env, JS_E_PARAM_INVALID, "asset must be an object");
return false;
}
FileAssetNapi *obj = nullptr;
if (!(napi_unwrap(env, element, reinterpret_cast<void **>(&obj)) == napi_ok && obj != nullptr)) {
NapiError::ThrowErrorWithIntCode(env, JS_E_PARAM_INVALID, "Failed to get asset napi object");
return false;
}
outAsset = obj->GetFileAssetInstance();
if (outAsset == nullptr) {
NapiError::ThrowErrorWithIntCode(env, JS_E_PARAM_INVALID, "FileAsset instance is null");
return false;
}
return true;
}
static napi_value ParseArgsTransAssetToCompatibleAsset(napi_env env, napi_callback_info info,
unique_ptr<MediaLibraryAsyncContext> &context)
{
if (!MediaLibraryNapiUtils::IsSystemApp()) {
NapiError::ThrowErrorWithIntCode(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
return nullptr;
}
if (MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, context, ARGS_ONE, ARGS_ONE) != napi_ok) {
NapiError::ThrowErrorWithIntCode(env, JS_E_PARAM_INVALID);
return nullptr;
}
bool isArray = false;
if (!(napi_is_array(env, context->argv[PARAM0], &isArray) == napi_ok && isArray)) {
NapiError::ThrowErrorWithIntCode(env, JS_E_PARAM_INVALID, "assets must be an array");
return nullptr;
}
uint32_t arrayLen = 0;
napi_get_array_length(env, context->argv[PARAM0], &arrayLen);
if (arrayLen == 0) {
NapiError::ThrowErrorWithIntCode(env, JS_E_PARAM_INVALID, "assets array is empty");
return nullptr;
}
for (uint32_t i = 0; i < arrayLen; i++) {
napi_value element = nullptr;
napi_get_element(env, context->argv[PARAM0], i, &element);
shared_ptr<FileAsset> fileAsset;
if (element == nullptr || !UnwrapAssetElement(env, element, fileAsset)) {
return nullptr;
}
context->transAssetPtrs.push_back(fileAsset);
}
napi_value result = nullptr;
napi_get_boolean(env, true, &result);
return result;
}
static void JSTransAssetToCompatibleAssetExecute(napi_env env, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSTransAssetToCompatibleAssetExecute");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
for (const auto &fileAsset : context->transAssetPtrs) {
std::string displayName = fileAsset->GetDisplayName();
std::string title = MediaFileUtils::GetTitleFromDisplayName(displayName);
if (!title.empty()) {
fileAsset->SetDisplayName(title + ".jpg");
}
std::string fileAssetUri = MediaFileUtils::GetFileAssetUri(
fileAsset->GetPath(), fileAsset->GetDisplayName(), fileAsset->GetId());
fileAsset->SetUri(MediaFileUtils::Encode(fileAssetUri));
fileAsset->SetMimeType("image/jpeg");
fileAsset->SetMemberValue(PhotoColumn::PHOTO_MEDIA_SUFFIX, std::string("jpg"));
int32_t width = fileAsset->GetWidth();
int32_t height = fileAsset->GetHeight();
PreferredCompatibleModeCheckUtils::GetDesireSize(width, height);
fileAsset->SetWidth(width);
fileAsset->SetHeight(height);
int64_t transCodeFileSize = fileAsset->GetInt64Member(PhotoColumn::PHOTO_TRANS_CODE_FILE_SIZE);
if (transCodeFileSize != 0) {
fileAsset->SetSize(transCodeFileSize);
}
}
context->retVal = E_OK;
}
static void JSTransAssetToCompatibleAssetCompleteCallback(napi_env env, napi_status status, void *data)
{
MediaLibraryTracer tracer;
tracer.Start("JSTransAssetToCompatibleAssetCompleteCallback");
auto *context = static_cast<MediaLibraryAsyncContext*>(data);
CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
jsContext->status = false;
if (context->error == ERR_DEFAULT) {
jsContext->status = true;
napi_value jsArray = nullptr;
napi_create_array(env, &jsArray);
for (size_t i = 0; i < context->transAssetPtrs.size(); i++) {
napi_value jsAsset = FileAssetNapi::CreatePhotoAsset(env, context->transAssetPtrs[i]);
napi_set_element(env, jsArray, static_cast<uint32_t>(i), jsAsset);
}
jsContext->data = jsArray;
napi_get_undefined(env, &jsContext->error);
} else {
napi_get_undefined(env, &jsContext->data);
context->HandleError(env, jsContext->error, true);
}
if (context->work != nullptr) {
tracer.Finish();
MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
context->work, *jsContext);
}
delete context;
}
napi_value MediaLibraryNapi::PhotoAccessTransAssetToCompatibleAsset(napi_env env, napi_callback_info info)
{
MediaLibraryTracer tracer;
tracer.Start("PhotoAccessTransAssetToCompatibleAsset");
NAPI_INFO_LOG("PhotoAccessTransAssetToCompatibleAsset enter");
unique_ptr<MediaLibraryAsyncContext> asyncContext = make_unique<MediaLibraryAsyncContext>();
CHECK_NULLPTR_RET(ParseArgsTransAssetToCompatibleAsset(env, info, asyncContext));
SetUserIdFromObjectInfo(asyncContext);
return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "TransAssetToCompatibleAsset",
JSTransAssetToCompatibleAssetExecute, JSTransAssetToCompatibleAssetCompleteCallback);
}
}
}