1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #define MLOG_TAG "MediaLibraryMetaRecovery"
16
17 #include "medialibrary_meta_recovery.h"
18
19 #include <cerrno>
20 #include <dirent.h>
21 #include <fcntl.h>
22
23 #include "acl.h"
24 #ifdef HAS_BATTERY_MANAGER_PART
25 #include "battery_srv_client.h"
26 #endif
27 #include "cloud_sync_helper.h"
28 #include "dfx_database_utils.h"
29 #include "dfx_utils.h"
30 #include "directory_ex.h"
31 #include "hisysevent.h"
32 #include "media_file_uri.h"
33 #include "media_file_utils.h"
34 #include "media_log.h"
35 #include "media_scanner_const.h"
36 #include "media_scanner_db.h"
37 #include "media_scanner_manager.h"
38 #include "metadata.h"
39 #include "metadata_extractor.h"
40 #include "medialibrary_album_fusion_utils.h"
41 #include "medialibrary_data_manager.h"
42 #include "medialibrary_db_const.h"
43 #include "medialibrary_errno.h"
44 #include "medialibrary_kvstore_manager.h"
45 #include "medialibrary_notify.h"
46 #include "medialibrary_photo_operations.h"
47 #include "medialibrary_rdb_transaction.h"
48 #include "medialibrary_rdb_utils.h"
49 #include "medialibrary_type_const.h"
50 #include "medialibrary_unistore_manager.h"
51 #include "mimetype_utils.h"
52 #include "parameter.h"
53 #include "photo_album_column.h"
54 #include "photo_file_utils.h"
55 #include "photo_map_column.h"
56 #include "post_event_utils.h"
57 #include "preferences.h"
58 #include "preferences_helper.h"
59 #include "result_set_utils.h"
60 #ifdef HAS_THERMAL_MANAGER_PART
61 #include "thermal_mgr_client.h"
62 #endif
63 #include "vision_column.h"
64
65 namespace OHOS {
66 namespace Media {
67 using namespace std;
68 using json = nlohmann::json;
69
70 namespace {
71 const string META_RECOVERY_ROOT_DIR = ROOT_MEDIA_DIR + ".meta/";
72 const string META_RECOVERY_META_PATH = ROOT_MEDIA_DIR + ".meta/Photo";
73 const string META_RECOVERY_ALBUM_PATH = META_RECOVERY_ROOT_DIR + "album.json";
74 const string META_STATUS_PATH = META_RECOVERY_ROOT_DIR + "status.json";
75 constexpr int32_t QUERY_BATCH_SIZE = 500;
76 constexpr int32_t META_RETRY_MAX_COUNTS = 10;
77 constexpr int32_t META_RETRY_INTERVAL = 100;
78 const std::string RDB_CONFIG = "/data/storage/el2/base/preferences/recovery_config.xml";
79 const std::string BACKUP_PHOTO_COUNT = "BACKUP_PHOTO_COUNT";
80 const std::string BACKUP_COST_TIME = "BACKUP_COST_TIME";
81 const std::string REBUILT_COUNT = "REBUILT_COUNT";
82 const std::string RECOVERY_BACKUP_TOTAL_COUNT = "RECOVERY_BACKUP_TOTAL_COUNT";
83 const std::string RECOVERY_SUCC_PHOTO_COUNT = "RECOVERY_SUCC_PHOTO_COUNT";
84 const std::string RECOVERY_COST_TIME = "RECOVERY_COST_TIME";
85 static const std::unordered_set<std::string> EXCLUDED_COLUMNS = {
86 MediaColumn::MEDIA_ID,
87 MediaColumn::MEDIA_VIRTURL_PATH,
88 PhotoColumn::PHOTO_THUMBNAIL_READY,
89 PhotoColumn::PHOTO_METADATA_FLAGS,
90 };
91 } // namespace
92
SetStartupParam()93 static void SetStartupParam()
94 {
95 static constexpr uint32_t BASE_USER_RANGE = 200000; // for get uid
96 uid_t uid = getuid() / BASE_USER_RANGE;
97 const string key = "multimedia.medialibrary.startup." + to_string(uid);
98 string value = "true";
99 int ret = SetParameter(key.c_str(), value.c_str());
100 if (ret != 0) {
101 MEDIA_ERR_LOG("Failed to set startup, result: %{public}d", ret);
102 } else {
103 MEDIA_INFO_LOG("Set startup success: %{public}s", to_string(uid).c_str());
104 }
105 }
106
RefreshThumbnail()107 static int32_t RefreshThumbnail()
108 {
109 MediaLibraryKvStoreManager::GetInstance().RebuildInvalidKvStore(KvStoreValueType::MONTH_ASTC);
110 MediaLibraryKvStoreManager::GetInstance().RebuildInvalidKvStore(KvStoreValueType::YEAR_ASTC);
111 Acl::AclSetDatabase();
112 return E_OK;
113 }
114
RefreshAlbumCount()115 static int32_t RefreshAlbumCount()
116 {
117 auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
118 CHECK_AND_RETURN_RET_LOG(rdbStore != nullptr, E_HAS_DB_ERROR, "RefreshAlbumCount: failed to get rdb store handler");
119
120 MediaLibraryRdbUtils::UpdateAllAlbums(rdbStore);
121 auto watch = MediaLibraryNotify::GetInstance();
122 CHECK_AND_RETURN_RET_LOG(watch != nullptr, E_ERR, "Can not get MediaLibraryNotify Instance");
123
124 watch->Notify(PhotoAlbumColumns::ALBUM_URI_PREFIX, NotifyType::NOTIFY_ADD);
125 watch->Notify(PhotoAlbumColumns::ALBUM_URI_PREFIX, NotifyType::NOTIFY_UPDATE);
126 return E_OK;
127 }
128
GetStringFromJson(const nlohmann::json & j,const std::string & key)129 static std::optional<std::string> GetStringFromJson(const nlohmann::json &j, const std::string &key)
130 {
131 if (j.contains(key) && j.at(key).is_string()) {
132 std::string value = j.at(key);
133 MEDIA_DEBUG_LOG("get string json ok, %{private}s: %{private}s", key.c_str(), value.c_str());
134 return std::optional<std::string>(value);
135 } else {
136 MEDIA_ERR_LOG("get key: %{private}s failed", key.c_str());
137 return std::nullopt;
138 }
139 }
140
GetNumberFromJson(const nlohmann::json & j,const std::string & key)141 static std::optional<int64_t> GetNumberFromJson(const nlohmann::json &j, const std::string &key)
142 {
143 if (j.contains(key) && j.at(key).is_number_integer()) {
144 int64_t value = j.at(key);
145 return std::optional<int64_t>(value);
146 } else {
147 MEDIA_ERR_LOG("get key: %{private}s failed", key.c_str());
148 return std::nullopt;
149 }
150 }
151
GetDoubleFromJson(const nlohmann::json & j,const std::string & key)152 static std::optional<double> GetDoubleFromJson(const nlohmann::json &j, const std::string &key)
153 {
154 if (j.contains(key) && j.at(key).is_number_float()) {
155 double value = j.at(key);
156 MEDIA_DEBUG_LOG("get double json ok, %{private}s: %{private}f", key.c_str(), value);
157 return std::optional<double>(value);
158 } else {
159 MEDIA_ERR_LOG("get key: %{private}s failed", key.c_str());
160 return std::nullopt;
161 }
162 }
163
SetValuesFromFileAsset(const FileAsset & fileAsset,NativeRdb::ValuesBucket & values,const std::unordered_map<std::string,ResultSetDataType> & columnInfoMap)164 static void SetValuesFromFileAsset(const FileAsset &fileAsset, NativeRdb::ValuesBucket &values,
165 const std::unordered_map<std::string, ResultSetDataType> &columnInfoMap)
166 {
167 for (const auto &[name, type] : columnInfoMap) {
168 if (type == TYPE_STRING) {
169 values.PutString(name, fileAsset.GetStrMember(name));
170 } else if (type == TYPE_INT32) {
171 values.PutInt(name, fileAsset.GetInt32Member(name));
172 } else if (type == TYPE_INT64) {
173 values.PutLong(name, fileAsset.GetInt64Member(name));
174 } else if (type == TYPE_DOUBLE) {
175 values.PutDouble(name, fileAsset.GetDoubleMember(name));
176 } else {
177 MEDIA_DEBUG_LOG("Invalid fileasset value type, name = %{private}s, type = %{public}d", name.c_str(), type);
178 }
179 }
180 }
181
SetValuesFromPhotoAlbum(shared_ptr<PhotoAlbum> & photoAlbumPtr,NativeRdb::ValuesBucket & values)182 static void SetValuesFromPhotoAlbum(shared_ptr<PhotoAlbum> &photoAlbumPtr, NativeRdb::ValuesBucket &values)
183 {
184 values.PutInt(PhotoAlbumColumns::ALBUM_TYPE, photoAlbumPtr->GetPhotoAlbumType());
185 values.PutInt(PhotoAlbumColumns::ALBUM_SUBTYPE, photoAlbumPtr->GetPhotoAlbumSubType());
186 values.PutString(PhotoAlbumColumns::ALBUM_NAME, photoAlbumPtr->GetAlbumName());
187 values.PutLong(PhotoAlbumColumns::ALBUM_DATE_MODIFIED, photoAlbumPtr->GetDateModified());
188 values.PutInt(PhotoAlbumColumns::CONTAINS_HIDDEN, photoAlbumPtr->GetContainsHidden());
189 values.PutString(PhotoAlbumColumns::ALBUM_BUNDLE_NAME, photoAlbumPtr->GetBundleName());
190 values.PutString(PhotoAlbumColumns::ALBUM_LOCAL_LANGUAGE, photoAlbumPtr->GetLocalLanguage());
191 values.PutInt(PhotoAlbumColumns::ALBUM_IS_LOCAL, photoAlbumPtr->GetIsLocal());
192 values.PutLong(PhotoAlbumColumns::ALBUM_DATE_ADDED, photoAlbumPtr->GetDateAdded());
193 values.PutString(PhotoAlbumColumns::ALBUM_LPATH, photoAlbumPtr->GetLPath());
194 values.PutInt(PhotoAlbumColumns::ALBUM_PRIORITY, photoAlbumPtr->GetPriority());
195 }
196
GetPhotoAlbumFromJsonPart1(const nlohmann::json & j,PhotoAlbum & photoAlbum)197 static bool GetPhotoAlbumFromJsonPart1(const nlohmann::json &j, PhotoAlbum &photoAlbum)
198 {
199 bool ret = true;
200
201 optional<string> bundleName = GetStringFromJson(j, PhotoAlbumColumns::ALBUM_BUNDLE_NAME);
202 if (bundleName.has_value()) {
203 photoAlbum.SetBundleName(bundleName.value());
204 } else {
205 ret = false;
206 }
207
208 optional<string> localLanguage = GetStringFromJson(j, PhotoAlbumColumns::ALBUM_LOCAL_LANGUAGE);
209 if (localLanguage.has_value()) {
210 photoAlbum.SetLocalLanguage(localLanguage.value());
211 } else {
212 ret = false;
213 }
214
215 optional<int64_t> dateAdded = GetNumberFromJson(j, PhotoAlbumColumns::ALBUM_DATE_ADDED);
216 if (dateAdded.has_value()) {
217 photoAlbum.SetDateAdded(dateAdded.value());
218 } else {
219 ret = false;
220 }
221
222 optional<int64_t> isLocal = GetNumberFromJson(j, PhotoAlbumColumns::ALBUM_IS_LOCAL);
223 if (isLocal.has_value()) {
224 photoAlbum.SetIsLocal((int32_t)isLocal.value());
225 } else {
226 ret = false;
227 }
228
229 optional<string> lPath = GetStringFromJson(j, PhotoAlbumColumns::ALBUM_LPATH);
230 if (lPath.has_value()) {
231 photoAlbum.SetLPath(lPath.value());
232 } else {
233 ret = false;
234 }
235
236 optional<int64_t> priority = GetNumberFromJson(j, PhotoAlbumColumns::ALBUM_PRIORITY);
237 if (priority.has_value()) {
238 photoAlbum.SetPriority((int32_t)priority.value());
239 } else {
240 ret = false;
241 }
242
243 return ret;
244 }
245
GetInstance()246 MediaLibraryMetaRecovery &MediaLibraryMetaRecovery::GetInstance()
247 {
248 static MediaLibraryMetaRecovery instance;
249 return instance;
250 }
251
CheckRecoveryState()252 void MediaLibraryMetaRecovery::CheckRecoveryState()
253 {
254 MediaLibraryMetaRecoveryState expect = MediaLibraryMetaRecoveryState::STATE_NONE;
255 if (recoveryState_.compare_exchange_strong(expect, MediaLibraryMetaRecoveryState::STATE_BACKING_UP)) {
256 std::thread([this]() {
257 int64_t backupStartTime = MediaFileUtils::UTCTimeMilliSeconds();
258 this->DoBackupMetadata();
259 int64_t backupTotalTime = MediaFileUtils::UTCTimeMilliSeconds() - backupStartTime;
260 backupCostTime_ += backupTotalTime;
261 MediaLibraryMetaRecoveryState expect = MediaLibraryMetaRecoveryState::STATE_BACKING_UP;
262 if (recoveryState_.compare_exchange_strong(expect, MediaLibraryMetaRecoveryState::STATE_NONE)) {
263 MEDIA_INFO_LOG("End backing up normaly");
264 } else {
265 MEDIA_INFO_LOG("End backing up interrupted");
266 }
267 }).detach();
268 } else {
269 MEDIA_INFO_LOG("Ignore backing up, current status = %{public}d", expect);
270 }
271 }
272
InterruptRecovery()273 void MediaLibraryMetaRecovery::InterruptRecovery()
274 {
275 switch (recoveryState_.load()) {
276 case MediaLibraryMetaRecoveryState::STATE_BACKING_UP: {
277 MediaLibraryMetaRecoveryState expect = MediaLibraryMetaRecoveryState::STATE_BACKING_UP;
278 if (recoveryState_.compare_exchange_strong(expect, MediaLibraryMetaRecoveryState::STATE_NONE)) {
279 MEDIA_INFO_LOG("InterruptRecovery: success send interrupt request");
280 } else {
281 MEDIA_INFO_LOG("InterruptRecovery: backup process is finished, no need to interrupt");
282 }
283 break;
284 }
285 case MediaLibraryMetaRecoveryState::STATE_RECOVERING: {
286 MEDIA_INFO_LOG("InterruptRecovery: need to interrupt recovery process");
287 break;
288 }
289 default: {
290 MEDIA_INFO_LOG("InterruptRecovery: nother recovery or backup is processing, ignore");
291 break;
292 }
293 }
294 }
295
LoadAlbumMaps(const string & path)296 void MediaLibraryMetaRecovery::LoadAlbumMaps(const string &path)
297 {
298 // 1. album.json to oldAlbumIdToLpath
299 int32_t ret = E_OK;
300 std::vector<shared_ptr<PhotoAlbum>> vecPhotoAlbum;
301 ret = ReadPhotoAlbumFromFile(path, vecPhotoAlbum);
302 CHECK_AND_RETURN_LOG(ret == E_OK, "read album file failed, path=%{public}s", DfxUtils::GetSafePath(path).c_str());
303
304 for (auto it : vecPhotoAlbum) {
305 oldAlbumIdToLpath[it->GetAlbumId()] = it->GetLPath();
306 MEDIA_INFO_LOG("oldAlbumIdToLpath, json id %{public}d, path=%{public}s", it->GetAlbumId(),
307 DfxUtils::GetSafePath(it->GetLPath()).c_str());
308 }
309 // 2. db PhotoAlbum to lpathToNewAlbumId
310 NativeRdb::RdbPredicates predicates(PhotoAlbumColumns::TABLE);
311 vector<string> columns = {PhotoAlbumColumns::ALBUM_ID,
312 PhotoAlbumColumns::ALBUM_LPATH};
313 auto resultSet = MediaLibraryRdbStore::QueryWithFilter(predicates, columns);
314 CHECK_AND_RETURN_LOG(resultSet != nullptr, "resultSet == nullptr)");
315
316 while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
317 int albumId = GetInt32Val(PhotoAlbumColumns::ALBUM_ID, resultSet);
318 string lPath = GetStringVal(PhotoAlbumColumns::ALBUM_LPATH, resultSet);
319 lpathToNewAlbumId[lPath] = albumId;
320 MEDIA_INFO_LOG("lpathToNewAlbumId, path=%{public}s db id %{public}d, ",
321 DfxUtils::GetSafePath(lPath).c_str(), albumId);
322 }
323 return;
324 }
325
DoDataBaseRecovery()326 void MediaLibraryMetaRecovery::DoDataBaseRecovery()
327 {
328 SetStartupParam();
329 StopCloudSync();
330 RefreshThumbnail();
331 MEDIA_INFO_LOG("Album recovery start");
332 if (AlbumRecovery(ROOT_MEDIA_DIR + ".meta/album.json") != E_OK) {
333 MEDIA_ERR_LOG("Recovery Album failed");
334 }
335 MEDIA_INFO_LOG("Album recovery end");
336
337 LoadAlbumMaps(ROOT_MEDIA_DIR+".meta/album.json");
338
339 MEDIA_INFO_LOG("Photo recovery start");
340 if (PhotoRecovery(META_RECOVERY_META_PATH) != E_OK) {
341 MEDIA_ERR_LOG("Recover Photo failed");
342 }
343 MEDIA_INFO_LOG("Photo recovery end");
344
345 RefreshAlbumCount();
346 RestartCloudSync();
347 oldAlbumIdToLpath.clear();
348 lpathToNewAlbumId.clear();
349 }
350
AlbumRecovery(const string & path)351 int32_t MediaLibraryMetaRecovery::AlbumRecovery(const string &path)
352 {
353 int32_t ret = E_OK;
354 std::vector<shared_ptr<PhotoAlbum>> vecPhotoAlbum;
355
356 do {
357 ret = access(path.c_str(), F_OK | R_OK);
358 CHECK_AND_BREAK_ERR_LOG(ret == E_OK,
359 "file is not exist or no read access, path=%{public}s", DfxUtils::GetSafePath(path).c_str());
360
361 ret = ReadPhotoAlbumFromFile(path, vecPhotoAlbum);
362 CHECK_AND_BREAK_ERR_LOG(ret == E_OK, "read album file failed, errCode = %{public}d", ret);
363
364 ret = InsertMetadataInDb(vecPhotoAlbum);
365 CHECK_AND_BREAK_ERR_LOG(ret == E_OK, "AlbumRecovery: insert album failed, errCode = %{public}d", ret);
366 MEDIA_INFO_LOG("AlbumRecovery: photo album is recovered successful");
367 } while (false);
368
369 return ret;
370 }
371
GetTotalBackupFileCount()372 static int32_t GetTotalBackupFileCount()
373 {
374 int count = 0;
375 if (access(META_RECOVERY_META_PATH.c_str(), F_OK) != E_OK) {
376 return count;
377 }
378
379 filesystem::path dir(META_RECOVERY_META_PATH);
380 for (const auto& entry : filesystem::recursive_directory_iterator(dir)) {
381 if (entry.is_regular_file() && entry.path().extension() == ".json") {
382 ++count;
383 }
384 }
385
386 return count;
387 }
388
PhotoRecovery(const string & path)389 int32_t MediaLibraryMetaRecovery::PhotoRecovery(const string &path)
390 {
391 string realPath;
392 int32_t bucket_id = -1;
393
394 if (!PathToRealPath(path, realPath)) {
395 if (errno == ENOENT) {
396 // Delte Metastatus Json;
397 remove(META_STATUS_PATH.c_str());
398 // Delete status
399 metaStatus.clear();
400 MEDIA_ERR_LOG("no meta file no need to recovery");
401 return E_OK;
402 }
403 MEDIA_ERR_LOG("Failed to get real path %{private}s, errno %{public}d", path.c_str(), errno);
404 return E_INVALID_PATH;
405 }
406
407 recoveryTotalBackupCnt_ = GetTotalBackupFileCount();
408 MEDIA_INFO_LOG("recovery success total backup");
409
410 if (!ScannerUtils::IsDirectory(realPath)) {
411 MEDIA_ERR_LOG("The path %{private}s is not a directory", realPath.c_str());
412 return E_INVALID_PATH;
413 }
414
415 int err = ScanMetaDir(path, bucket_id);
416 if (err != E_OK) {
417 MEDIA_ERR_LOG("Failed to ScanMetaDir, errCode=%{public}d", err);
418 }
419
420 recoverySuccCnt_ += ReadMetaRecoveryCountFromFile();
421
422 // Delte Metastatus Json;
423 err = remove(META_STATUS_PATH.c_str());
424 if (err != E_OK) {
425 MEDIA_WARN_LOG("Remove META_STATUS_PATH failed, errCode=%{public}d", err);
426 }
427 // Delete status
428 metaStatus.clear();
429
430 return err;
431 }
432
WriteSingleMetaDataById(int32_t rowId)433 int32_t MediaLibraryMetaRecovery::WriteSingleMetaDataById(int32_t rowId)
434 {
435 int ret = E_OK;
436
437 MEDIA_DEBUG_LOG("WriteSingleMetaDataById : rowId %{public}d", rowId);
438 auto asset = MediaLibraryAssetOperations::QuerySinglePhoto(rowId);
439 CHECK_AND_RETURN_RET_LOG(asset != nullptr, E_HAS_DB_ERROR, "QuerySinglePhoto : rowId %{public}d failed", rowId);
440
441 ret = WriteSingleMetaData(*asset);
442 if (ret == E_OK) {
443 backupSuccCnt_++;
444 }
445 return ret;
446 }
447
WriteSingleMetaData(const FileAsset & asset)448 int32_t MediaLibraryMetaRecovery::WriteSingleMetaData(const FileAsset &asset)
449 {
450 string metaFilePath;
451 int32_t ret = E_OK;
452
453 ret = PhotoFileUtils::GetMetaPathFromOrignalPath(asset.GetPath(), metaFilePath);
454 if (ret != E_OK) {
455 MEDIA_ERR_LOG("invalid photo path, path = %{public}s", DfxUtils::GetSafePath(asset.GetPath()).c_str());
456 return ret;
457 }
458
459 // Create direcotry
460 const string metaParentPath = MediaFileUtils::GetParentPath(metaFilePath);
461 if (!MediaFileUtils::CreateDirectory(metaParentPath)) {
462 MEDIA_ERR_LOG("photo: CreateDirectory failed, filePath = %{public}s",
463 DfxUtils::GetSafePath(metaParentPath).c_str());
464 return E_HAS_FS_ERROR;
465 }
466
467 // Create metadata file
468 ret = WriteMetadataToFile(metaFilePath, asset);
469 if (ret != E_OK) {
470 MEDIA_ERR_LOG("photo: WriteMetadataToFile failed, filePath = %{public}s",
471 DfxUtils::GetSafePath(metaFilePath).c_str());
472 return ret;
473 }
474
475 // Up to date
476 ret = UpdateMetadataFlagInDb(asset.GetId(), MetadataFlags::TYPE_UPTODATE);
477 if (ret != E_OK) {
478 MEDIA_ERR_LOG("photo: Up to date failed, filePath = %{public}s", DfxUtils::GetSafePath(metaFilePath).c_str());
479 return ret;
480 }
481
482 return ret;
483 }
484
DoBackupMetadata()485 void MediaLibraryMetaRecovery::DoBackupMetadata()
486 {
487 int32_t temp = 0;
488 int32_t tempLevel = 0;
489 int32_t batteryCapacity = 0;
490
491 #ifdef HAS_THERMAL_MANAGER_PART
492 auto& thermalMgrClient = PowerMgr::ThermalMgrClient::GetInstance();
493 temp = static_cast<int32_t>(thermalMgrClient.GetThermalSensorTemp(PowerMgr::SensorType::SHELL));
494 tempLevel = static_cast<int32_t>(thermalMgrClient.GetThermalLevel());
495 #endif
496 #ifdef HAS_BATTERY_MANAGER_PART
497 batteryCapacity = PowerMgr::BatterySrvClient::GetInstance().GetCapacity();
498 #endif
499 MEDIA_INFO_LOG("Start backing up, batteryCap = %{public}d, temp = %{public}d(%{public}d)",
500 batteryCapacity, temp, tempLevel);
501
502 // Backing up photo albums
503 AlbumBackup();
504
505 // Backing up photos
506 PhotoBackupBatch();
507 }
508
AlbumBackup()509 void MediaLibraryMetaRecovery::AlbumBackup()
510 {
511 vector<shared_ptr<PhotoAlbum>> photoAlbumVector;
512 MediaLibraryAssetOperations::QueryTotalAlbum(photoAlbumVector);
513 int photoAlbumCount = photoAlbumVector.size();
514 if (photoAlbumCount <= 0) {
515 MEDIA_INFO_LOG("AlbumBackup: no photo albums need to backup");
516 return;
517 }
518
519 MEDIA_INFO_LOG("AlbumBackup: album count = %{public}d", photoAlbumCount);
520 if (E_OK != WritePhotoAlbumToFile(META_RECOVERY_ALBUM_PATH, photoAlbumVector)) {
521 MEDIA_ERR_LOG("AlbumBackup: WritePhotoAlbumToFile failed");
522 }
523 }
524
PhotoBackupBatch()525 void MediaLibraryMetaRecovery::PhotoBackupBatch()
526 {
527 int32_t photoTotalCount = 0;
528 int32_t photoProcessedCount = 0;
529 int32_t photoSuccessedCount = 0;
530 vector<shared_ptr<FileAsset>> photoVector;
531 do {
532 if (recoveryState_.load() != MediaLibraryMetaRecoveryState::STATE_BACKING_UP) {
533 MEDIA_INFO_LOG("Photo backing up process is interrupted");
534 break;
535 }
536
537 photoVector.clear();
538 MediaLibraryAssetOperations::QueryTotalPhoto(photoVector, QUERY_BATCH_SIZE);
539 if (photoVector.size() > 0) {
540 photoTotalCount += photoVector.size();
541 PhotoBackup(photoVector, photoProcessedCount, photoSuccessedCount);
542 }
543 } while (photoVector.size() == QUERY_BATCH_SIZE);
544 MEDIA_INFO_LOG("Photo backup end, result = %{public}d/%{public}d/%{public}d",
545 photoSuccessedCount, photoProcessedCount, photoTotalCount);
546 backupSuccCnt_ += photoSuccessedCount;
547 }
548
PhotoBackup(const vector<shared_ptr<FileAsset>> & photoVector,int32_t & processCount,int32_t & successCount)549 void MediaLibraryMetaRecovery::PhotoBackup(const vector<shared_ptr<FileAsset>> &photoVector,
550 int32_t &processCount,
551 int32_t &successCount)
552 {
553 for (auto &asset : photoVector) {
554 // Check interrupt request
555 if (recoveryState_.load() != MediaLibraryMetaRecoveryState::STATE_BACKING_UP) {
556 MEDIA_INFO_LOG("Photo backing up process is interrupted");
557 break;
558 }
559
560 processCount++;
561
562 if (!asset) {
563 MEDIA_ERR_LOG("Photo asset pointer is null");
564 continue;
565 }
566
567 if (E_OK != WriteSingleMetaData(*asset)) {
568 MEDIA_ERR_LOG("WriteSingleMetaData failed");
569 continue;
570 }
571
572 successCount++;
573 }
574 }
575
ScanMetaDir(const string & path,int32_t bucket_id)576 int32_t MediaLibraryMetaRecovery::ScanMetaDir(const string &path, int32_t bucket_id)
577 {
578 int err = E_OK;
579 DIR *dirPath = nullptr;
580 struct dirent *ent = nullptr;
581 size_t len = path.length();
582 struct stat statInfo;
583
584 if (len >= FILENAME_MAX - 1) {
585 return ERR_INCORRECT_PATH;
586 }
587
588 auto fName = (char *)calloc(FILENAME_MAX, sizeof(char));
589 if (fName == nullptr) {
590 return ERR_MEM_ALLOC_FAIL;
591 }
592
593 if (strcpy_s(fName, FILENAME_MAX, path.c_str()) != ERR_SUCCESS) {
594 FREE_MEMORY_AND_SET_NULL(fName);
595 return ERR_MEM_ALLOC_FAIL;
596 }
597 fName[len++] = '/';
598 if ((dirPath = opendir(path.c_str())) == nullptr) {
599 MEDIA_ERR_LOG("Failed to opendir %{private}s, errno %{private}d", path.c_str(), errno);
600 FREE_MEMORY_AND_SET_NULL(fName);
601 VariantMap map = {{KEY_ERR_FILE, __FILE__}, {KEY_ERR_LINE, __LINE__}, {KEY_ERR_CODE, -errno},
602 {KEY_OPT_FILE, path}, {KEY_OPT_TYPE, OptType::SCAN}};
603 PostEventUtils::GetInstance().PostErrorProcess(ErrType::FILE_OPT_ERR, map);
604 return ERR_NOT_ACCESSIBLE;
605 }
606
607 int32_t recoverySuccessCnt = 0;
608 while ((ent = readdir(dirPath)) != nullptr) {
609 if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) {
610 continue;
611 }
612
613 if (strncpy_s(fName + len, FILENAME_MAX - len, ent->d_name, FILENAME_MAX - len)) {
614 continue;
615 }
616
617 if (lstat(fName, &statInfo) == -1) {
618 continue;
619 }
620
621 string currentPath = fName;
622 if (S_ISDIR(statInfo.st_mode)) {
623 int32_t cur_bucket = atoi(ent->d_name);
624 MEDIA_INFO_LOG("currentPath=%{public}s, path=%{public}s, cur_bucket %{public}d recovery start",
625 DfxUtils::GetSafePath(currentPath).c_str(), DfxUtils::GetSafePath(path).c_str(), cur_bucket);
626
627 // Recovery after interrupt, skip bucket which scanned.
628 if (metaStatus.find(cur_bucket) != metaStatus.end()) {
629 MEDIA_INFO_LOG("skip bucket id=%{public}d", cur_bucket);
630 continue;
631 }
632 (void)ScanMetaDir(currentPath, cur_bucket);
633 RefreshAlbumCount();
634 continue;
635 }
636
637 MEDIA_DEBUG_LOG("currentPath=%{public}s, path=%{public}s",
638 DfxUtils::GetSafePath(currentPath).c_str(), DfxUtils::GetSafePath(path).c_str());
639
640 FileAsset fileAsset;
641 if (ReadMetadataFromFile(currentPath, fileAsset) != E_OK) {
642 MEDIA_ERR_LOG("ScanMetaDir: ReadMetadataFrom file failed");
643 continue;
644 }
645
646 // Insert fileAsset to DB
647 if (InsertMetadataInDbRetry(fileAsset) == E_OK) {
648 recoverySuccessCnt++;
649 }
650 }
651
652 closedir(dirPath);
653 FREE_MEMORY_AND_SET_NULL(fName);
654
655 if (bucket_id != -1) {
656 err = WriteMetaStatusToFile(to_string(bucket_id), recoverySuccessCnt);
657 if (err != E_OK) {
658 MEDIA_ERR_LOG("write meta status failed");
659 }
660 MEDIA_INFO_LOG("cur_bucket %{public}d recovery end", bucket_id);
661 }
662
663 return err;
664 }
665
WriteJsonFile(const std::string & filePath,const nlohmann::json & j)666 bool MediaLibraryMetaRecovery::WriteJsonFile(const std::string &filePath, const nlohmann::json &j)
667 {
668 const string parentDir = MediaFileUtils::GetParentPath(filePath);
669 if (!MediaFileUtils::CreateDirectory(parentDir)) {
670 MEDIA_ERR_LOG("CreateDirectory failed, dir = %{public}s", DfxUtils::GetSafePath(parentDir).c_str());
671 return false;
672 }
673
674 std::ofstream outFile(filePath, std::ofstream::out | std::ofstream::trunc);
675 if (!outFile.is_open()) {
676 MEDIA_ERR_LOG("open filePath: %{private}s failed", filePath.c_str());
677 return false;
678 }
679 std::string jsonString = j.dump(-1, ' ', false, nlohmann::json::error_handler_t::replace);
680 outFile << jsonString << std::endl;
681 outFile.close();
682 return true;
683 }
684
ReadJsonFile(const std::string & filePath,nlohmann::json & j)685 bool MediaLibraryMetaRecovery::ReadJsonFile(const std::string &filePath, nlohmann::json &j)
686 {
687 std::ifstream inFile(filePath);
688 if (!inFile.is_open()) {
689 MEDIA_ERR_LOG("open filePath: %{private}s failed", filePath.c_str());
690 return false;
691 }
692 std::string buffer = std::string((istreambuf_iterator<char>(inFile)), istreambuf_iterator<char>());
693 j = json::parse(buffer, nullptr, false);
694 inFile.close();
695 return !j.is_discarded();
696 }
697
WriteMetadataToFile(const string & filePath,const FileAsset & fileAsset)698 int32_t MediaLibraryMetaRecovery::WriteMetadataToFile(const string &filePath, const FileAsset &fileAsset)
699 {
700 json jsonMetadata;
701 AddMetadataToJson(jsonMetadata, fileAsset);
702 if (!WriteJsonFile(filePath, jsonMetadata)) {
703 MEDIA_ERR_LOG("WriteJsonFile failed");
704 return E_FILE_OPER_FAIL;
705 }
706 return E_OK;
707 }
708
AddMetadataToJson(nlohmann::json & j,const FileAsset & fileAsset)709 void MediaLibraryMetaRecovery::AddMetadataToJson(nlohmann::json &j, const FileAsset &fileAsset)
710 {
711 std::unordered_map<std::string, ResultSetDataType> columnInfoMap = QueryRecoveryPhotosTableColumnInfo();
712 if (columnInfoMap.empty()) {
713 MEDIA_ERR_LOG("QueryRecoveryPhotosTableColumnInfo failed");
714 return;
715 }
716
717 for (const auto &[key, type] : columnInfoMap) {
718 if (type == TYPE_STRING) {
719 string value = fileAsset.GetStrMember(key);
720 MEDIA_DEBUG_LOG("Writejson string: %{private}s: %{private}s", key.c_str(), value.c_str());
721 j[key] = json::string_t(value);
722 } else if (type == TYPE_INT32) {
723 int32_t value = fileAsset.GetInt32Member(key);
724 MEDIA_DEBUG_LOG("Writejson int32_t: %{private}s: %{public}d", key.c_str(), value);
725 j[key] = json::number_integer_t(value);
726 } else if (type == TYPE_INT64) {
727 int64_t value = fileAsset.GetInt64Member(key);
728 j[key] = json::number_integer_t(value);
729 } else if (type == TYPE_DOUBLE) {
730 double value = fileAsset.GetDoubleMember(key);
731 MEDIA_DEBUG_LOG("Writejson double: %{private}s: %{public}f", key.c_str(), value);
732 j[key] = json::number_float_t(value);
733 } else {
734 MEDIA_ERR_LOG("WriteFile: error type: %{public}d", type);
735 }
736 }
737 }
738
GetMetadataFromJson(const nlohmann::json & j,FileAsset & fileAsset)739 bool MediaLibraryMetaRecovery::GetMetadataFromJson(const nlohmann::json &j, FileAsset &fileAsset)
740 {
741 std::unordered_map<std::string, ResultSetDataType> columnInfoMap = QueryRecoveryPhotosTableColumnInfo();
742 if (columnInfoMap.empty()) {
743 MEDIA_ERR_LOG("QueryRecoveryPhotosTableColumnInfo failed");
744 return false;
745 }
746
747 bool ret = true;
748 for (const auto &[name, type] : columnInfoMap) {
749 if (type == TYPE_STRING) {
750 optional<string> value = GetStringFromJson(j, name);
751 if (value.has_value()) {
752 fileAsset.SetMemberValue(name, value.value());
753 } else {
754 ret = false;
755 }
756 } else if (type == TYPE_INT32) {
757 optional<int64_t> value = GetNumberFromJson(j, name);
758 if (value.has_value()) {
759 fileAsset.SetMemberValue(name, (int32_t)value.value());
760 } else {
761 ret = false;
762 }
763 } else if (type == TYPE_INT64) {
764 optional<int64_t> value = GetNumberFromJson(j, name);
765 if (value.has_value()) {
766 fileAsset.SetMemberValue(name, (int64_t)value.value());
767 } else {
768 ret = false;
769 }
770 } else if (type == TYPE_DOUBLE) {
771 optional<double> value = GetDoubleFromJson(j, name);
772 if (value.has_value()) {
773 fileAsset.SetMemberValue(name, (double)value.value());
774 } else {
775 ret = false;
776 }
777 } else {
778 MEDIA_ERR_LOG("ReadFile: error %{public}d", type);
779 }
780 }
781
782 return ret;
783 }
784
ReadMetadataFromFile(const string & filePath,FileAsset & fileAsset)785 int32_t MediaLibraryMetaRecovery::ReadMetadataFromFile(const string &filePath, FileAsset &fileAsset)
786 {
787 int ret = E_OK;
788 json jsonMetadata;
789 if (!ReadJsonFile(filePath, jsonMetadata)) {
790 MEDIA_ERR_LOG("ReadJsonFile failed");
791 return E_FILE_OPER_FAIL;
792 }
793
794 if (!GetMetadataFromJson(jsonMetadata, fileAsset)) {
795 MEDIA_ERR_LOG("GetMetadataFromJson not all right");
796 }
797
798 // Meida file path
799 string mediaFilePath = filePath;
800 size_t pos = mediaFilePath.find(META_RECOVERY_META_RELATIVE_PATH);
801 if (pos != string::npos) {
802 mediaFilePath.replace(pos, META_RECOVERY_META_RELATIVE_PATH.length(), META_RECOVERY_PHOTO_RELATIVE_PATH);
803 }
804 if (MediaFileUtils::EndsWith(mediaFilePath, META_RECOVERY_META_FILE_SUFFIX)) {
805 mediaFilePath.erase(mediaFilePath.length() - META_RECOVERY_META_FILE_SUFFIX.length());
806 }
807 fileAsset.SetFilePath(mediaFilePath);
808
809 struct stat statInfo = { 0 };
810 if (stat(mediaFilePath.c_str(), &statInfo) != 0) {
811 MEDIA_ERR_LOG("ReadMetadataFromFile: stat syscall err %{public}d", errno);
812 ret = E_SYSCALL;
813 if (errno == ENOENT) {
814 remove(filePath.c_str());
815 }
816 }
817
818 return ret;
819 }
820
AddPhotoAlbumToJson(nlohmann::json & j,const PhotoAlbum & photoAlbum)821 void MediaLibraryMetaRecovery::AddPhotoAlbumToJson(nlohmann::json &j, const PhotoAlbum &photoAlbum)
822 {
823 j = json {
824 {PhotoAlbumColumns::ALBUM_ID, json::number_integer_t(photoAlbum.GetAlbumId())},
825 {PhotoAlbumColumns::ALBUM_TYPE, json::number_integer_t(photoAlbum.GetPhotoAlbumType())},
826 {PhotoAlbumColumns::ALBUM_SUBTYPE, json::number_integer_t(photoAlbum.GetPhotoAlbumSubType())},
827 {PhotoAlbumColumns::ALBUM_NAME, json::string_t(photoAlbum.GetAlbumName())},
828 {PhotoAlbumColumns::ALBUM_DATE_MODIFIED, json::number_integer_t(photoAlbum.GetDateModified())},
829 {PhotoAlbumColumns::CONTAINS_HIDDEN, json::number_integer_t(photoAlbum.GetContainsHidden())},
830 {PhotoAlbumColumns::ALBUM_ORDER, json::number_integer_t(photoAlbum.GetOrder())},
831 {PhotoAlbumColumns::ALBUM_BUNDLE_NAME, json::string_t(photoAlbum.GetBundleName())},
832 {PhotoAlbumColumns::ALBUM_LOCAL_LANGUAGE, json::string_t(photoAlbum.GetLocalLanguage())},
833 {PhotoAlbumColumns::ALBUM_DATE_ADDED, json::number_integer_t(photoAlbum.GetDateAdded())},
834 {PhotoAlbumColumns::ALBUM_IS_LOCAL, json::number_integer_t(photoAlbum.GetIsLocal())},
835 {PhotoAlbumColumns::ALBUM_LPATH, json::string_t(photoAlbum.GetLPath())},
836 {PhotoAlbumColumns::ALBUM_PRIORITY, json::number_integer_t(photoAlbum.GetPriority())}
837 };
838 }
839
GetPhotoAlbumFromJson(const nlohmann::json & j,PhotoAlbum & photoAlbum)840 bool MediaLibraryMetaRecovery::GetPhotoAlbumFromJson(const nlohmann::json &j, PhotoAlbum &photoAlbum)
841 {
842 bool ret = true;
843 optional<int64_t> albumId = GetNumberFromJson(j, PhotoAlbumColumns::ALBUM_ID);
844 if (albumId.has_value()) {
845 photoAlbum.SetAlbumId((int32_t)albumId.value());
846 } else {
847 ret = false;
848 }
849
850 optional<int64_t> albumType = GetNumberFromJson(j, PhotoAlbumColumns::ALBUM_TYPE);
851 if (albumType.has_value()) {
852 int32_t type = (int32_t)albumType.value();
853 photoAlbum.SetPhotoAlbumType(static_cast<PhotoAlbumType>(type));
854 } else {
855 ret = false;
856 }
857
858 optional<int64_t> albumSubType = GetNumberFromJson(j, PhotoAlbumColumns::ALBUM_SUBTYPE);
859 if (albumSubType.has_value()) {
860 int32_t type = (int32_t)albumSubType.value();
861 photoAlbum.SetPhotoAlbumSubType(static_cast<PhotoAlbumSubType>(type));
862 } else {
863 ret = false;
864 }
865
866 optional<string> albumName = GetStringFromJson(j, PhotoAlbumColumns::ALBUM_NAME);
867 if (albumName.has_value()) {
868 photoAlbum.SetAlbumName(albumName.value());
869 } else {
870 ret = false;
871 }
872
873 optional<int64_t> dateModified = GetNumberFromJson(j, PhotoAlbumColumns::ALBUM_DATE_MODIFIED);
874 if (dateModified.has_value()) {
875 photoAlbum.SetDateModified(dateModified.value());
876 } else {
877 ret = false;
878 }
879
880 optional<int64_t> containsHidden = GetNumberFromJson(j, PhotoAlbumColumns::CONTAINS_HIDDEN);
881 if (containsHidden.has_value()) {
882 photoAlbum.SetContainsHidden((int32_t)containsHidden.value());
883 } else {
884 ret = false;
885 }
886
887 optional<int64_t> order = GetNumberFromJson(j, PhotoAlbumColumns::ALBUM_ORDER);
888 if (order.has_value()) {
889 photoAlbum.SetOrder((int32_t)order.value());
890 } else {
891 ret = false;
892 }
893
894 return (ret && GetPhotoAlbumFromJsonPart1(j, photoAlbum));
895 // !! Do not add upgrade code here !!
896 }
897
WritePhotoAlbumToFile(const string & filePath,const vector<shared_ptr<PhotoAlbum>> & vecPhotoAlbum)898 int32_t MediaLibraryMetaRecovery::WritePhotoAlbumToFile(const string &filePath,
899 const vector<shared_ptr<PhotoAlbum>> &vecPhotoAlbum)
900 {
901 MEDIA_DEBUG_LOG("WritePhotoAlbumToFile start\n");
902
903 const string parentDir = MediaFileUtils::GetParentPath(filePath);
904 if (!MediaFileUtils::CreateDirectory(parentDir)) {
905 MEDIA_ERR_LOG("CreateDirectory failed, dir = %{public}s", DfxUtils::GetSafePath(parentDir).c_str());
906 return false;
907 }
908
909 json jsonArray = json::array();
910 for (auto &album : vecPhotoAlbum) {
911 if (album == nullptr) {
912 MEDIA_ERR_LOG("album == nullptr");
913 continue;
914 }
915
916 json jsonPhotoAlbumItem;
917 OHOS::Media::PhotoAlbum &photoAlbumRef = *album.get();
918 AddPhotoAlbumToJson(jsonPhotoAlbumItem, photoAlbumRef);
919
920 jsonArray.push_back(jsonPhotoAlbumItem);
921 }
922
923 if (!WriteJsonFile(filePath, jsonArray)) {
924 MEDIA_ERR_LOG("WriteJsonFile failed");
925 return E_FILE_OPER_FAIL;
926 }
927
928 MEDIA_DEBUG_LOG("WritePhotoAlbumToFile end\n");
929 return E_OK;
930 }
931
ReadPhotoAlbumFromFile(const string & filePath,vector<shared_ptr<PhotoAlbum>> & photoAlbumVector)932 int32_t MediaLibraryMetaRecovery::ReadPhotoAlbumFromFile(const string &filePath,
933 vector<shared_ptr<PhotoAlbum>> &photoAlbumVector)
934 {
935 json jsonArray;
936 if (!ReadJsonFile(filePath, jsonArray)) {
937 MEDIA_ERR_LOG("ReadJsonFile failed");
938 return E_FILE_OPER_FAIL;
939 }
940 if (!jsonArray.is_array()) {
941 MEDIA_ERR_LOG("json not is array");
942 return E_ERR;
943 }
944
945 int ret = E_OK;
946 for (const json &j : jsonArray) {
947 PhotoAlbum photoAlbum;
948 if (!GetPhotoAlbumFromJson(j, photoAlbum)) {
949 MEDIA_WARN_LOG("GetPhotoAlbumFromJson failed");
950 ret = E_ERR;
951 }
952 if (photoAlbum.GetPhotoAlbumSubType() != 1 && photoAlbum.GetLPath() == "") {
953 continue;
954 }
955 photoAlbumVector.emplace_back(make_shared<PhotoAlbum>(photoAlbum));
956 }
957
958 return ret;
959 }
960
WriteMetaStatusToFile(const string & keyPath,const int32_t status)961 int32_t MediaLibraryMetaRecovery::WriteMetaStatusToFile(const string &keyPath, const int32_t status)
962 {
963 json j;
964 if (!ReadJsonFile(META_STATUS_PATH, j)) {
965 MEDIA_WARN_LOG("ReadFile META_STATUS_PATH failed, will write new META_STATUS_PATH file");
966 j = json::object();
967 }
968
969 j[keyPath] = json::number_integer_t(status);
970
971 if (!WriteJsonFile(META_STATUS_PATH, j)) {
972 MEDIA_ERR_LOG("WriteJsonFile failed");
973 return E_FILE_OPER_FAIL;
974 }
975
976 return E_OK;
977 }
978
ReadMetaStatusFromFile(set<int32_t> & status)979 int32_t MediaLibraryMetaRecovery::ReadMetaStatusFromFile(set<int32_t> &status)
980 {
981 json j;
982 if (!ReadJsonFile(META_STATUS_PATH, j)) {
983 MEDIA_ERR_LOG("ReadFile META_STATUS_PATH failed");
984 return E_FILE_OPER_FAIL;
985 }
986
987 for (const auto& [key, value] : j.items()) {
988 if (!value.is_number_integer()) {
989 MEDIA_ERR_LOG("key: %{public}s not is number", key.c_str());
990 continue;
991 }
992 // Read bucket_id which finish recovery
993 int32_t val = atoi(key.c_str());
994 MEDIA_INFO_LOG("finish recovery bucket_id: %{public}s", key.c_str());
995 status.insert(val);
996 }
997
998 return E_OK;
999 }
1000
ReadMetaRecoveryCountFromFile()1001 int32_t MediaLibraryMetaRecovery::ReadMetaRecoveryCountFromFile()
1002 {
1003 json j;
1004 int32_t recoverySuccessCnt = 0;
1005
1006 if (!ReadJsonFile(META_STATUS_PATH, j)) {
1007 MEDIA_ERR_LOG("ReadFile META_STATUS_PATH failed");
1008 return E_FILE_OPER_FAIL;
1009 }
1010
1011 for (const auto& [key, value] : j.items()) {
1012 if (!value.is_number_integer()) {
1013 MEDIA_ERR_LOG("key: %{public}s not is number", key.c_str());
1014 continue;
1015 }
1016 int32_t val = value.get<int32_t>();
1017 recoverySuccessCnt += val;
1018 MEDIA_INFO_LOG("finish recovery bucket_id: %{public}s, recovery success count=%{public}d", key.c_str(), val);
1019 }
1020
1021 return recoverySuccessCnt;
1022 }
1023
UpdatePhotoOwnerAlbumId(NativeRdb::ValuesBucket & values)1024 bool MediaLibraryMetaRecovery::UpdatePhotoOwnerAlbumId(NativeRdb::ValuesBucket &values)
1025 {
1026 NativeRdb::ValueObject valueObject;
1027 if (!values.GetObject(PhotoColumn::PHOTO_OWNER_ALBUM_ID, valueObject)) {
1028 return false;
1029 }
1030
1031 int32_t oldOwnerAlbumId = 0;
1032 valueObject.GetInt(oldOwnerAlbumId);
1033 if (oldAlbumIdToLpath.find(oldOwnerAlbumId) == oldAlbumIdToLpath.end()) {
1034 return false;
1035 }
1036
1037 const std::string &lpath = oldAlbumIdToLpath[oldOwnerAlbumId];
1038 if (lpathToNewAlbumId.end() == lpathToNewAlbumId.find(lpath)) {
1039 return false;
1040 }
1041
1042 int32_t newOwnerAlbumId = lpathToNewAlbumId[lpath];
1043 if (newOwnerAlbumId == oldOwnerAlbumId) {
1044 return false;
1045 }
1046
1047 MEDIA_DEBUG_LOG("convert album %{public}d to %{public}d", oldOwnerAlbumId, newOwnerAlbumId);
1048 values.Delete(PhotoColumn::PHOTO_OWNER_ALBUM_ID);
1049 values.PutInt(PhotoColumn::PHOTO_OWNER_ALBUM_ID, newOwnerAlbumId);
1050
1051 return true;
1052 }
1053
InsertMetadataInDbRetry(const FileAsset & fileAsset)1054 int32_t MediaLibraryMetaRecovery::InsertMetadataInDbRetry(const FileAsset &fileAsset)
1055 {
1056 int32_t retry_cnt = 0;
1057 do {
1058 if (InsertMetadataInDb(fileAsset) == E_OK) {
1059 break;
1060 }
1061
1062 retry_cnt++;
1063 MEDIA_ERR_LOG("InsertMetadataInDb failed, retry_cnt = %{public}d", retry_cnt);
1064 this_thread::sleep_for(chrono::milliseconds(META_RETRY_INTERVAL));
1065 } while (retry_cnt < META_RETRY_MAX_COUNTS);
1066
1067 if (retry_cnt >= META_RETRY_MAX_COUNTS) {
1068 MEDIA_ERR_LOG("InsertMetadataInDb finally failed, retry_cnt = %{public}d", retry_cnt);
1069 VariantMap map = {{KEY_ERR_FILE, __FILE__}, {KEY_ERR_LINE, __LINE__}, {KEY_ERR_CODE, E_DB_FAIL},
1070 {KEY_OPT_TYPE, OptType::CREATE}};
1071 PostEventUtils::GetInstance().PostErrorProcess(ErrType::RECOVERY_ERR, map);
1072 return ERR_FAIL;
1073 }
1074
1075 return E_OK;
1076 }
1077
InsertMetadataInDb(const FileAsset & fileAsset)1078 int32_t MediaLibraryMetaRecovery::InsertMetadataInDb(const FileAsset &fileAsset)
1079 {
1080 std::string filePath = fileAsset.GetFilePath();
1081 MEDIA_DEBUG_LOG("InsertMetadataInDb: photo filepath = %{public}s", DfxUtils::GetSafePath(filePath).c_str());
1082 if (MediaLibraryAssetOperations::CheckExist(filePath) == E_OK) {
1083 MEDIA_DEBUG_LOG("InsertMetadataInDb: insert: photo is exist in db, ignore");
1084 return E_OK;
1085 }
1086
1087 std::unordered_map<std::string, ResultSetDataType> columnInfoMap = QueryRecoveryPhotosTableColumnInfo();
1088 if (columnInfoMap.empty()) {
1089 MEDIA_ERR_LOG("QueryRecoveryPhotosTableColumnInfo failed");
1090 return E_ERR;
1091 }
1092
1093 auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
1094 if (rdbStore == nullptr) {
1095 MEDIA_ERR_LOG("GetRdbStore failed, return nullptr");
1096 return E_HAS_DB_ERROR;
1097 }
1098
1099 NativeRdb::ValuesBucket valuesBucket;
1100 SetValuesFromFileAsset(fileAsset, valuesBucket, columnInfoMap);
1101
1102 // set meta flags uptodate, to avoid backup db into meta again
1103 if (UpdatePhotoOwnerAlbumId(valuesBucket)) {
1104 valuesBucket.PutInt(PhotoColumn::PHOTO_METADATA_FLAGS, static_cast<int>(MetadataFlags::TYPE_DIRTY));
1105 } else {
1106 valuesBucket.PutInt(PhotoColumn::PHOTO_METADATA_FLAGS, static_cast<int>(MetadataFlags::TYPE_UPTODATE));
1107 }
1108
1109 int64_t outRowId = -1;
1110 int32_t errCode = rdbStore->Insert(outRowId, PhotoColumn::PHOTOS_TABLE, valuesBucket);
1111 if (errCode != NativeRdb::E_OK) {
1112 MEDIA_ERR_LOG("Insert photo failed, errCode = %{public}d", errCode);
1113 return errCode;
1114 }
1115
1116 return E_OK;
1117 }
1118
InsertMetadataInDb(const std::vector<shared_ptr<PhotoAlbum>> & vecPhotoAlbum)1119 int32_t MediaLibraryMetaRecovery::InsertMetadataInDb(const std::vector<shared_ptr<PhotoAlbum>> &vecPhotoAlbum)
1120 {
1121 auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
1122 CHECK_AND_RETURN_RET_LOG(rdbStore != nullptr, E_HAS_DB_ERROR, "GetRdbStore failed, return nullptr)");
1123
1124 for (auto iter : vecPhotoAlbum) {
1125 MEDIA_INFO_LOG("InsertMetadataInDb: album name = %{private}s", iter->GetAlbumName().c_str());
1126 NativeRdb::RdbPredicates predicates(PhotoAlbumColumns::TABLE);
1127 vector<string> columns = {PhotoAlbumColumns::ALBUM_ID,
1128 PhotoAlbumColumns::ALBUM_LPATH};
1129 predicates.IsNotNull(PhotoAlbumColumns::ALBUM_LPATH)->And()->EqualTo(PhotoAlbumColumns::ALBUM_LPATH,
1130 iter->GetLPath());
1131 auto resultSet = MediaLibraryRdbStore::QueryWithFilter(predicates, columns);
1132 if (resultSet == nullptr) {
1133 MEDIA_ERR_LOG("resultSet == nullptr)");
1134 continue;
1135 }
1136 if (resultSet->GoToNextRow() == NativeRdb::E_OK) {
1137 MEDIA_ERR_LOG("skip duplicate lpath %{public}s", iter->GetLPath().c_str());
1138 continue;
1139 }
1140 std::shared_ptr<TransactionOperations> trans = make_shared<TransactionOperations>(__func__);
1141 int32_t errCode = NativeRdb::E_OK;
1142 std::function<int(void)> func = [&]()->int {
1143 // Insert album item
1144 NativeRdb::ValuesBucket valuesBucket;
1145 SetValuesFromPhotoAlbum(iter, valuesBucket);
1146 int64_t outRowId = -1;
1147 errCode = trans->Insert(outRowId, PhotoAlbumColumns::TABLE, valuesBucket);
1148 if (errCode != NativeRdb::E_OK) {
1149 MEDIA_ERR_LOG("InsertMetadataInDb: insert album failed, errCode = %{public}d", errCode);
1150 return errCode;
1151 }
1152
1153 // Update album order inserted just now
1154 int32_t changedRows = -1;
1155 NativeRdb::RdbPredicates predicatesOrder(PhotoAlbumColumns::TABLE);
1156 predicatesOrder.And()->EqualTo(PhotoAlbumColumns::ALBUM_ID, outRowId)
1157 ->And()
1158 ->NotEqualTo(PhotoAlbumColumns::ALBUM_ORDER, iter->GetOrder());
1159 valuesBucket.Clear();
1160 valuesBucket.PutInt(PhotoAlbumColumns::ALBUM_ORDER, iter->GetOrder());
1161 errCode = trans->Update(changedRows, valuesBucket, predicatesOrder);
1162 if (errCode != E_OK) {
1163 MEDIA_ERR_LOG("Update album order failed, err = %{public}d", errCode);
1164 return E_HAS_DB_ERROR;
1165 }
1166 if (changedRows > 0) {
1167 MEDIA_INFO_LOG("Update album order");
1168 }
1169 return errCode;
1170 };
1171 errCode = trans->RetryTrans(func);
1172 if (errCode != E_OK) {
1173 MEDIA_ERR_LOG("InsertMetadataInDb: trans retry fail!, ret:%{public}d", errCode);
1174 return errCode;
1175 }
1176 }
1177
1178 return E_OK;
1179 }
1180
UpdateMetadataFlagInDb(const int32_t fieldId,const MetadataFlags & flag)1181 int32_t MediaLibraryMetaRecovery::UpdateMetadataFlagInDb(const int32_t fieldId, const MetadataFlags &flag)
1182 {
1183 int32_t errCode = E_OK;
1184
1185 auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
1186 if (!rdbStore) {
1187 MEDIA_ERR_LOG("GetRdbStore failed");
1188 return E_HAS_DB_ERROR;
1189 }
1190
1191 NativeRdb::RdbPredicates predicates(PhotoColumn::PHOTOS_TABLE);
1192 predicates.EqualTo(MediaColumn::MEDIA_ID, fieldId);
1193
1194 NativeRdb::ValuesBucket values;
1195 values.PutInt(PhotoColumn::PHOTO_METADATA_FLAGS, static_cast<int>(flag));
1196
1197 int32_t changedRows = -1;
1198 errCode = rdbStore->Update(changedRows, values, predicates);
1199 if (errCode != E_OK) {
1200 MEDIA_ERR_LOG("Database update failed, err = %{public}d", errCode);
1201 return E_HAS_DB_ERROR;
1202 }
1203 return E_OK;
1204 }
1205
SetRdbRebuiltStatus(bool status)1206 int32_t MediaLibraryMetaRecovery::SetRdbRebuiltStatus(bool status)
1207 {
1208 rdbRebuilt_ = status;
1209 reBuiltCount_++;
1210 return E_OK;
1211 }
1212
StartAsyncRecovery()1213 int32_t MediaLibraryMetaRecovery::StartAsyncRecovery()
1214 {
1215 StatisticRestore();
1216
1217 MediaLibraryMetaRecoveryState oldState = recoveryState_.exchange(MediaLibraryMetaRecoveryState::STATE_RECOVERING);
1218 if (oldState == MediaLibraryMetaRecoveryState::STATE_RECOVERING) {
1219 MEDIA_INFO_LOG("recovery process is already running");
1220 return E_OK;
1221 }
1222
1223 if (oldState == MediaLibraryMetaRecoveryState::STATE_NONE) {
1224 bool hasStatusFile = bool(access(META_STATUS_PATH.c_str(), F_OK) == E_OK);
1225 MEDIA_INFO_LOG("rebuild status %{public}d, has status file %{public}d", rdbRebuilt_, hasStatusFile);
1226 if (!hasStatusFile && !rdbRebuilt_) {
1227 MEDIA_INFO_LOG("StartAsyncRecovery: no need to recovery");
1228 recoveryState_.exchange(MediaLibraryMetaRecoveryState::STATE_NONE);
1229 return E_OK;
1230 }
1231 oldState = hasStatusFile ? MediaLibraryMetaRecoveryState::STATE_RECOVERING_ABORT :
1232 MediaLibraryMetaRecoveryState::STATE_NONE;
1233 }
1234
1235 if (oldState == MediaLibraryMetaRecoveryState::STATE_RECOVERING_ABORT) {
1236 ReadMetaStatusFromFile(metaStatus);
1237 } else {
1238 // Create status.json if not exist
1239 const string parentDir = MediaFileUtils::GetParentPath(META_STATUS_PATH);
1240 if (MediaFileUtils::CreateDirectory(parentDir)) {
1241 MediaFileUtils::CreateFile(META_STATUS_PATH);
1242 } else {
1243 MEDIA_ERR_LOG("CreateDirectory failed, dir = %{public}s", DfxUtils::GetSafePath(parentDir).c_str());
1244 }
1245 }
1246
1247 std::thread([this]() {
1248 MEDIA_INFO_LOG("Start recovery");
1249 int64_t recoveryStartTime = MediaFileUtils::UTCTimeMilliSeconds();
1250 this->DoDataBaseRecovery();
1251 int64_t recoveryTotalTime = MediaFileUtils::UTCTimeMilliSeconds() - recoveryStartTime;
1252 bool isStatusFileExist = bool(access(META_STATUS_PATH.c_str(), F_OK) == E_OK);
1253 if (isStatusFileExist) {
1254 recoveryState_.exchange(MediaLibraryMetaRecoveryState::STATE_RECOVERING_ABORT);
1255 } else {
1256 recoveryState_.exchange(MediaLibraryMetaRecoveryState::STATE_NONE);
1257 }
1258 recoveryCostTime_ += recoveryTotalTime;
1259 RecoveryStatistic();
1260 }).detach();
1261
1262 return E_OK;
1263 }
1264
DeleteMetaDataByPath(const string & filePath)1265 int32_t MediaLibraryMetaRecovery::DeleteMetaDataByPath(const string &filePath)
1266 {
1267 string metaFilePath;
1268 if (PhotoFileUtils::GetMetaPathFromOrignalPath(filePath, metaFilePath) != E_OK) {
1269 MEDIA_ERR_LOG("DeleteMetaDataByPath: invalid photo filePath, %{public}s",
1270 DfxUtils::GetSafePath(filePath).c_str());
1271 return E_INVALID_PATH;
1272 }
1273
1274 if (remove(metaFilePath.c_str()) != 0 && errno != ENOENT) {
1275 MEDIA_ERR_LOG("remove metafile failed %{public}d, path %s", errno, DfxUtils::GetSafePath(metaFilePath).c_str());
1276 }
1277
1278 MEDIA_INFO_LOG("DeleteMetaDataByPath: metafile removed successful, %{public}s",
1279 DfxUtils::GetSafePath(metaFilePath).c_str());
1280 return E_OK;
1281 }
1282
StopCloudSync()1283 void MediaLibraryMetaRecovery::StopCloudSync()
1284 {
1285 MEDIA_INFO_LOG("Begin StopCloudSync");
1286 FileManagement::CloudSync::CloudSyncManager::GetInstance().StopSync(BUNDLE_NAME, true);
1287 }
1288
RestartCloudSync()1289 void MediaLibraryMetaRecovery::RestartCloudSync()
1290 {
1291 MEDIA_INFO_LOG("Begin reset cloud cursor");
1292 static uint32_t baseUserRange = 200000; // uid base offset
1293 uid_t uid = getuid() / baseUserRange;
1294 FileManagement::CloudSync::CloudSyncManager::GetInstance().ResetCursor();
1295
1296 int32_t ret = FileManagement::CloudSync::CloudSyncManager::GetInstance().StartSync(BUNDLE_NAME);
1297 if (ret != 0) {
1298 MEDIA_ERR_LOG("StartCloudSync fail, errcode=%{public}d", ret);
1299 }
1300 MEDIA_INFO_LOG("End StartCloudSync");
1301 }
1302
GetDataType(const std::string & name)1303 ResultSetDataType MediaLibraryMetaRecovery::GetDataType(const std::string &name)
1304 {
1305 auto it = FILEASSET_MEMBER_MAP.find(name);
1306 if (it == FILEASSET_MEMBER_MAP.end()) {
1307 MEDIA_ERR_LOG("FILEASSET_MEMBER_MAP not find name: %{public}s", name.c_str());
1308 return TYPE_NULL;
1309 }
1310
1311 switch (it->second) {
1312 case MEMBER_TYPE_INT32: {
1313 return TYPE_INT32;
1314 break;
1315 }
1316 case MEMBER_TYPE_INT64: {
1317 return TYPE_INT64;
1318 break;
1319 }
1320 case MEMBER_TYPE_STRING: {
1321 return TYPE_STRING;
1322 break;
1323 }
1324 case MEMBER_TYPE_DOUBLE: {
1325 return TYPE_DOUBLE;
1326 break;
1327 }
1328 default: {
1329 return TYPE_NULL;
1330 break;
1331 }
1332 }
1333 }
1334
QueryRecoveryPhotosTableColumnInfo()1335 std::unordered_map<std::string, ResultSetDataType> MediaLibraryMetaRecovery::QueryRecoveryPhotosTableColumnInfo()
1336 {
1337 MEDIA_DEBUG_LOG("QueryRecoveryPhotosTableColumnInfo");
1338 std::unordered_map<std::string, ResultSetDataType> columnInfoMap;
1339 std::vector<std::string> columnInfo = MediaLibraryAssetOperations::QueryPhotosTableColumnInfo();
1340 if (columnInfo.empty()) {
1341 MEDIA_ERR_LOG("GetPhotosTableColumnInfo failed");
1342 return columnInfoMap;
1343 }
1344
1345 for (const std::string &name : columnInfo) {
1346 if (EXCLUDED_COLUMNS.count(name) > 0) {
1347 continue;
1348 }
1349 ResultSetDataType type = GetDataType(name);
1350 columnInfoMap.emplace(name, type);
1351 MEDIA_DEBUG_LOG("photos table name: %{public}s, type: %{public}d", name.c_str(), type);
1352 }
1353
1354 return columnInfoMap;
1355 }
1356
ResetAllMetaDirty()1357 int32_t MediaLibraryMetaRecovery::ResetAllMetaDirty()
1358 {
1359 const std::string RESET_ALL_META_DIRTY_SQL =
1360 " UPDATE " + PhotoColumn::PHOTOS_TABLE + " SET " + PhotoColumn::PHOTO_METADATA_FLAGS +
1361 " = 0 " + " WHERE " + PhotoColumn::PHOTO_METADATA_FLAGS + " == 2; END;";
1362
1363 auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
1364 if (rdbStore == nullptr) {
1365 return E_HAS_DB_ERROR;
1366 }
1367
1368 int32_t err = rdbStore->ExecuteSql(RESET_ALL_META_DIRTY_SQL);
1369 if (err != NativeRdb::E_OK) {
1370 MEDIA_ERR_LOG("Fatal error! Failed to exec: %{public}s", RESET_ALL_META_DIRTY_SQL.c_str());
1371 }
1372 return err;
1373 }
1374
QueryInt(const NativeRdb::AbsRdbPredicates & predicates,const std::vector<std::string> & columns,const std::string & queryColumn,int32_t & value)1375 static int32_t QueryInt(const NativeRdb::AbsRdbPredicates &predicates,
1376 const std::vector<std::string> &columns, const std::string &queryColumn, int32_t &value)
1377 {
1378 auto resultSet = MediaLibraryRdbStore::QueryWithFilter(predicates, columns);
1379 if (resultSet == nullptr || resultSet->GoToFirstRow() != NativeRdb::E_OK) {
1380 return E_DB_FAIL;
1381 }
1382 value = GetInt32Val(queryColumn, resultSet);
1383 return E_OK;
1384 }
1385
QueryAllPhoto(bool backup)1386 static int32_t QueryAllPhoto(bool backup)
1387 {
1388 NativeRdb::RdbPredicates predicates(PhotoColumn::PHOTOS_TABLE);
1389 predicates.And()->BeginWrap()->EqualTo(PhotoColumn::PHOTO_POSITION, "1")->Or()
1390 ->EqualTo(PhotoColumn::PHOTO_POSITION, "3")->EndWrap();
1391
1392 if (backup) {
1393 predicates.BeginWrap()
1394 ->EqualTo(PhotoColumn::PHOTO_METADATA_FLAGS, static_cast<int>(MetadataFlags::TYPE_NEW))
1395 ->Or()
1396 ->EqualTo(PhotoColumn::PHOTO_METADATA_FLAGS, static_cast<int>(MetadataFlags::TYPE_DIRTY))
1397 ->Or()
1398 ->IsNull(PhotoColumn::PHOTO_METADATA_FLAGS)
1399 ->EndWrap();
1400 }
1401
1402 std::vector<std::string> columns = { "count(1) AS count" };
1403 std::string queryColumn = "count";
1404 int32_t count;
1405 int32_t errCode = QueryInt(predicates, columns, queryColumn, count);
1406 if (errCode != E_OK) {
1407 MEDIA_ERR_LOG("query local image fail: %{public}d", errCode);
1408 }
1409 return count;
1410 }
1411
StatisticSave()1412 void MediaLibraryMetaRecovery::StatisticSave()
1413 {
1414 int32_t errCode;
1415 shared_ptr<NativePreferences::Preferences> prefs =
1416 NativePreferences::PreferencesHelper::GetPreferences(RDB_CONFIG, errCode);
1417 if (!prefs) {
1418 MEDIA_ERR_LOG("get preferences error: %{public}d", errCode);
1419 return;
1420 }
1421 prefs->PutLong(BACKUP_PHOTO_COUNT, backupSuccCnt_);
1422 prefs->PutLong(BACKUP_COST_TIME, backupCostTime_);
1423 prefs->PutLong(REBUILT_COUNT, reBuiltCount_);
1424 prefs->PutLong(RECOVERY_BACKUP_TOTAL_COUNT, recoveryTotalBackupCnt_);
1425 prefs->PutLong(RECOVERY_SUCC_PHOTO_COUNT, recoverySuccCnt_);
1426 prefs->PutLong(RECOVERY_COST_TIME, recoveryCostTime_);
1427 prefs->FlushSync();
1428 }
1429
StatisticRestore()1430 void MediaLibraryMetaRecovery::StatisticRestore()
1431 {
1432 int32_t errCode;
1433 shared_ptr<NativePreferences::Preferences> prefs =
1434 NativePreferences::PreferencesHelper::GetPreferences(RDB_CONFIG, errCode);
1435 if (!prefs) {
1436 MEDIA_ERR_LOG("get preferences error: %{public}d", errCode);
1437 return;
1438 }
1439 backupSuccCnt_ = prefs->GetLong(BACKUP_PHOTO_COUNT, 0);
1440 backupCostTime_ = prefs->GetLong(BACKUP_COST_TIME, 0);
1441 reBuiltCount_ += prefs->GetLong(REBUILT_COUNT, 0); // reBuiltCount_ will be set before Restore
1442 recoveryTotalBackupCnt_ = prefs->GetLong(RECOVERY_BACKUP_TOTAL_COUNT, 0);
1443 recoverySuccCnt_ = prefs->GetLong(RECOVERY_SUCC_PHOTO_COUNT, 0);
1444 recoveryCostTime_ = prefs->GetLong(RECOVERY_COST_TIME, 0);
1445 }
1446
StatisticReset()1447 void MediaLibraryMetaRecovery::StatisticReset()
1448 {
1449 backupSuccCnt_ = 0;
1450 reBuiltCount_ = 0;
1451 backupCostTime_ = 0;
1452 recoverySuccCnt_ = 0;
1453 recoveryCostTime_ = 0;
1454 recoveryTotalBackupCnt_ = 0;
1455 StatisticSave();
1456 }
1457
RecoveryStatistic()1458 void MediaLibraryMetaRecovery::RecoveryStatistic()
1459 {
1460 static constexpr char MEDIA_LIBRARY[] = "MEDIALIBRARY";
1461 int64_t totalPhotoCount = QueryAllPhoto(false);
1462 int64_t totalbackupCount = GetTotalBackupFileCount();
1463 int ret = HiSysEventWrite(
1464 MEDIA_LIBRARY,
1465 "MEDIALIB_META_RECOVERY_INFO",
1466 HiviewDFX::HiSysEvent::EventType::STATISTIC,
1467 "TOTAL_PHOTO_COUNT", totalPhotoCount,
1468 "TOTAL_BACKUP_COUNT", totalbackupCount,
1469 "BACKUP_PHOTO_COUNT", backupSuccCnt_,
1470 "BACKUP_COST_TIME", backupCostTime_,
1471 "REBUILT_COUNT", reBuiltCount_,
1472 "RECOVERY_BACKUP_TOTAL_COUNT", recoveryTotalBackupCnt_,
1473 "RECOVERY_SUCC_PHOTO_COUNT", recoverySuccCnt_,
1474 "RECOVERY_COST_TIME", recoveryCostTime_);
1475 if (ret != 0) {
1476 MEDIA_ERR_LOG("RecoveryStatistic error:%{public}d", ret);
1477 }
1478 StatisticReset();
1479 }
1480 } // namespace Media
1481 } // namespace OHOS
1482