From a942134a14efeb455b28487864693cdda74ef8eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=80=95=E9=BA=BB=E7=83=A6?= <354431057@qq.com> Date: Fri, 12 Sep 2025 15:51:53 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E5=9C=B0=E5=9B=BE=E7=9B=B8=E5=86=8C?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 怕麻烦 <354431057@qq.com> --- .../medialibrary_data_extension/BUILD.gn | 5 + .../include/medialibrary_rdbstore.h | 3 + .../include/medialibrary_subscriber.h | 1 + .../include/operation/photo_map_code_column.h | 112 +++ .../operation/photo_map_code_operation.h | 112 +++ .../src/medialibrary_asset_operations.cpp | 12 + .../src/medialibrary_data_manager.cpp | 9 + .../src/medialibrary_rdbstore.cpp | 136 +++- .../src/medialibrary_subscriber.cpp | 38 +- .../src/medialibrary_upgrade_utils.cpp | 1 + .../src/operation/photo_map_code_column.cpp | 151 ++++ .../operation/photo_map_code_operation.cpp | 549 ++++++++++++++ .../innerkitsimpl/test/fuzztest/BUILD.gn | 3 + .../medialibrary_mapcode_fuzzer/BUILD.gn | 130 ++++ .../medialibrary_mapcode_fuzzer/corpus/init | 16 + .../corpus/medialibrary-architecture.png | Bin 0 -> 183853 bytes .../include/medialibrary_mapcode_fuzzer.h | 21 + .../medialibrary_mapcode_fuzzer/project.xml | 25 + .../src/medialibrary_mapcode_fuzzer.cpp | 551 ++++++++++++++ .../BUILD.gn | 71 ++ .../corpus/init | 16 + ...medialibrarycloudmapcodechecker_fuzzer.cpp | 157 ++++ .../medialibrarycloudmapcodechecker_fuzzer.h | 21 + .../project.xml | 25 + .../BUILD.gn | 86 +++ .../corpus/init | 16 + ...edialibrarycloudmediamapcodedao_fuzzer.cpp | 681 +++++++++++++++++ .../medialibrarycloudmediamapcodedao_fuzzer.h | 21 + .../project.xml | 25 + .../BUILD.gn | 18 + ..._cloud_sync_service_service_mapcode_test.h | 30 + ...loud_sync_service_service_mapcode_test.cpp | 693 ++++++++++++++++++ .../medialibrary_backup_clone_test/BUILD.gn | 10 + .../medialibrary_backup_clone_mapcode_test.h | 49 ++ ...medialibrary_backup_clone_mapcode_test.cpp | 612 ++++++++++++++++ .../medialibrary_backup_test/BUILD.gn | 3 + .../src/medialibrary_backup_test.cpp | 1 + ...medialibrary_cloud_asset_download_test.cpp | 3 + .../medialibrary_scanner_db_test/BUILD.gn | 4 + .../medialibrary_scanner_mapcode_db_test.h | 32 + .../medialibrary_scanner_mapcode_db_test.cpp | 414 +++++++++++ .../services/media_backup_extension/BUILD.gn | 3 + .../include/restore_map_code_utils.h | 45 ++ .../src/base_restore.cpp | 6 +- .../src/clone_restore.cpp | 5 + .../src/others_clone_restore.cpp | 5 + .../src/restore_map_code_utils.cpp | 113 +++ .../cloud_media_asset_manager.h | 1 + .../cloud_media_asset_manager.cpp | 66 ++ .../include/scanner/scanner_map_code_utils.h | 40 + .../src/scanner/media_scanner.cpp | 3 +- .../src/scanner/media_scanner_db.cpp | 14 + .../src/scanner/scanner_map_code_utils.cpp | 104 +++ .../utils/include/map_code_upload_checker.h | 49 ++ .../utils/src/map_code_upload_checker.cpp | 203 +++++ .../include/medialibrary_db_const.h | 3 +- .../include/dao/cloud_map_code_dao.h | 82 +++ .../service/cloud_media_photos_service.h | 5 + .../src/dao/cloud_map_code_dao.cpp | 188 +++++ .../service/cloud_media_photos_service.cpp | 44 +- 60 files changed, 5802 insertions(+), 40 deletions(-) create mode 100644 frameworks/innerkitsimpl/medialibrary_data_extension/include/operation/photo_map_code_column.h create mode 100644 frameworks/innerkitsimpl/medialibrary_data_extension/include/operation/photo_map_code_operation.h create mode 100644 frameworks/innerkitsimpl/medialibrary_data_extension/src/operation/photo_map_code_column.cpp create mode 100644 frameworks/innerkitsimpl/medialibrary_data_extension/src/operation/photo_map_code_operation.cpp create mode 100644 frameworks/innerkitsimpl/test/fuzztest/medialibrary_mapcode_fuzzer/BUILD.gn create mode 100644 frameworks/innerkitsimpl/test/fuzztest/medialibrary_mapcode_fuzzer/corpus/init create mode 100644 frameworks/innerkitsimpl/test/fuzztest/medialibrary_mapcode_fuzzer/corpus/medialibrary-architecture.png create mode 100644 frameworks/innerkitsimpl/test/fuzztest/medialibrary_mapcode_fuzzer/include/medialibrary_mapcode_fuzzer.h create mode 100644 frameworks/innerkitsimpl/test/fuzztest/medialibrary_mapcode_fuzzer/project.xml create mode 100644 frameworks/innerkitsimpl/test/fuzztest/medialibrary_mapcode_fuzzer/src/medialibrary_mapcode_fuzzer.cpp create mode 100644 frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmapcodechecker_fuzzer/BUILD.gn create mode 100644 frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmapcodechecker_fuzzer/corpus/init create mode 100644 frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmapcodechecker_fuzzer/medialibrarycloudmapcodechecker_fuzzer.cpp create mode 100644 frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmapcodechecker_fuzzer/medialibrarycloudmapcodechecker_fuzzer.h create mode 100644 frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmapcodechecker_fuzzer/project.xml create mode 100644 frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmediamapcodedao_fuzzer/BUILD.gn create mode 100644 frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmediamapcodedao_fuzzer/corpus/init create mode 100644 frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmediamapcodedao_fuzzer/medialibrarycloudmediamapcodedao_fuzzer.cpp create mode 100644 frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmediamapcodedao_fuzzer/medialibrarycloudmediamapcodedao_fuzzer.h create mode 100644 frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmediamapcodedao_fuzzer/project.xml create mode 100644 frameworks/innerkitsimpl/test/unittest/media_library_cloud_sync_service_test/include/media_cloud_sync_service_service_mapcode_test.h create mode 100644 frameworks/innerkitsimpl/test/unittest/media_library_cloud_sync_service_test/src/media_cloud_sync_service_service_mapcode_test.cpp create mode 100644 frameworks/innerkitsimpl/test/unittest/medialibrary_backup_clone_test/include/medialibrary_backup_clone_mapcode_test.h create mode 100644 frameworks/innerkitsimpl/test/unittest/medialibrary_backup_clone_test/src/medialibrary_backup_clone_mapcode_test.cpp create mode 100644 frameworks/innerkitsimpl/test/unittest/medialibrary_scanner_db_test/include/medialibrary_scanner_mapcode_db_test.h create mode 100644 frameworks/innerkitsimpl/test/unittest/medialibrary_scanner_db_test/src/medialibrary_scanner_mapcode_db_test.cpp create mode 100644 frameworks/services/media_backup_extension/include/restore_map_code_utils.h create mode 100644 frameworks/services/media_backup_extension/src/restore_map_code_utils.cpp create mode 100644 frameworks/services/media_scanner/include/scanner/scanner_map_code_utils.h create mode 100644 frameworks/services/media_scanner/src/scanner/scanner_map_code_utils.cpp create mode 100644 frameworks/utils/include/map_code_upload_checker.h create mode 100644 frameworks/utils/src/map_code_upload_checker.cpp create mode 100644 services/media_cloud_sync_service/include/dao/cloud_map_code_dao.h create mode 100644 services/media_cloud_sync_service/src/dao/cloud_map_code_dao.cpp diff --git a/frameworks/innerkitsimpl/medialibrary_data_extension/BUILD.gn b/frameworks/innerkitsimpl/medialibrary_data_extension/BUILD.gn index c8b333d6e1..a8b60cd6e0 100644 --- a/frameworks/innerkitsimpl/medialibrary_data_extension/BUILD.gn +++ b/frameworks/innerkitsimpl/medialibrary_data_extension/BUILD.gn @@ -214,6 +214,7 @@ ohos_shared_library("medialibrary_data_extension") { "${MEDIALIB_SERVICES_PATH}/media_scanner/src/scanner/metadata.cpp", "${MEDIALIB_SERVICES_PATH}/media_scanner/src/scanner/metadata_extractor.cpp", "${MEDIALIB_SERVICES_PATH}/media_scanner/src/scanner/scanner_utils.cpp", + "${MEDIALIB_SERVICES_PATH}/media_scanner/src/scanner/scanner_map_code_utils.cpp", ] media_fuse_source = [ @@ -409,6 +410,7 @@ ohos_shared_library("medialibrary_data_extension") { "${MEDIALIB_CLOUD_SYNC_SERVICE_PATH}/src/background/media_cloud_sync_backgroud_task.cpp", "${MEDIALIB_CLOUD_SYNC_SERVICE_PATH}/src/background/media_hidden_and_recycle_task.cpp", "${MEDIALIB_CLOUD_SYNC_SERVICE_PATH}/src/background/repair_video_dirty_and_quality_task.cpp", + "${MEDIALIB_CLOUD_SYNC_SERVICE_PATH}/src/dao/cloud_map_code_dao.cpp", ] media_cloud_sync_server_source += media_cloud_sync_server_source_vo @@ -649,6 +651,8 @@ ohos_shared_library("medialibrary_data_extension") { "src/medialibrary_object_utils.cpp", "src/medialibrary_photo_operations.cpp", "src/medialibrary_ptp_operations.cpp", + "src/operation/photo_map_code_column.cpp", + "src/operation/photo_map_code_operation.cpp", "src/medialibrary_rdbstore.cpp", "src/medialibrary_restore.cpp", "src/medialibrary_search_operations.cpp", @@ -687,6 +691,7 @@ ohos_shared_library("medialibrary_data_extension") { "src/trash_async_worker.cpp", "src/video_composition_callback_imp.cpp", "src/zip_util.cpp", + "${MEDIALIB_UTILS_PATH}/src/map_code_upload_checker.cpp", ] media_notification_source = [ diff --git a/frameworks/innerkitsimpl/medialibrary_data_extension/include/medialibrary_rdbstore.h b/frameworks/innerkitsimpl/medialibrary_data_extension/include/medialibrary_rdbstore.h index aa1a7986e4..f019cc54c7 100644 --- a/frameworks/innerkitsimpl/medialibrary_data_extension/include/medialibrary_rdbstore.h +++ b/frameworks/innerkitsimpl/medialibrary_data_extension/include/medialibrary_rdbstore.h @@ -163,6 +163,9 @@ public: EXPORT static int32_t UpdateEditDataSize(std::shared_ptr rdbStore, const std::string &photoId, const std::string &photoPath); + EXPORT static bool AddPhotoMapTable(NativeRdb::RdbStore &store); + EXPORT static bool AddPhotoMapTableIndex(const std::shared_ptr store); + EXPORT static bool AddPhotoMapTableData(const std::shared_ptr store); private: EXPORT static std::shared_ptr GetRaw(); EXPORT static const std::string CloudSyncTriggerFunc(const std::vector &args); diff --git a/frameworks/innerkitsimpl/medialibrary_data_extension/include/medialibrary_subscriber.h b/frameworks/innerkitsimpl/medialibrary_data_extension/include/medialibrary_subscriber.h index b35f302399..6088848710 100644 --- a/frameworks/innerkitsimpl/medialibrary_data_extension/include/medialibrary_subscriber.h +++ b/frameworks/innerkitsimpl/medialibrary_data_extension/include/medialibrary_subscriber.h @@ -87,6 +87,7 @@ private: DelayTask thumbnailBgDelayTask_{"thumbnailBgTask"}; EXPORT void ClearDirtyData(); EXPORT void DoBackgroundOperation(); + EXPORT void DoBackgroundOperationPart2(); EXPORT void DoThumbnailBgOperation(); EXPORT void StopBackgroundOperation(); EXPORT void StopThumbnailBgOperation(); diff --git a/frameworks/innerkitsimpl/medialibrary_data_extension/include/operation/photo_map_code_column.h b/frameworks/innerkitsimpl/medialibrary_data_extension/include/operation/photo_map_code_column.h new file mode 100644 index 0000000000..5469ca556e --- /dev/null +++ b/frameworks/innerkitsimpl/medialibrary_data_extension/include/operation/photo_map_code_column.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2025 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. + */ + +#ifndef PHOTO_MAP_CODE_COLUMN_H +#define PHOTO_MAP_CODE_COLUMN_H + +#include +#include + +namespace OHOS { +namespace Media { +#define EXPORT __attribute__ ((visibility ("default"))) +class PhotoMapCodeColumn { +public: + static const std::string PHOTOS_MAP_CODE_TABLE; + + static const std::string MAPCODE_ID EXPORT; + static const std::string MAPCODE_FILE_ID EXPORT; + static const std::string MAPCODE_LEVEL_3; + static const std::string MAPCODE_LEVEL_4; + static const std::string MAPCODE_LEVEL_5; + static const std::string MAPCODE_LEVEL_6; + static const std::string MAPCODE_LEVEL_7; + static const std::string MAPCODE_LEVEL_8; + static const std::string MAPCODE_LEVEL_9; + static const std::string MAPCODE_LEVEL_10; + static const std::string MAPCODE_LEVEL_11; + static const std::string MAPCODE_LEVEL_12; + static const std::string MAPCODE_LEVEL_13; + static const std::string MAPCODE_LEVEL_14; + static const std::string MAPCODE_LEVEL_15; + static const std::string MAPCODE_LEVEL_16; + static const std::string MAPCODE_LEVEL_17; + static const std::string MAPCODE_LEVEL_18; + static const std::string MAPCODE_LEVEL_19; + static const std::string MAPCODE_LEVEL_20; + + static const std::string MAPCODE_LEVEL_3_INDEX; + static const std::string MAPCODE_LEVEL_4_INDEX; + static const std::string MAPCODE_LEVEL_5_INDEX; + static const std::string MAPCODE_LEVEL_6_INDEX; + static const std::string MAPCODE_LEVEL_7_INDEX; + static const std::string MAPCODE_LEVEL_8_INDEX; + static const std::string MAPCODE_LEVEL_9_INDEX; + static const std::string MAPCODE_LEVEL_10_INDEX; + static const std::string MAPCODE_LEVEL_11_INDEX; + static const std::string MAPCODE_LEVEL_12_INDEX; + static const std::string MAPCODE_LEVEL_13_INDEX; + static const std::string MAPCODE_LEVEL_14_INDEX; + static const std::string MAPCODE_LEVEL_15_INDEX; + static const std::string MAPCODE_LEVEL_16_INDEX; + static const std::string MAPCODE_LEVEL_17_INDEX; + static const std::string MAPCODE_LEVEL_18_INDEX; + static const std::string MAPCODE_LEVEL_19_INDEX; + static const std::string MAPCODE_LEVEL_20_INDEX; + + static const std::string CREATE_MAP_CODE_TABLE; + + static const std::string CREATE_MAPCODE_LEVEL_3_INDEX; + static const std::string CREATE_MAPCODE_LEVEL_4_INDEX; + static const std::string CREATE_MAPCODE_LEVEL_5_INDEX; + static const std::string CREATE_MAPCODE_LEVEL_6_INDEX; + static const std::string CREATE_MAPCODE_LEVEL_7_INDEX; + static const std::string CREATE_MAPCODE_LEVEL_8_INDEX; + static const std::string CREATE_MAPCODE_LEVEL_9_INDEX; + static const std::string CREATE_MAPCODE_LEVEL_10_INDEX; + static const std::string CREATE_MAPCODE_LEVEL_11_INDEX; + static const std::string CREATE_MAPCODE_LEVEL_12_INDEX; + static const std::string CREATE_MAPCODE_LEVEL_13_INDEX; + static const std::string CREATE_MAPCODE_LEVEL_14_INDEX; + static const std::string CREATE_MAPCODE_LEVEL_15_INDEX; + static const std::string CREATE_MAPCODE_LEVEL_16_INDEX; + static const std::string CREATE_MAPCODE_LEVEL_17_INDEX; + static const std::string CREATE_MAPCODE_LEVEL_18_INDEX; + static const std::string CREATE_MAPCODE_LEVEL_19_INDEX; + static const std::string CREATE_MAPCODE_LEVEL_20_INDEX; + + static const std::string DROP_MAPCODE_LEVEL_3_INDEX; + static const std::string DROP_MAPCODE_LEVEL_4_INDEX; + static const std::string DROP_MAPCODE_LEVEL_5_INDEX; + static const std::string DROP_MAPCODE_LEVEL_6_INDEX; + static const std::string DROP_MAPCODE_LEVEL_7_INDEX; + static const std::string DROP_MAPCODE_LEVEL_8_INDEX; + static const std::string DROP_MAPCODE_LEVEL_9_INDEX; + static const std::string DROP_MAPCODE_LEVEL_10_INDEX; + static const std::string DROP_MAPCODE_LEVEL_11_INDEX; + static const std::string DROP_MAPCODE_LEVEL_12_INDEX; + static const std::string DROP_MAPCODE_LEVEL_13_INDEX; + static const std::string DROP_MAPCODE_LEVEL_14_INDEX; + static const std::string DROP_MAPCODE_LEVEL_15_INDEX; + static const std::string DROP_MAPCODE_LEVEL_16_INDEX; + static const std::string DROP_MAPCODE_LEVEL_17_INDEX; + static const std::string DROP_MAPCODE_LEVEL_18_INDEX; + static const std::string DROP_MAPCODE_LEVEL_19_INDEX; + static const std::string DROP_MAPCODE_LEVEL_20_INDEX; +}; +} // namespace Media +} // namespace OHOS + +#endif // PHOTO_MAP_CODE_OPERATIOIN_H diff --git a/frameworks/innerkitsimpl/medialibrary_data_extension/include/operation/photo_map_code_operation.h b/frameworks/innerkitsimpl/medialibrary_data_extension/include/operation/photo_map_code_operation.h new file mode 100644 index 0000000000..c6f36f4c9c --- /dev/null +++ b/frameworks/innerkitsimpl/medialibrary_data_extension/include/operation/photo_map_code_operation.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2025 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. + */ + +#ifndef PHOTO_MAP_CODE_OPERATIOIN_H +#define PHOTO_MAP_CODE_OPERATIOIN_H + +#include "values_bucket.h" +#include "medialibrary_rdbstore.h" +#include "medialibrary_type_const.h" + +#include "media_column.h" +#include "directory_ex.h" +#include "media_log.h" +#include "abs_rdb_predicates.h" +#include "photo_album_column.h" +#include "photo_map_column.h" +#include "cloud_media_file_utils.h" +#include "cloud_media_sync_utils.h" +#include "cloud_media_operation_code.h" +#include "medialibrary_unistore_manager.h" +#include "moving_photo_file_utils.h" +#include "result_set.h" +#include "result_set_utils.h" +#include "thumbnail_const.h" +#include "userfile_manager_types.h" +#include "result_set_reader.h" +#include "photos_po_writer.h" +#include "photo_album_po_writer.h" +#include "cloud_sync_convert.h" +#include "medialibrary_rdb_transaction.h" +#include "medialibrary_rdb_utils.h" +#include "scanner_utils.h" +#include "cloud_media_dao_const.h" +#include "media_gallery_sync_notify.h" +#include "cloud_media_sync_const.h" +#include "cloud_media_dao_utils.h" + +#include +#include + +namespace OHOS { +namespace Media { +#define EXPORT __attribute__ ((visibility ("default"))) +enum class PhotoMapType { + QUERY_AND_INSERT = 0, + UPDATE_AND_INSERT, +}; + +class PhotoMapData { +public: + int32_t fileId{-1}; + double latitude{0.0}; + double longitude{0.0}; + + PhotoMapData(int32_t fileIdIn, double latitudeIn, double longitudeIn) : fileId(fileIdIn), latitude(latitudeIn), + longitude(longitudeIn) {} + PhotoMapData() : fileId(-1), latitude(0.0), + longitude(0.0) {} +}; + +class PhotoMapCodeOperation { +public: + static int32_t InsertPhotosMapCodes(const std::vector &photoMapDatas, + const std::shared_ptr cloneLibraryRdb); + static int32_t GetPhotosMapCodesMRS(const std::vector &photoMapDatas, + const std::shared_ptr store); + static int32_t GetPhotoMapCode(const PhotoMapData &photoMapData, const PhotoMapType &photoMapType); + static int32_t UpgradePhotoMapCode(const std::shared_ptr store); + static int32_t RemovePhotosMapCodes(const std::vector &fileIds); + + static int32_t GetPhotosPoByInputValues(const std::vector &inputValues, + std::vector &photosPos, const std::vector &getValues); + +private: + static vector FilterFileIds(const vector &fileIds); + static int32_t DatasToMapCodes(const std::shared_ptr store, + const int count, const int32_t minId, const int32_t maxId); + static void GetPhotoMapCode(NativeRdb::ValuesBucket &mapValue, double lat, double lon); + static std::vector SetPoint(double lat, double lon); + static int64_t GetMapCode(std::vector &latAndLon, int level); + + static int32_t ExecSqlWithRetry(std::function execSql); + // 计算工具函数,内部可见 + static int64_t GetMapHilbertCode(double lat, double lon, int level); + static std::string Int64ToBinaryWithPadding(int64_t num, int width); + static void UpdateZoomLevelStep(int64_t zoomLevel, std::vector &point); + static int64_t DistanceFromPoint(int64_t xPosition, int64_t yPosition, int level); + + static int32_t DatasToMapCodesBySetpLevel(const std::shared_ptr store, + int32_t &i, int32_t endIndex, int32_t &deailCount, int stepLevel); +private: + static const int LEVEL_START; + static const int LEVEL_COUNT; + static const int STEP_COUNT; + static const int STEP_LEVEL; +}; +} // namespace Media +} // namespace OHOS + +#endif // PHOTO_MAP_CODE_OPERATIOIN_H diff --git a/frameworks/innerkitsimpl/medialibrary_data_extension/src/medialibrary_asset_operations.cpp b/frameworks/innerkitsimpl/medialibrary_data_extension/src/medialibrary_asset_operations.cpp index 13c915c8a3..d636f8fb45 100644 --- a/frameworks/innerkitsimpl/medialibrary_data_extension/src/medialibrary_asset_operations.cpp +++ b/frameworks/innerkitsimpl/medialibrary_data_extension/src/medialibrary_asset_operations.cpp @@ -90,6 +90,9 @@ #include "cloud_sync_helper.h" #include "refresh_business_name.h" +#include "scanner_map_code_utils.h" +#include "photo_map_code_operation.h" + using namespace std; using namespace OHOS::NativeRdb; @@ -2957,6 +2960,12 @@ static int32_t DeleteDbByIds(const string &table, vector &ids, const boo return deletedRows; } +static inline int32_t DeleteMapCodeByIds(vector &ids, const bool compatible) +{ + MEDIA_INFO_LOG("DeleteMapCodeByIds ids size %{public}zu", ids.size()); + return PhotoMapCodeOperation::RemovePhotosMapCodes(ids); +} + static void GetAlbumNamesById(DeletedFilesParams &filesParams) { MediaLibraryTracer tracer; @@ -3027,6 +3036,9 @@ int32_t MediaLibraryAssetOperations::DeleteFromDisk(AbsRdbPredicates &predicates "Failed to delete files in db, deletedRows: %{public}d, ids size: %{public}zu", deletedRows, fileParams.ids.size()); + int32_t mapCodeRet = DeleteMapCodeByIds(fileParams.ids, compatible); + MEDIA_DEBUG_LOG("DeleteMapCodeByIds mapCodeRet %{public}d", mapCodeRet); + MEDIA_INFO_LOG("Delete files in db, deletedRows: %{public}d", deletedRows); auto asyncWorker = MediaLibraryAsyncWorker::GetInstance(); CHECK_AND_RETURN_RET_LOG(asyncWorker != nullptr, E_ERR, "Can not get asyncWorker"); diff --git a/frameworks/innerkitsimpl/medialibrary_data_extension/src/medialibrary_data_manager.cpp b/frameworks/innerkitsimpl/medialibrary_data_extension/src/medialibrary_data_manager.cpp index bb4157d9e6..83de3ac00b 100644 --- a/frameworks/innerkitsimpl/medialibrary_data_extension/src/medialibrary_data_manager.cpp +++ b/frameworks/innerkitsimpl/medialibrary_data_extension/src/medialibrary_data_manager.cpp @@ -137,6 +137,7 @@ #include "medialibrary_photo_operations.h" #include "medialibrary_upgrade_utils.h" #include "settings_data_manager.h" +#include "photo_map_code_operation.h" using namespace std; using namespace OHOS::AppExecFwk; @@ -716,6 +717,14 @@ void HandleUpgradeRdbAsyncPart3(const shared_ptr rdbStore, rdbStore->SetOldVersion(VERSION_ADD_SOUTH_DEVICE_TYPE); RdbUpgradeUtils::SetUpgradeStatus(VERSION_ADD_SOUTH_DEVICE_TYPE, false); } + + if (oldVersion < VERSION_ADD_MAP_CODE_TABLE && + !RdbUpgradeUtils::HasUpgraded(VERSION_ADD_MAP_CODE_TABLE, false)) { + MediaLibraryRdbStore::AddPhotoMapTableIndex(rdbStore); + MediaLibraryRdbStore::AddPhotoMapTableData(rdbStore); + rdbStore->SetOldVersion(VERSION_ADD_MAP_CODE_TABLE); + RdbUpgradeUtils::SetUpgradeStatus(VERSION_ADD_MAP_CODE_TABLE, false); + } } void HandleUpgradeRdbAsyncPart2(const shared_ptr rdbStore, int32_t oldVersion) diff --git a/frameworks/innerkitsimpl/medialibrary_data_extension/src/medialibrary_rdbstore.cpp b/frameworks/innerkitsimpl/medialibrary_data_extension/src/medialibrary_rdbstore.cpp index f6205daef8..ac3b78e679 100755 --- a/frameworks/innerkitsimpl/medialibrary_data_extension/src/medialibrary_rdbstore.cpp +++ b/frameworks/innerkitsimpl/medialibrary_data_extension/src/medialibrary_rdbstore.cpp @@ -93,6 +93,9 @@ #include "media_app_uri_sensitive_column.h" #include "medialibrary_upgrade_utils.h" #include "media_config_info_column.h" +#include "scanner_map_code_utils.h" +#include "photo_map_code_column.h" +#include "photo_map_code_operation.h" using namespace std; using namespace OHOS::NativeRdb; @@ -627,6 +630,64 @@ void MediaLibraryRdbStore::AddPhotoDateAddedIndex(const shared_ptr sqls = { + PhotoMapCodeColumn::CREATE_MAP_CODE_TABLE, + }; + int32_t ret = ExecSqls(sqls, store); + MEDIA_INFO_LOG("End AddPhotoMapTable"); + return ret == NativeRdb::E_OK; +} + +bool MediaLibraryRdbStore::AddPhotoMapTableIndex(const shared_ptr store) +{ + MEDIA_INFO_LOG("start AddPhotoMapTableIndex"); + if (store == nullptr) { + MEDIA_ERR_LOG("GetStore is failed, store is nullptr."); + return false; + } + const vector sqls = { + PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_3_INDEX, + PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_4_INDEX, + PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_5_INDEX, + PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_6_INDEX, + PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_7_INDEX, + PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_8_INDEX, + PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_9_INDEX, + PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_10_INDEX, + PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_11_INDEX, + PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_12_INDEX, + PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_13_INDEX, + PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_14_INDEX, + PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_15_INDEX, + PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_16_INDEX, + PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_17_INDEX, + PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_18_INDEX, + PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_19_INDEX, + PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_20_INDEX, + }; + int32_t ret = ExecSqls(sqls, *store->GetRaw().get()); + + MEDIA_INFO_LOG("End AddPhotoMapTableIndex"); + return ret == NativeRdb::E_OK; +} + +bool MediaLibraryRdbStore::AddPhotoMapTableData(const shared_ptr store) +{ + MEDIA_INFO_LOG("start AddPhotoMapTableData"); + if (store == nullptr) { + MEDIA_ERR_LOG("GetStore is failed, store is nullptr."); + return false; + } + + int32_t ret = PhotoMapCodeOperation::UpgradePhotoMapCode(store); + MEDIA_INFO_LOG("End AddPhotoMapTableData"); + return ret == E_OK; +} + void MediaLibraryRdbStore::AddPhotoWhiteBlocksIndex(const shared_ptr store) { MEDIA_INFO_LOG("start AddPhotoWhiteBlocksIndex"); @@ -1860,6 +1921,27 @@ static const vector onCreateSqlStrs = { // tab_analysis_progress CREATE_TAB_ANALYSIS_PROGRESS, + + // tab_map_photo_map + PhotoMapCodeColumn::CREATE_MAP_CODE_TABLE, + PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_3_INDEX, + PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_4_INDEX, + PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_5_INDEX, + PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_6_INDEX, + PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_7_INDEX, + PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_8_INDEX, + PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_9_INDEX, + PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_10_INDEX, + PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_11_INDEX, + PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_12_INDEX, + PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_13_INDEX, + PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_14_INDEX, + PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_15_INDEX, + PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_16_INDEX, + PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_17_INDEX, + PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_18_INDEX, + PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_19_INDEX, + PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_20_INDEX, }; static int32_t ExecuteSql(RdbStore &store) @@ -2026,6 +2108,7 @@ void API10TableCreate(RdbStore &store) TriggerRemoveAssets(), TriggerDeletePhotoClearMap(), TriggerUpdateUserAlbumCount(), + PhotoMapCodeColumn::CREATE_MAP_CODE_TABLE, }; for (size_t i = 0; i < executeSqlStrs.size(); i++) { @@ -5242,6 +5325,37 @@ static void AddCloneSequenceColumns(RdbStore &store) MEDIA_INFO_LOG("add tab_old_photos clone_sequence columns end"); } +static void UpgradeExtensionPart10(RdbStore &store, int32_t oldVersion) +{ + if (oldVersion < VERSION_ADD_TAB_ANALYSIS_PROGRESS && + !RdbUpgradeUtils::HasUpgraded(VERSION_ADD_TAB_ANALYSIS_PROGRESS, true)) { + AddAnalysisProgress(store); + RdbUpgradeUtils::SetUpgradeStatus(VERSION_ADD_TAB_ANALYSIS_PROGRESS, true); + } + + if (oldVersion < VERSION_ADD_COMPOSITE_DISPLAY_STATUS_COLUMNS) { + AddCompositeDisplayStatusColumn(store); + } + + if (oldVersion < VERSION_ADD_INDEX_FOR_PHOTO_SORT_IN_ALBUM && + !RdbUpgradeUtils::HasUpgraded(VERSION_ADD_INDEX_FOR_PHOTO_SORT_IN_ALBUM, true)) { + AddIndexForPhotoSortInAlbum(store); + RdbUpgradeUtils::SetUpgradeStatus(VERSION_ADD_INDEX_FOR_PHOTO_SORT_IN_ALBUM, true); + } + + if (oldVersion < VERSION_ADD_TAB_OLD_PHOTOS_CLONE_SEQUENCE && + !RdbUpgradeUtils::HasUpgraded(VERSION_ADD_TAB_OLD_PHOTOS_CLONE_SEQUENCE, true)) { + AddCloneSequenceColumns(store); + RdbUpgradeUtils::SetUpgradeStatus(VERSION_ADD_TAB_OLD_PHOTOS_CLONE_SEQUENCE, true); + } + + if (oldVersion < VERSION_ADD_MAP_CODE_TABLE && + !RdbUpgradeUtils::HasUpgraded(VERSION_ADD_MAP_CODE_TABLE, true)) { + MediaLibraryRdbStore::AddPhotoMapTable(store); + RdbUpgradeUtils::SetUpgradeStatus(VERSION_ADD_MAP_CODE_TABLE, true); + } +} + static void UpgradeExtensionPart9(RdbStore &store, int32_t oldVersion) { if (oldVersion < VERSION_ADD_RELATIONSHIP_AND_UPDATE_TRIGGER && @@ -5280,27 +5394,7 @@ static void UpgradeExtensionPart9(RdbStore &store, int32_t oldVersion) RdbUpgradeUtils::SetUpgradeStatus(VERSION_ADD_SOUTH_DEVICE_TYPE, true); } - if (oldVersion < VERSION_ADD_TAB_ANALYSIS_PROGRESS && - !RdbUpgradeUtils::HasUpgraded(VERSION_ADD_TAB_ANALYSIS_PROGRESS, true)) { - AddAnalysisProgress(store); - RdbUpgradeUtils::SetUpgradeStatus(VERSION_ADD_TAB_ANALYSIS_PROGRESS, true); - } - - if (oldVersion < VERSION_ADD_COMPOSITE_DISPLAY_STATUS_COLUMNS) { - AddCompositeDisplayStatusColumn(store); - } - - if (oldVersion < VERSION_ADD_INDEX_FOR_PHOTO_SORT_IN_ALBUM && - !RdbUpgradeUtils::HasUpgraded(VERSION_ADD_INDEX_FOR_PHOTO_SORT_IN_ALBUM, true)) { - AddIndexForPhotoSortInAlbum(store); - RdbUpgradeUtils::SetUpgradeStatus(VERSION_ADD_INDEX_FOR_PHOTO_SORT_IN_ALBUM, true); - } - - if (oldVersion < VERSION_ADD_TAB_OLD_PHOTOS_CLONE_SEQUENCE && - !RdbUpgradeUtils::HasUpgraded(VERSION_ADD_TAB_OLD_PHOTOS_CLONE_SEQUENCE, true)) { - AddCloneSequenceColumns(store); - RdbUpgradeUtils::SetUpgradeStatus(VERSION_ADD_TAB_OLD_PHOTOS_CLONE_SEQUENCE, true); - } + UpgradeExtensionPart10(store, oldVersion); } static void UpgradeExtensionPart8(RdbStore &store, int32_t oldVersion) diff --git a/frameworks/innerkitsimpl/medialibrary_data_extension/src/medialibrary_subscriber.cpp b/frameworks/innerkitsimpl/medialibrary_data_extension/src/medialibrary_subscriber.cpp index f1b55daa16..89409cec21 100644 --- a/frameworks/innerkitsimpl/medialibrary_data_extension/src/medialibrary_subscriber.cpp +++ b/frameworks/innerkitsimpl/medialibrary_data_extension/src/medialibrary_subscriber.cpp @@ -89,6 +89,7 @@ #include "enhancement_manager.h" #include "cloud_enhancement_checker.h" #endif +#include "map_code_upload_checker.h" using namespace OHOS::AAFwk; @@ -759,6 +760,26 @@ void MedialibrarySubscriber::AgingTmpCompatibleDuplicates(bool isAge) } } +void MedialibrarySubscriber::DoBackgroundOperationPart2() +{ + RecoverBackgroundDownloadCloudMediaAsset(); + CloudMediaAssetManager::GetInstance().StartDeleteCloudMediaAssets(); + // compat old-version moving photo + MovingPhotoProcessor::StartProcess(); + MediaLibraryAlbumFusionUtils::CleanInvalidCloudAlbumAndData(true); + auto watch = MediaLibraryInotify::GetInstance(); + if (watch != nullptr) { + watch->DoAging(); + } + DfxMovingPhoto::AbnormalMovingPhotoStatistics(); + PhotoMimetypeOperation::UpdateInvalidMimeType(); + MapCodeUploadChecker::RepairNoMapCodePhoto(); + DfxManager::GetInstance()->HandleTwoDayMissions(); + DfxManager::GetInstance()->HandleOneWeekMissions(); + PhotoDayMonthYearOperation::RepairDateTime(); + backgroundTaskFactory_.Execute(); +} + void MedialibrarySubscriber::DoBackgroundOperation() { bool cond = (!backgroundDelayTask_.IsDelayTaskTimeOut() || !currentStatus_); @@ -804,21 +825,8 @@ void MedialibrarySubscriber::DoBackgroundOperation() } ret = DoUpdateBurstCoverLevelFromGallery(); CHECK_AND_PRINT_LOG(ret == E_OK, "DoUpdateBurstCoverLevelFromGallery faild"); - RecoverBackgroundDownloadCloudMediaAsset(); - CloudMediaAssetManager::GetInstance().StartDeleteCloudMediaAssets(); - // compat old-version moving photo - MovingPhotoProcessor::StartProcess(); - MediaLibraryAlbumFusionUtils::CleanInvalidCloudAlbumAndData(true); - auto watch = MediaLibraryInotify::GetInstance(); - if (watch != nullptr) { - watch->DoAging(); - } - DfxMovingPhoto::AbnormalMovingPhotoStatistics(); - PhotoMimetypeOperation::UpdateInvalidMimeType(); - DfxManager::GetInstance()->HandleTwoDayMissions(); - DfxManager::GetInstance()->HandleOneWeekMissions(); - PhotoDayMonthYearOperation::RepairDateTime(); - backgroundTaskFactory_.Execute(); + + DoBackgroundOperationPart2(); } static void PauseBackgroundDownloadCloudMedia() diff --git a/frameworks/innerkitsimpl/medialibrary_data_extension/src/medialibrary_upgrade_utils.cpp b/frameworks/innerkitsimpl/medialibrary_data_extension/src/medialibrary_upgrade_utils.cpp index 80a5850f22..d9564392cc 100644 --- a/frameworks/innerkitsimpl/medialibrary_data_extension/src/medialibrary_upgrade_utils.cpp +++ b/frameworks/innerkitsimpl/medialibrary_data_extension/src/medialibrary_upgrade_utils.cpp @@ -35,6 +35,7 @@ static unordered_map UPGRADE_VALUE_MAP = { { VERSION_ADD_TAB_ANALYSIS_PROGRESS, "VERSION_ADD_TAB_ANALYSIS_PROGRESS" }, { VERSION_ADD_INDEX_FOR_PHOTO_SORT_IN_ALBUM, "VERSION_ADD_INDEX_FOR_PHOTO_SORT_IN_ALBUM" }, { VERSION_ADD_TAB_OLD_PHOTOS_CLONE_SEQUENCE, "VERSION_ADD_TAB_OLD_PHOTOS_CLONE_SEQUENCE" }, + { VERSION_ADD_MAP_CODE_TABLE, "VERSION_ADD_MAP_CODE_TABLE"}, }; bool RdbUpgradeUtils::HasUpgraded(int32_t version, bool isSync) diff --git a/frameworks/innerkitsimpl/medialibrary_data_extension/src/operation/photo_map_code_column.cpp b/frameworks/innerkitsimpl/medialibrary_data_extension/src/operation/photo_map_code_column.cpp new file mode 100644 index 0000000000..21df9de105 --- /dev/null +++ b/frameworks/innerkitsimpl/medialibrary_data_extension/src/operation/photo_map_code_column.cpp @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2025 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 "PhotoMapCodeColumn" + +#include "base_column.h" +#include "photo_map_code_column.h" +#include +#include +#include +#include +#include +#include + +namespace OHOS { +namespace Media { +const std::string PhotoMapCodeColumn::PHOTOS_MAP_CODE_TABLE = "tab_map_photo_map"; + +const std::string PhotoMapCodeColumn::MAPCODE_ID = "id"; +const std::string PhotoMapCodeColumn::MAPCODE_FILE_ID = "file_id"; + +const std::string PhotoMapCodeColumn::MAPCODE_LEVEL_3 = "cell_3"; +const std::string PhotoMapCodeColumn::MAPCODE_LEVEL_4 = "cell_4"; +const std::string PhotoMapCodeColumn::MAPCODE_LEVEL_5 = "cell_5"; +const std::string PhotoMapCodeColumn::MAPCODE_LEVEL_6 = "cell_6"; +const std::string PhotoMapCodeColumn::MAPCODE_LEVEL_7 = "cell_7"; +const std::string PhotoMapCodeColumn::MAPCODE_LEVEL_8 = "cell_8"; +const std::string PhotoMapCodeColumn::MAPCODE_LEVEL_9 = "cell_9"; +const std::string PhotoMapCodeColumn::MAPCODE_LEVEL_10 = "cell_10"; +const std::string PhotoMapCodeColumn::MAPCODE_LEVEL_11 = "cell_11"; +const std::string PhotoMapCodeColumn::MAPCODE_LEVEL_12 = "cell_12"; +const std::string PhotoMapCodeColumn::MAPCODE_LEVEL_13 = "cell_13"; +const std::string PhotoMapCodeColumn::MAPCODE_LEVEL_14 = "cell_14"; +const std::string PhotoMapCodeColumn::MAPCODE_LEVEL_15 = "cell_15"; +const std::string PhotoMapCodeColumn::MAPCODE_LEVEL_16 = "cell_16"; +const std::string PhotoMapCodeColumn::MAPCODE_LEVEL_17 = "cell_17"; +const std::string PhotoMapCodeColumn::MAPCODE_LEVEL_18 = "cell_18"; +const std::string PhotoMapCodeColumn::MAPCODE_LEVEL_19 = "cell_19"; +const std::string PhotoMapCodeColumn::MAPCODE_LEVEL_20 = "cell_20"; + +const std::string PhotoMapCodeColumn::MAPCODE_LEVEL_3_INDEX = "map_cell_3_index"; +const std::string PhotoMapCodeColumn::MAPCODE_LEVEL_4_INDEX = "map_cell_4_index"; +const std::string PhotoMapCodeColumn::MAPCODE_LEVEL_5_INDEX = "map_cell_5_index"; +const std::string PhotoMapCodeColumn::MAPCODE_LEVEL_6_INDEX = "map_cell_6_index"; +const std::string PhotoMapCodeColumn::MAPCODE_LEVEL_7_INDEX = "map_cell_7_index"; +const std::string PhotoMapCodeColumn::MAPCODE_LEVEL_8_INDEX = "map_cell_8_index"; +const std::string PhotoMapCodeColumn::MAPCODE_LEVEL_9_INDEX = "map_cell_9_index"; +const std::string PhotoMapCodeColumn::MAPCODE_LEVEL_10_INDEX = "map_cell_10_index"; +const std::string PhotoMapCodeColumn::MAPCODE_LEVEL_11_INDEX = "map_cell_11_index"; +const std::string PhotoMapCodeColumn::MAPCODE_LEVEL_12_INDEX = "map_cell_12_index"; +const std::string PhotoMapCodeColumn::MAPCODE_LEVEL_13_INDEX = "map_cell_13_index"; +const std::string PhotoMapCodeColumn::MAPCODE_LEVEL_14_INDEX = "map_cell_14_index"; +const std::string PhotoMapCodeColumn::MAPCODE_LEVEL_15_INDEX = "map_cell_15_index"; +const std::string PhotoMapCodeColumn::MAPCODE_LEVEL_16_INDEX = "map_cell_16_index"; +const std::string PhotoMapCodeColumn::MAPCODE_LEVEL_17_INDEX = "map_cell_17_index"; +const std::string PhotoMapCodeColumn::MAPCODE_LEVEL_18_INDEX = "map_cell_18_index"; +const std::string PhotoMapCodeColumn::MAPCODE_LEVEL_19_INDEX = "map_cell_19_index"; +const std::string PhotoMapCodeColumn::MAPCODE_LEVEL_20_INDEX = "map_cell_20_index"; + +const std::string PhotoMapCodeColumn::CREATE_MAP_CODE_TABLE = "CREATE TABLE IF NOT EXISTS " + + PhotoMapCodeColumn::PHOTOS_MAP_CODE_TABLE + " (" + + PhotoMapCodeColumn::MAPCODE_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + + PhotoMapCodeColumn::MAPCODE_FILE_ID + " INTEGER DEFAULT 0, " + + PhotoMapCodeColumn::MAPCODE_LEVEL_3 + " BIGINT DEFAULT 0, " + + PhotoMapCodeColumn::MAPCODE_LEVEL_4 + " BIGINT DEFAULT 0, " + + PhotoMapCodeColumn::MAPCODE_LEVEL_5 + " BIGINT DEFAULT 0, " + + PhotoMapCodeColumn::MAPCODE_LEVEL_6 + " BIGINT DEFAULT 0, " + + PhotoMapCodeColumn::MAPCODE_LEVEL_7 + " BIGINT DEFAULT 0, " + + PhotoMapCodeColumn::MAPCODE_LEVEL_8 + " BIGINT DEFAULT 0, " + + PhotoMapCodeColumn::MAPCODE_LEVEL_9 + " BIGINT DEFAULT 0, " + + PhotoMapCodeColumn::MAPCODE_LEVEL_10 + " BIGINT DEFAULT 0, " + + PhotoMapCodeColumn::MAPCODE_LEVEL_11 + " BIGINT DEFAULT 0, " + + PhotoMapCodeColumn::MAPCODE_LEVEL_12 + " BIGINT DEFAULT 0, " + + PhotoMapCodeColumn::MAPCODE_LEVEL_13 + " BIGINT DEFAULT 0, " + + PhotoMapCodeColumn::MAPCODE_LEVEL_14 + " BIGINT DEFAULT 0, " + + PhotoMapCodeColumn::MAPCODE_LEVEL_15 + " BIGINT DEFAULT 0, " + + PhotoMapCodeColumn::MAPCODE_LEVEL_16 + " BIGINT DEFAULT 0, " + + PhotoMapCodeColumn::MAPCODE_LEVEL_17 + " BIGINT DEFAULT 0, " + + PhotoMapCodeColumn::MAPCODE_LEVEL_18 + " BIGINT DEFAULT 0, " + + PhotoMapCodeColumn::MAPCODE_LEVEL_19 + " BIGINT DEFAULT 0, " + + PhotoMapCodeColumn::MAPCODE_LEVEL_20 + " BIGINT DEFAULT 0 " + + ") "; + +const std::string PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_3_INDEX = BaseColumn::CreateIndex() + + MAPCODE_LEVEL_3_INDEX + " ON " + PHOTOS_MAP_CODE_TABLE + " (" + MAPCODE_LEVEL_3 + " DESC)"; +const std::string PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_4_INDEX = BaseColumn::CreateIndex() + + MAPCODE_LEVEL_4_INDEX + " ON " + PHOTOS_MAP_CODE_TABLE + " (" + MAPCODE_LEVEL_4 + " DESC)"; +const std::string PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_5_INDEX = BaseColumn::CreateIndex() + + MAPCODE_LEVEL_5_INDEX + " ON " + PHOTOS_MAP_CODE_TABLE + " (" + MAPCODE_LEVEL_5 + " DESC)"; +const std::string PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_6_INDEX = BaseColumn::CreateIndex() + + MAPCODE_LEVEL_6_INDEX + " ON " + PHOTOS_MAP_CODE_TABLE + " (" + MAPCODE_LEVEL_6 + " DESC)"; +const std::string PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_7_INDEX = BaseColumn::CreateIndex() + + MAPCODE_LEVEL_7_INDEX + " ON " + PHOTOS_MAP_CODE_TABLE + " (" + MAPCODE_LEVEL_7 + " DESC)"; +const std::string PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_8_INDEX = BaseColumn::CreateIndex() + + MAPCODE_LEVEL_8_INDEX + " ON " + PHOTOS_MAP_CODE_TABLE + " (" + MAPCODE_LEVEL_8 + " DESC)"; +const std::string PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_9_INDEX = BaseColumn::CreateIndex() + + MAPCODE_LEVEL_9_INDEX + " ON " + PHOTOS_MAP_CODE_TABLE + " (" + MAPCODE_LEVEL_9 + " DESC)"; +const std::string PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_10_INDEX = BaseColumn::CreateIndex() + + MAPCODE_LEVEL_10_INDEX + " ON " + PHOTOS_MAP_CODE_TABLE + " (" + MAPCODE_LEVEL_10 + " DESC)"; +const std::string PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_11_INDEX = BaseColumn::CreateIndex() + + MAPCODE_LEVEL_11_INDEX + " ON " + PHOTOS_MAP_CODE_TABLE + " (" + MAPCODE_LEVEL_11 + " DESC)"; +const std::string PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_12_INDEX = BaseColumn::CreateIndex() + + MAPCODE_LEVEL_12_INDEX + " ON " + PHOTOS_MAP_CODE_TABLE + " (" + MAPCODE_LEVEL_12 + " DESC)"; +const std::string PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_13_INDEX = BaseColumn::CreateIndex() + + MAPCODE_LEVEL_13_INDEX + " ON " + PHOTOS_MAP_CODE_TABLE + " (" + MAPCODE_LEVEL_13 + " DESC)"; +const std::string PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_14_INDEX = BaseColumn::CreateIndex() + + MAPCODE_LEVEL_14_INDEX + " ON " + PHOTOS_MAP_CODE_TABLE + " (" + MAPCODE_LEVEL_14 + " DESC)"; +const std::string PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_15_INDEX = BaseColumn::CreateIndex() + + MAPCODE_LEVEL_15_INDEX + " ON " + PHOTOS_MAP_CODE_TABLE + " (" + MAPCODE_LEVEL_15 + " DESC)"; +const std::string PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_16_INDEX = BaseColumn::CreateIndex() + + MAPCODE_LEVEL_16_INDEX + " ON " + PHOTOS_MAP_CODE_TABLE + " (" + MAPCODE_LEVEL_16 + " DESC)"; +const std::string PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_17_INDEX = BaseColumn::CreateIndex() + + MAPCODE_LEVEL_17_INDEX + " ON " + PHOTOS_MAP_CODE_TABLE + " (" + MAPCODE_LEVEL_17 + " DESC)"; +const std::string PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_18_INDEX = BaseColumn::CreateIndex() + + MAPCODE_LEVEL_18_INDEX + " ON " + PHOTOS_MAP_CODE_TABLE + " (" + MAPCODE_LEVEL_18 + " DESC)"; +const std::string PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_19_INDEX = BaseColumn::CreateIndex() + + MAPCODE_LEVEL_19_INDEX + " ON " + PHOTOS_MAP_CODE_TABLE + " (" + MAPCODE_LEVEL_19 + " DESC)"; +const std::string PhotoMapCodeColumn::CREATE_MAPCODE_LEVEL_20_INDEX = BaseColumn::CreateIndex() + + MAPCODE_LEVEL_20_INDEX + " ON " + PHOTOS_MAP_CODE_TABLE + " (" + MAPCODE_LEVEL_20 + " DESC)"; + +const std::string PhotoMapCodeColumn::DROP_MAPCODE_LEVEL_3_INDEX = BaseColumn::DropIndex() + MAPCODE_LEVEL_3_INDEX; +const std::string PhotoMapCodeColumn::DROP_MAPCODE_LEVEL_4_INDEX = BaseColumn::DropIndex() + MAPCODE_LEVEL_4_INDEX; +const std::string PhotoMapCodeColumn::DROP_MAPCODE_LEVEL_5_INDEX = BaseColumn::DropIndex() + MAPCODE_LEVEL_5_INDEX; +const std::string PhotoMapCodeColumn::DROP_MAPCODE_LEVEL_6_INDEX = BaseColumn::DropIndex() + MAPCODE_LEVEL_6_INDEX; +const std::string PhotoMapCodeColumn::DROP_MAPCODE_LEVEL_7_INDEX = BaseColumn::DropIndex() + MAPCODE_LEVEL_7_INDEX; +const std::string PhotoMapCodeColumn::DROP_MAPCODE_LEVEL_8_INDEX = BaseColumn::DropIndex() + MAPCODE_LEVEL_8_INDEX; +const std::string PhotoMapCodeColumn::DROP_MAPCODE_LEVEL_9_INDEX = BaseColumn::DropIndex() + MAPCODE_LEVEL_9_INDEX; +const std::string PhotoMapCodeColumn::DROP_MAPCODE_LEVEL_10_INDEX = BaseColumn::DropIndex() + MAPCODE_LEVEL_10_INDEX; +const std::string PhotoMapCodeColumn::DROP_MAPCODE_LEVEL_11_INDEX = BaseColumn::DropIndex() + MAPCODE_LEVEL_11_INDEX; +const std::string PhotoMapCodeColumn::DROP_MAPCODE_LEVEL_12_INDEX = BaseColumn::DropIndex() + MAPCODE_LEVEL_12_INDEX; +const std::string PhotoMapCodeColumn::DROP_MAPCODE_LEVEL_13_INDEX = BaseColumn::DropIndex() + MAPCODE_LEVEL_13_INDEX; +const std::string PhotoMapCodeColumn::DROP_MAPCODE_LEVEL_14_INDEX = BaseColumn::DropIndex() + MAPCODE_LEVEL_14_INDEX; +const std::string PhotoMapCodeColumn::DROP_MAPCODE_LEVEL_15_INDEX = BaseColumn::DropIndex() + MAPCODE_LEVEL_15_INDEX; +const std::string PhotoMapCodeColumn::DROP_MAPCODE_LEVEL_16_INDEX = BaseColumn::DropIndex() + MAPCODE_LEVEL_16_INDEX; +const std::string PhotoMapCodeColumn::DROP_MAPCODE_LEVEL_17_INDEX = BaseColumn::DropIndex() + MAPCODE_LEVEL_17_INDEX; +const std::string PhotoMapCodeColumn::DROP_MAPCODE_LEVEL_18_INDEX = BaseColumn::DropIndex() + MAPCODE_LEVEL_18_INDEX; +const std::string PhotoMapCodeColumn::DROP_MAPCODE_LEVEL_19_INDEX = BaseColumn::DropIndex() + MAPCODE_LEVEL_19_INDEX; +const std::string PhotoMapCodeColumn::DROP_MAPCODE_LEVEL_20_INDEX = BaseColumn::DropIndex() + MAPCODE_LEVEL_20_INDEX; +} // namespace Media +} // namespace OHOS diff --git a/frameworks/innerkitsimpl/medialibrary_data_extension/src/operation/photo_map_code_operation.cpp b/frameworks/innerkitsimpl/medialibrary_data_extension/src/operation/photo_map_code_operation.cpp new file mode 100644 index 0000000000..1acd3e1b72 --- /dev/null +++ b/frameworks/innerkitsimpl/medialibrary_data_extension/src/operation/photo_map_code_operation.cpp @@ -0,0 +1,549 @@ +/* + * Copyright (C) 2025 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 "PhotoMapCodeOperation" + +#include "photo_map_code_column.h" +#include "photo_map_code_operation.h" +#include "media_log.h" +#include "medialibrary_unistore_manager.h" +#include "medialibrary_subscriber.h" +#include "medialibrary_rdbstore.h" +#include "directory_ex.h" +#include "media_log.h" +#include "medialibrary_type_const.h" +#include "abs_rdb_predicates.h" +#include "photo_album_column.h" +#include "photo_map_column.h" +#include "cloud_media_file_utils.h" +#include "cloud_media_sync_utils.h" +#include "cloud_media_operation_code.h" +#include "medialibrary_unistore_manager.h" +#include "moving_photo_file_utils.h" +#include "result_set.h" +#include "result_set_utils.h" +#include "thumbnail_const.h" +#include "userfile_manager_types.h" +#include "result_set_reader.h" +#include "photos_po_writer.h" +#include "photo_album_po_writer.h" +#include "cloud_sync_convert.h" +#include "photo_map_column.h" +#include "medialibrary_rdb_transaction.h" +#include "medialibrary_rdb_utils.h" +#include "scanner_utils.h" +#include "cloud_media_dao_const.h" +#include "media_gallery_sync_notify.h" +#include "cloud_media_sync_const.h" +#include "cloud_media_dao_utils.h" +#include "base_column.h" +#include "medialibrary_data_manager_utils.h" + +#include "cpu_utils.h" +#include +#include +#include +#include +#include +#include + +namespace OHOS { +namespace Media { +using namespace std; +const int32_t COUNT_INDEX = 0; +const int32_t MIN_INDEX = 1; +const int32_t MAX_INDEX = 2; +const int32_t POINT_SIZE = 2; +const int FAST_COUNT = 30000; + +const int PhotoMapCodeOperation::LEVEL_START = 3; +const int PhotoMapCodeOperation::LEVEL_COUNT = 21; +const int PhotoMapCodeOperation::STEP_COUNT = 10; +const int PhotoMapCodeOperation::STEP_LEVEL = 20; + +static constexpr double DOUBLE_EPSILON = 1e-15; +static constexpr double MAX_LATITUDE_EPSILON = 1e-15 + 90.0; +static constexpr double MAX_LONGITUDE_EPSILON = 1e-15 + 180.0; + +int32_t PhotoMapCodeOperation::ExecSqlWithRetry(std::function execSql) +{ + int32_t currentTime{0}; + int32_t err = NativeRdb::E_OK; + while (currentTime < MAX_TRY_TIMES) { + err = execSql(); + if (err == NativeRdb::E_OK) { + break; + } else if (err == NativeRdb::E_SQLITE_LOCKED || err == NativeRdb::E_DATABASE_BUSY || + err == NativeRdb::E_SQLITE_BUSY) { + std::this_thread::sleep_for(std::chrono::milliseconds(TRANSACTION_WAIT_INTERVAL)); + currentTime++; + MEDIA_ERR_LOG("PhotoMapCodeOperation::ExecSqlWithRetry execSql busy, err: %{public}d, \ + currentTime: %{public}d", err, currentTime); + } else { + MEDIA_ERR_LOG("PhotoMapCodeOperation::ExecSqlWithRetry execSql failed, err: %{public}d, \ + currentTime: %{public}d", err, currentTime); + break; + } + } + return err; +} + +int32_t PhotoMapCodeOperation::InsertPhotosMapCodes(const std::vector &photoMapDatas, + const std::shared_ptr cloneLibraryRdb) +{ + vector mapValues; + int64_t rowNum{0}; + for (const auto &photoMapData : photoMapDatas) { + double longitude = photoMapData.longitude; + double latitude = photoMapData.latitude; + int32_t fileId = photoMapData.fileId; + NativeRdb::ValuesBucket mapValue; + if (fileId > 0 && fabs(longitude) > DOUBLE_EPSILON && fabs(latitude) > DOUBLE_EPSILON && + fabs(longitude) < MAX_LONGITUDE_EPSILON && fabs(latitude) < MAX_LATITUDE_EPSILON) { + mapValue.PutInt(PhotoMapCodeColumn::MAPCODE_FILE_ID, fileId); + PhotoMapCodeOperation::GetPhotoMapCode(mapValue, latitude, longitude); + mapValues.emplace_back(mapValue); + } + } + + // 地图数据入库 + MEDIA_DEBUG_LOG("RestoreMapCodeUtils::InsertPhotosMapCodes mapValues size %{public}zu \ + photoMapDatas size %{public}zu", mapValues.size(), photoMapDatas.size()); + if (mapValues.empty()) { + MEDIA_INFO_LOG("RestoreMapCodeUtils::InsertPhotosMapCodes mapValues.empty"); + return E_OK; + } + + int32_t ret = E_RDB; + auto rdbStore = Media::MediaLibraryUnistoreManager::GetInstance().GetRdbStore(); + if (rdbStore) { + ret = ExecSqlWithRetry([&]() { + return rdbStore->BatchInsert(rowNum, PhotoMapCodeColumn::PHOTOS_MAP_CODE_TABLE, mapValues); + }); + MEDIA_INFO_LOG("RestoreMapCodeUtils::InsertPhotosMapCodes BatchInsert rdbStore ret %{public}d", ret); + } else if (cloneLibraryRdb) { + ret = ExecSqlWithRetry([&]() { + return cloneLibraryRdb->BatchInsert(rowNum, PhotoMapCodeColumn::PHOTOS_MAP_CODE_TABLE, mapValues); + }); + MEDIA_INFO_LOG("RestoreMapCodeUtils::InsertPhotosMapCodes BatchInsert cloneLibraryRdb ret %{public}d", ret); + } else { + MEDIA_ERR_LOG("RestoreMapCodeUtils::InsertPhotosMapCodes BatchInsert rdbStore & cloneLibraryRdb both \ + null ret %{public}d", ret); + } + + return ret; +} + +int32_t PhotoMapCodeOperation::GetPhotosMapCodesMRS(const std::vector &photoMapDatas, + const std::shared_ptr store) +{ + vector mapValues; + int64_t rowNum{0}; + for (const auto &photoMapData : photoMapDatas) { + double longitude = photoMapData.longitude; + double latitude = photoMapData.latitude; + int32_t fileId = photoMapData.fileId; + NativeRdb::ValuesBucket mapValue; + if (fileId > 0 && fabs(longitude) > DOUBLE_EPSILON && fabs(latitude) > DOUBLE_EPSILON && + fabs(longitude) < MAX_LONGITUDE_EPSILON && fabs(latitude) < MAX_LATITUDE_EPSILON) { + mapValue.PutInt(PhotoMapCodeColumn::MAPCODE_FILE_ID, fileId); + PhotoMapCodeOperation::GetPhotoMapCode(mapValue, latitude, longitude); + mapValues.emplace_back(mapValue); + } + } + + // 地图数据入库 + MEDIA_DEBUG_LOG("RestoreMapCodeUtils::GetPhotosMapCodesMRS mapValues size %{public}zu \ + photoMapDatas size %{public}zu", mapValues.size(), photoMapDatas.size()); + if (mapValues.empty()) { + MEDIA_INFO_LOG("RestoreMapCodeUtils::GetPhotosMapCodesMRS mapValues.empty"); + return E_OK; + } + + int32_t ret = E_RDB; + if (store) { + ret = ExecSqlWithRetry([&]() { + return store->BatchInsert(rowNum, PhotoMapCodeColumn::PHOTOS_MAP_CODE_TABLE, mapValues); + }); + MEDIA_INFO_LOG("RestoreMapCodeUtils::GetPhotosMapCodesMRS BatchInsert rdbStore ret %{public}d", ret); + } else { + MEDIA_ERR_LOG("RestoreMapCodeUtils::GetPhotosMapCodesMRS BatchInsert rdbStore both \ + null ret %{public}d", ret); + } + + return ret; +} + +int32_t PhotoMapCodeOperation::GetPhotoMapCode(const PhotoMapData &photoMapData, const PhotoMapType &photoMapType) +{ + if (photoMapData.fileId <= 0) { + MEDIA_INFO_LOG("PhotoMapCodeOperation::GetPhotoMapCode failed fileId is zero"); + return E_OK; + } + auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore(); + if (!rdbStore) { + MEDIA_ERR_LOG("PhotoMapCodeOperation::GetPhotoMapCode failed rdbStore is null"); + return E_ERR; + } + + int32_t fileId = photoMapData.fileId; + if (photoMapType == PhotoMapType::QUERY_AND_INSERT) { + const std::string QUERY_MAP_CODE_INFO = "SELECT file_id, cell_18 FROM " + + PhotoMapCodeColumn::PHOTOS_MAP_CODE_TABLE + + " WHERE " + PhotoMapCodeColumn::MAPCODE_FILE_ID + " = " + to_string(fileId); + shared_ptr resultSet = rdbStore->QuerySql(QUERY_MAP_CODE_INFO); + int rowCount{-1}; + if (resultSet && resultSet->GetRowCount(rowCount) == NativeRdb::E_OK && rowCount > 0 && + resultSet->GoToFirstRow() == NativeRdb::E_OK) { + MEDIA_ERR_LOG("GetPhotoMapCode success. Query MapCode info in table"); + resultSet->Close(); + return E_OK; + } + if (resultSet) { + resultSet->Close(); + } + } + NativeRdb::ValuesBucket mapValue; + PhotoMapCodeOperation::GetPhotoMapCode(mapValue, photoMapData.latitude, photoMapData.longitude); + int32_t updateMapCount{0}; + std::string whereMapClause = PhotoMapCodeColumn::MAPCODE_FILE_ID + " = ?"; + std::vector whereMapArgs = { to_string(fileId) }; + std::string mapTableName = PhotoMapCodeColumn::PHOTOS_MAP_CODE_TABLE; + int32_t result{-1}; + if (photoMapType == PhotoMapType::UPDATE_AND_INSERT) { + result = rdbStore->Update(updateMapCount, mapTableName, mapValue, whereMapClause, whereMapArgs); + MEDIA_INFO_LOG("GetPhotoMapCode Update result %{public}d updateMapCount %{public}d", result, updateMapCount); + } + if (result != NativeRdb::E_OK || updateMapCount == 0) { + int64_t insertMapCount{-1}; + mapValue.PutInt(PhotoMapCodeColumn::MAPCODE_FILE_ID, fileId); + result = rdbStore->Insert(insertMapCount, mapTableName, mapValue); + MEDIA_INFO_LOG("GetPhotoMapCode Insert result %{public}d \ + insertMapCount %{public}lld", result, insertMapCount); + if (result < 0 || insertMapCount <= 0) { + MEDIA_ERR_LOG("GetPhotoMapCode Ineset failed"); + } + } + return result; +} + +void PhotoMapCodeOperation::GetPhotoMapCode(NativeRdb::ValuesBucket &mapValue, double lat, double lon) +{ + std::vector latAndlon = SetPoint(lat, lon); + for (int level = LEVEL_START; level < LEVEL_COUNT; level++) { + int64_t mapCode = GetMapCode(latAndlon, level); + mapValue.PutLong("cell_" + std::to_string(level), mapCode); + } +} + +int32_t PhotoMapCodeOperation::UpgradePhotoMapCode(const std::shared_ptr store) +{ + MEDIA_INFO_LOG("PhotoMapCodeOperation::UpgradePhotoMapCode"); + if (!store) { + MEDIA_ERR_LOG("UpgradePhotoMapCode failed. store is nullptr"); + return E_ERR; + } + + int32_t startFileId{0}; + int32_t ret{-1}; + // 获取当前数据状态 + const std::string sqlDataCounts = "SELECT COUNT(*) AS count, MIN(file_id) AS min_id, MAX(file_id) AS max_id FROM " + + PhotoColumn::PHOTOS_TABLE + " WHERE " + PhotoColumn::MEDIA_ID + " > " + to_string(startFileId) + " AND " + + PhotoColumn::PHOTO_LATITUDE + " <> 0 AND " + PhotoColumn::PHOTO_LONGITUDE + " <> 0"; + shared_ptr resultSet = store->QuerySql(sqlDataCounts); + int32_t rowCount{0}; + if (resultSet == nullptr) { + MEDIA_ERR_LOG("UpgradePhotoMapCode Failed to query data resultSet nullptr"); + return E_ERR; + } + + if (resultSet->GetRowCount(rowCount) != NativeRdb::E_OK || + rowCount == 0 || resultSet->GoToFirstRow() != NativeRdb::E_OK) { + MEDIA_ERR_LOG("UpgradePhotoMapCode Failed to query data rowCount %{public}d", rowCount); + resultSet->Close(); + return E_OK; + } + + int count{-1}; + resultSet->GetInt(COUNT_INDEX, count); + int minId{-1}; + resultSet->GetInt(MIN_INDEX, minId); + int maxId{-1}; + resultSet->GetInt(MAX_INDEX, maxId); + MEDIA_INFO_LOG("PhotoMapCodeOperation::UpgradePhotoMapCode count %{public}d", count); + resultSet->Close(); + ret = DatasToMapCodes(store, count, minId, maxId); + return ret; +} + + +vector PhotoMapCodeOperation::FilterFileIds(const vector &fileIds) +{ + MEDIA_INFO_LOG("PhotoMapCodeOperation::FilterFileIds fileIds size %{public}zu", fileIds.size()); + // 过滤出所有符合fileId格式的数据 + vector filterFiles; + for (auto it = fileIds.begin(); it != fileIds.end();) { + std::string fileId = (*it); + if (MediaLibraryDataManagerUtils::IsNumber(fileId)) { + filterFiles.push_back(fileId); + } + ++it; + } + + return filterFiles; +} + +int32_t PhotoMapCodeOperation::RemovePhotosMapCodes(const std::vector &fileIds) +{ + vector filterFiles = FilterFileIds(fileIds); + if (filterFiles.empty()) { + MEDIA_ERR_LOG("PhotoMapCodeOperation::RemovePhotosMapCodes filterFiles is empty"); + return E_OK; + } + + auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore(); + if (!rdbStore) { + MEDIA_ERR_LOG("PhotoMapCodeOperation::RemovePhotosMapCodes rdbStore is null"); + return E_ERR; + } + + std::string mapTableName = PhotoMapCodeColumn::PHOTOS_MAP_CODE_TABLE; + NativeRdb::RdbPredicates rdbPredicate(mapTableName); + rdbPredicate.In(PhotoMapCodeColumn::MAPCODE_FILE_ID, filterFiles); + int32_t rows = 0; + int32_t ret = rdbStore->Delete(rows, rdbPredicate); + return ret >= 0 ? E_OK : E_ERR; +} + +int32_t PhotoMapCodeOperation::GetPhotosPoByInputValues(const std::vector &inputValues, + std::vector &photosPos, const std::vector &getValues) +{ + auto rdbStore = Media::MediaLibraryUnistoreManager::GetInstance().GetRdbStore(); + if (!rdbStore) { + MEDIA_ERR_LOG("PhotoMapCodeOperation::GetPhotosPoByInputValues rdbStore is null"); + return E_ERR; + } + NativeRdb::AbsRdbPredicates predicates = NativeRdb::AbsRdbPredicates(PhotoColumn::PHOTOS_TABLE); + predicates.In(PhotoColumn::PHOTO_CLOUD_ID, static_cast>(inputValues)); + predicates.OrderByDesc(PhotoColumn::PHOTO_CLOUD_ID); + predicates.Limit(inputValues.size()); + auto resultSet = rdbStore->Query(predicates, getValues); + CHECK_AND_RETURN_RET_LOG(resultSet != nullptr, E_RESULT_SET_NULL, "DeleteMapCodesByPullDatas Failed to query."); + + int32_t ret = Media::ORM::ResultSetReader(resultSet).ReadRecords(photosPos); + MEDIA_DEBUG_LOG("GetPhotosPoByInputValues photosPos size %{public}zu", photosPos.size()); + return ret; +} + +int32_t PhotoMapCodeOperation::DatasToMapCodesBySetpLevel(const std::shared_ptr store, + int32_t &i, int32_t endIndex, int32_t &deailCount, int stepLevel) +{ + while (i <= endIndex) { + std::vector photoMapDatas; + const std::string sqlUpgradeData = "SELECT file_id, latitude, longitude FROM " + + PhotoColumn::PHOTOS_TABLE + " WHERE " + PhotoColumn::MEDIA_ID + " > " + to_string(i) + " AND " + + PhotoColumn::PHOTO_LATITUDE + " <> 0 AND " + PhotoColumn::PHOTO_LONGITUDE + " <> 0 " + + " ORDER BY " + PhotoColumn::MEDIA_ID + " ASC " + " LIMIT " + to_string(STEP_COUNT * stepLevel); + + shared_ptr resultSet = store->QuerySql(sqlUpgradeData); + int rowCount{-1}; + if (resultSet == nullptr) { + MEDIA_ERR_LOG("PhotoMapCodeOperation::DatasToMapCodesBySetpLevel resultSet nullptr"); + return E_ERR; + } + if (resultSet->GetRowCount(rowCount) != NativeRdb::E_OK || rowCount == 0 || + resultSet->GoToFirstRow() != NativeRdb::E_OK) { + MEDIA_ERR_LOG("PhotoMapCodeOperation::DatasToMapCodesBySetpLevel Data Empty rowCount %{public}d", + rowCount); + resultSet->Close(); + return E_ERR; + } + std::vector photosPos; + Media::ORM::ResultSetReader(resultSet).ReadRecords(photosPos); + resultSet->Close(); + if (photosPos.empty()) { + i++; + continue; + } + for (const auto &photoPo : photosPos) { + if (!(photoPo.longitude.has_value() && photoPo.latitude.has_value() && photoPo.fileId.has_value())) { + i++; + continue; + } + double longitude = photoPo.longitude.value(); + double latitude = photoPo.latitude.value(); + int32_t fileId = photoPo.fileId.value(); + if (fileId > i) { + i = fileId; + deailCount++; + } + if (fileId > 0 && fabs(longitude) > DOUBLE_EPSILON && fabs(latitude) > DOUBLE_EPSILON && + fabs(longitude) < MAX_LONGITUDE_EPSILON && fabs(latitude) < MAX_LATITUDE_EPSILON) { + PhotoMapData photoMapData(fileId, latitude, longitude); + photoMapDatas.emplace_back(photoMapData); + } + } + MEDIA_DEBUG_LOG("UpgradePhotoMapCode photoMapDatas size %{public}zu", photoMapDatas.size()); + GetPhotosMapCodesMRS(photoMapDatas, store); + } + return E_OK; +} + +int32_t PhotoMapCodeOperation::DatasToMapCodes(const std::shared_ptr store, + const int count, const int32_t minId, const int32_t maxId) +{ + MEDIA_INFO_LOG("PhotoMapCodeOperation::DatasToMapCodes start"); + int32_t ret{-1}; + int32_t deailCount{0}; + for (int32_t i = minId - 1; i <= maxId;) { + if (deailCount <= std::min(FAST_COUNT, count)) { + int32_t endIndex = i; + ret = DatasToMapCodesBySetpLevel(store, i, endIndex, deailCount, STEP_LEVEL); + if (ret != E_OK) { + MEDIA_INFO_LOG("DatasToMapCodes less than FAST_COUNT count: %{public}d", count); + return ret; + } + } else { + CpuAffinityType cpuAffinityType = CpuAffinityType::CPU_IDX_3; + Media::CpuUtils::SetSelfThreadAffinity(cpuAffinityType); + ret = DatasToMapCodesBySetpLevel(store, i, maxId, deailCount, 1); + Media::CpuUtils::ResetSelfThreadAffinity(); + MEDIA_INFO_LOG("DatasToMapCodes more than FAST_COUNT count: %{public}d", count); + return ret; + } + } + MEDIA_INFO_LOG("PhotoMapCodeOperation::DatasToMapCodes End"); + return ret; +} + +std::vector PhotoMapCodeOperation::SetPoint(double lat, double lon) +{ + std::vector latAndlon; + latAndlon.push_back(lat); + latAndlon.push_back(lon); + return latAndlon; +} + +int64_t PhotoMapCodeOperation::GetMapCode(std::vector &latAndLon, int level) +{ + if (latAndLon.size() < POINT_SIZE) { + return 0; + } + double lat = latAndLon[0]; + double lon = latAndLon[1]; + MEDIA_DEBUG_LOG("PhotoMapCodeOperation::GetMapCode level %{public}d", level); + return PhotoMapCodeOperation::GetMapHilbertCode(lat, lon, level); +} + +int64_t PhotoMapCodeOperation::GetMapHilbertCode(double lat, double lon, int level) +{ + double latPercent = (lat + 90) / 180.0; + double lonPercent = (lon + 180) / 360.0; + int64_t maxCoord = std::pow(2, std::floor(level)); + MEDIA_DEBUG_LOG("PhotoMapCodeOperation::GetMapHilbertCode latPercent %{public}f, \ + lonPercent %{public}f maxCoord %{public}lld", latPercent, lonPercent, maxCoord); + + int64_t latPosition = std::floor(latPercent * maxCoord) == maxCoord ? + std::floor(latPercent * maxCoord) - 1 : std::floor(latPercent * maxCoord); + int64_t lonPosition = std::floor(lonPercent * maxCoord) == maxCoord ? + std::floor(lonPercent * maxCoord) - 1 : std::floor(lonPercent * maxCoord); + MEDIA_DEBUG_LOG("QXY PhotoMapCodeOperation::GetMapHilbertCode latPosition %{public}lld,\ + lonPosition %{public}lld", latPosition, lonPosition); + + return PhotoMapCodeOperation::DistanceFromPoint(latPosition, lonPosition, std::floor(level)); +} + +std::string PhotoMapCodeOperation::Int64ToBinaryWithPadding(int64_t num, int width) +{ + MEDIA_DEBUG_LOG("PhotoMapCodeOperation::Int64ToBinaryWithPadding num %{public}lld, width %{public}d", num, width); + std::string binary = std::bitset<64>(num).to_string(); + MEDIA_DEBUG_LOG("PhotoMapCodeOperation::Int64ToBinaryWithPadding binary %{public}s", binary.c_str()); + + binary.erase(0, binary.find_first_not_of('0')); + if (binary.empty()) { + binary = "0"; + } + + if (binary.length() < width) { + binary = std::string(width - static_cast(binary.length()), '0') + binary; + } + MEDIA_DEBUG_LOG("PhotoMapCodeOperation::Int64ToBinaryWithPadding binary %{public}s", binary.c_str()); + return binary; +} + +void PhotoMapCodeOperation::UpdateZoomLevelStep(int64_t zoomLevel, std::vector &point) +{ + int64_t zoomLevelStep = zoomLevel; + while (zoomLevelStep > 1) { + int64_t zoomLevelCurrentValue = zoomLevelStep - 1; + for (size_t i = 0; i < point.size(); i++) { + if (point[i] & zoomLevelStep) { + point[0] ^= zoomLevelCurrentValue; + } else { + int64_t flagValue = (point[0] ^ point[i]) & zoomLevelCurrentValue; + point[0] ^= flagValue; + point[i] ^= flagValue; + } + } + zoomLevelStep >>= 1; + } + for (size_t i = 1; i < point.size(); i++) { + point[i] ^= point[i - 1]; + } + + int64_t valueStep = 0; + zoomLevelStep = zoomLevel; + while (zoomLevelStep > 1) { + if (point[point.size() - 1] & zoomLevelStep) { + valueStep ^= zoomLevelStep - 1; + } + zoomLevelStep >>= 1; + } + + for (size_t i = 0; i < point.size(); i++) { + point[i] ^= valueStep; + } +} + +int64_t PhotoMapCodeOperation::DistanceFromPoint(int64_t latPosition, int64_t lonPosition, int level) +{ + MEDIA_DEBUG_LOG("PhotoMapCodeOperation::DistanceFromPoint latPosition %{public}lld, \ + lonPosition %{public}lld level %{public}d", latPosition, lonPosition, level); + int64_t zoomLevel = 1 << (level - 1); + std::vector point = {latPosition, lonPosition}; + + UpdateZoomLevelStep(zoomLevel, point); + + std::vector xBitStr; + for (size_t i = 0; i < point.size(); i++) { + int64_t value = point[i]; + std::string binaryStr = Int64ToBinaryWithPadding(value, level); + xBitStr.push_back(binaryStr); + } + std::string codeStrCode = ""; + for (int i = 0; i < level; i++) { + for (size_t y = 0; y < xBitStr.size(); y++) { + codeStrCode += xBitStr[y][i]; + } + } + MEDIA_DEBUG_LOG("PhotoMapCodeOperation::DistanceFromPoint codeStrCode is %{public}s", codeStrCode.c_str()); + + int64_t hilbertCode = std::bitset<64>(codeStrCode).to_ullong(); + MEDIA_DEBUG_LOG("PhotoMapCodeOperation::DistanceFromPoint hilbertCode is %{public}lld", hilbertCode); + + return hilbertCode; +} +} // namespace Media +} // namespace OHOS diff --git a/frameworks/innerkitsimpl/test/fuzztest/BUILD.gn b/frameworks/innerkitsimpl/test/fuzztest/BUILD.gn index ebda3855b0..39512307f9 100644 --- a/frameworks/innerkitsimpl/test/fuzztest/BUILD.gn +++ b/frameworks/innerkitsimpl/test/fuzztest/BUILD.gn @@ -82,6 +82,9 @@ group("media_library_fuzztest") { "medialibraryuripermissionoperations_fuzzer:MediaLibraryUriPermissionOperationsFuzzTest", "medialibraryurisensitiveoperations_fuzzer:MediaLibraryUriSensitiveOperationsFuzzTest", "medialibraryutil_fuzzer:MediaLibraryUtilFuzzTest", + "medialibrarycloudmediamapcodedao_fuzzer:MediaLibraryCloudMediaMapCodeDaoFuzzTest", + "medialibrarycloudmapcodechecker_fuzzer:MediaLibraryCloudMapCodeCheckerFuzzTest", + "medialibrary_mapcode_fuzzer:MediaLibraryMapCodeFuzzTest", ] if (defined(global_parts_info.hdf_drivers_interface_usb) && diff --git a/frameworks/innerkitsimpl/test/fuzztest/medialibrary_mapcode_fuzzer/BUILD.gn b/frameworks/innerkitsimpl/test/fuzztest/medialibrary_mapcode_fuzzer/BUILD.gn new file mode 100644 index 0000000000..03c4ed9415 --- /dev/null +++ b/frameworks/innerkitsimpl/test/fuzztest/medialibrary_mapcode_fuzzer/BUILD.gn @@ -0,0 +1,130 @@ +# Copyright (c) 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. + +import("//build/test.gni") +import("//foundation/multimedia/media_library/media_library.gni") + +ohos_fuzztest("MediaLibraryMapCodeFuzzTest") { + module_out_path = "media_library/media_library" + fuzz_config_file = "." + + include_dirs = [ + "./include", + "${MEDIALIB_INNERKITS_PATH}/medialibrary_data_extension/include", + "${MEDIALIB_SERVICES_PATH}/media_multistages_capture/include/dfx", + "${MEDIALIB_SERVICES_PATH}/media_analysis_extension/include", + + "${MEDIALIB_CLOUD_SYNC_PATH}/include", + "${MEDIALIB_INTERFACES_PATH}/inner_api/media_library_helper/include", + "${MEDIALIB_INTERFACES_PATH}/kits/js/include", + "${MEDIALIB_UTILS_PATH}/include", + "${MEDIALIB_INNERKITS_PATH}/media_library_helper/include", + "${MEDIALIB_INNERKITS_PATH}/medialibrary_data_extension/include/config", + "${MEDIALIB_INNERKITS_PATH}/medialibrary_data_extension/include/event_handler", + "${MEDIALIB_SERVICES_PATH}/media_backup_extension/include/classify", + "${MEDIALIB_SERVICES_PATH}/media_backup_extension/include/geo_dictionary", + "${MEDIALIB_SERVICES_PATH}/media_backup_extension/include/geo_knowledge", + "${MEDIALIB_SERVICES_PATH}/media_backup_extension/include/highlight", + "${MEDIALIB_SERVICES_PATH}/media_backup_extension/include/log", + "${MEDIALIB_SERVICES_PATH}/media_backup_extension/include/report", + "${MEDIALIB_SERVICES_PATH}/media_backup_extension/include/restore", + "${MEDIALIB_SERVICES_PATH}/media_backup_extension/include", + "${MEDIALIB_SERVICES_PATH}/media_scanner/include/scanner", + + + "${MEDIALIB_INTERFACES_PATH}/innerkits/native/include", + "${MEDIALIB_INNERKITS_PATH}/medialibrary_data_extension/include/operation", + "${MEDIALIB_INNERKITS_PATH}/medialibrary_data_extension/include/photo_album_operation", + "${MEDIALIB_INNERKITS_PATH}/medialibrary_data_extension/include", + "${MEDIALIB_INNERKITS_PATH}/medialibrary_data_extension/include/orm", + "${MEDIALIB_INNERKITS_PATH}/medialibrary_data_extension/include/orm/po", + "${MEDIALIB_SERVICES_PATH}/media_analysis_extension/include", + "${MEDIALIB_SERVICES_PATH}/media_async_worker/include", + "${MEDIALIB_SERVICES_PATH}/media_thumbnail/include", + "${MEDIALIB_SERVICES_PATH}/media_cloud_sync_notify_handle/include", + "${MEDIALIB_SERVICES_PATH}/media_permission/include", + "${MEDIALIB_SERVICES_PATH}/media_power_efficiency/include", + "${MEDIALIB_SERVICES_PATH}/media_visit_count/include", + ] + + sources = [ + "${MEDIALIB_INNERKITS_PATH}/media_library_helper/src/photo_file_utils.cpp", + "${MEDIALIB_SERVICES_PATH}/media_analysis_extension/src/media_analysis_callback_stub.cpp", + "${MEDIALIB_SERVICES_PATH}/media_analysis_extension/src/media_analysis_helper.cpp", + "${MEDIALIB_SERVICES_PATH}/media_analysis_extension/src/media_analysis_proxy.cpp", + "${MEDIALIB_SERVICES_PATH}/media_cloud_sync/src/cloud_sync_utils/cloud_sync_utils.cpp", + "${MEDIALIB_UTILS_PATH}/src/cpu_utils.cpp", + "src/medialibrary_mapcode_fuzzer.cpp", + "${MEDIALIB_INNERKITS_PATH}/medialibrary_data_extension/src/operation/photo_map_code_column.cpp", + "${MEDIALIB_INNERKITS_PATH}/medialibrary_data_extension/src/operation/photo_map_code_operation.cpp", + "${MEDIALIB_SERVICES_PATH}/media_backup_extension/src/restore_map_code_utils.cpp", + "${MEDIALIB_SERVICES_PATH}/media_scanner/src/scanner/scanner_map_code_utils.cpp", + ] + + deps = [ + "${MEDIALIB_INNERKITS_PATH}/media_library_helper:media_library", + "${MEDIALIB_INNERKITS_PATH}/medialibrary_data_extension:medialibrary_data_extension", + ] + + external_deps = [ + "ability_base:configuration", + "ability_base:want", + "ability_base:zuri", + "ability_runtime:ability_context_native", + "ability_runtime:ability_manager", + "ability_runtime:abilitykit_native", + "ability_runtime:app_context", + "ability_runtime:app_manager", + "ability_runtime:dataobs_manager", + "ability_runtime:extensionkit_native", + "ability_runtime:runtime", + "ability_runtime:uri_permission_mgr", + "access_token:libaccesstoken_sdk", + "access_token:libprivacy_sdk", + "c_utils:utils", + "common_event_service:cesfwk_innerkits", + "data_share:datashare_common", + "data_share:datashare_consumer", + "data_share:datashare_provider", + "dfs_service:cloudsync_kit_inner", + "eventhandler:libeventhandler", + "file_api:filemgmt_libn", + "hilog:libhilog", + "hitrace:hitrace_meter", + "image_framework:image_native", + "init:libbegetutil", + "ipc:ipc_core", + "ipc:ipc_single", + "ipc:ipc_napi", + "kv_store:distributeddata_inner", + "napi:ace_napi", + "player_framework:media_client", + "relational_store:native_rdb", + "relational_store:rdb_data_share_adapter", + "safwk:system_ability_fwk", + "samgr:samgr_proxy", + "window_manager:libdm", + + "ability_runtime:napi_base_context", + "background_task_mgr:bgtaskmgr_innerkits", + "bundle_framework:appexecfwk_core", + "e2fsprogs:libext2_uuid", + "ffrt:libffrt", + "hisysevent:libhisysevent", + "i18n:intl_util", + "libxml2:libxml2", + "os_account:libaccountkits", + "os_account:os_account_innerkits", + "zlib:shared_libz", + ] +} diff --git a/frameworks/innerkitsimpl/test/fuzztest/medialibrary_mapcode_fuzzer/corpus/init b/frameworks/innerkitsimpl/test/fuzztest/medialibrary_mapcode_fuzzer/corpus/init new file mode 100644 index 0000000000..6198079a28 --- /dev/null +++ b/frameworks/innerkitsimpl/test/fuzztest/medialibrary_mapcode_fuzzer/corpus/init @@ -0,0 +1,16 @@ +/* + * Copyright (c) 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. + */ + +FUZZ \ No newline at end of file diff --git a/frameworks/innerkitsimpl/test/fuzztest/medialibrary_mapcode_fuzzer/corpus/medialibrary-architecture.png b/frameworks/innerkitsimpl/test/fuzztest/medialibrary_mapcode_fuzzer/corpus/medialibrary-architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..dedea835de6500a2ac050269eb7a51d4a918eb1c GIT binary patch literal 183853 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 literal 0 HcmV?d00001 diff --git a/frameworks/innerkitsimpl/test/fuzztest/medialibrary_mapcode_fuzzer/include/medialibrary_mapcode_fuzzer.h b/frameworks/innerkitsimpl/test/fuzztest/medialibrary_mapcode_fuzzer/include/medialibrary_mapcode_fuzzer.h new file mode 100644 index 0000000000..59d0940a2d --- /dev/null +++ b/frameworks/innerkitsimpl/test/fuzztest/medialibrary_mapcode_fuzzer/include/medialibrary_mapcode_fuzzer.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 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. + */ + +#ifndef MEDIAUTIL_FUZZER_H +#define MEDIAUTIL_FUZZER_H + +#define FUZZ_PROJECT_NAME "medialibrary_mapcode_fuzzer" + +#endif \ No newline at end of file diff --git a/frameworks/innerkitsimpl/test/fuzztest/medialibrary_mapcode_fuzzer/project.xml b/frameworks/innerkitsimpl/test/fuzztest/medialibrary_mapcode_fuzzer/project.xml new file mode 100644 index 0000000000..d6679ccc2e --- /dev/null +++ b/frameworks/innerkitsimpl/test/fuzztest/medialibrary_mapcode_fuzzer/project.xml @@ -0,0 +1,25 @@ + + + + + + 1000 + + 300 + + 4096 + + diff --git a/frameworks/innerkitsimpl/test/fuzztest/medialibrary_mapcode_fuzzer/src/medialibrary_mapcode_fuzzer.cpp b/frameworks/innerkitsimpl/test/fuzztest/medialibrary_mapcode_fuzzer/src/medialibrary_mapcode_fuzzer.cpp new file mode 100644 index 0000000000..768dcf4aaf --- /dev/null +++ b/frameworks/innerkitsimpl/test/fuzztest/medialibrary_mapcode_fuzzer/src/medialibrary_mapcode_fuzzer.cpp @@ -0,0 +1,551 @@ +/* + * Copyright (c) 2023 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. + */ +#include "medialibrary_mapcode_fuzzer.h" + +#include +#include +#include +#include +#define private public +#include "ability_context_impl.h" +#include "app_mgr_interface.h" +#include "cloud_sync_utils.h" +#include "cpu_utils.h" +#include "medialibrary_album_operations.h" +#include "medialibrary_analysis_album_operations.h" +#include "medialibrary_app_uri_permission_operations.h" +#include "medialibrary_appstate_observer.h" +#include "media_log.h" +#include "medialibrary_command.h" +#include "medialibrary_dir_operations.h" +#include "medialibrary_uripermission_operations.h" +#include "medialibrary_data_manager.h" +#include "medialibrary_data_manager_utils.h" +#include "multistages_capture_dfx_first_visit.h" +#include "rdb_predicates.h" +#include "datashare_values_bucket.h" +#include "media_analysis_proxy.h" +#include "media_analysis_helper.h" +#include "background_cloud_file_processor.h" +#include "medialibrary_db_const.h" +#include "scanner_utils.h" +#include "medialibrary_rdbstore.h" +#include "medialibrary_unistore_manager.h" +#include "database_adapter.h" +#include "userfile_manager_types.h" +#include "medialibrary_operation.h" +#include "datashare_helper.h" + +#include "medialibrary_common_utils.h" +#include "permission_utils.h" +#include "photo_file_utils.h" + +#include "photo_map_code_column.h" +#include "photo_map_code_operation.h" +#include "restore_map_code_utils.h" +#include "scanner_map_code_utils.h" +#include "metadata.h" +#include "photos_po.h" +#include "media_column.h" +#include "base_restore.h" +#include "backup_const.h" +#include "values_bucket.h" +#include "medialibrary_type_const.h" +#include "directory_ex.h" +#include "abs_rdb_predicates.h" +#include "photo_album_column.h" +#include "photo_map_column.h" +#include "cloud_media_file_utils.h" +#include "cloud_media_sync_utils.h" +#include "cloud_media_operation_code.h" +#include "moving_photo_file_utils.h" +#include "result_set.h" +#include "result_set_utils.h" +#include "thumbnail_const.h" +#include "result_set_reader.h" +#include "photos_po_writer.h" +#include "photo_album_po_writer.h" +#include "cloud_sync_convert.h" +#include "medialibrary_rdb_transaction.h" +#include "medialibrary_rdb_utils.h" +#include "cloud_media_dao_const.h" +#include "media_gallery_sync_notify.h" +#include "cloud_media_sync_const.h" +#include "cloud_media_dao_utils.h" +#undef private +namespace OHOS { +using namespace std; +const std::string ROOT_MEDIA_DIR = "/storage/cloud/files/"; +const std::string DISPLAY_NAME = "IMG_20250306_202859.jpg"; +const std::string FILE_HIDDEN = ".FileHidden/"; +static const int32_t E_ERR = -1; +const int32_t NUM_BYTES = 1; +static const int32_t MIN_CPU_AFFINITY_TYPE = -1; +static const int32_t MAX_CPU_AFFINITY_TYPE = 11; +static const string PHOTOS_TABLE = "Photos"; +std::shared_ptr g_rdbStore; +FuzzedDataProvider *FDP = nullptr; + +static inline Uri FuzzUri() +{ + return Uri(FDP->ConsumeBytesAsString(NUM_BYTES)); +} + +static inline Media::CpuAffinityType FuzzCpuAffinityType() +{ + int32_t value = FDP->ConsumeIntegralInRange(MIN_CPU_AFFINITY_TYPE, MAX_CPU_AFFINITY_TYPE); + return static_cast(value); +} + +static inline Media::MediaLibraryCommand FuzzMediaLibraryCmd() +{ + return Media::MediaLibraryCommand(FuzzUri()); +} + +static int32_t InsertAsset(string photoId) +{ + if (g_rdbStore == nullptr) { + return E_ERR; + } + NativeRdb::ValuesBucket values; + values.PutString(Media::PhotoColumn::PHOTO_ID, photoId); + values.PutString(Media::MediaColumn::MEDIA_FILE_PATH, FDP->ConsumeBytesAsString(NUM_BYTES)); + values.PutString(Media::PhotoColumn::PHOTO_LAST_VISIT_TIME, FDP->ConsumeBytesAsString(NUM_BYTES)); + int64_t fileId = 0; + g_rdbStore->Insert(fileId, PHOTOS_TABLE, values); + return static_cast(fileId); +} + +void SetTables() +{ + vector createTableSqlList = { + Media::PhotoColumn::CREATE_PHOTO_TABLE, + Media::PhotoMapCodeColumn::CREATE_MAP_CODE_TABLE, + }; + for (auto &createTableSql : createTableSqlList) { + CHECK_AND_RETURN_LOG(g_rdbStore != nullptr, "g_rdbStore is null."); + int32_t ret = g_rdbStore->ExecuteSql(createTableSql); + if (ret != NativeRdb::E_OK) { + MEDIA_ERR_LOG("Execute sql %{private}s failed.", createTableSql.c_str()); + return; + } + MEDIA_DEBUG_LOG("Execute sql %{private}s success.", createTableSql.c_str()); + } +} +static void Init() +{ + auto stageContext = std::make_shared(); + auto abilityContextImpl = std::make_shared(); + abilityContextImpl->SetStageContext(stageContext); + int32_t sceneCode = 0; + auto ret = Media::MediaLibraryDataManager::GetInstance()->InitMediaLibraryMgr(abilityContextImpl, + abilityContextImpl, sceneCode); + CHECK_AND_RETURN_LOG(ret == NativeRdb::E_OK, "InitMediaLibrary Mgr failed, ret: %{public}d.", ret); + auto rdbStore = Media::MediaLibraryUnistoreManager::GetInstance().GetRdbStore(); + if (rdbStore == nullptr) { + MEDIA_ERR_LOG("rdbStore is nullptr."); + return; + } + g_rdbStore = rdbStore; + SetTables(); +} + +static void CommandTest() +{ + NativeRdb::ValuesBucket value; + int32_t operationObject1 = FDP->ConsumeIntegral(); + int32_t operationType1 = FDP->ConsumeIntegral(); + int32_t mediaLibraryApi1 = FDP->ConsumeIntegral(); + Media::MediaLibraryCommand cmd(static_cast(operationObject1), + static_cast(operationType1), static_cast(mediaLibraryApi1)); + cmd.SetTableName(FDP->ConsumeBytesAsString(NUM_BYTES)); + cmd.SetBundleName(FDP->ConsumeBytesAsString(NUM_BYTES)); + cmd.SetDeviceName(FDP->ConsumeBytesAsString(NUM_BYTES)); + cmd.SetResult(FDP->ConsumeBytesAsString(NUM_BYTES)); + int32_t operationObject2 = FDP->ConsumeIntegral(); + cmd.SetOprnObject(static_cast(operationObject2)); + cmd.GetOprnFileId(); + cmd.SetOprnAssetId(FDP->ConsumeBytesAsString(NUM_BYTES)); + DataShare::DataSharePredicates pred; + cmd.SetDataSharePred(pred); + cmd.SetValueBucket(value); + Media::MediaLibraryCommand cmdValueBucket(FuzzUri(), value); + int32_t operationObject3 = FDP->ConsumeIntegral(); + int32_t operationType2 = FDP->ConsumeIntegral(); + int32_t mediaLibraryApi2 = FDP->ConsumeIntegral(); + Media::MediaLibraryCommand cmdValueBucket2(static_cast(operationObject3), + static_cast(operationType2), value, + static_cast(mediaLibraryApi2)); + int32_t operationObject4 = FDP->ConsumeIntegral(); + int32_t operationType3 = FDP->ConsumeIntegral(); + int32_t mediaLibraryApi3 = FDP->ConsumeIntegral(); + Media::MediaLibraryCommand cmdDevice(static_cast(operationObject4), + static_cast(operationType3), FDP->ConsumeBytesAsString(NUM_BYTES), + static_cast(mediaLibraryApi3)); +} + +static void DirOperationTest() +{ + int32_t operationObject = FDP->ConsumeIntegral(); + int32_t operationType = FDP->ConsumeIntegral(); + int32_t mediaLibraryApi = FDP->ConsumeIntegral(); + Media::MediaLibraryCommand cmd(static_cast(operationObject), + static_cast(operationType), static_cast(mediaLibraryApi)); + Media::MediaLibraryDirOperations::HandleDirOperation(cmd); + Media::MediaLibraryDirOperations::CreateDirOperation(cmd); + Media::MediaLibraryDirOperations::TrashDirOperation(cmd); +} + +static void UriPermissionTest() +{ + int32_t operationObject = FDP->ConsumeIntegral(); + int32_t operationType = FDP->ConsumeIntegral(); + int32_t mediaLibraryApi = FDP->ConsumeIntegral(); + Media::MediaLibraryCommand cmd(static_cast(operationObject), + static_cast(operationType), static_cast(mediaLibraryApi)); + NativeRdb::ValuesBucket rdbValueBucket; + rdbValueBucket.Put(Media::PERMISSION_FILE_ID, FDP->ConsumeIntegral()); + rdbValueBucket.Put(Media::PERMISSION_BUNDLE_NAME, FDP->ConsumeBytesAsString(NUM_BYTES)); + rdbValueBucket.Put(Media::PERMISSION_MODE, "r"); + rdbValueBucket.Put(Media::PERMISSION_TABLE_TYPE, FDP->ConsumeBytesAsString(NUM_BYTES)); + cmd.SetValueBucket(rdbValueBucket); + Media::UriPermissionOperations::HandleUriPermOperations(cmd); + Media::UriPermissionOperations::HandleUriPermInsert(cmd); + Media::UriPermissionOperations::InsertBundlePermission(FDP->ConsumeIntegral(), + FDP->ConsumeBytesAsString(NUM_BYTES), FDP->ConsumeBytesAsString(NUM_BYTES), + FDP->ConsumeBytesAsString(NUM_BYTES)); + Media::UriPermissionOperations::DeleteBundlePermission(FDP->ConsumeBytesAsString(NUM_BYTES), + FDP->ConsumeBytesAsString(NUM_BYTES), FDP->ConsumeBytesAsString(NUM_BYTES)); + string mode = "r"; + Media::UriPermissionOperations::CheckUriPermission(FDP->ConsumeBytesAsString(NUM_BYTES), mode); + + Media::UriPermissionOperations::GetUriPermissionMode(FDP->ConsumeBytesAsString(NUM_BYTES), + FDP->ConsumeBytesAsString(NUM_BYTES), FDP->ConsumeIntegral(), mode); + Media::UriPermissionOperations::UpdateOperation(cmd); + Media::UriPermissionOperations::InsertOperation(cmd); + std::vector rdbValues; + Media::UriPermissionOperations::BatchInsertOperation(cmd, rdbValues); + Media::UriPermissionOperations::DeleteOperation(cmd); + std::vector sharedValues; + DataShare::DataShareValuesBucket valueTest1; + valueTest1.Put(Media::AppUriPermissionColumn::FILE_ID, "file_id"); + valueTest1.Put(Media::AppUriPermissionColumn::APP_ID, "appid"); + DataShare::DataShareValuesBucket valueTest2; + valueTest2.Put(Media::AppUriPermissionColumn::FILE_ID, "file_id"); + valueTest2.Put(Media::AppUriPermissionColumn::APP_ID, "appid"); + sharedValues.push_back(valueTest1); + sharedValues.push_back(valueTest2); + Media::UriPermissionOperations::GrantUriPermission(cmd, sharedValues); + Media::UriPermissionOperations::DeleteAllTemporaryAsync(); +} + +static void AnalysisTest() +{ + std::vector columns; + NativeRdb::ValuesBucket values; + DataShare::DataSharePredicates pred; + int32_t operationObject = FDP->ConsumeIntegral(); + int32_t operationType = FDP->ConsumeIntegral(); + int32_t mediaLibraryApi = FDP->ConsumeIntegral(); + Media::MediaLibraryCommand cmd(static_cast(operationObject), + static_cast(operationType), static_cast(mediaLibraryApi)); + cmd.SetTableName("Photos"); + Media::MergeAlbumInfo info1; + info1.albumId = FDP->ConsumeIntegral(); + Media::MergeAlbumInfo info2; + info2.albumId = FDP->ConsumeIntegral(); + std::vector infos; + infos.push_back(info1); + infos.push_back(info2); + Media::MediaLibraryAnalysisAlbumOperations::UpdateMergeGroupAlbumsInfo(infos); + Media::MediaLibraryAnalysisAlbumOperations::HandleGroupPhotoAlbum( + static_cast(FDP->ConsumeIntegral()), values, pred); + Media::MediaLibraryAnalysisAlbumOperations::QueryGroupPhotoAlbum(cmd, columns); + Media::MediaLibraryAnalysisAlbumOperations::UpdateGroupPhotoAlbumById(FDP->ConsumeIntegral()); +} + +static void AppPermissionTest() +{ + std::vector columns; + NativeRdb::RdbPredicates rdbPred("Photos"); + DataShare::DataSharePredicates sharedPred; + int32_t operationObject = FDP->ConsumeIntegral(); + int32_t operationType = FDP->ConsumeIntegral(); + int32_t mediaLibraryApi = FDP->ConsumeIntegral(); + Media::MediaLibraryCommand cmd(static_cast(operationObject), + static_cast(operationType), static_cast(mediaLibraryApi)); + Media::MediaLibraryAppUriPermissionOperations::HandleInsertOperation(cmd); + std::vector sharedValues; + DataShare::DataShareValuesBucket values; + values.Put(Media::AppUriPermissionColumn::FILE_ID, "file_id"); + values.Put(Media::AppUriPermissionColumn::APP_ID, "appid"); + sharedValues.push_back(values); + Media::MediaLibraryAppUriPermissionOperations::BatchInsert(cmd, sharedValues); + Media::MediaLibraryAppUriPermissionOperations::DeleteOperation(rdbPred); + Media::MediaLibraryAppUriPermissionOperations::QueryOperation(sharedPred, columns); +} + +static void AppStateTest() +{ + Media::MedialibraryAppStateObserverManager::GetInstance().SubscribeAppState(); + Media::MedialibraryAppStateObserverManager::GetInstance().UnSubscribeAppState(); +} + +static void MediaLibraryManagerTest() +{ + Media::MediaLibraryDataManagerUtils::IsNumber(FDP->ConsumeBytesAsString(NUM_BYTES)); + Media::MediaLibraryDataManagerUtils::GetOperationType(FDP->ConsumeBytesAsString(NUM_BYTES)); + Media::MediaLibraryDataManagerUtils::GetDisPlayNameFromPath(FDP->ConsumeBytesAsString(NUM_BYTES)); + std::vector whereArgs; + std::string str = FDP->ConsumeBytesAsString(NUM_BYTES); + Media::MediaLibraryDataManagerUtils::ObtionCondition(str, whereArgs); + Media::MediaLibraryDataManagerUtils::GetTypeUriByUri(str); +} + +static void MultistageAdapterTest() +{ + Media::MediaLibraryCommand cmd = FuzzMediaLibraryCmd(); + Media::DatabaseAdapter::Update(cmd); + MEDIA_INFO_LOG("MultistageAdapterTest"); +} + +static void MultistageTest() +{ + std::string photoId = FDP->ConsumeBytesAsString(NUM_BYTES); + int32_t fileId = InsertAsset(photoId); + MEDIA_INFO_LOG("fileId: %{public}d.", fileId); + Media::MultiStagesCaptureDfxFirstVisit::GetInstance().Report(photoId, fileId); + MEDIA_INFO_LOG("MultistageTest"); +} + +static void ActiveAnalysisTest() +{ + std::vector fileIds; + fileIds.push_back("1"); + Media::MediaAnalysisHelper::StartMediaAnalysisServiceSync( + static_cast(Media::MediaAnalysisProxy::ActivateServiceType::START_SERVICE_OCR), fileIds); + Media::MediaAnalysisHelper::StartMediaAnalysisServiceAsync( + static_cast(Media::MediaAnalysisProxy::ActivateServiceType::START_SERVICE_OCR), fileIds); + Media::MediaAnalysisHelper::AsyncStartMediaAnalysisService( + static_cast(Media::MediaAnalysisProxy::ActivateServiceType::START_SERVICE_OCR), fileIds); + Media::MediaAnalysisHelper::StartPortraitCoverSelectionAsync(fileIds.at(0)); + (void)Media::MediaAnalysisHelper::ParseGeoInfo(fileIds, true); +} + +static void CloudDownloadTest() +{ + Media::BackgroundCloudFileProcessor::StartTimer(); + int sleepTime = 200; + std::this_thread::sleep_for(std::chrono::microseconds(sleepTime)); + Media::BackgroundCloudFileProcessor::StopTimer(); +} + +static void CpuUtilsTest() +{ + Media::CpuUtils::SlowDown(); + Media::CpuAffinityType cpuAffinityType = FuzzCpuAffinityType(); + Media::CpuUtils::SetSelfThreadAffinity(cpuAffinityType); + Media::CpuUtils::ResetSelfThreadAffinity(); + Media::CpuUtils::ResetCpu(); +} + +static void CommonUtilsTest() +{ + std::string str = FDP->ConsumeBytesAsString(NUM_BYTES); + Media::MediaLibraryCommonUtils::CanConvertStrToInt32(str); +} + +static void CloudSyncUtilsTest() +{ + Media::CloudSyncUtils::IsUnlimitedTrafficStatusOn(); + Media::CloudSyncUtils::IsCloudSyncSwitchOn(); + Media::CloudSyncUtils::IsCloudDataAgingPolicyOn(); +} + +static void ScannerUtilsTest() +{ + std::string pathOrDisplayName = FDP->ConsumeBool() ? DISPLAY_NAME : FDP->ConsumeBytesAsString(NUM_BYTES); + Media::ScannerUtils::GetFileExtension(pathOrDisplayName); + + std::string path = FDP->ConsumeBool() ? "" : FDP->ConsumeBytesAsString(NUM_BYTES); + Media::ScannerUtils::IsDirectory(path); + Media::ScannerUtils::IsRegularFile(path); + + path = FDP->ConsumeBool() ? FILE_HIDDEN : FDP->ConsumeBytesAsString(NUM_BYTES); + Media::ScannerUtils::IsFileHidden(path); + + std::string dir = FDP->ConsumeBytesAsString(NUM_BYTES); + Media::ScannerUtils::GetRootMediaDir(dir); + + std::string displayName = FDP->ConsumeBytesAsString(NUM_BYTES); + Media::ScannerUtils::GetFileTitle(displayName); + + path = FDP->ConsumeBool() ? FILE_HIDDEN : FDP->ConsumeBytesAsString(NUM_BYTES); + Media::ScannerUtils::IsDirHidden(path, true); + + path = FDP->ConsumeBool() ? ROOT_MEDIA_DIR + "Pictures": FDP->ConsumeBytesAsString(NUM_BYTES); + Media::ScannerUtils::CheckSkipScanList(path); +} + +static void MetadataToMapCodeTest() +{ + Media::Metadata metadata; + metadata.SetFileId(FDP->ConsumeIntegralInRange(MIN_CPU_AFFINITY_TYPE, MAX_CPU_AFFINITY_TYPE)); + Media::ScannerMapCodeUtils::MetadataToMapCode(metadata); +} + +static void DeleteMapCodesByFileIdsMetaTest() +{ + vector fileIds; + fileIds.push_back(FDP->ConsumeBytesAsString(NUM_BYTES)); + Media::ScannerMapCodeUtils::DeleteMapCodesByFileIds(fileIds); +} + +static void FileInfosToMapCodeTest() +{ + std::shared_ptr mediaLibraryRdb = g_rdbStore->GetRaw(); + vector fileInfos; + Media::FileInfo fileInfo; + fileInfo.fileIdNew = FDP->ConsumeIntegral(); + fileInfo.latitude = FDP->ConsumeFloatingPoint(); + fileInfo.longitude = FDP->ConsumeFloatingPoint(); + fileInfos.push_back(fileInfo); + Media::RestoreMapCodeUtils::FileInfosToMapCode(mediaLibraryRdb, fileInfos); +} + +static void FileInfoToMapCodeTest() +{ + Media::FileInfo fileInfo; + fileInfo.fileIdNew = FDP->ConsumeIntegral(); + fileInfo.latitude = FDP->ConsumeFloatingPoint(); + fileInfo.longitude = FDP->ConsumeFloatingPoint(); + std::shared_ptr mediaLibraryRdb = g_rdbStore->GetRaw(); + Media::RestoreMapCodeUtils::FileInfoToMapCode(fileInfo, mediaLibraryRdb); +} + +static void DeleteMapCodesByFileIdsFileTest() +{ + vector fileIds; + fileIds.push_back(FDP->ConsumeRemainingBytesAsString()); + Media::RestoreMapCodeUtils::DeleteMapCodesByFileIds(fileIds); +} + +static void GetPhotosMapCodesTest() +{ + std::vector photoMapDatas; + std::shared_ptr cloneLibraryRdb = g_rdbStore->GetRaw(); + Media::PhotoMapData photoMapData; + photoMapData.longitude = FDP->ConsumeFloatingPoint(); + photoMapData.latitude = FDP->ConsumeFloatingPoint(); + photoMapData.fileId = FDP->ConsumeIntegral(); + photoMapDatas.push_back(photoMapData); + Media::PhotoMapCodeOperation::InsertPhotosMapCodes(photoMapDatas, cloneLibraryRdb); +} + +static void GetPhotosMapCodesMRSTest() +{ + std::vector photoMapDatas; + Media::PhotoMapData photoMapData; + photoMapData.longitude = FDP->ConsumeFloatingPoint(); + photoMapData.latitude = FDP->ConsumeFloatingPoint(); + photoMapData.fileId = FDP->ConsumeIntegral(); + photoMapDatas.push_back(photoMapData); + std::shared_ptr store = g_rdbStore; + Media::PhotoMapCodeOperation::GetPhotosMapCodesMRS(photoMapDatas, store); +} + +static void GetPhotoMapCodeTest() +{ + Media::PhotoMapData photoMapData; + photoMapData.longitude = FDP->ConsumeFloatingPoint(); + photoMapData.latitude = FDP->ConsumeFloatingPoint(); + photoMapData.fileId = FDP->ConsumeIntegral(); + Media::PhotoMapType photoMapType = Media::PhotoMapType(FDP->ConsumeIntegral()); + Media::PhotoMapCodeOperation::GetPhotoMapCode(photoMapData, photoMapType); +} + +static void UpgradePhotoMapCodeTest() +{ + std::shared_ptr store = g_rdbStore; + Media::PhotoMapCodeOperation::UpgradePhotoMapCode(store); +} + +static void RemovePhotosMapCodesTest() +{ + std::vector fileIds; + fileIds.push_back(FDP->ConsumeBytesAsString(NUM_BYTES)); + fileIds.push_back(FDP->ConsumeRemainingBytesAsString()); + Media::PhotoMapCodeOperation::RemovePhotosMapCodes(fileIds); +} + +static void GetPhotosPoByInputValuesTest() +{ + std::vector inputValues; + inputValues = {FDP->ConsumeBytesAsString(NUM_BYTES), FDP->ConsumeRemainingBytesAsString()}; + // 入参 接收容器 + std::vector photosPos; + std::vector getValues; + getValues = {FDP->ConsumeBytesAsString(NUM_BYTES), FDP->ConsumeRemainingBytesAsString()}; + Media::PhotoMapCodeOperation::GetPhotosPoByInputValues(inputValues, photosPos, getValues); +} +} // namespace OHOS + +extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + OHOS::Init(); + return 0; +} + +/* Fuzzer entry point */ +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + /* Run your code on data */ + int sleepTime = 100; + std::this_thread::sleep_for(std::chrono::microseconds(sleepTime)); + FuzzedDataProvider fdp(data, size); + OHOS::FDP = &fdp; + if (data == nullptr) { + return 0; + } + OHOS::CommandTest(); + OHOS::DirOperationTest(); + OHOS::UriPermissionTest(); + OHOS::AnalysisTest(); + OHOS::AppPermissionTest(); + OHOS::AppStateTest(); + OHOS::MediaLibraryManagerTest(); + OHOS::MultistageAdapterTest(); + OHOS::MultistageTest(); + OHOS::ActiveAnalysisTest(); + OHOS::CloudDownloadTest(); + OHOS::CpuUtilsTest(); + OHOS::CommonUtilsTest(); + OHOS::CloudSyncUtilsTest(); + OHOS::ScannerUtilsTest(); + + // 增加新增地图编码场景的测试 + OHOS::MetadataToMapCodeTest(); + OHOS::DeleteMapCodesByFileIdsMetaTest(); + OHOS::FileInfosToMapCodeTest(); + OHOS::FileInfoToMapCodeTest(); + OHOS::DeleteMapCodesByFileIdsFileTest(); + OHOS::GetPhotosMapCodesTest(); + OHOS::GetPhotosMapCodesMRSTest(); + OHOS::GetPhotoMapCodeTest(); + OHOS::UpgradePhotoMapCodeTest(); + OHOS::RemovePhotosMapCodesTest(); + OHOS::GetPhotosPoByInputValuesTest(); + return 0; +} diff --git a/frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmapcodechecker_fuzzer/BUILD.gn b/frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmapcodechecker_fuzzer/BUILD.gn new file mode 100644 index 0000000000..adc0a6fd91 --- /dev/null +++ b/frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmapcodechecker_fuzzer/BUILD.gn @@ -0,0 +1,71 @@ +# Copyright (c) 2025 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. + +import("//build/test.gni") +import("//foundation/multimedia/media_library/media_library.gni") + +ohos_fuzztest("MediaLibraryCloudMapCodeCheckerFuzzTest") { + module_out_path = "media_library/media_library" + fuzz_config_file = "." + + include_dirs = [ + "./include", + "${MEDIALIB_INNERKITS_PATH}/medialibrary_data_extension/include", + "${MEDIALIB_SERVICES_PATH}/media_cloud_sync/include/cloud_dirty_handle", + ] + + sources = [ + "${MEDIALIB_SERVICES_PATH}/media_cloud_sync/src/cloud_dirty_handle/cloud_upload_checker.cpp", + "./medialibrarycloudmapcodechecker_fuzzer.cpp", + "${MEDIALIB_UTILS_PATH}/src/map_code_upload_checker.cpp", + "${MEDIALIB_INNERKITS_PATH}/medialibrary_data_extension/src/operation/photo_map_code_column.cpp", + "${MEDIALIB_INNERKITS_PATH}/medialibrary_data_extension/src/operation/photo_map_code_operation.cpp", + ] + + deps = [ + "${MEDIALIB_INNERKITS_PATH}/media_library_helper:media_library", + "${MEDIALIB_INNERKITS_PATH}/medialibrary_data_extension:medialibrary_data_extension", + ] + + external_deps = [ + "ability_base:configuration", + "ability_base:want", + "ability_base:zuri", + "ability_runtime:ability_context_native", + "ability_runtime:ability_manager", + "ability_runtime:abilitykit_native", + "ability_runtime:app_context", + "ability_runtime:app_manager", + "ability_runtime:dataobs_manager", + "ability_runtime:runtime", + "bundle_framework:appexecfwk_core", + "c_utils:utils", + "common_event_service:cesfwk_innerkits", + "data_share:datashare_common", + "data_share:datashare_consumer", + "data_share:datashare_provider", + "dfs_service:cloudsync_kit_inner", + "hilog:libhilog", + "hitrace:hitrace_meter", + "image_framework:image_native", + "ipc:ipc_single", + "kv_store:distributeddata_inner", + "napi:ace_napi", + "player_framework:media_client", + "preferences:native_preferences", + "relational_store:native_rdb", + "relational_store:rdb_data_share_adapter", + "user_file_service:file_access_extension_ability_kit", + "dfs_service:cloudfile_kit", + ] +} diff --git a/frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmapcodechecker_fuzzer/corpus/init b/frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmapcodechecker_fuzzer/corpus/init new file mode 100644 index 0000000000..14cddafc02 --- /dev/null +++ b/frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmapcodechecker_fuzzer/corpus/init @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2025 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. + */ + +FUZZ diff --git a/frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmapcodechecker_fuzzer/medialibrarycloudmapcodechecker_fuzzer.cpp b/frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmapcodechecker_fuzzer/medialibrarycloudmapcodechecker_fuzzer.cpp new file mode 100644 index 0000000000..6183d83ab8 --- /dev/null +++ b/frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmapcodechecker_fuzzer/medialibrarycloudmapcodechecker_fuzzer.cpp @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2025 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. + */ + +#include "medialibrarycloudmapcodechecker_fuzzer.h" + +#include +#include + +#define private public +#include "cloud_upload_checker.h" +#undef private +#include "ability_context_impl.h" +#include "media_log.h" +#include "medialibrary_command.h" +#include "medialibrary_data_manager.h" +#include "medialibrary_rdbstore.h" +#include "medialibrary_unistore_manager.h" + +#include "map_code_upload_checker.h" +#include "photo_map_code_column.h" +#include "photo_map_code_operation.h" + +namespace OHOS { +using namespace std; +using namespace AbilityRuntime; +static const string PHOTOS_TABLE = "Photos"; +static const int32_t E_ERR = -1; +std::shared_ptr g_rdbStore; +static inline int32_t FuzzInt32(const uint8_t *data, size_t size) +{ + if (data == nullptr || size < sizeof(int32_t)) { + return 0; + } + return static_cast(*data); +} + +static inline int64_t FuzzInt64(const uint8_t *data, size_t size) +{ + if (data == nullptr || size < sizeof(int64_t)) { + return 0; + } + return static_cast(*data); +} + +static inline string FuzzString(const uint8_t *data, size_t size) +{ + return {reinterpret_cast(data), size}; +} + +static int32_t InsertPhotoAsset(const uint8_t *data, size_t size) +{ + if (g_rdbStore == nullptr || data == nullptr || size < sizeof(int32_t) + sizeof(int64_t)) { + return E_ERR; + } + int offset = 0; + NativeRdb::ValuesBucket values; + values.PutString(Media::MediaColumn::MEDIA_FILE_PATH, FuzzString(data, size)); + values.PutInt(Media::PhotoColumn::PHOTO_DIRTY, FuzzInt32(data + offset, size)); + offset += sizeof(int64_t); + values.PutLong(Media::MediaColumn::MEDIA_SIZE, FuzzInt64(data + offset, size)); + int64_t fileId = 0; + g_rdbStore->Insert(fileId, PHOTOS_TABLE, values); + return static_cast(fileId); +} + +static Media::CheckedPhotoInfo FuzzCheckedPhotoInfo(const uint8_t *data, size_t size) +{ + Media::CheckedPhotoInfo checkedPhotoInfo = { + .fileId = InsertPhotoAsset(data, size), + .path = FuzzString(data, size) + }; + return checkedPhotoInfo; +} + +static vector FuzzVectorCheckedPhotoInfo(const uint8_t *data, size_t size) +{ + return {FuzzCheckedPhotoInfo(data, size)}; +} + +static void RepairNoOriginMapCodeTest(const uint8_t *data, size_t size) +{ + Media::CloudUploadChecker::QueryLcdPhotoCount(FuzzInt32(data, size)); + int32_t startFileId = InsertPhotoAsset(data, size); + int32_t curFileId = -1; + Media::CloudUploadChecker::QueryPhotoInfo(startFileId); + Media::CloudUploadChecker::HandlePhotoInfos(FuzzVectorCheckedPhotoInfo(data, size), curFileId); + Media::CloudUploadChecker::RepairNoOriginPhoto(); + + Media::MapCodeUploadChecker::RepairNoMapCodePhoto(); +} + +static void CloudMapCodeCheckerTest(const uint8_t *data, size_t size) +{ + RepairNoOriginMapCodeTest(data, size); +} + +void SetTables() +{ + vector createTableSqlList = { + Media::PhotoColumn::CREATE_PHOTO_TABLE, + Media::PhotoMapCodeColumn::CREATE_MAP_CODE_TABLE, + Media::PhotoAlbumColumns::CREATE_TABLE, + }; + for (auto &createTableSql : createTableSqlList) { + CHECK_AND_RETURN_LOG(g_rdbStore != nullptr, "g_rdbStore is null"); + int32_t ret = g_rdbStore->ExecuteSql(createTableSql); + if (ret != NativeRdb::E_OK) { + MEDIA_ERR_LOG("Execute sql %{private}s failed", createTableSql.c_str()); + return; + } + MEDIA_DEBUG_LOG("Execute sql %{private}s success", createTableSql.c_str()); + } +} + +static void Init() +{ + auto stageContext = std::make_shared(); + auto abilityContextImpl = std::make_shared(); + abilityContextImpl->SetStageContext(stageContext); + int32_t sceneCode = 0; + auto ret = Media::MediaLibraryDataManager::GetInstance()->InitMediaLibraryMgr(abilityContextImpl, + abilityContextImpl, sceneCode); + CHECK_AND_RETURN_LOG(ret == NativeRdb::E_OK, "InitMediaLibraryMgr failed, ret: %{public}d", ret); + + auto rdbStore = Media::MediaLibraryUnistoreManager::GetInstance().GetRdbStore(); + if (rdbStore == nullptr) { + MEDIA_ERR_LOG("rdbStore is nullptr"); + return; + } + g_rdbStore = rdbStore; + SetTables(); +} +} // namespace OHOS + +extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + OHOS::Init(); + return 0; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + OHOS::CloudMapCodeCheckerTest(data, size); + return 0; +} \ No newline at end of file diff --git a/frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmapcodechecker_fuzzer/medialibrarycloudmapcodechecker_fuzzer.h b/frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmapcodechecker_fuzzer/medialibrarycloudmapcodechecker_fuzzer.h new file mode 100644 index 0000000000..279223a20c --- /dev/null +++ b/frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmapcodechecker_fuzzer/medialibrarycloudmapcodechecker_fuzzer.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 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. + */ + +#ifndef NOTIFYCHANGE_FUZZER_H +#define NOTIFYCHANGE_FUZZER_H + +#define FUZZ_PROJECT_NAME "medialibrarycloudmapcodechecker_fuzzer" + +#endif \ No newline at end of file diff --git a/frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmapcodechecker_fuzzer/project.xml b/frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmapcodechecker_fuzzer/project.xml new file mode 100644 index 0000000000..ac512adc94 --- /dev/null +++ b/frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmapcodechecker_fuzzer/project.xml @@ -0,0 +1,25 @@ + + + + + + 128 + + 300 + + 4096 + + diff --git a/frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmediamapcodedao_fuzzer/BUILD.gn b/frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmediamapcodedao_fuzzer/BUILD.gn new file mode 100644 index 0000000000..d8614364c0 --- /dev/null +++ b/frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmediamapcodedao_fuzzer/BUILD.gn @@ -0,0 +1,86 @@ +# Copyright (c) 2025 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. + +import("//build/test.gni") +import("//foundation/multimedia/media_library/media_library.gni") + +ohos_fuzztest("MediaLibraryCloudMediaMapCodeDaoFuzzTest") { + module_out_path = "media_library/media_library" + fuzz_config_file = "." + + include_dirs = [ + "./include", + "${MEDIALIB_INNERKITS_PATH}/medialibrary_data_extension/include", + "${MEDIALIB_SERVICES_PATH}/inner_api/native/cloud_sync", + "${MEDIALIB_INTERFACES_PATH}/kits/js/include", + "${MEDIALIB_INNERKITS_PATH}/media_library_cloud_sync/include", + ] + + sources = [ + "${MEDIALIB_CLOUD_SYNC_SERVICE_PATH}/src/dao/cloud_media_common_dao.cpp", + "${MEDIALIB_CLOUD_SYNC_SERVICE_PATH}/src/dao/cloud_media_photos_dao.cpp", + "${MEDIALIB_CLOUD_SYNC_SERVICE_PATH}/src/dto/cloud_file_data_dto.cpp", + "${MEDIALIB_CLOUD_SYNC_SERVICE_PATH}/src/dto/photo_album_dto.cpp", + "${MEDIALIB_CLOUD_SYNC_SERVICE_PATH}/src/dto/photos_dto.cpp", + "${MEDIALIB_CLOUD_SYNC_SERVICE_PATH}/src/utils/cloud_media_file_utils.cpp", + "${MEDIALIB_CLOUD_SYNC_SERVICE_PATH}/src/utils/cloud_media_dao_utils.cpp", + "${MEDIALIB_CLOUD_SYNC_SERVICE_PATH}/src/utils/cloud_media_sync_utils.cpp", + "${MEDIALIB_CLOUD_SYNC_SERVICE_PATH}/src/utils/cloud_sync_convert.cpp", + "${MEDIALIB_CLOUD_SYNC_SERVICE_PATH}/src/utils/media_gallery_sync_notify.cpp", + "${MEDIALIB_JS_PATH}/src/userfile_client.cpp", + "./medialibrarycloudmediamapcodedao_fuzzer.cpp", + "${MEDIALIB_CLOUD_SYNC_SERVICE_PATH}/src/dao/cloud_map_code_dao.cpp", + "${MEDIALIB_INNERKITS_PATH}/medialibrary_data_extension/src/operation/photo_map_code_column.cpp", + "${MEDIALIB_INNERKITS_PATH}/medialibrary_data_extension/src/operation/photo_map_code_operation.cpp", + ] + + deps = [ + "${MEDIALIB_INNERKITS_PATH}/media_library_cloud_sync:media_library_cloud_sync", + "${MEDIALIB_INNERKITS_PATH}/media_library_helper:media_library", + "${MEDIALIB_INNERKITS_PATH}/medialibrary_data_extension:medialibrary_data_extension", + "${MEDIALIB_INTERFACES_PATH}/kits/js:medialibrary_nutils", + ] + + external_deps = [ + "ability_base:configuration", + "ability_base:want", + "ability_base:zuri", + "ability_runtime:ability_context_native", + "ability_runtime:ability_manager", + "ability_runtime:abilitykit_native", + "ability_runtime:app_context", + "ability_runtime:app_manager", + "ability_runtime:dataobs_manager", + "ability_runtime:napi_base_context", + "ability_runtime:runtime", + "access_token:libtokenid_sdk", + "bundle_framework:appexecfwk_core", + "c_utils:utils", + "common_event_service:cesfwk_innerkits", + "data_share:datashare_common", + "data_share:datashare_consumer", + "data_share:datashare_provider", + "dfs_service:cloudsync_kit_inner", + "hilog:libhilog", + "hitrace:hitrace_meter", + "image_framework:image_native", + "ipc:ipc_napi", + "ipc:ipc_single", + "kv_store:distributeddata_inner", + "napi:ace_napi", + "player_framework:media_client", + "relational_store:native_rdb", + "relational_store:rdb_data_share_adapter", + "dfs_service:cloudfile_kit", + ] +} diff --git a/frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmediamapcodedao_fuzzer/corpus/init b/frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmediamapcodedao_fuzzer/corpus/init new file mode 100644 index 0000000000..14cddafc02 --- /dev/null +++ b/frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmediamapcodedao_fuzzer/corpus/init @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2025 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. + */ + +FUZZ diff --git a/frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmediamapcodedao_fuzzer/medialibrarycloudmediamapcodedao_fuzzer.cpp b/frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmediamapcodedao_fuzzer/medialibrarycloudmediamapcodedao_fuzzer.cpp new file mode 100644 index 0000000000..5dbc5c2f6b --- /dev/null +++ b/frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmediamapcodedao_fuzzer/medialibrarycloudmediamapcodedao_fuzzer.cpp @@ -0,0 +1,681 @@ +/* + * Copyright (c) 2025 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. + */ + +#include "medialibrarycloudmediamapcodedao_fuzzer.h" + +#include +#include +#include +#define private public +#include "cloud_media_photos_dao.h" +#undef private +#include "ability_context_impl.h" +#include "media_log.h" +#include "medialibrary_command.h" +#include "medialibrary_data_manager.h" +#include "medialibrary_rdbstore.h" +#include "medialibrary_type_const.h" +#include "medialibrary_unistore_manager.h" +#include "medialibrary_db_const.h" +#include "photo_map_column.h" + +#include "cloud_map_code_dao.h" + +namespace OHOS { +using namespace std; +using namespace OHOS::Media; +using namespace OHOS::Media::CloudSync; +const int32_t FILEID = 1; +const int32_t NUM_BYTES = 1; +const int32_t INT32_COUNT = 27; +const uint32_t UINT32_COUNT = 2; +const int64_t INT64_COUNT = 15; +const int8_t BOOL_COUNT = 15; +const int8_t STRING_COUNT = 16; +const int MIN_SIZE = sizeof(int32_t) * INT32_COUNT + sizeof(int64_t) * INT64_COUNT + + sizeof(int8_t) * (BOOL_COUNT + STRING_COUNT) + sizeof(uint32_t) * UINT32_COUNT; +const string PHOTOMAP_TABLE = "PhotoMap"; +const string PHOTOS_TABLE = "Photos"; +const string ALBUM_TABLE = "PhotoAlbum"; +std::shared_ptr g_rdbStore; +FuzzedDataProvider* FDP; +std::shared_ptr cloudMediaPhotosDao = std::make_shared(); + +std::shared_ptr cloudMediaMapCodeDao = std::make_shared(); + +static inline Media::DirtyTypes FuzzDirtyTypes() +{ + int32_t value = FDP->ConsumeIntegral() % 8; + if (value >= static_cast(Media::DirtyTypes::TYPE_SYNCED) && + value <= static_cast(Media::DirtyTypes::TYPE_COPY)) { + return static_cast(value); + } + return Media::DirtyTypes::TYPE_SDIRTY; +} + +static inline Media::CleanType FuzzCleanType() +{ + return FDP->ConsumeBool() ? Media::CleanType::TYPE_NEED_CLEAN : Media::CleanType::TYPE_NOT_CLEAN; +} + +static inline Media::DirtyType FuzzDirtyType() +{ + int32_t value = FDP->ConsumeIntegral() % 8; + if (value >= static_cast(Media::DirtyType::TYPE_SYNCED) && + value <= static_cast(Media::DirtyType::TYPE_COPY)) { + return static_cast(value); + } + return Media::DirtyType::TYPE_RETRY; +} + +static inline Media::CloudFilePosition FuzzCloudFilePosition() +{ + int32_t value = FDP->ConsumeIntegral() % 2; + if (value >= static_cast(Media::CloudFilePosition::POSITION_LOCAL) && + value <= static_cast(Media::CloudFilePosition::POSITION_CLOUD)) { + return static_cast(value); + } + return Media::CloudFilePosition::POSITION_LOCAL; +} + +static inline Media::CloudSync::PhotoPosition FuzzPhotoPosition() +{ + int32_t value = FDP->ConsumeIntegral() % 3; + if (value >= static_cast(Media::CloudSync::PhotoPosition::POSITION_LOCAL) && + value <= static_cast(Media::CloudSync::PhotoPosition::POSITION_BOTH)) { + return static_cast(value); + } + return Media::CloudSync::PhotoPosition::POSITION_BOTH; +} + +static inline Media::SyncStatusType FuzzSyncStatusType() +{ + int32_t value = FDP->ConsumeIntegral() % 2; + if (value >= static_cast(Media::SyncStatusType::TYPE_BACKUP) && + value <= static_cast(Media::SyncStatusType::TYPE_UPLOAD)) { + return static_cast(value); + } + return Media::SyncStatusType::TYPE_VISIBLE; +} + +static CloudMediaPullDataDto FuzzCloudMediaPullDataDto() +{ + CloudMediaPullDataDto pullData; + pullData.attributesTitle = FDP->ConsumeBool() ? FDP->ConsumeBytesAsString(NUM_BYTES) : ""; + pullData.hasProperties = true; + pullData.propertiesSourceFileName = "." + FDP->ConsumeBytesAsString(NUM_BYTES); + pullData.basicFileType = FDP->ConsumeBool() ? FILE_TYPE_LIVEPHOTO : FILE_TYPE_VIDEO; + pullData.basicEditedTime = FDP->ConsumeIntegral(); + pullData.basicFileName = "IMG_20250425_123456.jpg"; + pullData.cloudId = "3d4970270f8d4b15b4ced48bd7f25dd44c7ad693ae57426d863fec74422b328e"; + pullData.propertiesSourcePath = FDP->ConsumeBool() ? "/Pictures/Screenshots/DCIM/Camera" : ""; + pullData.hasAttributes = true; + pullData.attributesMediaType = FDP->ConsumeIntegral(); + pullData.duration = FDP->ConsumeIntegral(); + pullData.attributesHidden = FDP->ConsumeIntegral(); + pullData.attributesHiddenTime = FDP->ConsumeIntegral(); + pullData.attributesRelativePath = FDP->ConsumeBytesAsString(NUM_BYTES); + pullData.attributesVirtualPath = FDP->ConsumeBytesAsString(NUM_BYTES); + pullData.attributesMetaDateModified = FDP->ConsumeIntegral(); + pullData.attributesSubtype = FDP->ConsumeIntegral(); + pullData.attributesBurstCoverLevel = FDP->ConsumeIntegral(); + pullData.attributesBurstKey = FDP->ConsumeBytesAsString(NUM_BYTES); + pullData.attributesDateYear = FDP->ConsumeBytesAsString(NUM_BYTES); + pullData.attributesDateMonth = FDP->ConsumeBytesAsString(NUM_BYTES); + pullData.attributesDateDay = FDP->ConsumeBytesAsString(NUM_BYTES); + pullData.attributesShootingMode = FDP->ConsumeBytesAsString(NUM_BYTES); + pullData.attributesShootingModeTag = FDP->ConsumeBytesAsString(NUM_BYTES); + pullData.attributesDynamicRangeType = FDP->ConsumeIntegral(); + pullData.attributesFrontCamera = FDP->ConsumeBytesAsString(NUM_BYTES); + pullData.attributesEditTime = FDP->ConsumeIntegral(); + pullData.attributesOriginalSubtype = FDP->ConsumeIntegral(); + pullData.attributesCoverPosition = FDP->ConsumeIntegral(); + pullData.attributesMovingPhotoEffectMode = FDP->ConsumeIntegral(); + pullData.attributesSupportedWatermarkType = FDP->ConsumeIntegral(); + pullData.attributesStrongAssociation = FDP->ConsumeIntegral(); + pullData.attributesFileId = FILEID; + return pullData; +} + +static int32_t InsertPhotoAsset() +{ + if (g_rdbStore == nullptr) { + return E_ERR; + } + NativeRdb::ValuesBucket values; + values.PutString(PhotoColumn::PHOTO_CLOUD_ID, "3d4970270f8d4b15b4ced48bd7f25dd44c7ad693ae57426d863fec74422b338e"); + values.PutInt(PhotoColumn::PHOTO_DIRTY, static_cast(FuzzDirtyTypes())); + values.PutInt(PhotoColumn::PHOTO_POSITION, static_cast(FuzzCloudFilePosition())); + values.PutInt(PhotoColumn::PHOTO_THUMBNAIL_READY, static_cast(ThumbnailReady::GENERATE_THUMB_COMPLETED)); + values.PutInt(PhotoColumn::PHOTO_LCD_VISIT_TIME, static_cast(LcdReady::GENERATE_LCD_COMPLETED)); + values.PutLong(PhotoColumn::MEDIA_DATE_TRASHED, 0); + values.PutLong(PhotoColumn::MEDIA_TIME_PENDING, 0); + values.PutString(PhotoColumn::MEDIA_NAME, "IMG_20250425_123456.jpg"); + int64_t fileId = 0; + g_rdbStore->Insert(fileId, PHOTOS_TABLE, values); + return static_cast(fileId); +} + +static int32_t UpdatePhotoAsset() +{ + if (g_rdbStore == nullptr) { + return E_ERR; + } + NativeRdb::ValuesBucket values; + values.PutString(PhotoColumn::PHOTO_CLOUD_ID, "3d4970270f8d4b15b4ced48bd7f25dd44c7ad693ae57426d863fec74422b358e"); + values.PutInt(PhotoColumn::PHOTO_CLEAN_FLAG, static_cast(FuzzCleanType())); + values.PutInt(PhotoColumn::PHOTO_DIRTY, static_cast(FuzzDirtyType())); + values.PutInt(PhotoColumn::PHOTO_POSITION, FuzzPhotoPosition()); + values.PutLong(PhotoColumn::PHOTO_CLOUD_VERSION, 0); + values.PutInt(PhotoColumn::PHOTO_SYNC_STATUS, static_cast(FuzzSyncStatusType())); + values.PutNull(PhotoColumn::PHOTO_ORIGINAL_ASSET_CLOUD_ID); + values.PutLong(PhotoColumn::PHOTO_META_DATE_MODIFIED, FDP->ConsumeIntegral()); + int64_t fileId = 0; + g_rdbStore->Insert(fileId, PHOTOS_TABLE, values); + return static_cast(fileId); +} + +static int32_t QueryPhotoAsset() +{ + if (g_rdbStore == nullptr) { + return E_ERR; + } + NativeRdb::ValuesBucket values; + values.PutInt(PhotoColumn::MEDIA_SIZE, FDP->ConsumeIntegral()); + values.PutInt(PhotoColumn::PHOTO_OWNER_ALBUM_ID, FDP->ConsumeIntegral()); + values.PutInt(PhotoColumn::MEDIA_HIDDEN, FDP->ConsumeBool()); + values.PutInt(PhotoColumn::PHOTO_ORIENTATION, FDP->ConsumeIntegral()); + values.PutString(PhotoColumn::MEDIA_NAME, "IMG_20250425_123456.jpg"); + int64_t fileId = 0; + g_rdbStore->Insert(fileId, PHOTOS_TABLE, values); + return static_cast(fileId); +} + +static int32_t QueryPhotomapAsset() +{ + if (g_rdbStore == nullptr) { + return E_ERR; + } + NativeRdb::ValuesBucket values; + values.PutInt(PhotoMap::ASSET_ID, 1); + int64_t fileId = 0; + g_rdbStore->Insert(fileId, PHOTOMAP_TABLE, values); + return static_cast(fileId); +} + +static int32_t QueryAlbumAsset() +{ + if (g_rdbStore == nullptr) { + return E_ERR; + } + NativeRdb::ValuesBucket values; + values.PutInt(PhotoAlbumColumns::ALBUM_TYPE, static_cast(PhotoAlbumType::USER)); + values.PutInt(PhotoAlbumColumns::ALBUM_DIRTY, static_cast(DirtyType::TYPE_NEW)); + values.PutString(PhotoAlbumColumns::ALBUM_CLOUD_ID, HIDDEN_ALBUM); + values.PutString(PhotoAlbumColumns::ALBUM_LPATH, "records"); + int64_t fileId = 0; + g_rdbStore->Insert(fileId, ALBUM_TABLE, values); + return static_cast(fileId); +} + +static int32_t InsertDeleteAsset() +{ + if (g_rdbStore == nullptr) { + return E_ERR; + } + NativeRdb::ValuesBucket values; + values.PutString(Media::PhotoColumn::PHOTO_CLOUD_ID, + "3d4970270f8d4b15b4ced48bd7f25dd44c7ad693ae57426d863fec74422b368e"); + values.PutString(MediaColumn::MEDIA_FILE_PATH, FDP->ConsumeBytesAsString(NUM_BYTES)); + int64_t fileId = 0; + g_rdbStore->Insert(fileId, PHOTOS_TABLE, values); + return static_cast(fileId); +} + +static void BatchInsertFileFuzzer() +{ + auto photoRefresh = std::make_shared(); + std::map recordAnalysisAlbumMaps; + recordAnalysisAlbumMaps.insert(std::make_pair(PhotoMap::ALBUM_ID, FDP->ConsumeIntegral())); + + std::map> recordAlbumMaps; + recordAlbumMaps.insert(std::make_pair(PhotoMap::ALBUM_ID, std::set({FDP->ConsumeIntegral()}))); + recordAlbumMaps.insert(std::make_pair(PhotoMap::DIRTY, + std::set({static_cast(FuzzDirtyTypes())}))); + + NativeRdb::ValuesBucket valuesBucket; + valuesBucket.PutString(MediaColumn::MEDIA_FILE_PATH, FDP->ConsumeBytesAsString(NUM_BYTES)); + std::vector insertFiles; + cloudMediaPhotosDao->BatchInsertFile(recordAnalysisAlbumMaps, recordAlbumMaps, insertFiles, photoRefresh); + + insertFiles.push_back(valuesBucket); + InsertPhotoAsset(); + cloudMediaPhotosDao->BatchInsertFile(recordAnalysisAlbumMaps, recordAlbumMaps, insertFiles, photoRefresh); +} + +static void UpdateRecordToDatabaseFuzzer() +{ + auto photoRefresh = std::make_shared(); + CloudMediaPullDataDto pullData = FuzzCloudMediaPullDataDto(); + bool isLocal = true; + bool mtimeChanged = FDP->ConsumeBool(); + std::set refreshAlbums; + int32_t number = 5; + std::vector stats(number, 0); + UpdatePhotoAsset(); + QueryPhotomapAsset(); + cloudMediaPhotosDao->UpdateRecordToDatabase(pullData, isLocal, mtimeChanged, refreshAlbums, stats, photoRefresh); +} + +static void ConflictDataMergeFuzzer() +{ + auto photoRefresh = std::make_shared(); + CloudMediaPullDataDto pullData = FuzzCloudMediaPullDataDto(); + std::string fullPath; + bool cloudStd = FDP->ConsumeBool(); + std::set albumIds; + std::set refreshAlbums; + cloudMediaPhotosDao->ConflictDataMerge(pullData, fullPath, cloudStd, albumIds, refreshAlbums, photoRefresh); +} + +static void GetInsertParamsFuzzer() +{ + CloudMediaPullDataDto pullData = FuzzCloudMediaPullDataDto(); + std::map recordAnalysisAlbumMaps; + std::map> recordAlbumMaps; + std::set refreshAlbums; + std::vector insertFiles; + cloudMediaPhotosDao->GetInsertParams(pullData, recordAnalysisAlbumMaps, recordAlbumMaps, + refreshAlbums, insertFiles); +} + +static void BatchQueryLocalFuzzer() +{ + vector datas; + CloudMediaPullDataDto pullData = FuzzCloudMediaPullDataDto(); + datas.emplace_back(pullData); + std::vector columns; + int32_t rowCount = -1; + cloudMediaPhotosDao->BatchQueryLocal(datas, columns, rowCount); +} + +static void GetLocalKeyDataFuzzer() +{ + KeyData localKeyData; + QueryPhotoAsset(); + NativeRdb::AbsRdbPredicates predicates = NativeRdb::AbsRdbPredicates(PhotoColumn::PHOTOS_TABLE); + predicates.EqualTo(PhotoColumn::MEDIA_NAME, "IMG_20250425_123456.jpg"); + std::vector columns; + columns.emplace_back(PhotoColumn::PHOTO_OWNER_ALBUM_ID); + auto resultSet = g_rdbStore->Query(predicates, columns); + CHECK_AND_RETURN_LOG(resultSet != nullptr, "Failed to query."); + QueryAlbumAsset(); + cloudMediaPhotosDao->GetLocalKeyData(localKeyData, resultSet); +} + +static void JudgeConflictFuzzer() +{ + CloudMediaPullDataDto pullData = FuzzCloudMediaPullDataDto(); + KeyData localKeyData; + KeyData cloudKeyData; + cloudMediaPhotosDao->JudgeConflict(pullData, localKeyData, cloudKeyData); + + pullData.attributesSrcAlbumIds.emplace_back("default-album-4"); + cloudMediaPhotosDao->JudgeConflict(pullData, localKeyData, cloudKeyData); + + localKeyData.sourceAlbum = "default-album-4"; + localKeyData.lPath = FDP->ConsumeBool() ? "localKeyData_lPath" : "lPath"; + cloudKeyData.lPath = FDP->ConsumeBool() ? "cloudKeyData_lPath" : "lPath"; + cloudMediaPhotosDao->JudgeConflict(pullData, localKeyData, cloudKeyData); + + localKeyData.isize = FDP->ConsumeIntegral(); + cloudKeyData.isize = FDP->ConsumeIntegral(); + cloudMediaPhotosDao->JudgeConflict(pullData, localKeyData, cloudKeyData); + + localKeyData.lPath = FDP->ConsumeBool() ? "localKeyData_lPath" : "lPath"; + cloudKeyData.lPath = FDP->ConsumeBool() ? "cloudKeyData_lPath" : "lPath"; + cloudMediaPhotosDao->JudgeConflict(pullData, localKeyData, cloudKeyData); + + localKeyData.displayName = "localKeyData_displayName"; + cloudKeyData.displayName = "cloudKeyData_displayName"; + cloudMediaPhotosDao->JudgeConflict(pullData, localKeyData, cloudKeyData); +} + +static void GetRecordsFuzzer() +{ + std::vector cloudIds; + InsertPhotoAsset(); + cloudMediaPhotosDao->GetRetryRecords(cloudIds); + cloudMediaPhotosDao->GetCheckRecords(cloudIds); + string cloudId = "3d4970270f8d4b15b4ced48bd7f25dd44c7ad693ae57426d863fec74422b378e"; + cloudIds.emplace_back(cloudId); + cloudIds.emplace_back(FDP->ConsumeBytesAsString(NUM_BYTES)); + cloudMediaPhotosDao->GetRetryRecords(cloudIds); + cloudMediaPhotosDao->GetCheckRecords(cloudIds); + + int32_t querySize = FDP->ConsumeIntegral() & 0xf; + std::vector createdRecords; + std::vector cloudRecordPoList; + std::vector copyRecords; + cloudMediaPhotosDao->GetCreatedRecords(querySize, createdRecords); + cloudMediaPhotosDao->GetMetaModifiedRecords(querySize, cloudRecordPoList); + cloudMediaPhotosDao->GetFileModifiedRecords(querySize, cloudRecordPoList); + cloudMediaPhotosDao->GetCopyRecords(querySize, copyRecords); +} + +static void GetDeletedRecordsAssetFuzzer() +{ + int32_t limitSize = FDP->ConsumeBool() ? FDP->ConsumeIntegral() : -1; + std::vector cloudRecordPoList; + InsertPhotoAsset(); + cloudMediaPhotosDao->GetDeletedRecordsAsset(limitSize, cloudRecordPoList); +} + +static void GetPhotoLocalInfoFuzzer() +{ + std::vector records; + PhotosDto photo; + photo.cloudId = "3d4970270f8d4b15b4ced48bd7f25dd44c7ad693ae57426d863fec74422b388e"; + photo.fileId = FDP->ConsumeIntegral(); + records.emplace_back(photo); + std::unordered_map infoMap; + std::string type = FDP->ConsumeBool() ? PhotoColumn::PHOTO_CLOUD_ID : PhotoColumn::PHOTO_CLOUD_VERSION; + InsertPhotoAsset(); + cloudMediaPhotosDao->GetPhotoLocalInfo(records, infoMap, type); +} + +static void UpdateLocalAlbumMapFuzzer() +{ + std::string cloudId = "3d4970270f8d4b15b4ced48bd7f25dd44c7ad693ae57426d863fec74422b398e"; + InsertPhotoAsset(); + cloudMediaPhotosDao->UpdateLocalAlbumMap(cloudId); +} + +static void DeleteSameNamePhotoFuzzer() +{ + PhotosDto photo; + photo.fileId = FDP->ConsumeIntegral() & 0xf; + InsertPhotoAsset(); + cloudMediaPhotosDao->DeleteSameNamePhoto(photo); +} + +static void GetSameNamePhotoCountFuzzer() +{ + PhotosDto photo; + photo.displayName = "IMG_20250425_123456.jpg"; + photo.size = FDP->ConsumeIntegral() & 0xff; + photo.ownerAlbumId = FDP->ConsumeIntegral() & 0xf; + photo.mediaType = MediaType::MEDIA_TYPE_IMAGE; + photo.rotation = FDP->ConsumeIntegral() & 0xf; + bool isHide = FDP->ConsumeBool(); + int32_t count = 1; + QueryPhotoAsset(); + cloudMediaPhotosDao->GetSameNamePhotoCount(photo, isHide, count); +} + +static void UpdatePhotoCreatedRecordFuzzer() +{ + auto photoRefresh = std::make_shared(); + PhotosDto record; + record.fileId = FDP->ConsumeIntegral() & 0xf; + record.cloudId = "3d4970270f8d4b15b4ced48bd7f25dd44c7ad693ae57426d863fec74422b408e"; + record.version = FDP->ConsumeIntegral() & 0xf; + std::unordered_map localMap; + UpdatePhotoAsset(); + cloudMediaPhotosDao->UpdatePhotoCreatedRecord(record, localMap, photoRefresh); +} + +static void OnModifyPhotoRecordFuzzer() +{ + auto photoRefresh = std::make_shared(); + PhotosDto record; + cloudMediaPhotosDao->OnModifyPhotoRecord(record, photoRefresh); + + record.cloudId = "3d4970270f8d4b15b4ced48bd7f25dd44c7ad693ae57426d863fec74422b418e"; + record.version = FDP->ConsumeIntegral(); + UpdatePhotoAsset(); + cloudMediaPhotosDao->OnModifyPhotoRecord(record, photoRefresh); +} + +static void UpdateFdirtyVersionFuzzer() +{ + auto photoRefresh = std::make_shared(); + PhotosDto record; + record.cloudId = "3d4970270f8d4b15b4ced48bd7f25dd44c7ad693ae57426d863fec74422b428e"; + record.version = FDP->ConsumeIntegral(); + UpdatePhotoAsset(); + cloudMediaPhotosDao->UpdateFdirtyVersion(record, photoRefresh); +} + +static void OnDeleteRecordsAssetFuzzer() +{ + auto photoRefresh = std::make_shared(); + PhotosDto record; + record.cloudId = "3d4970270f8d4b15b4ced48bd7f25dd44c7ad693ae57426d863fec74422b438e"; + record.version = FDP->ConsumeIntegral(); + UpdatePhotoAsset(); + cloudMediaPhotosDao->OnDeleteRecordsAsset(record, photoRefresh); +} + +static void OnCopyPhotoRecordFuzzer() +{ + auto photoRefresh = std::make_shared(); + PhotosDto record; + record.cloudId = "3d4970270f8d4b15b4ced48bd7f25dd44c7ad693ae57426d863fec74422b448e"; + record.fileId = FDP->ConsumeIntegral() & 0xf; + record.path = FDP->ConsumeBytesAsString(NUM_BYTES); + record.version = FDP->ConsumeIntegral() & 0xf; + record.cloudVersion = 0; + UpdatePhotoAsset(); + cloudMediaPhotosDao->OnCopyPhotoRecord(record, photoRefresh); +} + +static void ClearCloudInfoFuzzer() +{ + std::string cloudId = "3d4970270f8d4b15b4ced48bd7f25dd44c7ad693ae57426d863fec74422b458e"; + UpdatePhotoAsset(); + cloudMediaPhotosDao->ClearCloudInfo(cloudId); +} + +static void DeleteFileNotExistPhotoFuzzer() +{ + auto photoRefresh = std::make_shared(); + std::string path; + InsertDeleteAsset(); + cloudMediaPhotosDao->DeleteFileNotExistPhoto(path, photoRefresh); +} + +static void HandleSameNameRenameFuzzer() +{ + auto photoRefresh = std::make_shared(); + PhotosDto photo; + photo.fileName = "image.jpg"; + UpdatePhotoAsset(); + cloudMediaPhotosDao->HandleSameNameRename(photo, photoRefresh); +} + +static void UpdatePhotoVisibleFuzzer() +{ + UpdatePhotoAsset(); + cloudMediaPhotosDao->UpdatePhotoVisible(); +} + +static void UpdateAlbumInternalFuzzer() +{ + std::set refreshAlbums; + refreshAlbums.insert(FDP->ConsumeBytesAsString(NUM_BYTES)); + cloudMediaPhotosDao->UpdateAlbumInternal(refreshAlbums); +} + +static void SetRetryFuzzer() +{ + std::string cloudId = "3d4970270f8d4b15b4ced48bd7f25dd44c7ad693ae57426d863fec74422b468e"; + UpdatePhotoAsset(); + cloudMediaPhotosDao->SetRetry(cloudId); +} + +static void DeleteLocalByCloudIdFuzzer() +{ + auto photoRefresh = std::make_shared(); + std::string cloudId = "3d4970270f8d4b15b4ced48bd7f25dd44c7ad693ae57426d863fec74422b478e"; + InsertDeleteAsset(); + cloudMediaPhotosDao->DeleteLocalByCloudId(cloudId, photoRefresh); +} + +static void UpdateFailRecordsCloudIdFuzzer() +{ + auto photoRefresh = std::make_shared(); + PhotosDto record; + record.cloudId = "3d4970270f8d4b15b4ced48bd7f25dd44c7ad693ae57426d863fec74422b488e"; + record.fileId = 1; + std::unordered_map localMap; + localMap.insert({"1", LocalInfo()}); + cloudMediaPhotosDao->UpdateFailRecordsCloudId(record, localMap, photoRefresh); +} + +static void InsertAndRemoveFailedRecordFuzzer() +{ + int32_t fileId = FDP->ConsumeIntegral(); + std::string cloudId = "3d4970270f8d4b15b4ced48bd7f25dd44c7ad693ae57426d863fec74422b438e"; + cloudMediaPhotosDao->InsertPhotoCreateFailedRecord(fileId); + cloudMediaPhotosDao->InsertPhotoModifyFailedRecord(cloudId); + cloudMediaPhotosDao->InsertPhotoCopyFailedRecord(fileId); + cloudMediaPhotosDao->RemovePhotoCreateFailedRecord(fileId); + cloudMediaPhotosDao->RemovePhotoModifyFailedRecord(cloudId); + cloudMediaPhotosDao->RemovePhotoCopyFailedRecord(fileId); + cloudMediaPhotosDao->ClearPhotoFailedRecords(); +} + +static void InsertMapCodeFuzzer() +{ + vector datas; + CloudMediaPullDataDto pullData = FuzzCloudMediaPullDataDto(); + datas.emplace_back(pullData); + cloudMediaMapCodeDao->InsertDatasToMapCode(datas); +} + +static void UpdateMapCodeFuzzer() +{ + CloudMediaPullDataDto pullData = FuzzCloudMediaPullDataDto(); + cloudMediaMapCodeDao->UpdateDataToMapCode(pullData); +} + +static void DeleteMapCodesFuzzer() +{ + vector datas; + CloudMediaPullDataDto pullData = FuzzCloudMediaPullDataDto(); + datas.emplace_back(pullData); + cloudMediaMapCodeDao->DeleteMapCodesByPullDatas(datas); +} + +static void DeleteMapCodeFuzzer() +{ + CloudMediaPullDataDto pullData = FuzzCloudMediaPullDataDto(); + cloudMediaMapCodeDao->DeleteMapCodesByPullData(pullData); +} + +static void MediaLibraryCloudMediaPhotosDaoFuzzer() +{ + BatchInsertFileFuzzer(); + UpdateRecordToDatabaseFuzzer(); + ConflictDataMergeFuzzer(); + GetInsertParamsFuzzer(); + BatchQueryLocalFuzzer(); + GetLocalKeyDataFuzzer(); + JudgeConflictFuzzer(); + GetRecordsFuzzer(); + + GetDeletedRecordsAssetFuzzer(); + GetPhotoLocalInfoFuzzer(); + UpdateLocalAlbumMapFuzzer(); + DeleteSameNamePhotoFuzzer(); + GetSameNamePhotoCountFuzzer(); + + UpdatePhotoCreatedRecordFuzzer(); + OnModifyPhotoRecordFuzzer(); + UpdateFdirtyVersionFuzzer(); + OnDeleteRecordsAssetFuzzer(); + OnCopyPhotoRecordFuzzer(); + + InsertMapCodeFuzzer(); + UpdateMapCodeFuzzer(); + DeleteMapCodesFuzzer(); + DeleteMapCodeFuzzer(); + + ClearCloudInfoFuzzer(); + DeleteFileNotExistPhotoFuzzer(); + HandleSameNameRenameFuzzer(); + UpdatePhotoVisibleFuzzer(); + UpdateAlbumInternalFuzzer(); + SetRetryFuzzer(); + DeleteLocalByCloudIdFuzzer(); + UpdateFailRecordsCloudIdFuzzer(); + InsertAndRemoveFailedRecordFuzzer(); +} + +void SetTables() +{ + vector createTableSqlList = { + PhotoColumn::CREATE_PHOTO_TABLE, + PhotoAlbumColumns::CREATE_TABLE, + PhotoMap::CREATE_TABLE, + }; + for (auto &createTableSql : createTableSqlList) { + int32_t ret = g_rdbStore->ExecuteSql(createTableSql); + if (ret != NativeRdb::E_OK) { + MEDIA_ERR_LOG("Execute sql %{private}s failed", createTableSql.c_str()); + return; + } + MEDIA_DEBUG_LOG("Execute sql %{private}s success", createTableSql.c_str()); + } +} + +static void Init() +{ + auto stageContext = std::make_shared(); + auto abilityContextImpl = std::make_shared(); + abilityContextImpl->SetStageContext(stageContext); + int32_t sceneCode = 0; + auto ret = Media::MediaLibraryDataManager::GetInstance()->InitMediaLibraryMgr(abilityContextImpl, + abilityContextImpl, sceneCode); + CHECK_AND_RETURN_LOG(ret == NativeRdb::E_OK, "InitMediaLibraryMgr failed, ret: %{public}d", ret); + + auto rdbStore = Media::MediaLibraryUnistoreManager::GetInstance().GetRdbStore(); + if (rdbStore == nullptr) { + MEDIA_ERR_LOG("rdbStore is nullptr"); + return; + } + g_rdbStore = rdbStore; + SetTables(); +} +} // namespace OHOS + +extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + OHOS::Init(); + return 0; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + /* Run your code on data */ + FuzzedDataProvider fdp(data, size); + OHOS::FDP = &fdp; + if (data == nullptr || size < OHOS::MIN_SIZE) { + return 0; + } + OHOS::MediaLibraryCloudMediaPhotosDaoFuzzer(); + return 0; +} \ No newline at end of file diff --git a/frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmediamapcodedao_fuzzer/medialibrarycloudmediamapcodedao_fuzzer.h b/frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmediamapcodedao_fuzzer/medialibrarycloudmediamapcodedao_fuzzer.h new file mode 100644 index 0000000000..388177a235 --- /dev/null +++ b/frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmediamapcodedao_fuzzer/medialibrarycloudmediamapcodedao_fuzzer.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 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. + */ + +#ifndef NOTIFYCHANGE_FUZZER_H +#define NOTIFYCHANGE_FUZZER_H + +#define FUZZ_PROJECT_NAME "medialibrarycloudmediamapcodedao_fuzzer" + +#endif diff --git a/frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmediamapcodedao_fuzzer/project.xml b/frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmediamapcodedao_fuzzer/project.xml new file mode 100644 index 0000000000..8cd958170c --- /dev/null +++ b/frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmediamapcodedao_fuzzer/project.xml @@ -0,0 +1,25 @@ + + + + + + 1000 + + 300 + + 4096 + + diff --git a/frameworks/innerkitsimpl/test/unittest/media_library_cloud_sync_service_test/BUILD.gn b/frameworks/innerkitsimpl/test/unittest/media_library_cloud_sync_service_test/BUILD.gn index 117181ccf1..6f86ebda2b 100644 --- a/frameworks/innerkitsimpl/test/unittest/media_library_cloud_sync_service_test/BUILD.gn +++ b/frameworks/innerkitsimpl/test/unittest/media_library_cloud_sync_service_test/BUILD.gn @@ -329,9 +329,27 @@ ohos_unittest("media_library_cloud_sync_service_service_test") { ] sources = [ + "${MEDIALIB_CLOUD_SYNC_SERVICE_PATH}/src/dao/cloud_media_common_dao.cpp", + "${MEDIALIB_CLOUD_SYNC_SERVICE_PATH}/src/dao/cloud_media_photos_dao.cpp", + "${MEDIALIB_CLOUD_SYNC_SERVICE_PATH}/src/dto/cloud_file_data_dto.cpp", + "${MEDIALIB_CLOUD_SYNC_SERVICE_PATH}/src/dto/photo_album_dto.cpp", + "${MEDIALIB_CLOUD_SYNC_SERVICE_PATH}/src/dto/photos_dto.cpp", + "${MEDIALIB_CLOUD_SYNC_SERVICE_PATH}/src/utils/cloud_media_file_utils.cpp", + "${MEDIALIB_CLOUD_SYNC_SERVICE_PATH}/src/utils/cloud_media_dao_utils.cpp", + "${MEDIALIB_CLOUD_SYNC_SERVICE_PATH}/src/utils/cloud_media_sync_utils.cpp", + "${MEDIALIB_CLOUD_SYNC_SERVICE_PATH}/src/utils/cloud_sync_convert.cpp", + "${MEDIALIB_CLOUD_SYNC_SERVICE_PATH}/src/utils/media_gallery_sync_notify.cpp", + "../medialibrary_unittest_utils/src/medialibrary_unittest_utils.cpp", "src/media_cloud_sync_service_service_test.cpp", "src/media_cloud_sync_test_utils.cpp", + + "${MEDIALIB_UTILS_PATH}/src/cpu_utils.cpp", + "src/media_cloud_sync_service_service_mapcode_test.cpp", + "${MEDIALIB_CLOUD_SYNC_SERVICE_PATH}/src/dao/cloud_map_code_dao.cpp", + "${MEDIALIB_INNERKITS_PATH}/medialibrary_data_extension/src/operation/photo_map_code_column.cpp", + "${MEDIALIB_INNERKITS_PATH}/medialibrary_data_extension/src/operation/photo_map_code_operation.cpp", + "${MEDIALIB_CLOUD_SYNC_SERVICE_PATH}/src/service/cloud_media_photos_service.cpp", ] deps = [ diff --git a/frameworks/innerkitsimpl/test/unittest/media_library_cloud_sync_service_test/include/media_cloud_sync_service_service_mapcode_test.h b/frameworks/innerkitsimpl/test/unittest/media_library_cloud_sync_service_test/include/media_cloud_sync_service_service_mapcode_test.h new file mode 100644 index 0000000000..dd3264bbb9 --- /dev/null +++ b/frameworks/innerkitsimpl/test/unittest/media_library_cloud_sync_service_test/include/media_cloud_sync_service_service_mapcode_test.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2025 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. + */ + +#ifndef OHOS_MEDIA_CLOUD_SYNC_SERVICES_SERVICES_MAPCODE_TEST_H +#define OHOS_MEDIA_CLOUD_SYNC_SERVICES_SERVICES_MAPCODE_TEST_H + +#include "gtest/gtest.h" + +namespace OHOS::Media::CloudSync { +class CloudMediaSyncServiceMapCodeTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; +} // namespace OHOS::Media::CloudSync +#endif // OHOS_MEDIA_CLOUD_SYNC_SERVICES_SERVICES_MAPCODE_TEST_H diff --git a/frameworks/innerkitsimpl/test/unittest/media_library_cloud_sync_service_test/src/media_cloud_sync_service_service_mapcode_test.cpp b/frameworks/innerkitsimpl/test/unittest/media_library_cloud_sync_service_test/src/media_cloud_sync_service_service_mapcode_test.cpp new file mode 100644 index 0000000000..4abbe75ced --- /dev/null +++ b/frameworks/innerkitsimpl/test/unittest/media_library_cloud_sync_service_test/src/media_cloud_sync_service_service_mapcode_test.cpp @@ -0,0 +1,693 @@ +/* + * Copyright (C) 2025 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 "MediaCloudSync" + +#include "media_cloud_sync_service_service_mapcode_test.h" + +#include +#include +#include +#include +#include +#define private public +#include "cloud_media_album_service.h" +#include "medialibrary_unittest_utils.h" +#include "cloud_media_photos_service.h" +#undef private + +#define private public +#define protected public +#include "file_utils.h" +#include "cloud_media_asset_manager.h" +#include "cloud_media_asset_download_operation.h" +#include "cloud_media_asset_callback.h" +#include "cloud_media_asset_observer.h" +#include "cloud_media_asset_types.h" +#undef private +#undef protected + +#include "cloud_map_code_dao.h" +#include "photo_map_code_column.h" +#include "photo_map_code_operation.h" +#include "cloud_media_pull_data_dto.h" + +using namespace testing::ext; +using namespace OHOS::AAFwk; + +namespace OHOS::Media::CloudSync { +static std::shared_ptr g_rdbStore; + +const int NUMBER_1 = 1; +const int NUMBER_10 = 10; +const int NUMBER_11 = 11; +const int NUMBER_15 = 15; +const int NUMBER_16 = 16; +const int NUMBER_20 = 20; +const int NUMBER_21 = 21; +const int NUMBER_25 = 25; + +void SetTestTables() +{ + vector createTableSqlList = { + PhotoColumn::CREATE_PHOTO_TABLE, + PhotoMapCodeColumn::CREATE_MAP_CODE_TABLE, + }; + for (auto &createTableSql : createTableSqlList) { + int32_t ret = g_rdbStore->ExecuteSql(createTableSql); + if (ret != NativeRdb::E_OK) { + MEDIA_ERR_LOG("Execute sql %{private}s failed", createTableSql.c_str()); + return; + } + MEDIA_DEBUG_LOG("Execute sql %{private}s success", createTableSql.c_str()); + } +} +void CleanTestTables() +{ + vector dropTableList = { + PhotoColumn::PHOTOS_TABLE, + PhotoMapCodeColumn::PHOTOS_MAP_CODE_TABLE, + }; + for (auto &dropTable : dropTableList) { + string dropSql = "DROP TABLE " + dropTable + ";"; + int32_t ret = g_rdbStore->ExecuteSql(dropSql); + if (ret != NativeRdb::E_OK) { + MEDIA_ERR_LOG("Drop %{public}s table failed", dropTable.c_str()); + return; + } + MEDIA_DEBUG_LOG("Drop %{public}s table success", dropTable.c_str()); + } +} + +static int32_t ClearTable(const string &table) +{ + NativeRdb::RdbPredicates predicates(table); + + int32_t rows = 0; + int32_t err = g_rdbStore->Delete(rows, predicates); + if (err != E_OK) { + MEDIA_ERR_LOG("Failed to clear album table, err: %{public}d", err); + return E_HAS_DB_ERROR; + } + return E_OK; +} + +static int32_t InsertDataToPhotos2() +{ + const std::string photoTable = PhotoColumn::PHOTOS_TABLE; + int64_t rowId = -1; + int32_t ret = E_OK; + for (int i = NUMBER_16; i <= NUMBER_20; i++) { + NativeRdb::ValuesBucket value; + value.PutString(PhotoColumn::PHOTO_CLOUD_ID, std::to_string(i)); + value.PutInt(PhotoColumn::MEDIA_ID, -i); + ret = g_rdbStore->Insert(rowId, photoTable, value); + std::cout << "rowId: " << rowId << ", ret: " << ret << std::endl; + if (ret != E_OK) { + GTEST_LOG_(ERROR) << "insert photoTable table 2 failed"; + } + } + + for (int i = NUMBER_21; i <= NUMBER_25; i++) { + NativeRdb::ValuesBucket value; + value.PutString(PhotoColumn::PHOTO_CLOUD_ID, std::to_string(i)); + value.PutInt(PhotoColumn::MEDIA_ID, i); + value.PutDouble(PhotoColumn::PHOTO_LATITUDE, stod("0.0")); + value.PutDouble(PhotoColumn::PHOTO_LONGITUDE, stod("0.0")); + ret = g_rdbStore->Insert(rowId, photoTable, value); + std::cout << "rowId: " << rowId << ", ret: " << ret << std::endl; + if (ret != E_OK) { + GTEST_LOG_(ERROR) << "insert photoTable table 2 failed"; + } + } + return E_OK; +} + +static int32_t InsertDataToPhotos() +{ + const std::string photoTable = PhotoColumn::PHOTOS_TABLE; + int64_t rowId = -1; + int32_t ret = E_OK; + + for (int i = NUMBER_1; i <= NUMBER_10; i++) { + NativeRdb::ValuesBucket value; + value.PutString(PhotoColumn::PHOTO_CLOUD_ID, std::to_string(i)); + value.PutDouble(PhotoColumn::PHOTO_LATITUDE, stod("30.1")); + value.PutDouble(PhotoColumn::PHOTO_LONGITUDE, stod("130.1")); + ret = g_rdbStore->Insert(rowId, photoTable, value); + std::cout << "rowId: " << rowId << ", ret: " << ret << std::endl; + if (ret != E_OK) { + GTEST_LOG_(ERROR) << "insert photoTable table 1 failed"; + } + } + + for (int i = NUMBER_11; i <= NUMBER_15; i++) { + NativeRdb::ValuesBucket value; + value.PutString(PhotoColumn::PHOTO_CLOUD_ID, std::to_string(i)); + ret = g_rdbStore->Insert(rowId, photoTable, value); + std::cout << "rowId: " << rowId << ", ret: " << ret << std::endl; + if (ret != E_OK) { + GTEST_LOG_(ERROR) << "insert photoTable table 2 failed"; + } + } + + InsertDataToPhotos2(); + return E_OK; +} + +static int32_t InsertDataToMapCode() +{ + return E_OK; +} + +void CloudMediaSyncServiceMapCodeTest::SetUpTestCase() +{ + GTEST_LOG_(INFO) << "SetUpTestCase"; + MediaLibraryUnitTestUtils::Init(); + g_rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore(); + if (g_rdbStore == nullptr) { + GTEST_LOG_(ERROR) << "init g_rdbStore failed"; + exit(1); + } + SetTestTables(); + ClearTable(PhotoMapCodeColumn::PHOTOS_MAP_CODE_TABLE); + InsertDataToPhotos(); + + InsertDataToMapCode(); +} + +void CloudMediaSyncServiceMapCodeTest::TearDownTestCase() +{ + GTEST_LOG_(INFO) << "TearDownTestCase"; + CleanTestTables(); + + g_rdbStore = nullptr; +} + +void CloudMediaSyncServiceMapCodeTest::SetUp() {} + +void CloudMediaSyncServiceMapCodeTest::TearDown() {} + +static std::atomic number(0); + +int GetNumber() +{ + return ++number; +} + +std::string GetTitle(int64_t ×tamp) +{ + return "IMG_" + to_string(timestamp) + "_" + to_string(GetNumber()); +} + +int64_t GetTimestamp() +{ + auto now = std::chrono::system_clock::now(); + auto duration = now.time_since_epoch(); + auto seconds = std::chrono::duration_cast(duration); + return seconds.count() + GetNumber(); +} + +int32_t InsertCloudMapCodeINDb(int64_t &fileId, std::string &data) +{ + int64_t timestamp = GetTimestamp(); + string title = GetTitle(timestamp); + string displayName = title + ".jpg"; + data = "/storage/cloud/files/photo/1/" + title + ".jpg"; + NativeRdb::ValuesBucket valuesBucket; + valuesBucket.PutString(MediaColumn::MEDIA_FILE_PATH, data); + valuesBucket.PutString(MediaColumn::MEDIA_TITLE, title); + valuesBucket.PutString(MediaColumn::MEDIA_NAME, displayName); + valuesBucket.PutInt(MediaColumn::MEDIA_TYPE, static_cast(MediaType::MEDIA_TYPE_IMAGE)); + valuesBucket.PutInt(PhotoColumn::PHOTO_POSITION, static_cast(PhotoPositionType::CLOUD)); + valuesBucket.PutInt(PhotoColumn::PHOTO_CLEAN_FLAG, static_cast(CleanType::TYPE_NOT_CLEAN)); + // 完善数据 + valuesBucket.PutInt(PhotoColumn::PHOTO_DIRTY, static_cast(DirtyType::TYPE_DELETED)); + int32_t ret = g_rdbStore->Insert(fileId, PhotoColumn::PHOTOS_TABLE, valuesBucket); + EXPECT_EQ(ret, E_OK); + MEDIA_INFO_LOG("InsertCloudAsset fileId is %{public}s", to_string(fileId).c_str()); + return ret; +} + +HWTEST_F(CloudMediaSyncServiceMapCodeTest, CloudMediaPhotosService_MapInsert_Test_001, TestSize.Level1) +{ + CloudMediaPhotosService service; + CloudMediaPullDataDto data1; + data1.basicFileName = "name1"; + data1.basicFileType = -1; + data1.hasProperties = true; + data1.propertiesSourceFileName = ""; + data1.hasAttributes = false; + std::vector pullDatas = {data1}; + std::vector failedRecords; + int32_t ret = service.MapInsert(pullDatas, failedRecords); + EXPECT_EQ(ret, E_OK); + EXPECT_EQ(failedRecords.size(), 0); + pullDatas.clear(); + failedRecords.clear(); + data1.cloudId = "1"; + failedRecords.push_back("1"); + pullDatas = {data1}; + ret = service.MapInsert(pullDatas, failedRecords); + EXPECT_EQ(ret, E_OK); + EXPECT_EQ(failedRecords.size(), 1); + pullDatas.clear(); + failedRecords.clear(); + data1.cloudId = "2"; + data1.latitude = 31.2; + pullDatas = {data1}; + ret = service.MapInsert(pullDatas, failedRecords); + EXPECT_EQ(ret, E_OK); + EXPECT_EQ(failedRecords.size(), 0); +} + +HWTEST_F(CloudMediaSyncServiceMapCodeTest, CloudMediaPhotosService_MapInsert_Test_0011, TestSize.Level1) +{ + CloudMediaPhotosService service; + CloudMediaPullDataDto data1; + std::vector pullDatas; + std::vector failedRecords; + + data1.cloudId = "3"; + data1.latitude = 31.2; + data1.longitude = 131.2; + CloudMediaPullDataDto data2; + data2.cloudId = "4"; + data2.latitude = 31.2; + data2.longitude = 131.2; + pullDatas = {data1, data2}; + int32_t ret = service.MapInsert(pullDatas, failedRecords); + EXPECT_EQ(ret, E_OK); + EXPECT_EQ(failedRecords.size(), 0); + + pullDatas.clear(); + failedRecords.clear(); + data1.cloudId = "5"; + data1.latitude = 31.2; + data1.longitude = 131.2; + pullDatas = {data1}; + ret = service.MapInsert(pullDatas, failedRecords); + EXPECT_EQ(ret, E_OK); + EXPECT_EQ(failedRecords.size(), 0); + pullDatas.clear(); + failedRecords.clear(); + data1.cloudId = "6"; + data1.latitude = 31.2; + data1.longitude = 131.2; + data2.cloudId = "7"; + data2.latitude = 31.2; + data2.longitude = 131.2; + CloudMediaPullDataDto data3; + data2.cloudId = "8"; + data2.latitude = 31.2; + data2.longitude = 131.2; + pullDatas = {data1, data2, data3}; + failedRecords = {"8"}; + ret = service.MapInsert(pullDatas, failedRecords); + EXPECT_EQ(ret, E_OK); + EXPECT_EQ(failedRecords.size(), 1); +} + +HWTEST_F(CloudMediaSyncServiceMapCodeTest, CloudMediaPhotosService_MapInsert_Test_0012, TestSize.Level1) +{ + CloudMediaPhotosService service; + CloudMediaPullDataDto data1; + std::vector pullDatas; + std::vector failedRecords; + + data1.cloudId = "99"; + data1.latitude = 31.2; + data1.longitude = 131.2; + pullDatas = {data1}; + int32_t ret = service.MapInsert(pullDatas, failedRecords); + EXPECT_EQ(ret, E_OK); + EXPECT_EQ(failedRecords.size(), 0); + + pullDatas.clear(); + failedRecords.clear(); + data1.cloudId = "99"; + data1.latitude = 31.2; + data1.longitude = 131.2; + pullDatas = {data1}; + failedRecords = {"99"}; + ret = service.MapInsert(pullDatas, failedRecords); + EXPECT_EQ(ret, E_OK); + EXPECT_EQ(failedRecords.size(), 1); +} +HWTEST_F(CloudMediaSyncServiceMapCodeTest, CloudMediaPhotosService_MapInsert_Test_002, TestSize.Level1) +{ + CloudMapCodeDao mapCodeDao; + std::vector pullDatas; + int32_t ret = mapCodeDao.InsertDatasToMapCode(pullDatas); + EXPECT_EQ(ret, E_OK); + CloudMediaPullDataDto data1; + data1.cloudId = "99"; + pullDatas = {data1}; + ret = mapCodeDao.InsertDatasToMapCode(pullDatas); + EXPECT_EQ(ret, E_OK); + pullDatas.clear(); + data1.cloudId = "1"; + pullDatas = {data1}; + ret = mapCodeDao.InsertDatasToMapCode(pullDatas); + EXPECT_EQ(ret, E_OK); + pullDatas.clear(); + data1.cloudId = "11"; + pullDatas = {data1}; + ret = mapCodeDao.InsertDatasToMapCode(pullDatas); + EXPECT_EQ(ret, E_OK); + pullDatas.clear(); + data1.cloudId = "16"; + pullDatas = {data1}; + ret = mapCodeDao.InsertDatasToMapCode(pullDatas); + EXPECT_EQ(ret, E_OK); +} +HWTEST_F(CloudMediaSyncServiceMapCodeTest, CloudMediaPhotosService_MapInsert_Test_003, TestSize.Level1) +{ + std::vector photoMapDatas; + int32_t result = PhotoMapCodeOperation::InsertPhotosMapCodes(photoMapDatas, nullptr); + EXPECT_EQ(result, E_OK); + int32_t fileId = 20; + double latitude = -50.0; + double longitude = -130.0; + PhotoMapData photoMapData(fileId, latitude, longitude); + photoMapDatas.push_back(photoMapData); + result = PhotoMapCodeOperation::InsertPhotosMapCodes(photoMapDatas, nullptr); + EXPECT_EQ(result, E_OK); + photoMapDatas.clear(); + fileId = -8; + latitude = -50.0; + longitude = -130.0; + PhotoMapData photoMapData1(fileId, latitude, longitude); + photoMapDatas.push_back(photoMapData1); + result = PhotoMapCodeOperation::InsertPhotosMapCodes(photoMapDatas, nullptr); + EXPECT_EQ(result, E_OK); + photoMapDatas.clear(); + fileId = 21; + latitude = 0.0; + longitude = -130.0; + PhotoMapData photoMapData2(fileId, latitude, longitude); + photoMapDatas.push_back(photoMapData2); + result = PhotoMapCodeOperation::InsertPhotosMapCodes(photoMapDatas, nullptr); + EXPECT_EQ(result, E_OK); + photoMapDatas.clear(); + fileId = 22; + latitude = 0.0; + longitude = 0.0; + PhotoMapData photoMapData3(fileId, latitude, longitude); + photoMapDatas.push_back(photoMapData3); + result = PhotoMapCodeOperation::InsertPhotosMapCodes(photoMapDatas, nullptr); + EXPECT_EQ(result, E_OK); + + + photoMapDatas.clear(); + fileId = 23; + latitude = 20.0; + longitude = 130.0; + PhotoMapData photoMapData4(fileId, latitude, longitude); + photoMapDatas.push_back(photoMapData4); + result = PhotoMapCodeOperation::InsertPhotosMapCodes(photoMapDatas, g_rdbStore->GetRaw()); + EXPECT_EQ(result, E_OK); +} + +HWTEST_F(CloudMediaSyncServiceMapCodeTest, CloudMediaPhotosService_MapInsert_Test_004, TestSize.Level1) +{ + std::function execSql; + execSql = []() { return NativeRdb::E_OK; }; + int32_t result = PhotoMapCodeOperation::ExecSqlWithRetry(execSql); + EXPECT_EQ(result, E_OK); + bool firstCall = true; + execSql = [&]() { + if (firstCall) { + firstCall = false; + return NativeRdb::E_SQLITE_BUSY; + } else { + return NativeRdb::E_OK; + } + }; + result = PhotoMapCodeOperation::ExecSqlWithRetry(execSql); + EXPECT_EQ(result, E_OK); + + execSql = []() { return NativeRdb::E_SQLITE_BUSY; }; + result = PhotoMapCodeOperation::ExecSqlWithRetry(execSql); + EXPECT_NE(result, E_OK); + + execSql = []() { return NativeRdb::E_SQLITE_IOERR; }; + result = PhotoMapCodeOperation::ExecSqlWithRetry(execSql); + EXPECT_NE(result, E_OK); +} +HWTEST_F(CloudMediaSyncServiceMapCodeTest, CloudMediaPhotosService_MapUpdate_Test_001, TestSize.Level1) +{ + CloudMediaPhotosService service; + CloudMediaPullDataDto pullData; + pullData.localDirty = 0; + pullData.localDateModified = ""; + pullData.attributesEditedTimeMs = 1; + pullData.localDateAdded = ""; + int32_t ret = service.MapUpdate(pullData); + EXPECT_EQ(ret, E_OK); + + pullData.cloudId = "99"; + ret = service.MapUpdate(pullData); + EXPECT_EQ(ret, E_OK); + + pullData.cloudId = "1"; + ret = service.MapUpdate(pullData); + EXPECT_EQ(ret, E_OK); + + pullData.cloudId = "11"; + ret = service.MapUpdate(pullData); + EXPECT_EQ(ret, E_OK); + + pullData.cloudId = "16"; + ret = service.MapUpdate(pullData); + EXPECT_EQ(ret, E_OK); + + pullData.cloudId = "21"; + ret = service.MapUpdate(pullData); + EXPECT_EQ(ret, E_OK); +} +HWTEST_F(CloudMediaSyncServiceMapCodeTest, CloudMediaPhotosService_MapUpdate_Test_002, TestSize.Level1) +{ + CloudMapCodeDao mapCodeDao; + CloudMediaPullDataDto pullData; + + int32_t ret = mapCodeDao.UpdateDataToMapCode(pullData); + EXPECT_EQ(ret, E_OK); + + pullData.localDirty = 0; + pullData.localDateModified = ""; + pullData.attributesEditedTimeMs = 1; + pullData.localDateAdded = ""; + ret = mapCodeDao.UpdateDataToMapCode(pullData); + EXPECT_EQ(ret, E_OK); + + pullData.cloudId = "99"; + ret = mapCodeDao.UpdateDataToMapCode(pullData); + EXPECT_EQ(ret, E_OK); + + pullData.cloudId = "2"; + ret = mapCodeDao.UpdateDataToMapCode(pullData); + EXPECT_EQ(ret, E_OK); + + pullData.cloudId = "12"; + ret = mapCodeDao.UpdateDataToMapCode(pullData); + EXPECT_EQ(ret, E_OK); + + pullData.cloudId = "17"; + ret = mapCodeDao.UpdateDataToMapCode(pullData); + EXPECT_EQ(ret, E_OK); + + pullData.cloudId = "22"; + ret = mapCodeDao.UpdateDataToMapCode(pullData); + EXPECT_EQ(ret, E_OK); +} +HWTEST_F(CloudMediaSyncServiceMapCodeTest, CloudMediaPhotosService_MapDelete_Test_001, TestSize.Level1) +{ + CloudMediaPhotosService service; + CloudMediaPullDataDto pullData; + pullData.localDirty = 0; + pullData.localDateModified = ""; + pullData.attributesEditedTimeMs = 1; + pullData.localDateAdded = ""; + int32_t ret = service.MapDelete(pullData); + EXPECT_EQ(ret, E_OK); + + pullData.cloudId = "99"; + ret = service.MapDelete(pullData); + EXPECT_EQ(ret, E_OK); + + pullData.cloudId = "1"; + ret = service.MapDelete(pullData); + EXPECT_EQ(ret, E_OK); + + pullData.cloudId = "11"; + ret = service.MapDelete(pullData); + EXPECT_EQ(ret, E_OK); + + pullData.cloudId = "16"; + ret = service.MapDelete(pullData); + EXPECT_EQ(ret, E_OK); + + pullData.cloudId = "21"; + ret = service.MapDelete(pullData); + EXPECT_EQ(ret, E_OK); +} +HWTEST_F(CloudMediaSyncServiceMapCodeTest, CloudMediaPhotosService_MapDelete_Test_002, TestSize.Level1) +{ + CloudMapCodeDao mapCodeDao; + CloudMediaPullDataDto pullData; + int32_t ret = mapCodeDao.DeleteMapCodesByPullData(pullData); + EXPECT_EQ(ret, E_OK); + pullData.localDirty = 0; + pullData.localDateModified = ""; + pullData.attributesEditedTimeMs = 1; + pullData.localDateAdded = ""; + ret = mapCodeDao.DeleteMapCodesByPullData(pullData); + EXPECT_EQ(ret, E_OK); + + pullData.cloudId = "99"; + ret = mapCodeDao.DeleteMapCodesByPullData(pullData); + EXPECT_EQ(ret, E_OK); + + pullData.cloudId = "2"; + ret = mapCodeDao.DeleteMapCodesByPullData(pullData); + EXPECT_EQ(ret, E_OK); + + pullData.cloudId = "12"; + ret = mapCodeDao.DeleteMapCodesByPullData(pullData); + EXPECT_EQ(ret, E_OK); + + pullData.cloudId = "17"; + ret = mapCodeDao.DeleteMapCodesByPullData(pullData); + EXPECT_EQ(ret, E_OK); + + pullData.cloudId = "22"; + ret = mapCodeDao.DeleteMapCodesByPullData(pullData); + EXPECT_EQ(ret, E_OK); +} +HWTEST_F(CloudMediaSyncServiceMapCodeTest, CloudMediaPhotosService_MapDelete_Test_003, TestSize.Level1) +{ + CloudMapCodeDao mapCodeDao; + std::vector pullDatas; + + int32_t ret = mapCodeDao.DeleteMapCodesByPullDatas(pullDatas); + EXPECT_EQ(ret, E_OK); + + CloudMediaPullDataDto pullData; + pullData.localDirty = 0; + pullData.localDateModified = ""; + pullData.attributesEditedTimeMs = 1; + pullData.localDateAdded = ""; + pullData.cloudId = "99"; + pullDatas.push_back(pullData); + ret = mapCodeDao.DeleteMapCodesByPullDatas(pullDatas); + EXPECT_EQ(ret, E_OK); + pullDatas.clear(); + pullData.cloudId = "3"; + pullDatas.push_back(pullData); + ret = mapCodeDao.DeleteMapCodesByPullDatas(pullDatas); + EXPECT_EQ(ret, E_OK); + pullDatas.clear(); + pullData.cloudId = "13"; + pullDatas.push_back(pullData); + ret = mapCodeDao.DeleteMapCodesByPullDatas(pullDatas); + EXPECT_EQ(ret, E_OK); + pullDatas.clear(); + pullData.cloudId = "18"; + pullDatas.push_back(pullData); + ret = mapCodeDao.DeleteMapCodesByPullDatas(pullDatas); + EXPECT_EQ(ret, E_OK); +} + +HWTEST_F(CloudMediaSyncServiceMapCodeTest, CloudMediaPhotosService_MapDelete_Test_0031, TestSize.Level1) +{ + CloudMapCodeDao mapCodeDao; + std::vector pullDatas; + CloudMediaPullDataDto pullData; + pullData.cloudId = "23"; + pullDatas.push_back(pullData); + int32_t ret = mapCodeDao.DeleteMapCodesByPullDatas(pullDatas); + EXPECT_EQ(ret, E_OK); + + pullDatas.clear(); + pullData.cloudId = "99"; + pullDatas.push_back(pullData); + pullData.cloudId = "4"; + pullDatas.push_back(pullData); + pullData.cloudId = "13"; + pullDatas.push_back(pullData); + ret = mapCodeDao.DeleteMapCodesByPullDatas(pullDatas); + EXPECT_EQ(ret, E_OK); + + CloudMediaAssetManager &instance = CloudMediaAssetManager::GetInstance(); + ret = instance.UpdateCloudMediaAssets(); + EXPECT_EQ(ret, E_OK); + + int64_t fileId4 = 365; + std::string data1 = ""; + ret = InsertCloudMapCodeINDb(fileId4, data1); + EXPECT_EQ(ret, E_OK); + ret = instance.ClearDeletedMapData(); + EXPECT_EQ(ret, E_OK); +} +HWTEST_F(CloudMediaSyncServiceMapCodeTest, CloudMediaPhotosService_GetPhotosMapCodesMRS_Test_001, TestSize.Level1) +{ + std::vector photoMapDatas; + int32_t result = PhotoMapCodeOperation::GetPhotosMapCodesMRS(photoMapDatas, g_rdbStore); + EXPECT_EQ(result, E_OK); + int32_t fileId = 20; + double latitude = -50.0; + double longitude = -130.0; + PhotoMapData photoMapData(fileId, latitude, longitude); + photoMapDatas.push_back(photoMapData); + result = PhotoMapCodeOperation::GetPhotosMapCodesMRS(photoMapDatas, g_rdbStore); + EXPECT_EQ(result, E_OK); + fileId = 1; + latitude = 50.0; + longitude = 130.0; + PhotoMapData photoMapData1(fileId, latitude, longitude); + photoMapDatas.push_back(photoMapData1); + result = PhotoMapCodeOperation::GetPhotosMapCodesMRS(photoMapDatas, nullptr); + EXPECT_NE(result, E_OK); + + result = PhotoMapCodeOperation::GetPhotosMapCodesMRS(photoMapDatas, g_rdbStore); + EXPECT_EQ(result, E_OK); +} + +HWTEST_F(CloudMediaSyncServiceMapCodeTest, CloudMediaPhotosService_DatasToMapCodes_Test_001, TestSize.Level1) +{ + int32_t result = PhotoMapCodeOperation::DatasToMapCodes(g_rdbStore, 20, 2, 0); + EXPECT_NE(result, E_OK); + + result = PhotoMapCodeOperation::DatasToMapCodes(g_rdbStore, 4, 26, 30); + EXPECT_NE(result, E_OK); + + result = PhotoMapCodeOperation::DatasToMapCodes(g_rdbStore, 50, 1, 10); + EXPECT_EQ(result, E_OK); + + result = PhotoMapCodeOperation::DatasToMapCodes(g_rdbStore, 800, 11, 15); + EXPECT_NE(result, E_OK); + + result = PhotoMapCodeOperation::DatasToMapCodes(g_rdbStore, 35000, 16, 20); + EXPECT_NE(result, E_OK); + + result = PhotoMapCodeOperation::DatasToMapCodes(g_rdbStore, 4, 21, 25); + EXPECT_NE(result, E_OK); + + result = PhotoMapCodeOperation::DatasToMapCodes(g_rdbStore, 10, 1, 25); + EXPECT_NE(result, E_OK); +} + +} // namespace OHOS::Media::CloudSync \ No newline at end of file diff --git a/frameworks/innerkitsimpl/test/unittest/medialibrary_backup_clone_test/BUILD.gn b/frameworks/innerkitsimpl/test/unittest/medialibrary_backup_clone_test/BUILD.gn index 0d9c509240..3bb7a98c9d 100755 --- a/frameworks/innerkitsimpl/test/unittest/medialibrary_backup_clone_test/BUILD.gn +++ b/frameworks/innerkitsimpl/test/unittest/medialibrary_backup_clone_test/BUILD.gn @@ -113,6 +113,13 @@ ohos_unittest("medialibrary_backup_clone_test") { "./src/group_photo/clone_restore_group_photo_test.cpp", "./src/portrait_album/portrait_album_clone_test.cpp", "./src/portrait_album/portrait_album_source.cpp", + + "${MEDIALIB_UTILS_PATH}/src/cpu_utils.cpp", + "${MEDIALIB_SERVICES_PATH}/media_backup_extension/src/restore_map_code_utils.cpp", + "${MEDIALIB_INNERKITS_PATH}/medialibrary_data_extension/src/operation/photo_map_code_column.cpp", + "${MEDIALIB_INNERKITS_PATH}/medialibrary_data_extension/src/operation/photo_map_code_operation.cpp", + "./src/medialibrary_backup_clone_mapcode_test.cpp", + "${MEDIALIB_UTILS_PATH}/src/map_code_upload_checker.cpp", ] source_ipc_client = [ @@ -162,6 +169,9 @@ ohos_unittest("medialibrary_backup_clone_test") { "relational_store:rdb_data_share_adapter", "samgr:samgr_proxy", "zlib:shared_libz", + "preferences:native_preferences", + "googletest:gtest_main", + "googletest:gmock_main", ] defines = [] diff --git a/frameworks/innerkitsimpl/test/unittest/medialibrary_backup_clone_test/include/medialibrary_backup_clone_mapcode_test.h b/frameworks/innerkitsimpl/test/unittest/medialibrary_backup_clone_test/include/medialibrary_backup_clone_mapcode_test.h new file mode 100644 index 0000000000..a53dd0d08d --- /dev/null +++ b/frameworks/innerkitsimpl/test/unittest/medialibrary_backup_clone_test/include/medialibrary_backup_clone_mapcode_test.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 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. + */ + +#ifndef MEDIALIBRARY_BACKUP_CLONE_MAPCODE_TEST_H +#define MEDIALIBRARY_BACKUP_CLONE_MAPCODE_TEST_H + +#define private public +#define protected public +#include "gtest/gtest.h" +#include +#include "rdb_helper.h" +#include "result_set_utils.h" +#include "backup_const.h" +#include "medialibrary_subscriber.h" +#undef protected +#undef private + +namespace OHOS { +namespace Media { +class MediaLibraryBackupCloneMapCodeTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +class MockMedialibrarySubscriber : public Media::MedialibrarySubscriber { +public: + MockMedialibrarySubscriber() = default; + ~MockMedialibrarySubscriber() = default; + + MOCK_METHOD0(IsCurrentStatusOn, bool()); +}; +} // namespace Media +} // namespace OHOS +#endif // MEDIALIBRARY_BACKUP_CLONE_MAPCODE_TEST_H diff --git a/frameworks/innerkitsimpl/test/unittest/medialibrary_backup_clone_test/src/medialibrary_backup_clone_mapcode_test.cpp b/frameworks/innerkitsimpl/test/unittest/medialibrary_backup_clone_test/src/medialibrary_backup_clone_mapcode_test.cpp new file mode 100644 index 0000000000..974e006ba1 --- /dev/null +++ b/frameworks/innerkitsimpl/test/unittest/medialibrary_backup_clone_test/src/medialibrary_backup_clone_mapcode_test.cpp @@ -0,0 +1,612 @@ +/* + * Copyright (C) 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 "BackupCloneTest" + +#include "medialibrary_backup_clone_mapcode_test.h" + +#define private public +#define protected public +#include "backup_database_utils.h" +#include "backup_file_utils.h" +#include "clone_source.h" +#include "media_column.h" +#include "media_log.h" +#include "media_file_utils.h" +#include "backup_restore_service.h" +#include "base_restore.h" +#include "clone_restore.h" +#include "clone_restore_analysis_data.h" +#include "clone_restore_geo.h" +#include "clone_restore_classify.h" +#include "video_face_clone.h" +#include "beauty_score_clone.h" +#include "search_index_clone.h" +#include "medialibrary_rdb_utils.h" +#include "medialibrary_unistore_manager.h" +#include "medialibrary_unittest_utils.h" +#include "others_clone_restore.h" +#include "photos_dao.h" +#include "photos_data_handler.h" +#include "burst_key_generator.h" +#include "medialibrary_asset_operations.h" +#include "medialibrary_rdb_transaction.h" +#include "medialibrary_sync_operation.h" +#include "medialibrary_object_utils.h" +#include "medialibrary_rdbstore.h" + +#include "medialibrary_restore.h" +#include "photo_map_code_column.h" +#include "photo_map_code_operation.h" +#include "restore_map_code_utils.h" +#include "map_code_upload_checker.h" +#include "preferences.h" +#include "preferences_helper.h" +#include "medialibrary_data_manager.h" +#include "ability_context_impl.h" +#include "restore_map_code_utils.h" +#include "table_event_handler.h" +#undef protected +#undef private + +using namespace std; +using namespace OHOS; +using namespace testing::ext; +using namespace OHOS::NativeRdb; + +namespace OHOS { +namespace Media { +const std::string RDB_CONFIG = "/data/storage/el2/base/preferences/rdb_config.xml"; +static const std::string RDB_UPGRADE_EVENT = "/data/storage/el2/base/preferences/rdb_upgrade_events.xml"; +const std::string RDB_OLD_VERSION = "rdb_old_version"; + +const int NUMBER_1 = 1; +const int NUMBER_10 = 10; +const int NUMBER_11 = 11; +const int NUMBER_15 = 15; +const int NUMBER_16 = 16; +const int NUMBER_20 = 20; +const int NUMBER_21 = 21; +const int NUMBER_25 = 25; + +class RestoreMapCodeCallBack : public NativeRdb::RdbOpenCallback { +public: + int OnCreate(NativeRdb::RdbStore &rdbStore) override; + int OnUpgrade(NativeRdb::RdbStore &rdbStore, int oldVersion, int newVersion) override; +}; + +int RestoreMapCodeCallBack::OnCreate(RdbStore &store) +{ + return store.ExecuteSql(PhotoMapCodeColumn::CREATE_MAP_CODE_TABLE); +} + +int RestoreMapCodeCallBack::OnUpgrade(RdbStore &store, int oldVersion, int newVersion) +{ + return 0; +} + +static std::shared_ptr g_rdbStore; +std::shared_ptr mediaLibraryRdb; + +void SetTestTables() +{ + vector createTableSqlList = { + PhotoColumn::CREATE_PHOTO_TABLE, + PhotoMapCodeColumn::CREATE_MAP_CODE_TABLE, + }; + for (auto &createTableSql : createTableSqlList) { + int32_t ret = g_rdbStore->ExecuteSql(createTableSql); + if (ret != NativeRdb::E_OK) { + MEDIA_ERR_LOG("Execute sql %{private}s failed", createTableSql.c_str()); + return; + } + MEDIA_DEBUG_LOG("Execute sql %{private}s success", createTableSql.c_str()); + } +} + +static int32_t InsertDataToPhotos2() +{ + const std::string photoTable = PhotoColumn::PHOTOS_TABLE; + int64_t rowId = -1; + int32_t ret = E_OK; + for (int i = NUMBER_16; i <= NUMBER_20; i++) { + NativeRdb::ValuesBucket value; + value.PutString(PhotoColumn::PHOTO_CLOUD_ID, std::to_string(i)); + value.PutInt(PhotoColumn::MEDIA_ID, -i); + ret = g_rdbStore->Insert(rowId, photoTable, value); + std::cout << "rowId: " << rowId << ", ret: " << ret << std::endl; + if (ret != E_OK) { + GTEST_LOG_(ERROR) << "insert photoTable table 2 failed"; + } + } + + for (int i = NUMBER_21; i <= NUMBER_25; i++) { + NativeRdb::ValuesBucket value; + value.PutString(PhotoColumn::PHOTO_CLOUD_ID, std::to_string(i)); + value.PutInt(PhotoColumn::MEDIA_ID, i); + value.PutDouble(PhotoColumn::PHOTO_LATITUDE, stod("0.0")); + value.PutDouble(PhotoColumn::PHOTO_LONGITUDE, stod("0.0")); + ret = g_rdbStore->Insert(rowId, photoTable, value); + std::cout << "rowId: " << rowId << ", ret: " << ret << std::endl; + if (ret != E_OK) { + GTEST_LOG_(ERROR) << "insert photoTable table 2 failed"; + } + } + return E_OK; +} + +static int32_t InsertDataToPhotos() +{ + const std::string photoTable = PhotoColumn::PHOTOS_TABLE; + int64_t rowId = -1; + int32_t ret = E_OK; + for (int i = NUMBER_1; i <= NUMBER_10; i++) { + NativeRdb::ValuesBucket value; + value.PutString(PhotoColumn::PHOTO_CLOUD_ID, std::to_string(i)); + value.PutDouble(PhotoColumn::PHOTO_LATITUDE, stod("30.1")); + value.PutDouble(PhotoColumn::PHOTO_LONGITUDE, stod("130.1")); + + ret = g_rdbStore->Insert(rowId, photoTable, value); + std::cout << "rowId: " << rowId << ", ret: " << ret << std::endl; + if (ret != E_OK) { + GTEST_LOG_(ERROR) << "insert photoTable table 1 failed"; + } + } + + for (int i = NUMBER_11; i <= NUMBER_15; i++) { + NativeRdb::ValuesBucket value; + value.PutString(PhotoColumn::PHOTO_CLOUD_ID, std::to_string(i)); + ret = g_rdbStore->Insert(rowId, photoTable, value); + std::cout << "rowId: " << rowId << ", ret: " << ret << std::endl; + if (ret != E_OK) { + GTEST_LOG_(ERROR) << "insert photoTable table 2 failed"; + } + } + + InsertDataToPhotos2(); + return E_OK; +} + +void CleanTestTables() +{ + vector dropTableList = { + PhotoColumn::PHOTOS_TABLE, + PhotoMapCodeColumn::PHOTOS_MAP_CODE_TABLE, + }; + for (auto &dropTable : dropTableList) { + string dropSql = "DROP TABLE " + dropTable + ";"; + int32_t ret = g_rdbStore->ExecuteSql(dropSql); + if (ret != NativeRdb::E_OK) { + MEDIA_ERR_LOG("Drop %{public}s table failed", dropTable.c_str()); + return; + } + MEDIA_DEBUG_LOG("Drop %{public}s table success", dropTable.c_str()); + } +} + +static int32_t ClearTable(const string &table) +{ + NativeRdb::RdbPredicates predicates(table); + + int32_t rows = 0; + int32_t err = g_rdbStore->Delete(rows, predicates); + if (err != E_OK) { + MEDIA_ERR_LOG("Failed to clear album table, err: %{public}d", err); + return E_HAS_DB_ERROR; + } + return E_OK; +} + +void MediaLibraryBackupCloneMapCodeTest::SetUpTestCase() +{ + GTEST_LOG_(INFO) << "SetUpTestCase"; + const string dbPath = "/data/test/medialibrary_map_test.db"; + NativeRdb::RdbStoreConfig config(dbPath); + RestoreMapCodeCallBack helper; + int32_t ret = MediaLibraryUnitTestUtils::InitUnistore(config, 1, helper); + EXPECT_EQ(ret, E_OK); + g_rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore(); + if (g_rdbStore == nullptr) { + GTEST_LOG_(ERROR) << "init g_rdbStore failed"; + exit(1); + } + mediaLibraryRdb = g_rdbStore->GetRaw(); + if (mediaLibraryRdb == nullptr) { + GTEST_LOG_(ERROR) << "init mediaLibraryRdb failed"; + exit(1); + } + SetTestTables(); + ClearTable(PhotoMapCodeColumn::PHOTOS_MAP_CODE_TABLE); + InsertDataToPhotos(); +} + +void MediaLibraryBackupCloneMapCodeTest::TearDownTestCase() +{ + GTEST_LOG_(INFO) << "TearDownTestCase"; + + ClearTable(PhotoMapCodeColumn::PHOTOS_MAP_CODE_TABLE); + ClearTable(PhotoColumn::PHOTOS_TABLE); + + g_rdbStore = nullptr; +} + +void MediaLibraryBackupCloneMapCodeTest::SetUp() {} + +void MediaLibraryBackupCloneMapCodeTest::TearDown() {} + +HWTEST_F(MediaLibraryBackupCloneMapCodeTest, medialibrary_upgrade_to_mapcode_test_001, TestSize.Level2) +{ + CleanTestTables(); + int32_t errCode = g_rdbStore->ExecuteSql(PhotoColumn::CREATE_PHOTO_TABLE); + EXPECT_EQ(errCode, E_OK); + + InsertDataToPhotos(); + NativeRdb::RdbStore *rawPtr = mediaLibraryRdb.get(); + bool ret = MediaLibraryRdbStore::AddPhotoMapTable(*rawPtr); + EXPECT_EQ(ret, true); + + errCode = E_ERR; + shared_ptr prefsEvent = + NativePreferences::PreferencesHelper::GetPreferences(RDB_UPGRADE_EVENT, errCode); + MEDIA_INFO_LOG("rdb_upgrade_events prefs errCode: %{public}d", errCode); + CHECK_AND_RETURN_LOG(prefsEvent != nullptr, "prefsEvent is nullptr"); + string versionKey = "VERSION_ADD_MAP_CODE_TABLE"; + prefsEvent->PutInt(versionKey, 0); + prefsEvent->FlushSync(); + errCode = E_ERR; + shared_ptr prefsConf = + NativePreferences::PreferencesHelper::GetPreferences(RDB_CONFIG, errCode); + EXPECT_EQ(errCode == E_OK, true); + int32_t version = VERSION_ADD_MAP_CODE_TABLE - 1; + prefsConf->PutInt(RDB_OLD_VERSION, version); + prefsConf->FlushSync(); + TableEventHandler().OnUpgrade(g_rdbStore, version, MEDIA_RDB_VERSION); + + ret = MediaLibraryRdbStore::AddPhotoMapTableIndex(nullptr); + EXPECT_EQ(ret, false); + ret = MediaLibraryRdbStore::AddPhotoMapTableIndex(g_rdbStore); + EXPECT_EQ(ret, true); + ret = MediaLibraryRdbStore::AddPhotoMapTableData(nullptr); + EXPECT_EQ(ret, false); + ret = MediaLibraryRdbStore::AddPhotoMapTableData(g_rdbStore); + EXPECT_EQ(ret, true); +} + + +HWTEST_F(MediaLibraryBackupCloneMapCodeTest, medialibrary_upgrade_to_mapcode_test_002, TestSize.Level2) +{ + CleanTestTables(); + int32_t errCode = g_rdbStore->ExecuteSql(PhotoColumn::CREATE_PHOTO_TABLE); + EXPECT_EQ(errCode, E_OK); + + int32_t ret = PhotoMapCodeOperation::UpgradePhotoMapCode(g_rdbStore); + EXPECT_EQ(ret, E_OK); + + InsertDataToPhotos(); + + errCode = g_rdbStore->ExecuteSql(PhotoMapCodeColumn::CREATE_MAP_CODE_TABLE); + EXPECT_EQ(errCode, E_OK); + + ret = PhotoMapCodeOperation::UpgradePhotoMapCode(nullptr); + EXPECT_EQ(ret, E_ERR); + + ret = PhotoMapCodeOperation::UpgradePhotoMapCode(g_rdbStore); + EXPECT_EQ(ret, E_OK); +} + +HWTEST_F(MediaLibraryBackupCloneMapCodeTest, medialibrary_upgrade_to_mapcode_test_003, TestSize.Level2) +{ + int32_t errCode; + shared_ptr prefsEvent = + NativePreferences::PreferencesHelper::GetPreferences(RDB_UPGRADE_EVENT, errCode); + MEDIA_INFO_LOG("rdb_upgrade_events prefs errCode: %{public}d", errCode); + CHECK_AND_RETURN_LOG(prefsEvent != nullptr, "prefsEvent is nullptr"); + string versionKey = "VERSION_ADD_MAP_CODE_TABLE"; + prefsEvent->PutInt(versionKey, 0); + prefsEvent->FlushSync(); + + shared_ptr prefsConf = + NativePreferences::PreferencesHelper::GetPreferences(RDB_CONFIG, errCode); + EXPECT_EQ(errCode == E_OK, true); + int32_t version = VERSION_ADD_MAP_CODE_TABLE - 1; + prefsConf->PutInt(RDB_OLD_VERSION, version); + prefsConf->FlushSync(); + + int32_t oldVersion = VERSION_ADD_MAP_CODE_TABLE - 1; + prefsConf->GetInt(RDB_OLD_VERSION, oldVersion); + EXPECT_EQ(oldVersion != VERSION_ADD_MAP_CODE_TABLE, true); + + if (oldVersion < VERSION_ADD_MAP_CODE_TABLE) { + NativeRdb::RdbStore *rawPtr = mediaLibraryRdb.get(); + bool ret = MediaLibraryRdbStore::AddPhotoMapTable(*rawPtr); + EXPECT_EQ(ret, true); + } + EXPECT_EQ(oldVersion != VERSION_ADD_MAP_CODE_TABLE, true); + + oldVersion = VERSION_ADD_MAP_CODE_TABLE - 1; + if (oldVersion < VERSION_ADD_MAP_CODE_TABLE) { + bool ret = MediaLibraryRdbStore::AddPhotoMapTableIndex(g_rdbStore); + EXPECT_EQ(ret, true); + ret = MediaLibraryRdbStore::AddPhotoMapTableData(g_rdbStore); + EXPECT_EQ(ret, true); + g_rdbStore->SetOldVersion(VERSION_ADD_MAP_CODE_TABLE); + } + prefsConf->GetInt(RDB_OLD_VERSION, oldVersion); + EXPECT_NE(oldVersion == VERSION_ADD_MAP_CODE_TABLE, true); +} + +HWTEST_F(MediaLibraryBackupCloneMapCodeTest, medialibrary_upgrade_to_mapcode_test_004, TestSize.Level2) +{ + auto stageContext = std::make_shared(); + auto abilityContextImpl = std::make_shared(); + abilityContextImpl->SetStageContext(stageContext); + int32_t sceneCode = 0; + auto ret = Media::MediaLibraryDataManager::GetInstance()->InitMediaLibraryMgr(abilityContextImpl, + abilityContextImpl, sceneCode); + EXPECT_EQ(ret, E_OK); +} + +HWTEST_F(MediaLibraryBackupCloneMapCodeTest, medialibrary_dirtydata_to_mapcode_test_001, TestSize.Level2) +{ + CleanTestTables(); + int32_t errCode = g_rdbStore->ExecuteSql(PhotoColumn::CREATE_PHOTO_TABLE); + EXPECT_EQ(errCode, E_OK); + InsertDataToPhotos(); + + std::shared_ptr subscriber = std::make_shared(); + subscriber->currentStatus_ = false; + bool ret = MapCodeUploadChecker::RepairNoMapCodePhoto(); + EXPECT_EQ(ret, true); +} + +HWTEST_F(MediaLibraryBackupCloneMapCodeTest, medialibrary_dirtydata_to_mapcode_test_002, TestSize.Level2) +{ + CleanTestTables(); + int32_t errCode = g_rdbStore->ExecuteSql(PhotoColumn::CREATE_PHOTO_TABLE); + EXPECT_EQ(errCode, E_OK); + InsertDataToPhotos(); + + std::shared_ptr subscriber = std::make_shared(); + EXPECT_CALL(*subscriber, IsCurrentStatusOn()).WillRepeatedly(testing::Return(true)); + subscriber->currentStatus_ = true; + bool ret = MapCodeUploadChecker::RepairNoMapCodePhoto(); + EXPECT_EQ(ret, true); +} + +HWTEST_F(MediaLibraryBackupCloneMapCodeTest, medialibrary_backup_files_to_mapcode_test_001, TestSize.Level2) +{ + CleanTestTables(); + + int32_t errCode = g_rdbStore->ExecuteSql(PhotoColumn::CREATE_PHOTO_TABLE); + EXPECT_EQ(errCode, E_OK); + + g_rdbStore = nullptr; + mediaLibraryRdb = nullptr; + FileInfo fileInfo; + fileInfo.fileIdNew = 31; + fileInfo.latitude = 30.12; + fileInfo.longitude = 130.11; + vector fileInfos; + fileInfos.push_back(fileInfo); + int32_t ret = RestoreMapCodeUtils::FileInfosToMapCode(mediaLibraryRdb, fileInfos); + EXPECT_EQ(ret, E_OK); + + mediaLibraryRdb = MediaLibraryUnistoreManager::GetInstance().GetRdbStore()->GetRaw(); + ret = RestoreMapCodeUtils::FileInfosToMapCode(mediaLibraryRdb, fileInfos); + EXPECT_EQ(ret, E_OK); + + g_rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore(); + ret = RestoreMapCodeUtils::FileInfosToMapCode(mediaLibraryRdb, fileInfos); + EXPECT_EQ(ret, E_OK); + + fileInfos.clear(); + ret = RestoreMapCodeUtils::FileInfosToMapCode(mediaLibraryRdb, fileInfos); + EXPECT_EQ(ret, E_OK); + + fileInfo.fileIdNew = 2; + fileInfo.latitude = 0.0; + fileInfo.longitude = 0.0; + fileInfos.push_back(fileInfo); + ret = RestoreMapCodeUtils::FileInfosToMapCode(mediaLibraryRdb, fileInfos); + EXPECT_EQ(ret, E_OK); + + fileInfos.clear(); + fileInfo.fileIdNew = -2; + fileInfo.latitude = 10.0; + fileInfo.longitude = 120.0; + fileInfos.push_back(fileInfo); + ret = RestoreMapCodeUtils::FileInfosToMapCode(mediaLibraryRdb, fileInfos); + EXPECT_EQ(ret, E_OK); +} + +HWTEST_F(MediaLibraryBackupCloneMapCodeTest, medialibrary_backup_files_to_mapcode_test_0011, TestSize.Level2) +{ + FileInfo fileInfo; + vector fileInfos; + fileInfo.fileIdNew = 33; + fileInfo.latitude = 10.0; + fileInfo.longitude = 120.0; + fileInfos.push_back(fileInfo); + int32_t ret = RestoreMapCodeUtils::FileInfosToMapCode(mediaLibraryRdb, fileInfos); + EXPECT_EQ(ret, E_OK); + + ret = MapCodeUploadChecker::QueryMapCodeCount(1); + EXPECT_EQ(ret > 0, E_OK); + std::vector result1 = MapCodeUploadChecker::QueryMapCodeInfo(1); + EXPECT_EQ(result1.size() > 0, true); + + int32_t curFileId = 1; + MapCodeUploadChecker::HandleMapCodeInfos(result1, curFileId); + EXPECT_EQ(curFileId > 1, true); + + ret = MapCodeUploadChecker::QueryMapCodeCount(100); + EXPECT_NE(ret, E_OK); + std::vector result100 = MapCodeUploadChecker::QueryMapCodeInfo(1); + EXPECT_EQ(result100.size() > 0, false); + curFileId = 100; + MapCodeUploadChecker::HandleMapCodeInfos(result100, curFileId); + EXPECT_EQ(curFileId == 100, true); + + errCode = g_rdbStore->ExecuteSql(PhotoMapCodeColumn::CREATE_MAP_CODE_TABLE); + EXPECT_EQ(errCode, E_OK); +} + +HWTEST_F(MediaLibraryBackupCloneMapCodeTest, medialibrary_backup_files_to_mapcode_test_0012, TestSize.Level2) +{ + InsertDataToPhotos(); + + FileInfo fileInfo; + vector fileInfos; + fileInfo.fileIdNew = 31; + fileInfo.latitude = 30.12; + fileInfo.longitude = 130.11; + fileInfos.push_back(fileInfo); + int32_t ret = RestoreMapCodeUtils::FileInfosToMapCode(mediaLibraryRdb, fileInfos); + EXPECT_EQ(ret, E_OK); + + mediaLibraryRdb = MediaLibraryUnistoreManager::GetInstance().GetRdbStore()->GetRaw(); + ret = RestoreMapCodeUtils::FileInfosToMapCode(mediaLibraryRdb, fileInfos); + EXPECT_EQ(ret, E_OK); + + g_rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore(); + ret = RestoreMapCodeUtils::FileInfosToMapCode(mediaLibraryRdb, fileInfos); + EXPECT_EQ(ret, E_OK); + + fileInfos.clear(); + ret = RestoreMapCodeUtils::FileInfosToMapCode(mediaLibraryRdb, fileInfos); + EXPECT_EQ(ret, E_OK); + + fileInfo.fileIdNew = 2; + fileInfo.latitude = 0.0; + fileInfo.longitude = 0.0; + fileInfos.push_back(fileInfo); + ret = RestoreMapCodeUtils::FileInfosToMapCode(mediaLibraryRdb, fileInfos); + EXPECT_EQ(ret, E_OK); + + fileInfos.clear(); + fileInfo.fileIdNew = -2; + fileInfo.latitude = 10.0; + fileInfo.longitude = 120.0; + fileInfos.push_back(fileInfo); + ret = RestoreMapCodeUtils::FileInfosToMapCode(mediaLibraryRdb, fileInfos); + EXPECT_EQ(ret, E_OK); +} + +HWTEST_F(MediaLibraryBackupCloneMapCodeTest, medialibrary_backup_files_to_mapcode_test_0013, TestSize.Level2) +{ + InsertDataToPhotos(); + + FileInfo fileInfo; + vector fileInfos; + fileInfo.fileIdNew = 33; + fileInfo.latitude = 10.0; + fileInfo.longitude = 120.0; + fileInfos.push_back(fileInfo); + int32_t ret = RestoreMapCodeUtils::FileInfosToMapCode(mediaLibraryRdb, fileInfos); + EXPECT_EQ(ret, E_OK); + + ret = MapCodeUploadChecker::QueryMapCodeCount(1); + EXPECT_EQ(ret > 0, E_OK); + result1 = MapCodeUploadChecker::QueryMapCodeInfo(1); + EXPECT_EQ(result1.size() > 0, true); + + int32_t curFileId = 1; + MapCodeUploadChecker::HandleMapCodeInfos(result1, curFileId); + EXPECT_EQ(curFileId > 1, true); + + ret = MapCodeUploadChecker::QueryMapCodeCount(100); + EXPECT_NE(ret, E_OK); + result100 = MapCodeUploadChecker::QueryMapCodeInfo(1); + EXPECT_EQ(result100.size() > 0, false); + curFileId = 100; + MapCodeUploadChecker::HandleMapCodeInfos(result100, curFileId); + EXPECT_EQ(curFileId == 100, true); +} + +HWTEST_F(MediaLibraryBackupCloneMapCodeTest, medialibrary_backup_files_to_mapcode_test_002, TestSize.Level2) +{ + g_rdbStore = nullptr; + mediaLibraryRdb = nullptr; + FileInfo fileInfo; + fileInfo.fileIdNew = 4; + fileInfo.latitude = 30.12; + fileInfo.longitude = 130.11; + int32_t ret = RestoreMapCodeUtils::FileInfoToMapCode(fileInfo, mediaLibraryRdb); + EXPECT_EQ(ret, E_OK); + + mediaLibraryRdb = MediaLibraryUnistoreManager::GetInstance().GetRdbStore()->GetRaw(); + ret = RestoreMapCodeUtils::FileInfoToMapCode(fileInfo, mediaLibraryRdb); + EXPECT_EQ(ret, E_OK); + + g_rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore(); + ret = RestoreMapCodeUtils::FileInfoToMapCode(fileInfo, mediaLibraryRdb); + EXPECT_EQ(ret, E_OK); + + fileInfo.fileIdNew = 5; + fileInfo.latitude = 0.0; + fileInfo.longitude = 0.0; + ret = RestoreMapCodeUtils::FileInfoToMapCode(fileInfo, mediaLibraryRdb); + EXPECT_EQ(ret, E_OK); + + fileInfo.fileIdNew = -5; + fileInfo.latitude = 10.0; + fileInfo.longitude = 120.0; + ret = RestoreMapCodeUtils::FileInfoToMapCode(fileInfo, mediaLibraryRdb); + EXPECT_EQ(ret, E_OK); + + fileInfo.fileIdNew = 6; + fileInfo.latitude = 10.0; + fileInfo.longitude = 120.0; + ret = RestoreMapCodeUtils::FileInfoToMapCode(fileInfo, mediaLibraryRdb); + EXPECT_EQ(ret, E_OK); +} + +HWTEST_F(MediaLibraryBackupCloneMapCodeTest, medialibrary_backup_files_delete_mapcode_test_001, TestSize.Level2) +{ + vector idList; + vector idListTest = {"DeleteMetadata"}; + + bool ret = RestoreMapCodeUtils::DeleteMapCodesByFileIds(idList); + EXPECT_NE(ret, true); + + ret = RestoreMapCodeUtils::DeleteMapCodesByFileIds(idListTest); + EXPECT_NE(ret, true); + + vector idListTestFive = {"7", "8"}; + ret = RestoreMapCodeUtils::DeleteMapCodesByFileIds(idListTestFive); + EXPECT_NE(ret, true); + + vector idListTestTwo = {"9"}; + ret = RestoreMapCodeUtils::DeleteMapCodesByFileIds(idListTestTwo); + EXPECT_NE(ret, true); + + vector idListTestThree = {"10", "99"}; + ret = RestoreMapCodeUtils::DeleteMapCodesByFileIds(idListTestThree); + EXPECT_NE(ret, true); + + vector idListTestFour = {"11", "DeleteMetadata"}; + ret = RestoreMapCodeUtils::DeleteMapCodesByFileIds(idListTestFour); + EXPECT_NE(ret, true); + + vector idListTestSix = {"99", "DeleteMetadata"}; + ret = RestoreMapCodeUtils::DeleteMapCodesByFileIds(idListTestSix); + EXPECT_NE(ret, true); + + vector idListTestSeven = {"12", "13", "14"}; + ret = RestoreMapCodeUtils::DeleteMapCodesByFileIds(idListTestSeven); + EXPECT_NE(ret, true); +} +} // namespace Media +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/innerkitsimpl/test/unittest/medialibrary_backup_test/BUILD.gn b/frameworks/innerkitsimpl/test/unittest/medialibrary_backup_test/BUILD.gn index 36b401835a..ad98f4347c 100755 --- a/frameworks/innerkitsimpl/test/unittest/medialibrary_backup_test/BUILD.gn +++ b/frameworks/innerkitsimpl/test/unittest/medialibrary_backup_test/BUILD.gn @@ -107,6 +107,9 @@ ohos_unittest("medialibrary_backup_test") { "./src/restore/photos_restore_test.cpp", "./src/restore/tab_old_photos_restore_test.cpp", "./src/utils/database_utils.cpp", + "${MEDIALIB_SERVICES_PATH}/media_backup_extension/src/restore_map_code_utils.cpp", + "${MEDIALIB_INNERKITS_PATH}/medialibrary_data_extension/src/operation/photo_map_code_column.cpp", + "${MEDIALIB_INNERKITS_PATH}/medialibrary_data_extension/src/operation/photo_map_code_operation.cpp", ] source_ipc_client = [ diff --git a/frameworks/innerkitsimpl/test/unittest/medialibrary_backup_test/src/medialibrary_backup_test.cpp b/frameworks/innerkitsimpl/test/unittest/medialibrary_backup_test/src/medialibrary_backup_test.cpp index 4a8131c449..e6a4cbbc9c 100644 --- a/frameworks/innerkitsimpl/test/unittest/medialibrary_backup_test/src/medialibrary_backup_test.cpp +++ b/frameworks/innerkitsimpl/test/unittest/medialibrary_backup_test/src/medialibrary_backup_test.cpp @@ -41,6 +41,7 @@ #undef private #undef protected #include "mimetype_utils.h" +#include "restore_map_code_utils.h" using namespace std; using namespace OHOS; diff --git a/frameworks/innerkitsimpl/test/unittest/medialibrary_cloud_asset_download_test/src/medialibrary_cloud_asset_download_test.cpp b/frameworks/innerkitsimpl/test/unittest/medialibrary_cloud_asset_download_test/src/medialibrary_cloud_asset_download_test.cpp index e6ce4913f3..43363a95c9 100644 --- a/frameworks/innerkitsimpl/test/unittest/medialibrary_cloud_asset_download_test/src/medialibrary_cloud_asset_download_test.cpp +++ b/frameworks/innerkitsimpl/test/unittest/medialibrary_cloud_asset_download_test/src/medialibrary_cloud_asset_download_test.cpp @@ -558,6 +558,9 @@ HWTEST_F(MediaLibraryCloudAssetDownloadTest, cloud_asset_download_manager_test_0 ret = instance.UpdateBothLocalAndCloudAssets(); EXPECT_EQ(ret, E_OK); + ret = instance.ClearDeletedMapData(); + EXPECT_EQ(ret, E_OK); + ret = instance.ClearDeletedDbData(); EXPECT_EQ(ret, E_OK); MEDIA_INFO_LOG("cloud_asset_download_manager_test_015 End"); diff --git a/frameworks/innerkitsimpl/test/unittest/medialibrary_scanner_db_test/BUILD.gn b/frameworks/innerkitsimpl/test/unittest/medialibrary_scanner_db_test/BUILD.gn index 0b8a92d889..7fbfa6c84d 100644 --- a/frameworks/innerkitsimpl/test/unittest/medialibrary_scanner_db_test/BUILD.gn +++ b/frameworks/innerkitsimpl/test/unittest/medialibrary_scanner_db_test/BUILD.gn @@ -33,6 +33,10 @@ ohos_unittest("medialibrary_scanner_db_test") { "./src/medialibrary_media_scanner_test.cpp", "./src/medialibrary_metadata_extractor_test.cpp", "./src/medialibrary_scanner_db_test.cpp", + "./src/medialibrary_scanner_mapcode_db_test.cpp", + "${MEDIALIB_SERVICES_PATH}/media_scanner/src/scanner/scanner_map_code_utils.cpp", + "${MEDIALIB_INNERKITS_PATH}/medialibrary_data_extension/src/operation/photo_map_code_column.cpp", + "${MEDIALIB_INNERKITS_PATH}/medialibrary_data_extension/src/operation/photo_map_code_operation.cpp", ] deps = [ "${MEDIALIB_INNERKITS_PATH}/media_library_helper:media_library", diff --git a/frameworks/innerkitsimpl/test/unittest/medialibrary_scanner_db_test/include/medialibrary_scanner_mapcode_db_test.h b/frameworks/innerkitsimpl/test/unittest/medialibrary_scanner_db_test/include/medialibrary_scanner_mapcode_db_test.h new file mode 100644 index 0000000000..f4038cc0a1 --- /dev/null +++ b/frameworks/innerkitsimpl/test/unittest/medialibrary_scanner_db_test/include/medialibrary_scanner_mapcode_db_test.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2023 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. + */ + +#ifndef MEDIALIBRARY_SCANNER_MAPCODE_UNIT_TEST_H +#define MEDIALIBRARY_SCANNER_MAPCODE_UNIT_TEST_H + +#include + +namespace OHOS { +namespace Media { +class MediaLibraryScannerMapCodeTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; +} // namespace Media +} // namespace OHOS +#endif // MEDIALIBRARY_SCANNER_MAPCODE_UNIT_TEST_H \ No newline at end of file diff --git a/frameworks/innerkitsimpl/test/unittest/medialibrary_scanner_db_test/src/medialibrary_scanner_mapcode_db_test.cpp b/frameworks/innerkitsimpl/test/unittest/medialibrary_scanner_db_test/src/medialibrary_scanner_mapcode_db_test.cpp new file mode 100644 index 0000000000..c8dda1fcd5 --- /dev/null +++ b/frameworks/innerkitsimpl/test/unittest/medialibrary_scanner_db_test/src/medialibrary_scanner_mapcode_db_test.cpp @@ -0,0 +1,414 @@ +/* + * Copyright (C) 2023 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. + */ +#include +#include + +#include "medialibrary_errno.h" +#include "medialibrary_object_utils.h" +#include "medialibrary_scanner_mapcode_db_test.h" +#include "medialibrary_unittest_utils.h" +#include "thumbnail_utils.h" +#define private public +#include "media_scanner_db.h" +#undef private + +#include "scanner_map_code_utils.h" +#include "photo_map_code_column.h" +#include "photo_map_code_operation.h" + +using namespace std; +using namespace OHOS; +using namespace testing::ext; + +namespace OHOS { +namespace Media { +static shared_ptr g_rdbStore = nullptr; + +void SetTestTables() +{ + vector createTableSqlList = { + PhotoColumn::CREATE_PHOTO_TABLE, + PhotoMapCodeColumn::CREATE_MAP_CODE_TABLE, + }; + for (auto &createTableSql : createTableSqlList) { + int32_t ret = g_rdbStore->ExecuteSql(createTableSql); + if (ret != NativeRdb::E_OK) { + MEDIA_ERR_LOG("Execute sql %{private}s failed", createTableSql.c_str()); + return; + } + MEDIA_DEBUG_LOG("Execute sql %{private}s success", createTableSql.c_str()); + } +} + +void CleanTestTables() +{ + vector dropTableList = { + PhotoColumn::PHOTOS_TABLE, + PhotoMapCodeColumn::PHOTOS_MAP_CODE_TABLE, + }; + for (auto &dropTable : dropTableList) { + string dropSql = "DROP TABLE " + dropTable + ";"; + int32_t ret = g_rdbStore->ExecuteSql(dropSql); + if (ret != NativeRdb::E_OK) { + MEDIA_ERR_LOG("Drop %{public}s table failed", dropTable.c_str()); + return; + } + MEDIA_DEBUG_LOG("Drop %{public}s table success", dropTable.c_str()); + } +} + +static int32_t ClearTable(const string &table) +{ + NativeRdb::RdbPredicates predicates(table); + + int32_t rows = 0; + int32_t err = g_rdbStore->Delete(rows, predicates); + if (err != E_OK) { + MEDIA_ERR_LOG("Failed to clear album table, err: %{public}d", err); + return E_HAS_DB_ERROR; + } + return E_OK; +} + +void MediaLibraryScannerMapCodeTest::SetUpTestCase(void) +{ + MediaLibraryUnitTestUtils::Init(); + g_rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore(); + SetTestTables(); + ClearTable(PhotoMapCodeColumn::CREATE_MAP_CODE_TABLE); + + // MediaLibraryUnitTestUtils::CleanTestFiles(); + // MediaLibraryUnitTestUtils::CleanBundlePermission(); +} + +void MediaLibraryScannerMapCodeTest::TearDownTestCase(void) +{ + // std::this_thread::sleep_for(std::chrono::seconds(SLEEP_FIVE_SECONDS)); + // 数据库数据清空 + ClearTable(PhotoMapCodeColumn::CREATE_MAP_CODE_TABLE); + ClearTable(PhotoColumn::PHOTOS_TABLE); +} + +// SetUp:Execute before each test case +void MediaLibraryScannerMapCodeTest::SetUp() {} + +void MediaLibraryScannerMapCodeTest::TearDown(void) {} + +HWTEST_F(MediaLibraryScannerMapCodeTest, medialib_DeleteMapCode_test_001, TestSize.Level1) +{ + vector idList; + vector idListTest = {"DeleteMetadata"}; + + bool ret = ScannerMapCodeUtils::DeleteMapCodesByFileIds(idList); + EXPECT_EQ(ret, true); + + ret = ScannerMapCodeUtils::DeleteMapCodesByFileIds(idListTest); + EXPECT_EQ(ret, true); + + vector idListTestFive = {"1", "2"}; + ret = ScannerMapCodeUtils::DeleteMapCodesByFileIds(idListTestFive); + EXPECT_EQ(ret, true); + + vector idListTestTwo = {"3"}; + ret = ScannerMapCodeUtils::DeleteMapCodesByFileIds(idListTestTwo); + EXPECT_EQ(ret, true); + + vector idListTestThree = {"4", "99"}; + ret = ScannerMapCodeUtils::DeleteMapCodesByFileIds(idListTestThree); + EXPECT_EQ(ret, true); + + vector idListTestFour = {"5", "DeleteMetadata"}; + ret = ScannerMapCodeUtils::DeleteMapCodesByFileIds(idListTestFour); + EXPECT_EQ(ret, true); + + vector idListTestSix = {"99", "DeleteMetadata"}; + ret = ScannerMapCodeUtils::DeleteMapCodesByFileIds(idListTestSix); + EXPECT_EQ(ret, true); + + vector idListTestSeven = {"6", "7", "8", "9", "10"}; + ret = ScannerMapCodeUtils::DeleteMapCodesByFileIds(idListTestSeven); + EXPECT_EQ(ret, true); +} + +HWTEST_F(MediaLibraryScannerMapCodeTest, medialib_InsertMapCode_test_001, TestSize.Level1) +{ + Metadata metadata; + int32_t fileId = -1; + metadata.SetFileId(fileId); + bool ret = ScannerMapCodeUtils::MetadataToMapCode(metadata); + EXPECT_EQ(ret, true); + + fileId = 1; + metadata.SetFileId(fileId); + ret = ScannerMapCodeUtils::MetadataToMapCode(metadata); + EXPECT_EQ(ret, true); + + fileId = -1; + double latitude = 0.0; + double longitude = 0.0; + metadata.SetFileId(fileId); + metadata.SetLatitude(latitude); + metadata.SetLongitude(longitude); + ret = ScannerMapCodeUtils::MetadataToMapCode(metadata); + EXPECT_EQ(ret, true); + + fileId = -1; + latitude = 30.0; + longitude = 120.0; + metadata.SetFileId(fileId); + metadata.SetLatitude(latitude); + metadata.SetLongitude(longitude); + ret = ScannerMapCodeUtils::MetadataToMapCode(metadata); + EXPECT_EQ(ret, true); + + fileId = 1; + latitude = 30.0; + longitude = 120.0; + metadata.SetFileId(fileId); + metadata.SetLatitude(latitude); + metadata.SetLongitude(longitude); + ret = ScannerMapCodeUtils::MetadataToMapCode(metadata); + EXPECT_EQ(ret, true); + + fileId = 2; + latitude = -30.0; + longitude = 120.0; + metadata.SetFileId(fileId); + metadata.SetLatitude(latitude); + metadata.SetLongitude(longitude); + ret = ScannerMapCodeUtils::MetadataToMapCode(metadata); + EXPECT_EQ(ret, true); +} + +HWTEST_F(MediaLibraryScannerMapCodeTest, medialib_InsertMapCode_test_0011, TestSize.Level1) +{ + Metadata metadata; + int32_t fileId = 3; + double latitude = -30.0; + double longitude = -120.0; + metadata.SetFileId(fileId); + metadata.SetLatitude(latitude); + metadata.SetLongitude(longitude); + bool ret = ScannerMapCodeUtils::MetadataToMapCode(metadata); + EXPECT_EQ(ret, true); + + fileId = 4; + latitude = 0.0; + longitude = 120.0; + metadata.SetFileId(fileId); + metadata.SetLatitude(latitude); + metadata.SetLongitude(longitude); + ret = ScannerMapCodeUtils::MetadataToMapCode(metadata); + EXPECT_EQ(ret, true); + + fileId = 5; + latitude = 30.0; + longitude = 0.0; + metadata.SetFileId(fileId); + metadata.SetLatitude(latitude); + metadata.SetLongitude(longitude); + ret = ScannerMapCodeUtils::MetadataToMapCode(metadata); + EXPECT_EQ(ret, true); + + fileId = 6; + latitude = 0.0; + longitude = 0.0; + metadata.SetFileId(fileId); + metadata.SetLatitude(latitude); + metadata.SetLongitude(longitude); + ret = ScannerMapCodeUtils::MetadataToMapCode(metadata); + EXPECT_EQ(ret, true); + + fileId = 6; + latitude = 40.0; + longitude = 110.0; + metadata.SetFileId(fileId); + metadata.SetLatitude(latitude); + metadata.SetLongitude(longitude); + ret = ScannerMapCodeUtils::MetadataToMapCode(metadata); + EXPECT_EQ(ret, true); +} + +HWTEST_F(MediaLibraryScannerMapCodeTest, medialib_InsertMapCode_test_002, TestSize.Level1) +{ + int32_t fileId = -1; + double latitude = -50.0; + double longitude = -130.0; + PhotoMapData photoMapData(fileId, latitude, longitude); + int32_t result = PhotoMapCodeOperation::GetPhotoMapCode(photoMapData, PhotoMapType::UPDATE_AND_INSERT); + EXPECT_EQ(result, E_OK); + + fileId = 8; + latitude = -50.0; + longitude = -130.0; + PhotoMapData photoMapData1(fileId, latitude, longitude); + result = PhotoMapCodeOperation::GetPhotoMapCode(photoMapData1, PhotoMapType::UPDATE_AND_INSERT); + EXPECT_EQ(result, E_OK); + + fileId = 9; + latitude = -50.0; + longitude = -130.0; + PhotoMapData photoMapData2(fileId, latitude, longitude); + result = PhotoMapCodeOperation::GetPhotoMapCode(photoMapData2, PhotoMapType::QUERY_AND_INSERT); + EXPECT_EQ(result, E_OK); + + fileId = 10; + latitude = -50.0; + longitude = -130.0; + PhotoMapData photoMapData3(fileId, latitude, longitude); + result = PhotoMapCodeOperation::GetPhotoMapCode(photoMapData3, PhotoMapType::QUERY_AND_INSERT); + EXPECT_EQ(result, E_OK); +} + +HWTEST_F(MediaLibraryScannerMapCodeTest, medialib_UpdateMapCode_test_001, TestSize.Level1) +{ + Metadata metadata; + int32_t fileId = 6; + double latitude = 50.0; + double longitude = 130.0; + metadata.SetFileId(fileId); + metadata.SetLatitude(latitude); + metadata.SetLongitude(longitude); + bool ret = ScannerMapCodeUtils::MetadataToMapCode(metadata); + EXPECT_EQ(ret, true); + + fileId = 7; + latitude = -50.0; + longitude = -130.0; + metadata.SetFileId(fileId); + metadata.SetLatitude(latitude); + metadata.SetLongitude(longitude); + ret = ScannerMapCodeUtils::MetadataToMapCode(metadata); + EXPECT_EQ(ret, true); +} + +HWTEST_F(MediaLibraryScannerMapCodeTest, medialib_UpdateMapCode_test_002, TestSize.Level1) +{ + int32_t fileId = 8; + double latitude = -50.0; + double longitude = -130.0; + PhotoMapData photoMapData(fileId, latitude, longitude); + int32_t result = PhotoMapCodeOperation::GetPhotoMapCode(photoMapData, PhotoMapType::UPDATE_AND_INSERT); + EXPECT_EQ(result, E_OK); + + fileId = 8; + latitude = -50.0; + longitude = -130.0; + PhotoMapData photoMapData1(fileId, latitude, longitude); + result = PhotoMapCodeOperation::GetPhotoMapCode(photoMapData1, PhotoMapType::QUERY_AND_INSERT); + EXPECT_EQ(result, E_OK); + + fileId = 9; + latitude = -50.0; + longitude = -130.0; + PhotoMapData photoMapData2(fileId, latitude, longitude); + result = PhotoMapCodeOperation::GetPhotoMapCode(photoMapData2, PhotoMapType::UPDATE_AND_INSERT); + EXPECT_EQ(result, E_OK); + + fileId = 9; + latitude = -50.0; + longitude = -130.0; + PhotoMapData photoMapData3(fileId, latitude, longitude); + result = PhotoMapCodeOperation::GetPhotoMapCode(photoMapData3, PhotoMapType::QUERY_AND_INSERT); + EXPECT_EQ(result, E_OK); +} + +HWTEST_F(MediaLibraryScannerMapCodeTest, medialib_DeleteMapCode_test_002, TestSize.Level1) +{ + MediaScannerDb mediaScannerDb; + vector idList; + vector idListTest = {"DeleteMetadata"}; + + bool ret = ScannerMapCodeUtils::DeleteMapCodesByFileIds(idList); + EXPECT_EQ(ret, true); + + ret = ScannerMapCodeUtils::DeleteMapCodesByFileIds(idListTest); + EXPECT_EQ(ret, true); + + vector idListTestFive = {"1", "2"}; + ret = ScannerMapCodeUtils::DeleteMapCodesByFileIds(idListTestFive); + EXPECT_EQ(ret, true); + + vector idListTestTwo = {"3"}; + ret = ScannerMapCodeUtils::DeleteMapCodesByFileIds(idListTestTwo); + EXPECT_EQ(ret, true); + + vector idListTestThree = {"4", "99"}; + ret = ScannerMapCodeUtils::DeleteMapCodesByFileIds(idListTestThree); + EXPECT_EQ(ret, true); + + vector idListTestFour = {"5", "DeleteMetadata"}; + ret = ScannerMapCodeUtils::DeleteMapCodesByFileIds(idListTestFour); + EXPECT_EQ(ret, true); + + vector idListTestSix = {"99", "DeleteMetadata"}; + ret = ScannerMapCodeUtils::DeleteMapCodesByFileIds(idListTestSix); + EXPECT_EQ(ret, true); +} + +HWTEST_F(MediaLibraryScannerMapCodeTest, medialib_DeleteMapCode_test_003, TestSize.Level1) +{ + MediaScannerDb mediaScannerDb; + vector idList; + vector idListTest = {"DeleteMetadata"}; + + int32_t ret = PhotoMapCodeOperation::RemovePhotosMapCodes(idList); + EXPECT_EQ(ret, E_OK); + + ret = PhotoMapCodeOperation::RemovePhotosMapCodes(idListTest); + EXPECT_EQ(ret, E_OK); + + vector idListTest1 = {"1", "2"}; + ret = PhotoMapCodeOperation::RemovePhotosMapCodes(idListTest1); + EXPECT_EQ(ret, E_OK); + + vector idListTest2 = {"3"}; + ret = PhotoMapCodeOperation::RemovePhotosMapCodes(idListTest2); + EXPECT_EQ(ret, E_OK); + + vector idListTest3 = {"4", "99"}; + ret = PhotoMapCodeOperation::RemovePhotosMapCodes(idListTest3); + EXPECT_EQ(ret, E_OK); + + vector idListTest4 = {"5", "DeleteMetadata"}; + ret = PhotoMapCodeOperation::RemovePhotosMapCodes(idListTest4); + EXPECT_EQ(ret, E_OK); + + vector idListTestFive = {"6", "7"}; + ret = PhotoMapCodeOperation::RemovePhotosMapCodes(idListTestFive); + EXPECT_EQ(ret, E_OK); + + vector idListTestTwo = {"8"}; + ret = PhotoMapCodeOperation::RemovePhotosMapCodes(idListTestTwo); + EXPECT_EQ(ret, E_OK); + + vector idListTestThree = {"9", "99"}; + ret = PhotoMapCodeOperation::RemovePhotosMapCodes(idListTestThree); + EXPECT_EQ(ret, E_OK); + + vector idListTestFour = {"10", "DeleteMetadata"}; + ret = PhotoMapCodeOperation::RemovePhotosMapCodes(idListTestFour); + EXPECT_EQ(ret, E_OK); + + vector idListTestSix = {"99", "DeleteMetadata"}; + ret = PhotoMapCodeOperation::RemovePhotosMapCodes(idListTestSix); + EXPECT_EQ(ret, E_OK); + + vector idListTestSeven = {"11", "12", "13"}; + ret = PhotoMapCodeOperation::RemovePhotosMapCodes(idListTestSeven); + EXPECT_EQ(ret, E_OK); +} +} // namespace Media +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/services/media_backup_extension/BUILD.gn b/frameworks/services/media_backup_extension/BUILD.gn index ad4d6cbf99..e8c80909d7 100755 --- a/frameworks/services/media_backup_extension/BUILD.gn +++ b/frameworks/services/media_backup_extension/BUILD.gn @@ -104,6 +104,9 @@ ohos_shared_library("mediabackup") { "./src/restore/photos_restore.cpp", "./src/restore/tab_old_photos_restore.cpp", "./src/upgrade_restore.cpp", + "./src/restore_map_code_utils.cpp", + "${MEDIALIB_INNERKITS_PATH}/medialibrary_data_extension/src/operation/photo_map_code_column.cpp", + "${MEDIALIB_INNERKITS_PATH}/medialibrary_data_extension/src/operation/photo_map_code_operation.cpp", ] source_ipc_client = [ diff --git a/frameworks/services/media_backup_extension/include/restore_map_code_utils.h b/frameworks/services/media_backup_extension/include/restore_map_code_utils.h new file mode 100644 index 0000000000..d79db7729d --- /dev/null +++ b/frameworks/services/media_backup_extension/include/restore_map_code_utils.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2025 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. + */ + +#ifndef RESTORE_MAP_CODE_UTILS_H +#define RESTORE_MAP_CODE_UTILS_H + +#include "media_column.h" +#include "base_restore.h" +#include "backup_const.h" + +#include +#include +#include +#include +#include + +namespace OHOS { +namespace Media { +#define EXPORT __attribute__ ((visibility ("default"))) + +class RestoreMapCodeUtils { +public: + EXPORT static int32_t FileInfosToMapCode(const std::shared_ptr mediaLibraryRdb, + const vector &fileInfos); + EXPORT static int32_t FileInfoToMapCode(const FileInfo &fileInfo, + const std::shared_ptr mediaLibraryRdb); + + EXPORT static int64_t DeleteMapCodesByFileIds(const vector &fileIds); +}; +} // namespace Media +} // namespace OHOS + +#endif // RESTORE_MAP_CODE_UTILS_H diff --git a/frameworks/services/media_backup_extension/src/base_restore.cpp b/frameworks/services/media_backup_extension/src/base_restore.cpp index 771385eff6..d5e1046b4f 100644 --- a/frameworks/services/media_backup_extension/src/base_restore.cpp +++ b/frameworks/services/media_backup_extension/src/base_restore.cpp @@ -54,7 +54,7 @@ #include "medialibrary_rdb_transaction.h" #include "database_report.h" #include "ohos_account_kits.h" - +#include "restore_map_code_utils.h" namespace OHOS { namespace Media { @@ -1166,6 +1166,10 @@ int BaseRestore::InsertPhoto(int32_t sceneCode, std::vector &fileInfos startInsert - startGenerate, rowNum, startInsertRelated - startInsert, startMove - startInsertRelated, fileMoveCount, fileMoveCount - videoFileMoveCount, videoFileMoveCount, startRestore - startMove, startUpdate - startRestore, end - startUpdate); + + int32_t mapErrCode = RestoreMapCodeUtils::FileInfosToMapCode(mediaLibraryRdb_, fileInfos); + MEDIA_INFO_LOG("BaseRestore::InsertPhoto FileInfosToMapCode mapErrCode %{public}d photoRowNum %{public}lld", + mapErrCode, static_cast(rowNum)); return E_OK; } diff --git a/frameworks/services/media_backup_extension/src/clone_restore.cpp b/frameworks/services/media_backup_extension/src/clone_restore.cpp index 0f393f1765..a558dd5a1b 100644 --- a/frameworks/services/media_backup_extension/src/clone_restore.cpp +++ b/frameworks/services/media_backup_extension/src/clone_restore.cpp @@ -49,6 +49,7 @@ #include "ohos_account_kits.h" #include "media_config_info_column.h" #include "settings_data_manager.h" +#include "restore_map_code_utils.h" #ifdef CLOUD_SYNC_MANAGER #include "cloud_sync_manager.h" @@ -914,6 +915,10 @@ int CloneRestore::InsertPhoto(vector &fileInfos) (long)(startInsertPhoto - startGenerate), (long)photoRowNum, (long)(startInsertRelated - startInsertPhoto), (long)(startMove - startInsertRelated), (long)fileMoveCount, (long)(fileMoveCount - videoFileMoveCount), (long)videoFileMoveCount, (long)(startUpdate - startMove), (long)(end - startUpdate)); + + int32_t mapErrCode = RestoreMapCodeUtils::FileInfosToMapCode(mediaLibraryRdb_, fileInfos); + MEDIA_INFO_LOG("CloneRestore::InsertPhoto FileInfosToMapCode mapErrCode %{public}d \ + photoRowNum %{public}lld", mapErrCode, static_cast(photoRowNum)); return E_OK; } diff --git a/frameworks/services/media_backup_extension/src/others_clone_restore.cpp b/frameworks/services/media_backup_extension/src/others_clone_restore.cpp index 2350a275f1..b172a9f815 100644 --- a/frameworks/services/media_backup_extension/src/others_clone_restore.cpp +++ b/frameworks/services/media_backup_extension/src/others_clone_restore.cpp @@ -39,6 +39,7 @@ #include "media_scanner.h" #include "medialibrary_rdb_transaction.h" #include "upgrade_restore_task_report.h" +#include "restore_map_code_utils.h" namespace OHOS { namespace Media { @@ -925,6 +926,10 @@ void OthersCloneRestore::InsertPhoto(std::vector &fileInfos) (long)(startInsert - start), (long)rowNum, (long)(startMove - startInsert), (long)fileMoveCount, (long)(fileMoveCount - videoFileMoveCount), (long)videoFileMoveCount, (long)(startUpdate - startMove), (long)(end - startUpdate)); + + int32_t mapErrCode = RestoreMapCodeUtils::FileInfosToMapCode(mediaLibraryRdb_, fileInfos); + MEDIA_INFO_LOG("OthersCloneRestore::InsertPhoto FileInfosToMapCode mapErrCode \ + %{public}d rowNum %{public}lld", mapErrCode, static_cast(rowNum)); } bool OthersCloneRestore::NeedBatchQueryPhotoForPortrait(const std::vector &fileInfos, diff --git a/frameworks/services/media_backup_extension/src/restore_map_code_utils.cpp b/frameworks/services/media_backup_extension/src/restore_map_code_utils.cpp new file mode 100644 index 0000000000..82a7bee3a8 --- /dev/null +++ b/frameworks/services/media_backup_extension/src/restore_map_code_utils.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2025 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 RESTORE_MAP_LOG_TAG "RestoreMapCode" + +#include "restore_map_code_utils.h" +#include "media_column.h" +#include "photo_map_code_operation.h" + +#include "medialibrary_rdbstore.h" +#include "directory_ex.h" +#include "media_log.h" +#include "medialibrary_type_const.h" + +#include "application_context.h" +#include "backup_database_utils.h" +#include "backup_dfx_utils.h" +#include "backup_file_utils.h" +#include "backup_log_utils.h" +#include "cloud_sync_manager.h" +#include "cloud_sync_utils.h" +#include "directory_ex.h" +#include "extension_context.h" +#include "media_log.h" +#include "media_file_utils.h" +#include "media_scanner_manager.h" +#include "medialibrary_asset_operations.h" +#include "medialibrary_data_manager.h" +#include "medialibrary_object_utils.h" +#include "medialibrary_rdb_utils.h" +#include "medialibrary_type_const.h" +#include "medialibrary_errno.h" +#include "moving_photo_file_utils.h" +#include +#include "parameters.h" +#include "photo_album_column.h" +#include "result_set_utils.h" +#include "userfilemgr_uri.h" +#include "medialibrary_notify.h" +#include "upgrade_restore_task_report.h" +#include "medialibrary_rdb_transaction.h" +#include "database_report.h" +#include "ohos_account_kits.h" + +#include +#include +#include +#include +#include +#include + +namespace OHOS { +namespace Media { +using namespace std; +static constexpr double DOUBLE_EPSILON = 1e-15; +static constexpr double MAX_LATITUDE_EPSILON = 1e-15 + 90.0; +static constexpr double MAX_LONGITUDE_EPSILON = 1e-15 + 180.0; + +int32_t RestoreMapCodeUtils::FileInfosToMapCode(const std::shared_ptr mediaLibraryRdb, + const vector &fileInfos) +{ + MEDIA_DEBUG_LOG("RestoreMapCodeUtils::FileInfosToMapCode fileInfos size %{public}zu", fileInfos.size()); + vector photoMapDatas; + int64_t rowNum = 0; + for (const auto &fileInfo : fileInfos) { + double longitude = fileInfo.longitude; + double latitude = fileInfo.latitude; + int32_t fileId = fileInfo.fileIdNew; + if (fileId > 0 && fabs(longitude) > DOUBLE_EPSILON && fabs(latitude) > DOUBLE_EPSILON && + fabs(longitude) < MAX_LONGITUDE_EPSILON && fabs(latitude) < MAX_LATITUDE_EPSILON) { + PhotoMapData photoMapData(fileId, latitude, longitude); + photoMapDatas.emplace_back(photoMapData); + } + } + + return PhotoMapCodeOperation::InsertPhotosMapCodes(photoMapDatas, mediaLibraryRdb); +} + +int32_t RestoreMapCodeUtils::FileInfoToMapCode(const FileInfo &fileInfo, + const std::shared_ptr mediaLibraryRdb) +{ + MEDIA_DEBUG_LOG("RestoreMapCodeUtils::FileInfoToMapCode"); + vector photoMapDatas; + int64_t rowNum = 0; + double longitude = fileInfo.longitude; + double latitude = fileInfo.latitude; + int32_t fileId = fileInfo.fileIdNew; + if (fileId > 0 && fabs(longitude) > DOUBLE_EPSILON && fabs(latitude) > DOUBLE_EPSILON && + fabs(longitude) < MAX_LONGITUDE_EPSILON && fabs(latitude) < MAX_LATITUDE_EPSILON) { + PhotoMapData photoMapData(fileId, latitude, longitude); + photoMapDatas.emplace_back(photoMapData); + } + + return PhotoMapCodeOperation::InsertPhotosMapCodes(photoMapDatas, mediaLibraryRdb); +} + +int64_t RestoreMapCodeUtils::DeleteMapCodesByFileIds(const vector &fileIds) +{ + return PhotoMapCodeOperation::RemovePhotosMapCodes(fileIds); +} +} // namespace Media +} // namespace OHOS diff --git a/frameworks/services/media_cloud_sync/include/media_cloud_asset_download/cloud_media_asset_manager.h b/frameworks/services/media_cloud_sync/include/media_cloud_asset_download/cloud_media_asset_manager.h index f5582ac619..31121af5f0 100644 --- a/frameworks/services/media_cloud_sync/include/media_cloud_asset_download/cloud_media_asset_manager.h +++ b/frameworks/services/media_cloud_sync/include/media_cloud_asset_download/cloud_media_asset_manager.h @@ -89,6 +89,7 @@ private: EXPORT int32_t ClearDeletedDbData(); EXPORT int32_t ForceRetainDownloadCloudMediaEx(CloudMediaRetainType retainType); + EXPORT int32_t ClearDeletedMapData(); private: std::shared_ptr operation_{nullptr}; inline static std::atomic doDeleteTask_{TaskDeleteState::IDLE}; diff --git a/frameworks/services/media_cloud_sync/src/media_cloud_asset_download/cloud_media_asset_manager.cpp b/frameworks/services/media_cloud_sync/src/media_cloud_asset_download/cloud_media_asset_manager.cpp index ea09085391..e461fe79e7 100644 --- a/frameworks/services/media_cloud_sync/src/media_cloud_asset_download/cloud_media_asset_manager.cpp +++ b/frameworks/services/media_cloud_sync/src/media_cloud_asset_download/cloud_media_asset_manager.cpp @@ -56,6 +56,13 @@ #include "cloud_media_asset_uri.h" #include "dfx_const.h" +#include "scanner_map_code_utils.h" +#include "photos_po.h" +#include "result_set_reader.h" +#include "photos_po_writer.h" +#include "photo_map_code_column.h" +#include "photo_map_code_operation.h" + using namespace std; using namespace OHOS::NativeRdb; @@ -406,6 +413,10 @@ void CloudMediaAssetManager::DeleteAllCloudMediaAssetsOperation(AsyncTaskData *d break; } ret = DeleteBatchCloudFile(fileIds); + + bool retMap = ScannerMapCodeUtils::DeleteMapCodesByFileIds(fileIds); + CHECK_AND_BREAK_ERR_LOG(retMap, "DeleteMapCodesByFileIds failed!"); + CHECK_AND_BREAK_ERR_LOG(ret == E_OK, "DeleteBatchCloudFile failed!"); for (size_t i = 0; i < fileIds.size(); i++) { CHECK_AND_PRINT_LOG(DeleteEditdata(paths[i]) == E_OK, "DeleteEditdata error."); @@ -659,12 +670,67 @@ int32_t CloudMediaAssetManager::ClearDeletedDbData() return E_OK; } +int32_t CloudMediaAssetManager::ClearDeletedMapData() +{ + MEDIA_INFO_LOG("start ClearDeletedMapData."); + MediaLibraryTracer tracer; + tracer.Start("ClearDeletedMapData"); + + auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore(); + CHECK_AND_RETURN_RET_LOG(rdbStore != nullptr, E_ERR, "ClearDeletedMapData failed. rdbStore is null."); + + AbsRdbPredicates predicates(PhotoColumn::PHOTOS_TABLE); + predicates.EqualTo(PhotoColumn::PHOTO_DIRTY, to_string(static_cast(DirtyType::TYPE_DELETED))); + predicates.Limit(CloudSync::LIMIT_SIZE); + + const std::vector COLUMNS_QUERY = { + PhotoColumn::MEDIA_ID, + }; + auto resultSet = rdbStore->Query(predicates, COLUMNS_QUERY); + CHECK_AND_RETURN_RET_LOG(resultSet != nullptr, E_RESULT_SET_NULL, "ClearDeletedMapData Failed to query."); + std::vector photosPos; + int32_t ret = ORM::ResultSetReader(resultSet).ReadRecords(photosPos); + if (photosPos.empty()) { + MEDIA_ERR_LOG("DeleteMapCodesByPullDatas to-deleted idList size equals to 0"); + return E_OK; + } + + vector fileIds; + for (const auto &photoPo : photosPos) { + if (!(photoPo.fileId.has_value())) { + MEDIA_ERR_LOG("DeleteMapCodesByPullData photosPos is null"); + continue; + } + int32_t fileId = photoPo.fileId.value(); + fileIds.push_back(std::to_string(fileId)); + } + + if (fileIds.empty()) { + MEDIA_ERR_LOG("DeleteMapCodesByPullDatas to-deleted fileIds size equals to 0"); + return E_OK; + } + + int32_t deletedRows = -1; + std::string mapTableName = PhotoMapCodeColumn::PHOTOS_MAP_CODE_TABLE; + NativeRdb::RdbPredicates rdbPredicate(mapTableName); + rdbPredicate.In(PhotoMapCodeColumn::MAPCODE_FILE_ID, fileIds); + ret = rdbStore->Delete(deletedRows, rdbPredicate); + + CHECK_AND_RETURN_RET_LOG((ret == E_OK && deletedRows >= 0), E_ERR, + "Failed to ClearDeletedDbData, ret: %{public}d, deletedRows: %{public}d", ret, deletedRows); + MEDIA_INFO_LOG("ClearDeletedDbData successfully. ret: %{public}d, deletedRows: %{public}d", ret, deletedRows); + return E_OK; +} + int32_t CloudMediaAssetManager::UpdateBothLocalAndCloudAssets(CloudMediaRetainType retainType) { MEDIA_INFO_LOG("start UpdateBothLocalAndCloudAssets."); MediaLibraryTracer tracer; tracer.Start("UpdateBothLocalAndCloudAssets"); + int32_t deleteMapRet = ClearDeletedMapData(); + MEDIA_INFO_LOG("ClearDeletedMapData result. deleteMapRet %{public}d.", deleteMapRet); + int32_t deleteRet = ClearDeletedDbData(); CHECK_AND_PRINT_LOG(deleteRet == E_OK, "ClearDeletedDbData failed. ret %{public}d.", deleteRet); diff --git a/frameworks/services/media_scanner/include/scanner/scanner_map_code_utils.h b/frameworks/services/media_scanner/include/scanner/scanner_map_code_utils.h new file mode 100644 index 0000000000..e5e98c896e --- /dev/null +++ b/frameworks/services/media_scanner/include/scanner/scanner_map_code_utils.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2025 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. + */ + +#ifndef SCAN_MAP_CODE_UTILS_H +#define SCAN_MAP_CODE_UTILS_H + +#include "metadata.h" + +#include +#include +#include +#include +#include + +namespace OHOS { +namespace Media { +#define EXPORT __attribute__ ((visibility ("default"))) + +class ScannerMapCodeUtils { +public: + EXPORT static bool MetadataToMapCode(const Metadata &metadata); + + EXPORT static bool DeleteMapCodesByFileIds(const vector &fileIds); +}; +} // namespace Media +} // namespace OHOS + +#endif // SCAN_MAP_CODE_UTILS_H diff --git a/frameworks/services/media_scanner/src/scanner/media_scanner.cpp b/frameworks/services/media_scanner/src/scanner/media_scanner.cpp index 795320b38d..224b7eee09 100644 --- a/frameworks/services/media_scanner/src/scanner/media_scanner.cpp +++ b/frameworks/services/media_scanner/src/scanner/media_scanner.cpp @@ -39,7 +39,7 @@ #include "asset_accurate_refresh.h" #include "medialibrary_photo_operations.h" #include "refresh_business_name.h" - +#include "scanner_map_code_utils.h" namespace OHOS { namespace Media { using namespace std; @@ -804,6 +804,7 @@ int32_t MediaScannerObj::CleanupDirectory() if (!deleteIdList.empty()) { mediaScannerDb_->DeleteMetadata(deleteIdList, table); + ScannerMapCodeUtils::DeleteMapCodesByFileIds(deleteIdList); } } diff --git a/frameworks/services/media_scanner/src/scanner/media_scanner_db.cpp b/frameworks/services/media_scanner/src/scanner/media_scanner_db.cpp index 474cb4e6fb..bc5a63dde7 100644 --- a/frameworks/services/media_scanner/src/scanner/media_scanner_db.cpp +++ b/frameworks/services/media_scanner/src/scanner/media_scanner_db.cpp @@ -41,6 +41,7 @@ #include "values_bucket.h" #include "post_event_utils.h" #include "photo_file_utils.h" +#include "scanner_map_code_utils.h" namespace OHOS { namespace Media { @@ -392,6 +393,11 @@ string MediaScannerDb::InsertMetadata(const Metadata &metadata, string &tableNam if (mediaTypeUri.empty()) { return ""; } + + if (!ScannerMapCodeUtils::MetadataToMapCode(metadata)) { + MEDIA_ERR_LOG("MediaDataAbility Insert MapCode failed, return"); + } + if (api == MediaLibraryApi::API_10) { return MediaFileUtils::GetUriByExtrConditions(mediaTypeUri + "/", to_string(rowNum), MediaFileUtils::GetExtraUri(metadata.GetFileName(), metadata.GetFilePath())) + "?api_version=10"; @@ -470,6 +476,11 @@ string MediaScannerDb::UpdateMetadata(const Metadata &metadata, string &tableNam } CHECK_AND_RETURN_RET(!mediaTypeUri.empty(), ""); + + if (!ScannerMapCodeUtils::MetadataToMapCode(metadata)) { + MEDIA_ERR_LOG("MediaDataAbility Insert MapCode failed, return"); + } + CHECK_AND_RETURN_RET(api != MediaLibraryApi::API_10, MakeFileUri(mediaTypeUri, metadata)); return MediaFileUtils::GetUriByExtrConditions(mediaTypeUri + "/", to_string(metadata.GetFileId())); } @@ -499,6 +510,9 @@ bool MediaScannerDb::DeleteMetadata(const vector &idList, const string & NativeRdb::RdbPredicates rdbPredicate(tableName); rdbPredicate.In(MEDIA_DATA_DB_ID, idList); int32_t ret = rdbStore->Delete(rdbPredicate); + if (ret && !ScannerMapCodeUtils::DeleteMapCodesByFileIds(idList)) { + MEDIA_ERR_LOG("MediaScannerDb DeleteMetadata MapCode failed, return"); + } return ret == static_cast(idList.size()); } diff --git a/frameworks/services/media_scanner/src/scanner/scanner_map_code_utils.cpp b/frameworks/services/media_scanner/src/scanner/scanner_map_code_utils.cpp new file mode 100644 index 0000000000..f4c9e00ef9 --- /dev/null +++ b/frameworks/services/media_scanner/src/scanner/scanner_map_code_utils.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2025 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 SCANNER_MAP_LOG_TAG "ScannerMapCode" + +#include "scanner_map_code_utils.h" +#include "media_column.h" +#include "photo_map_code_column.h" +#include "photo_map_code_operation.h" + +#include "medialibrary_rdbstore.h" +#include "directory_ex.h" +#include "media_log.h" +#include "medialibrary_type_const.h" +#include "medialibrary_unistore_manager.h" +#include "values_bucket.h" +#include "post_event_utils.h" +#include "abs_rdb_predicates.h" +#include "media_scanner_db.h" +#include "ipc_skeleton.h" +#include "medialibrary_asset_operations.h" +#include "media_error_column.h" +#include "media_file_utils.h" +#include "media_log.h" +#include "medialibrary_command.h" +#include "medialibrary_data_manager.h" +#include "medialibrary_db_const.h" +#include "medialibrary_errno.h" +#include "medialibrary_rdb_transaction.h" +#include "medialibrary_rdb_utils.h" +#include "medialibrary_smartalbum_map_operations.h" +#include "rdb_errno.h" +#include "rdb_utils.h" +#include "result_set.h" +#include "result_set_utils.h" +#include "userfile_manager_types.h" +#include "userfilemgr_uri.h" +#include "values_bucket.h" +#include "post_event_utils.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace OHOS { +namespace Media { +using namespace std; +static constexpr double DOUBLE_EPSILON = 1e-15; +static constexpr double MAX_LATITUDE_EPSILON = 1e-15 + 90.0; +static constexpr double MAX_LONGITUDE_EPSILON = 1e-15 + 180.0; + +bool ScannerMapCodeUtils::MetadataToMapCode(const Metadata &metadata) +{ + MEDIA_DEBUG_LOG("ScannerMapCodeUtils::MetadataToMapCode data_->GetFileId() %{private}d", + metadata.GetFileId()); + NativeRdb::ValuesBucket mapValue; + string whereMapClause = PhotoMapCodeColumn::MAPCODE_FILE_ID + " = ?"; + vector whereMapArgs = { to_string(metadata.GetFileId()) }; + + // 数据入库点 + double longitude = metadata.GetLongitude(); + double latitude = metadata.GetLatitude(); + const std::string mapTableName = PhotoMapCodeColumn::PHOTOS_MAP_CODE_TABLE; + + if (metadata.GetFileId() > 0 && + fabs(longitude) > DOUBLE_EPSILON && fabs(latitude) > DOUBLE_EPSILON && + fabs(longitude) < MAX_LONGITUDE_EPSILON && fabs(latitude) < MAX_LATITUDE_EPSILON) { + PhotoMapData photoMapData(metadata.GetFileId(), latitude, longitude); + int32_t ret = PhotoMapCodeOperation::GetPhotoMapCode(photoMapData, PhotoMapType::UPDATE_AND_INSERT); + if (ret == E_OK) { + return true; + } + return false; + } + return true; +} + +bool ScannerMapCodeUtils::DeleteMapCodesByFileIds(const vector &fileIds) +{ + MEDIA_INFO_LOG("ScannerMapCodeUtils::DeleteMapCodesByFileIds fileIds size %{public}zu", fileIds.size()); + if (fileIds.size() == 0) { + return true; + } + + int32_t ret = PhotoMapCodeOperation::RemovePhotosMapCodes(fileIds); + return ret == E_OK; +} +} // namespace Media +} // namespace OHOS diff --git a/frameworks/utils/include/map_code_upload_checker.h b/frameworks/utils/include/map_code_upload_checker.h new file mode 100644 index 0000000000..6a36d5853b --- /dev/null +++ b/frameworks/utils/include/map_code_upload_checker.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2025 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. + */ + +#ifndef MAP_CODE_UPLOAD_CHECKER_H +#define MAP_CODE_UPLOAD_CHECKER_H + +#include "rdb_predicates.h" + +#include +#include +#include +#include + +namespace OHOS { +namespace Media { +struct CheckedMapCodeInfo { + int32_t fileId; + double latitude; + double longitude; +}; + +class MapCodeUploadChecker { +public: + static bool RepairNoMapCodePhoto(); + +private: + static void HandleMapCodePhoto(); + static std::vector QueryMapCodeInfo(int32_t startFileId); + static void HandleMapCodeInfos(const std::vector &mapCodeInfos, int32_t &curFileId); + static int32_t QueryMapCodeCount(int32_t startFileId); + +private: + static std::mutex mutex_; +}; +} // namespace Media +} // namespace OHOS +#endif // MAP_CODE_UPLOAD_CHECKER_H \ No newline at end of file diff --git a/frameworks/utils/src/map_code_upload_checker.cpp b/frameworks/utils/src/map_code_upload_checker.cpp new file mode 100644 index 0000000000..0faa4f73a5 --- /dev/null +++ b/frameworks/utils/src/map_code_upload_checker.cpp @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2025 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 MAP_UPLOAD_CHECKERLOG_TAG "MapUploadChecker" + +#include "map_code_upload_checker.h" +#include "media_column.h" +#include "photo_map_code_column.h" +#include "photo_map_code_operation.h" +#include "media_file_utils.h" +#include "media_file_uri.h" +#include "medialibrary_unistore_manager.h" +#include "photo_album_column.h" +#include "thumbnail_const.h" +#include "preferences.h" +#include "preferences_helper.h" +#include "result_set_utils.h" +#include "medialibrary_object_utils.h" +#include "medialibrary_photo_operations.h" +#include "moving_photo_file_utils.h" +#include "medialibrary_subscriber.h" +#include "scanner_map_code_utils.h" +#include + +namespace OHOS { +namespace Media { +using namespace std; +using namespace NativeRdb; + +const std::int32_t BATCH_SIZE = 500; + +const int SCANMAP_DEFAULT_VERSION = 0; +const int SCANMAP_CURRENT_VERSION = 1; + +const std::string TASK_PROGRESS_XML = "/data/storage/el2/base/preferences/task_progress.xml"; +const std::string NO_MAPCODE_PHOTO_NUMBER = "no_mapcode_photo_number"; +const std::string NO_PHOTO_BUT_MAPCODE_NUMBER = "no_origin_but_lcd_photo_number"; +const std::string SCANMAP_VERSION = "scanmap_version"; + +static constexpr double DOUBLE_EPSILON = 1e-15; +static constexpr double MAX_LATITUDE_EPSILON = 1e-15 + 90.0; +static constexpr double MAX_LONGITUDE_EPSILON = 1e-15 + 180.0; + +std::mutex MapCodeUploadChecker::mutex_; + +const std::string SQL_PHOTOS_TABLE_QUERY_POSTION_PHOTO = "SELECT" + " COUNT( * ) AS Count " + "FROM" + " Photos " + "WHERE" + " latitude <> 0" + " AND longitude <> 0" + " AND file_id > ?;"; + +const std::string SQL_PHOTOS_TABLE_QUERY_PHOTO = "SELECT" + " file_id," + " latitude," + " longitude " + "FROM" + " Photos " + "WHERE" + " latitude <> 0" + " AND longitude <> 0" + " AND file_id > ?" + " LIMIT ?;"; + +void MapCodeUploadChecker::HandleMapCodeInfos(const std::vector &photoInfos, int32_t &curFileId) +{ + MEDIA_INFO_LOG("MapCodeUploadChecker::HandleMapCodeInfos start"); + for (const CheckedMapCodeInfo &photoInfo : photoInfos) { + CHECK_AND_BREAK_INFO_LOG(MedialibrarySubscriber::IsCurrentStatusOn(), "current status is off, break"); + curFileId = photoInfo.fileId; + NativeRdb::ValuesBucket mapValue; + std::string whereMapClause = PhotoMapCodeColumn::MAPCODE_FILE_ID + " = ?"; + std::vector whereMapArgs = { to_string(curFileId)}; + + // 数据入库点 + int32_t fileId = photoInfo.fileId; + double longitude = photoInfo.longitude; + double latitude = photoInfo.latitude; + const std::string mapTableName = PhotoMapCodeColumn::PHOTOS_MAP_CODE_TABLE; + + if (fileId > 0 && fabs(longitude) > DOUBLE_EPSILON && fabs(latitude) > DOUBLE_EPSILON && + fabs(longitude) < MAX_LONGITUDE_EPSILON && fabs(latitude) < MAX_LATITUDE_EPSILON) { + PhotoMapData photoMapData(fileId, latitude, longitude); + PhotoMapCodeOperation::GetPhotoMapCode(photoMapData, PhotoMapType::QUERY_AND_INSERT); + } + } +} + +std::vector MapCodeUploadChecker::QueryMapCodeInfo(int32_t startFileId) +{ + MEDIA_INFO_LOG("MapCodeUploadChecker::QueryMapCodeInfo start"); + std::vector photoInfos; + auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore(); + CHECK_AND_RETURN_RET_LOG(rdbStore != nullptr, photoInfos, + "MapCodeUploadChecker::QueryMapCodeInfo Failed to get rdbstore!"); + + const std::vector bindArgs = {startFileId, BATCH_SIZE}; + auto resultSet = rdbStore->QuerySql(SQL_PHOTOS_TABLE_QUERY_PHOTO, bindArgs); + bool cond = resultSet != nullptr && resultSet->GoToFirstRow() == NativeRdb::E_OK; + CHECK_AND_RETURN_RET_LOG(cond, photoInfos, + "MapCodeUploadChecker::QueryMapCodeInfo resultSet is null or count is 0"); + + do { + CheckedMapCodeInfo photoInfo; + photoInfo.fileId = get(ResultSetUtils::GetValFromColumn(MediaColumn::MEDIA_ID, + resultSet, TYPE_INT32)); + photoInfo.latitude = get(ResultSetUtils::GetValFromColumn(PhotoColumn::PHOTO_LATITUDE, + resultSet, TYPE_DOUBLE)); + photoInfo.longitude = get(ResultSetUtils::GetValFromColumn(PhotoColumn::PHOTO_LONGITUDE, + resultSet, TYPE_DOUBLE)); + photoInfos.push_back(photoInfo); + } while (MedialibrarySubscriber::IsCurrentStatusOn() && resultSet->GoToNextRow() == NativeRdb::E_OK); + MEDIA_INFO_LOG("MapCodeUploadChecker::QueryMapCodeInfo end photoInfos size %{public}zu", photoInfos.size()); + resultSet->Close(); + return photoInfos; +} + +int32_t MapCodeUploadChecker::QueryMapCodeCount(int32_t startFileId) +{ + auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore(); + CHECK_AND_RETURN_RET_LOG(rdbStore != nullptr, E_HAS_DB_ERROR, "Failed to get rdbStore."); + const std::vector bindArgs = {startFileId}; + auto resultSet = rdbStore->QuerySql(SQL_PHOTOS_TABLE_QUERY_POSTION_PHOTO, bindArgs); + if (resultSet == nullptr) { + return E_ERR; + } + if (resultSet->GoToFirstRow() != NativeRdb::E_OK) { + MEDIA_ERR_LOG("MapCodeUploadChecker::QueryMapCodeCount resultSet is null or count is 0"); + resultSet->Close(); + return E_OK; + } + int32_t ret = get(ResultSetUtils::GetValFromColumn("Count", resultSet, TYPE_INT32)); + resultSet->Close(); + return ret; +} + +void MapCodeUploadChecker::HandleMapCodePhoto() +{ + MEDIA_INFO_LOG("MapCodeUploadChecker::HandleMapCodePhoto start handle no mapcode photo!"); + int64_t startTime = MediaFileUtils::UTCTimeMilliSeconds(); + int32_t errCode = E_OK; + shared_ptr prefs = + NativePreferences::PreferencesHelper::GetPreferences(TASK_PROGRESS_XML, errCode); + CHECK_AND_RETURN_LOG(prefs, "MapCodeUploadChecker::HandleMapCodePhoto get preferences \ + error: %{public}d", errCode); + int32_t curFileId = prefs->GetInt(NO_MAPCODE_PHOTO_NUMBER, 0); + MEDIA_DEBUG_LOG("MapCodeUploadChecker::HandleMapCodePhoto start id: %{public}d", curFileId); + while (MedialibrarySubscriber::IsCurrentStatusOn() && QueryMapCodeCount(curFileId) > 0) { + std::vector photoInfos = QueryMapCodeInfo(curFileId); + HandleMapCodeInfos(photoInfos, curFileId); + prefs->PutInt(NO_MAPCODE_PHOTO_NUMBER, curFileId); + prefs->FlushSync(); + } + MEDIA_DEBUG_LOG("end handle no origin photo! cost: %{public}" + PRId64, MediaFileUtils::UTCTimeMilliSeconds() - startTime); + return; +} + +bool MapCodeUploadChecker::RepairNoMapCodePhoto() +{ + MEDIA_INFO_LOG("MapCodeUploadChecker::RepairNoMapCodePhoto In"); + std::unique_lock lock(mutex_, std::defer_lock); + if (!(lock.try_lock())) { + MEDIA_WARN_LOG("MapCodeUploadChecker::RepairNoMapCodePhoto Repairing no mapCode \ + photos has started, skipping this operation"); + return false; + } + + MEDIA_DEBUG_LOG("MapCodeUploadChecker::RepairNoMapCodePhoto start repair no map code photo!"); + int32_t errCode = E_OK; + shared_ptr prefs = + NativePreferences::PreferencesHelper::GetPreferences(TASK_PROGRESS_XML, errCode); + if (!prefs) { + MEDIA_ERR_LOG("MapCodeUploadChecker::RepairNoMapCodePhoto get preferences error: %{public}d", + errCode); + return false; + } + int scanmapVersion = prefs->GetInt(SCANMAP_VERSION, SCANMAP_DEFAULT_VERSION); + MEDIA_DEBUG_LOG("MapCodeUploadChecker::RepairNoMapCodePhoto scanmap version: %{public}d", scanmapVersion); + if (scanmapVersion < SCANMAP_CURRENT_VERSION) { + prefs->PutInt(NO_MAPCODE_PHOTO_NUMBER, 0); + prefs->PutInt(SCANMAP_VERSION, SCANMAP_CURRENT_VERSION); + prefs->FlushSync(); + } + HandleMapCodePhoto(); + MEDIA_DEBUG_LOG("end RepairNoMapCodePhoto no origin photo!"); + return true; +} +} // namespace Media +} // namespace OHOS diff --git a/interfaces/inner_api/media_library_helper/include/medialibrary_db_const.h b/interfaces/inner_api/media_library_helper/include/medialibrary_db_const.h index 070c0f4c4c..eb4de26998 100644 --- a/interfaces/inner_api/media_library_helper/include/medialibrary_db_const.h +++ b/interfaces/inner_api/media_library_helper/include/medialibrary_db_const.h @@ -24,7 +24,7 @@ namespace OHOS { namespace Media { -const int32_t MEDIA_RDB_VERSION = 361; +const int32_t MEDIA_RDB_VERSION = 362; enum { VERSION_ADD_CLOUD = 2, @@ -251,6 +251,7 @@ enum { VERSION_ADD_COMPOSITE_DISPLAY_STATUS_COLUMNS = 359, VERSION_ADD_INDEX_FOR_PHOTO_SORT_IN_ALBUM = 360, VERSION_ADD_TAB_OLD_PHOTOS_CLONE_SEQUENCE = 361, + VERSION_ADD_MAP_CODE_TABLE = 362, }; enum { MEDIA_API_VERSION_DEFAULT = 8, diff --git a/services/media_cloud_sync_service/include/dao/cloud_map_code_dao.h b/services/media_cloud_sync_service/include/dao/cloud_map_code_dao.h new file mode 100644 index 0000000000..a67967cea0 --- /dev/null +++ b/services/media_cloud_sync_service/include/dao/cloud_map_code_dao.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2025 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. + */ + +#ifndef CLOUD_MAP_CODE_UTILS_H +#define CLOUD_MAP_CODE_UTILS_H + +#include "media_column.h" +#include "cloud_media_photos_dao.h" +#include "cloud_media_pull_data_dto.h" +#include "photo_map_code_operation.h" + +#include "medialibrary_rdbstore.h" +#include "directory_ex.h" +#include "media_log.h" +#include "medialibrary_type_const.h" + +#include "abs_rdb_predicates.h" +#include "photo_album_column.h" +#include "photo_map_column.h" +#include "cloud_media_file_utils.h" +#include "cloud_media_sync_utils.h" +#include "cloud_media_operation_code.h" +#include "medialibrary_unistore_manager.h" +#include "moving_photo_file_utils.h" +#include "result_set.h" +#include "result_set_utils.h" +#include "thumbnail_const.h" +#include "userfile_manager_types.h" +#include "result_set_reader.h" +#include "photos_po_writer.h" +#include "photos_po.h" +#include "photo_album_po_writer.h" +#include "cloud_sync_convert.h" +#include "medialibrary_rdb_transaction.h" +#include "medialibrary_rdb_utils.h" +#include "scanner_utils.h" +#include "cloud_media_dao_const.h" +#include "media_gallery_sync_notify.h" +#include "cloud_media_sync_const.h" +#include "cloud_media_dao_utils.h" + +#include +#include +#include +#include +#include +#include + +namespace OHOS { +namespace Media { +#define EXPORT __attribute__ ((visibility ("default"))) + +class CloudMapCodeDao { +public: + EXPORT static int32_t InsertDatasToMapCode(std::vector &pullDatas); + EXPORT static int32_t UpdateDataToMapCode(const CloudSync::CloudMediaPullDataDto &pullData); + + // 删除场景(MediaLibraryAssetOperations::DeleteFromDisk) + EXPORT static int32_t DeleteMapCodesByPullDatas(std::vector &pullDatas); + EXPORT static int32_t DeleteMapCodesByPullData(const CloudSync::CloudMediaPullDataDto &pullData); +private: + EXPORT static int32_t GetPhotosPoByPullDatas(std::vector &pullDatas, + std::vector &photosPos, const std::vector &getValues); + EXPORT static int32_t GetPhotosPoByPullData(const CloudSync::CloudMediaPullDataDto &pullData, + std::vector &photosPos, const std::vector &getValues); +}; +} // namespace Media +} // namespace OHOS + +#endif // MAP_CODE_UTILS_H diff --git a/services/media_cloud_sync_service/include/service/cloud_media_photos_service.h b/services/media_cloud_sync_service/include/service/cloud_media_photos_service.h index b4fbf79ee9..0e78f72979 100644 --- a/services/media_cloud_sync_service/include/service/cloud_media_photos_service.h +++ b/services/media_cloud_sync_service/include/service/cloud_media_photos_service.h @@ -34,6 +34,7 @@ #include "asset_accurate_refresh.h" #include "album_accurate_refresh.h" #include "media_operate_result.h" +#include "cloud_map_code_dao.h" namespace OHOS::Media::CloudSync { class EXPORT CloudMediaPhotosService { @@ -122,10 +123,14 @@ private: void RefreshAnalysisAlbum(const std::string &cloudId); int32_t RemoveLocalFile(const CloudMediaPullDataDto &pullData); + int32_t MapUpdate(const CloudMediaPullDataDto &pullData); + int32_t MapDelete(const CloudMediaPullDataDto &pullData); + int32_t MapInsert(const std::vector &pullData, std::vector &failedRecords); private: CloudMediaPhotoServiceProcessor processor_; CloudMediaPhotosDao photosDao_; CloudMediaCommonDao commonDao_; + CloudMapCodeDao mapCodeDao_; }; } // namespace OHOS::Media::CloudSync #endif // OHOS_MEDIA_CLOUD_SYNC_CLOUD_MEDIA_PHOTOS_SERVICE_H \ No newline at end of file diff --git a/services/media_cloud_sync_service/src/dao/cloud_map_code_dao.cpp b/services/media_cloud_sync_service/src/dao/cloud_map_code_dao.cpp new file mode 100644 index 0000000000..a8141dd3be --- /dev/null +++ b/services/media_cloud_sync_service/src/dao/cloud_map_code_dao.cpp @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2025 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 CLOUD_MAP_LOG_TAG "CloudMapCode" + +#include "cloud_map_code_dao.h" + +namespace OHOS { +namespace Media { +using namespace std; +static constexpr double DOUBLE_EPSILON = 1e-15; +static constexpr double MAX_LATITUDE_EPSILON = 1e-15 + 90.0; +static constexpr double MAX_LONGITUDE_EPSILON = 1e-15 + 180.0; + +int32_t CloudMapCodeDao::InsertDatasToMapCode(std::vector &pullDatas) +{ + MEDIA_INFO_LOG("CloudMapCodeDao::InsertDatasToMapCode"); + if (pullDatas.empty()) { + return 0; + } + + const std::vector COLUMNS_QUERY = { + PhotoColumn::MEDIA_ID, + PhotoColumn::PHOTO_LATITUDE, + PhotoColumn::PHOTO_LONGITUDE, + }; + std::vector photosPos; + + CloudMapCodeDao::GetPhotosPoByPullDatas(pullDatas, photosPos, COLUMNS_QUERY); + + vector photoMapDatas; + for (const auto &photoPo : photosPos) { + if (!(photoPo.longitude.has_value() && photoPo.latitude.has_value() && photoPo.fileId.has_value())) { + MEDIA_DEBUG_LOG("CloudMapCodeDao::InsertDatasToMapCode photosPos is null"); + continue; + } + + double longitude = photoPo.longitude.value(); + double latitude = photoPo.latitude.value(); + int32_t fileId = photoPo.fileId.value(); + if (fileId > 0 && fabs(longitude) > DOUBLE_EPSILON && fabs(latitude) > DOUBLE_EPSILON && + fabs(longitude) < MAX_LONGITUDE_EPSILON && fabs(latitude) < MAX_LATITUDE_EPSILON) { + PhotoMapData photoMapData(fileId, latitude, longitude); + photoMapDatas.emplace_back(photoMapData); + } + } + + return PhotoMapCodeOperation::InsertPhotosMapCodes(photoMapDatas, nullptr); +} + +int32_t CloudMapCodeDao::UpdateDataToMapCode(const CloudSync::CloudMediaPullDataDto &pullData) +{ + MEDIA_INFO_LOG("CloudMapCodeDao::UpdateDataToMapCode"); + const std::vector COLUMNS_QUERY = { + PhotoColumn::MEDIA_ID, + PhotoColumn::PHOTO_LATITUDE, + PhotoColumn::PHOTO_LONGITUDE, + }; + std::vector photosPos; + + CloudMapCodeDao::GetPhotosPoByPullData(pullData, photosPos, COLUMNS_QUERY); + if (photosPos.empty()) { + MEDIA_INFO_LOG("UpdateDataToMapCode photoPo is empty"); + return E_OK; + } + Media::ORM::PhotosPo photoPo = photosPos[0]; + if (!(photoPo.longitude.has_value() && photoPo.latitude.has_value() && photoPo.fileId.has_value())) { + MEDIA_ERR_LOG("UpdateDataToMapCode photoPo is null"); + return E_OK; + } + double longitude = photoPo.longitude.value(); + double latitude = photoPo.latitude.value(); + int32_t fileId = photoPo.fileId.value(); + MEDIA_DEBUG_LOG("CloudMapCodeDao::UpdateDataToMapCode photosPos fileId longitude latitude"); + NativeRdb::ValuesBucket mapValue; + if (fileId > 0 && fabs(longitude) > DOUBLE_EPSILON && fabs(latitude) > DOUBLE_EPSILON && + fabs(longitude) < MAX_LONGITUDE_EPSILON && fabs(latitude) < MAX_LATITUDE_EPSILON) { + PhotoMapData photoMapData(fileId, latitude, longitude); + return PhotoMapCodeOperation::GetPhotoMapCode(photoMapData, PhotoMapType::UPDATE_AND_INSERT); + } + MEDIA_INFO_LOG("CloudMapCodeDao::UpdateDataToMapCode end"); + return NativeRdb::E_OK; +} + +int32_t CloudMapCodeDao::DeleteMapCodesByPullDatas(std::vector &pullDatas) +{ + MEDIA_INFO_LOG("CloudMapCodeDao::DeleteMapCodesByPullDatas"); + if (pullDatas.empty()) { + return E_OK; + } + std::vector photosPos; + const std::vector COLUMNS_QUERY = { + PhotoColumn::MEDIA_ID, + }; + + CloudMapCodeDao::GetPhotosPoByPullDatas(pullDatas, photosPos, COLUMNS_QUERY); + + if (photosPos.empty()) { + MEDIA_INFO_LOG("DeleteMapCodesByPullDatas photosPos size equals to 0"); + return E_OK; + } + + vector fileIds; + for (const auto &photoPo : photosPos) { + if (!(photoPo.fileId)) { + MEDIA_DEBUG_LOG("DeleteMapCodesByPullDatas photosPos is null"); + continue; + } + + int32_t fileId = photoPo.fileId.value(); + MEDIA_DEBUG_LOG("DeleteMapCodesByPullDatas photosPos fileId %{private}d", fileId); + fileIds.push_back(std::to_string(fileId)); + } + + int32_t ret = PhotoMapCodeOperation::RemovePhotosMapCodes(fileIds); + MEDIA_INFO_LOG("DeleteMapCodesByPullDatas ret is %{public}d", ret); + return ret; +} + +int32_t CloudMapCodeDao::DeleteMapCodesByPullData(const CloudSync::CloudMediaPullDataDto &pullData) +{ + MEDIA_INFO_LOG("CloudMapCodeDao::DeleteMapCodesByPullData"); + + std::vector photosPos; + const std::vector COLUMNS_QUERY = { + PhotoColumn::MEDIA_ID, + }; + + CloudMapCodeDao::GetPhotosPoByPullData(pullData, photosPos, COLUMNS_QUERY); + + if (photosPos.empty()) { + MEDIA_ERR_LOG("DeleteMapCodesByPullData to-deleted idList size equals to 0"); + return E_OK; + } + + vector fileIds; + for (const auto &photoPo : photosPos) { + if (!(photoPo.fileId)) { + MEDIA_DEBUG_LOG("DeleteMapCodesByPullData photosPos is null"); + continue; + } + + int32_t fileId = photoPo.fileId.value(); + MEDIA_DEBUG_LOG("DeleteMapCodesByPullData photosPos fileId %{private}d", fileId); + fileIds.push_back(std::to_string(fileId)); + } + + int32_t ret = PhotoMapCodeOperation::RemovePhotosMapCodes(fileIds); + MEDIA_INFO_LOG("DeleteMapCodesByPullData ret is %{public}d", ret); + return ret; +} + +int32_t CloudMapCodeDao::GetPhotosPoByPullDatas(std::vector &pullDatas, + std::vector &photosPos, const std::vector &getValues) +{ + std::vector cloudIds; + for (auto mergeData = pullDatas.begin(); mergeData != pullDatas.end();) { + std::string cloudId = mergeData->cloudId; + cloudIds.push_back(cloudId); + mergeData++; + } + + int32_t ret = PhotoMapCodeOperation::GetPhotosPoByInputValues(cloudIds, photosPos, getValues); + return ret; +} + +int32_t CloudMapCodeDao::GetPhotosPoByPullData(const CloudSync::CloudMediaPullDataDto &pullData, + std::vector &photosPos, const std::vector &getValues) +{ + std::vector cloudIds; + cloudIds.push_back(pullData.cloudId); + + int32_t ret = PhotoMapCodeOperation::GetPhotosPoByInputValues(cloudIds, photosPos, getValues); + return ret; +} +} // namespace Media +} // namespace OHOS \ No newline at end of file diff --git a/services/media_cloud_sync_service/src/service/cloud_media_photos_service.cpp b/services/media_cloud_sync_service/src/service/cloud_media_photos_service.cpp index 91b8fe8731..c7d5e2ad4f 100644 --- a/services/media_cloud_sync_service/src/service/cloud_media_photos_service.cpp +++ b/services/media_cloud_sync_service/src/service/cloud_media_photos_service.cpp @@ -45,6 +45,40 @@ using ChangeType = OHOS::AAFwk::ChangeInfo::ChangeType; namespace OHOS::Media::CloudSync { +int32_t CloudMediaPhotosService::MapUpdate(const CloudMediaPullDataDto &pullData) +{ + MEDIA_INFO_LOG("CloudMediaPhotosService::MapUpdate"); + return mapCodeDao_.UpdateDataToMapCode(pullData); +} + +int32_t CloudMediaPhotosService::MapDelete(const CloudMediaPullDataDto &pullData) +{ + MEDIA_INFO_LOG("CloudMediaPhotosService::MapDelete"); + return mapCodeDao_.DeleteMapCodesByPullData(pullData); +} + +int32_t CloudMediaPhotosService::MapInsert(const std::vector &pullDatas, + std::vector &failedRecords) +{ + std::vector allPullDatas(pullDatas); + MEDIA_INFO_LOG("CloudMediaPhotosService::MapInsert pullDatas size %{public}zu, allPullDatas size %{public}zu \ + failedRecords size %{public}zu", pullDatas.size(), allPullDatas.size(), failedRecords.size()); + for (auto mergeData = allPullDatas.begin(); mergeData != allPullDatas.end();) { + std::string id = mergeData->cloudId; + auto it = std::find(failedRecords.begin(), failedRecords.end(), id); + if (it == failedRecords.end()) { + MEDIA_INFO_LOG("MapInsert GetLocalKey Data failed"); + mergeData++; + } else { + mergeData = allPullDatas.erase(mergeData); // 把合一数据剔除,剩下的就是纯新增数据 + } + } + MEDIA_INFO_LOG("CloudMediaPhotosService::MapInsert allPullDatas size %{public}zu \ + to InsertDatasToMapCode", allPullDatas.size()); + int32_t ret = mapCodeDao_.InsertDatasToMapCode(allPullDatas); + return ret; +} + int32_t CloudMediaPhotosService::PullDelete(const CloudMediaPullDataDto &data, std::set &refreshAlbums, std::shared_ptr &photoRefresh) { @@ -504,12 +538,16 @@ int32_t CloudMediaPhotosService::HandleRecord(const std::vector &cl stats[StatsIndex::NEW_RECORDS_COUNT]++; // crash, stats的长度组要手动设置 } else if (!pullData.localPath.empty()) { if (pullData.basicIsDelete) { + ret = MapDelete(pullData); + MEDIA_INFO_LOG("CloudMediaPhotosService::HandleRecord MapDelete ret %{public}d", ret); ret = PullDelete(pullData, refreshAlbums, photoRefresh); changeType = ChangeType::DELETE; stats[StatsIndex::DELETE_RECORDS_COUNT]++; } else { ret = PullUpdate(pullData, refreshAlbums, fdirtyData, stats, photoRefresh); changeType = ChangeType::UPDATE; + int32_t retMap = MapUpdate(pullData); + MEDIA_INFO_LOG("CloudMediaPhotosService::HandleRecord PullUpdate MapUpdate retMap %{public}d", retMap); } } if (ret == E_STOP) { @@ -622,7 +660,11 @@ int32_t CloudMediaPhotosService::OnFetchRecords(const std::vector & int32_t CloudMediaPhotosService::OnDentryFileInsert( const std::vector &pullDatas, std::vector &failedRecords) { - return PullInsert(pullDatas, failedRecords); + int32_t ret = PullInsert(pullDatas, failedRecords); + int32_t retMap = MapInsert(pullDatas, failedRecords); + MEDIA_INFO_LOG("CloudMediaPhotosService::OnDentryFileInsert. ret %{public}d pullDatas size \ + %{public}zu MapInsert retMap %{public}d", ret, pullDatas.size(), retMap); + return ret; } int32_t CloudMediaPhotosService::GetRetryRecords(std::vector &cloudIds) -- Gitee From a23ecb79876a52f227aa7b1b485eafabfd372f95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=80=95=E9=BA=BB=E7=83=A6?= <354431057@qq.com> Date: Fri, 12 Sep 2025 16:30:41 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E5=9C=B0=E5=9B=BE=E7=9B=B8=E5=86=8C?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 怕麻烦 <354431057@qq.com> --- .../medialibrarycloudmediamapcodedao_fuzzer.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmediamapcodedao_fuzzer/medialibrarycloudmediamapcodedao_fuzzer.cpp b/frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmediamapcodedao_fuzzer/medialibrarycloudmediamapcodedao_fuzzer.cpp index 5dbc5c2f6b..1ecbd8dd11 100644 --- a/frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmediamapcodedao_fuzzer/medialibrarycloudmediamapcodedao_fuzzer.cpp +++ b/frameworks/innerkitsimpl/test/fuzztest/medialibrarycloudmediamapcodedao_fuzzer/medialibrarycloudmediamapcodedao_fuzzer.cpp @@ -164,7 +164,7 @@ static int32_t InsertPhotoAsset() values.PutLong(PhotoColumn::MEDIA_TIME_PENDING, 0); values.PutString(PhotoColumn::MEDIA_NAME, "IMG_20250425_123456.jpg"); int64_t fileId = 0; - g_rdbStore->Insert(fileId, PHOTOS_TABLE, values); + g_rdbStore->Insert(fileId, OHOS::PHOTOS_TABLE, values); return static_cast(fileId); } @@ -183,7 +183,7 @@ static int32_t UpdatePhotoAsset() values.PutNull(PhotoColumn::PHOTO_ORIGINAL_ASSET_CLOUD_ID); values.PutLong(PhotoColumn::PHOTO_META_DATE_MODIFIED, FDP->ConsumeIntegral()); int64_t fileId = 0; - g_rdbStore->Insert(fileId, PHOTOS_TABLE, values); + g_rdbStore->Insert(fileId, OHOS::PHOTOS_TABLE, values); return static_cast(fileId); } @@ -199,7 +199,7 @@ static int32_t QueryPhotoAsset() values.PutInt(PhotoColumn::PHOTO_ORIENTATION, FDP->ConsumeIntegral()); values.PutString(PhotoColumn::MEDIA_NAME, "IMG_20250425_123456.jpg"); int64_t fileId = 0; - g_rdbStore->Insert(fileId, PHOTOS_TABLE, values); + g_rdbStore->Insert(fileId, OHOS::PHOTOS_TABLE, values); return static_cast(fileId); } @@ -211,7 +211,7 @@ static int32_t QueryPhotomapAsset() NativeRdb::ValuesBucket values; values.PutInt(PhotoMap::ASSET_ID, 1); int64_t fileId = 0; - g_rdbStore->Insert(fileId, PHOTOMAP_TABLE, values); + g_rdbStore->Insert(fileId, OHOS::PHOTOMAP_TABLE, values); return static_cast(fileId); } @@ -226,7 +226,7 @@ static int32_t QueryAlbumAsset() values.PutString(PhotoAlbumColumns::ALBUM_CLOUD_ID, HIDDEN_ALBUM); values.PutString(PhotoAlbumColumns::ALBUM_LPATH, "records"); int64_t fileId = 0; - g_rdbStore->Insert(fileId, ALBUM_TABLE, values); + g_rdbStore->Insert(fileId, OHOS::ALBUM_TABLE, values); return static_cast(fileId); } @@ -240,7 +240,7 @@ static int32_t InsertDeleteAsset() "3d4970270f8d4b15b4ced48bd7f25dd44c7ad693ae57426d863fec74422b368e"); values.PutString(MediaColumn::MEDIA_FILE_PATH, FDP->ConsumeBytesAsString(NUM_BYTES)); int64_t fileId = 0; - g_rdbStore->Insert(fileId, PHOTOS_TABLE, values); + g_rdbStore->Insert(fileId, OHOS::PHOTOS_TABLE, values); return static_cast(fileId); } -- Gitee