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 #include "photos_clone.h"
16
17 #include <uuid.h>
18 #include <numeric>
19
20 #include "rdb_store.h"
21 #include "result_set_utils.h"
22 #include "photo_album_dao.h"
23 #include "backup_const.h"
24 #include "media_log.h"
25 #include "album_plugin_config.h"
26 #include "userfile_manager_types.h"
27
28 namespace OHOS::Media {
ToString(const FileInfo & fileInfo)29 std::string PhotosClone::ToString(const FileInfo &fileInfo)
30 {
31 return "FileInfo[ fileId: " + std::to_string(fileInfo.fileIdOld) + ", displayName: " + fileInfo.displayName +
32 ", bundleName: " + fileInfo.bundleName + ", lPath: " + fileInfo.lPath +
33 ", size: " + std::to_string(fileInfo.fileSize) + ", fileType: " + std::to_string(fileInfo.fileType) + " ]";
34 }
35
ToLower(const std::string & str)36 std::string PhotosClone::ToLower(const std::string &str)
37 {
38 std::string lowerStr;
39 std::transform(
40 str.begin(), str.end(), std::back_inserter(lowerStr), [](unsigned char c) { return std::tolower(c); });
41 return lowerStr;
42 }
43
44 /**
45 * @brief Get Row Count of Photos in Album.
46 */
GetPhotosRowCountInPhotoMap()47 int32_t PhotosClone::GetPhotosRowCountInPhotoMap()
48 {
49 std::string querySql = this->SQL_PHOTOS_TABLE_COUNT_IN_PHOTO_MAP;
50 CHECK_AND_RETURN_RET_LOG(this->mediaLibraryOriginalRdb_ != nullptr, 0,
51 "Media_Restore: mediaLibraryOriginalRdb_ is null.");
52 auto resultSet = this->mediaLibraryOriginalRdb_->QuerySql(querySql);
53 bool cond = (resultSet == nullptr || resultSet->GoToFirstRow() != NativeRdb::E_OK);
54 CHECK_AND_RETURN_RET(!cond, 0);
55 return GetInt32Val("count", resultSet);
56 }
57
58 /**
59 * @brief Get Row Count of Cloud Photos in Album.
60 */
GetCloudPhotosRowCountInPhotoMap()61 int32_t PhotosClone::GetCloudPhotosRowCountInPhotoMap()
62 {
63 std::string querySql = this->SQL_CLOUD_PHOTOS_TABLE_COUNT_IN_PHOTO_MAP;
64 CHECK_AND_RETURN_RET_LOG(this->mediaLibraryOriginalRdb_ != nullptr, 0,
65 "singleCloud Media_Restore: mediaLibraryOriginalRdb_ is null.");
66 auto resultSet = this->mediaLibraryOriginalRdb_->QuerySql(querySql);
67 bool cond = (resultSet == nullptr || resultSet->GoToFirstRow() != NativeRdb::E_OK);
68 CHECK_AND_RETURN_RET(!cond, 0);
69 return GetInt32Val("count", resultSet);
70 }
71
72 /**
73 * @brief Get Row Count of Photos not in Album.
74 */
GetPhotosRowCountNotInPhotoMap()75 int32_t PhotosClone::GetPhotosRowCountNotInPhotoMap()
76 {
77 std::string querySql = this->SQL_PHOTOS_TABLE_COUNT_NOT_IN_PHOTO_MAP;
78 CHECK_AND_RETURN_RET_LOG(this->mediaLibraryOriginalRdb_ != nullptr, 0,
79 "Media_Restore: mediaLibraryOriginalRdb_ is null.");
80 auto resultSet = this->mediaLibraryOriginalRdb_->QuerySql(querySql);
81 bool cond = (resultSet == nullptr || resultSet->GoToFirstRow() != NativeRdb::E_OK);
82 CHECK_AND_RETURN_RET(!cond, 0);
83 return GetInt32Val("count", resultSet);
84 }
85
86 /**
87 * @brief Get Row Count of Cloud Photos not in Album.
88 */
GetCloudPhotosRowCountNotInPhotoMap()89 int32_t PhotosClone::GetCloudPhotosRowCountNotInPhotoMap()
90 {
91 std::string querySql = this->SQL_CLOUD_PHOTOS_TABLE_COUNT_NOT_IN_PHOTO_MAP;
92 CHECK_AND_RETURN_RET_LOG(this->mediaLibraryOriginalRdb_ != nullptr, 0,
93 "singleCloud Media_Restore: mediaLibraryOriginalRdb_ is null.");
94 auto resultSet = this->mediaLibraryOriginalRdb_->QuerySql(querySql);
95 bool cond = (resultSet == nullptr || resultSet->GoToFirstRow() != NativeRdb::E_OK);
96 CHECK_AND_RETURN_RET(!cond, 0);
97 return GetInt32Val("count", resultSet);
98 }
99
100 /**
101 * @brief Query the Photos Info, which is in PhotoAlbum, from the Original MediaLibrary Database.
102 */
GetPhotosInPhotoMap(int32_t offset,int32_t pageSize)103 std::shared_ptr<NativeRdb::ResultSet> PhotosClone::GetPhotosInPhotoMap(int32_t offset, int32_t pageSize)
104 {
105 std::vector<NativeRdb::ValueObject> bindArgs = {offset, pageSize};
106 CHECK_AND_RETURN_RET_LOG(this->mediaLibraryOriginalRdb_ != nullptr, nullptr,
107 "Media_Restore: mediaLibraryOriginalRdb_ is null.");
108 return this->mediaLibraryOriginalRdb_->QuerySql(this->SQL_PHOTOS_TABLE_QUERY_IN_PHOTO_MAP, bindArgs);
109 }
110
111 /**
112 * @brief Query the Cloud Photos Info, which is in PhotoAlbum, from the Original MediaLibrary Database.
113 */
GetCloudPhotosInPhotoMap(int32_t offset,int32_t pageSize)114 std::shared_ptr<NativeRdb::ResultSet> PhotosClone::GetCloudPhotosInPhotoMap(int32_t offset, int32_t pageSize)
115 {
116 std::vector<NativeRdb::ValueObject> bindArgs = {offset, pageSize};
117 CHECK_AND_RETURN_RET_LOG(this->mediaLibraryOriginalRdb_ != nullptr, nullptr,
118 "singleCloud Media_Restore: mediaLibraryOriginalRdb_ is null.");
119 return this->mediaLibraryOriginalRdb_->QuerySql(this->SQL_CLOUD_PHOTOS_TABLE_QUERY_IN_PHOTO_MAP, bindArgs);
120 }
121
122 /**
123 * @brief Query the Photos Info, which is not in PhotoAlbum, from the Original MediaLibrary Database.
124 */
GetPhotosNotInPhotoMap(int32_t offset,int32_t pageSize)125 std::shared_ptr<NativeRdb::ResultSet> PhotosClone::GetPhotosNotInPhotoMap(int32_t offset, int32_t pageSize)
126 {
127 std::vector<NativeRdb::ValueObject> bindArgs = {offset, pageSize};
128 if (this->mediaLibraryOriginalRdb_ == nullptr) {
129 MEDIA_ERR_LOG("Media_Restore: mediaLibraryOriginalRdb_ is null.");
130 return nullptr;
131 }
132 return this->mediaLibraryOriginalRdb_->QuerySql(this->SQL_PHOTOS_TABLE_QUERY_NOT_IN_PHOTO_MAP, bindArgs);
133 }
134
135 /**
136 * @brief Query the Cloud Photos Info, which is not in PhotoAlbum, from the Original MediaLibrary Database.
137 */
GetCloudPhotosNotInPhotoMap(int32_t offset,int32_t pageSize)138 std::shared_ptr<NativeRdb::ResultSet> PhotosClone::GetCloudPhotosNotInPhotoMap(int32_t offset, int32_t pageSize)
139 {
140 std::vector<NativeRdb::ValueObject> bindArgs = {offset, pageSize};
141 if (this->mediaLibraryOriginalRdb_ == nullptr) {
142 MEDIA_ERR_LOG("singleCloud Media_Restore: mediaLibraryOriginalRdb_ is null.");
143 return nullptr;
144 }
145 return this->mediaLibraryOriginalRdb_->QuerySql(this->SQL_CLOUD_PHOTOS_TABLE_QUERY_NOT_IN_PHOTO_MAP, bindArgs);
146 }
147
148 /**
149 * @note If the lPath is empty, return '/Pictures/其它' string.
150 * If the lPath is '/Pictures/ScreenShots', return '/Pictures/ScreenShots' string.
151 * Otherwise, return the lPath of the FileInfo.
152 */
FindAlbumInfo(const FileInfo & fileInfo)153 PhotoAlbumDao::PhotoAlbumRowData PhotosClone::FindAlbumInfo(const FileInfo &fileInfo)
154 {
155 PhotoAlbumDao::PhotoAlbumRowData albumInfo;
156 std::string lPath = fileInfo.lPath;
157 // Scenario 2, WHEN FileInfo is in hidden album, THEN override lPath to the folder in sourcePath.
158 // Scenario 3, WHEN FileInfo is not belongs to any album, THEN override lPath to the folder in sourcePath.
159 // Note, sourcePath is a sign of the possible scenaio that the file is not in any album.
160 bool islPathMiss = !fileInfo.sourcePath.empty() && (fileInfo.hidden == 1 || fileInfo.recycledTime != 0);
161 islPathMiss = islPathMiss || fileInfo.lPath.empty();
162 if (islPathMiss) {
163 lPath = this->photoAlbumDao_.ParseSourcePathToLPath(fileInfo.sourcePath);
164 MEDIA_INFO_LOG("Media_Restore: fix lPath of album.fileInfo.lPath: %{public}s, "
165 "lPathFromSourcePath: %{public}s, lowercase: %{public}s, FileInfo Object: %{public}s",
166 fileInfo.lPath.c_str(),
167 lPath.c_str(),
168 this->ToLower(lPath).c_str(),
169 this->ToString(fileInfo).c_str());
170 }
171 // Scenario 1, WHEN FileInfo is in /Pictures/Screenshots and Video type, THEN redirect to /Pictures/Screenrecords
172 if (this->ToLower(lPath) == this->ToLower(AlbumPlugin::LPATH_SCREEN_SHOTS) &&
173 fileInfo.fileType == MediaType::MEDIA_TYPE_VIDEO) {
174 albumInfo = this->photoAlbumDao_.BuildAlbumInfoOfRecorders();
175 albumInfo = this->photoAlbumDao_.GetOrCreatePhotoAlbum(albumInfo);
176 MEDIA_INFO_LOG("Media_Restore: screenshots redirect to screenrecords, fileInfo.lPath: %{public}s, "
177 "lPath: %{public}s, Object: %{public}s, albumInfo: %{public}s",
178 fileInfo.lPath.c_str(),
179 lPath.c_str(),
180 this->ToString(fileInfo).c_str(),
181 this->photoAlbumDao_.ToString(albumInfo).c_str());
182 return albumInfo;
183 }
184 albumInfo = this->photoAlbumDao_.BuildAlbumInfoByLPath(lPath);
185 return this->photoAlbumDao_.GetOrCreatePhotoAlbum(albumInfo);
186 }
187
188 /**
189 * @brief Find the lPath of the PhotoAlbum related to Photos from target database.
190 */
FindlPath(const FileInfo & fileInfo)191 std::string PhotosClone::FindlPath(const FileInfo &fileInfo)
192 {
193 PhotoAlbumDao::PhotoAlbumRowData albumInfo = this->FindAlbumInfo(fileInfo);
194 return albumInfo.lPath;
195 }
196
197 /**
198 * @brief Find the albumId of the PhotoAlbum related to Photos from target database.
199 */
FindAlbumId(const FileInfo & fileInfo)200 int32_t PhotosClone::FindAlbumId(const FileInfo &fileInfo)
201 {
202 PhotoAlbumDao::PhotoAlbumRowData albumInfo = this->FindAlbumInfo(fileInfo);
203 return albumInfo.albumId;
204 }
205
206 /**
207 * @brief Find the packageName of the PhotoAlbum related to Photos from target database.
208 */
FindPackageName(const FileInfo & fileInfo)209 std::string PhotosClone::FindPackageName(const FileInfo &fileInfo)
210 {
211 PhotoAlbumDao::PhotoAlbumRowData albumInfo = this->FindAlbumInfo(fileInfo);
212 // Only provide the package name of the SOURCE album.
213 bool cond = (albumInfo.albumType != static_cast<int32_t>(PhotoAlbumType::SOURCE) ||
214 albumInfo.albumSubType != static_cast<int32_t>(PhotoAlbumSubType::SOURCE_GENERIC));
215 CHECK_AND_RETURN_RET(!cond, "");
216 return albumInfo.albumName;
217 }
218
219 /**
220 * @brief Find the bundleName of the PhotoAlbum related to Photos from target database.
221 */
FindBundleName(const FileInfo & fileInfo)222 std::string PhotosClone::FindBundleName(const FileInfo &fileInfo)
223 {
224 PhotoAlbumDao::PhotoAlbumRowData albumInfo = this->FindAlbumInfo(fileInfo);
225 // Only provide the bundle name of the SOURCE album.
226 if (albumInfo.albumType != static_cast<int32_t>(PhotoAlbumType::SOURCE) ||
227 albumInfo.albumSubType != static_cast<int32_t>(PhotoAlbumSubType::SOURCE_GENERIC)) {
228 return "";
229 }
230 return albumInfo.bundleName;
231 }
232
FindDuplicateBurstKey()233 std::vector<PhotosDao::PhotosRowData> PhotosClone::FindDuplicateBurstKey()
234 {
235 std::vector<PhotosDao::PhotosRowData> result;
236 std::string querySql = this->SQL_PHOTOS_TABLE_BURST_KEY_DUPLICATE_QUERY;
237 int rowCount = 0;
238 int offset = 0;
239 int pageSize = 200;
240 do {
241 std::vector<NativeRdb::ValueObject> bindArgs = {offset, pageSize};
242 if (this->mediaLibraryOriginalRdb_ == nullptr) {
243 MEDIA_ERR_LOG("Media_Restore: mediaLibraryOriginalRdb_ is null.");
244 break;
245 }
246 auto resultSet = this->mediaLibraryTargetRdb_->QuerySql(querySql, bindArgs);
247 if (resultSet == nullptr) {
248 MEDIA_ERR_LOG("Query resultSql is null.");
249 break;
250 }
251 while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
252 PhotosDao::PhotosRowData info;
253 info.burstKey = GetStringVal("burst_key", resultSet);
254 info.ownerAlbumId = GetInt32Val("owner_album_id", resultSet);
255 result.emplace_back(info);
256 }
257 // Check if there are more rows to fetch.
258 resultSet->GetRowCount(rowCount);
259 offset += pageSize;
260 } while (rowCount > 0);
261 return result;
262 }
263
FindPhotoQuality(const FileInfo & fileInfo)264 int32_t PhotosClone::FindPhotoQuality(const FileInfo &fileInfo)
265 {
266 if (fileInfo.photoQuality == 1) {
267 return 0;
268 }
269 return fileInfo.photoQuality;
270 }
271
ToString(const std::vector<NativeRdb::ValueObject> & values)272 std::string PhotosClone::ToString(const std::vector<NativeRdb::ValueObject> &values)
273 {
274 std::vector<std::string> result;
275 for (auto &value : values) {
276 std::string str;
277 value.GetString(str);
278 result.emplace_back(str + ", ");
279 }
280 return std::accumulate(result.begin(), result.end(), std::string());
281 }
282
283 /**
284 * @brief generate a uuid
285 *
286 * @return std::string uuid with 32 characters
287 */
GenerateUuid()288 std::string PhotosClone::GenerateUuid()
289 {
290 uuid_t uuid;
291 uuid_generate(uuid);
292 char str[UUID_STR_LENGTH] = {};
293 uuid_unparse(uuid, str);
294 return str;
295 }
296
297 /**
298 * @brief Fix Duplicate burst_key in Photos, which is used in different PhotoAlbum.
299 */
FixDuplicateBurstKeyInDifferentAlbum(std::atomic<uint64_t> & totalNumber)300 int32_t PhotosClone::FixDuplicateBurstKeyInDifferentAlbum(std::atomic<uint64_t> &totalNumber)
301 {
302 std::vector<PhotosDao::PhotosRowData> duplicateBurstKeyList = this->FindDuplicateBurstKey();
303 totalNumber += static_cast<uint64_t>(duplicateBurstKeyList.size());
304 MEDIA_INFO_LOG("Media_Restore: onProcess Update otherTotalNumber_: %{public}lld", (long long)totalNumber);
305 std::string executeSql = this->SQL_PHOTOS_TABLE_BURST_KEY_UPDATE;
306 for (auto &info : duplicateBurstKeyList) {
307 if (info.burstKey.empty()) {
308 continue;
309 }
310 std::string burstKeyNew = this->GenerateUuid();
311 std::vector<NativeRdb::ValueObject> bindArgs = {burstKeyNew, info.ownerAlbumId, info.burstKey};
312 MEDIA_INFO_LOG("Media_Restore: executeSql = %{public}s, bindArgs=%{public}s",
313 executeSql.c_str(),
314 this->ToString(bindArgs).c_str());
315 if (this->mediaLibraryOriginalRdb_ == nullptr) {
316 MEDIA_ERR_LOG("Media_Restore: mediaLibraryOriginalRdb_ is null.");
317 break;
318 }
319 int32_t ret = this->mediaLibraryTargetRdb_->ExecuteSql(executeSql, bindArgs);
320 if (ret != NativeRdb::E_OK) {
321 MEDIA_ERR_LOG("Media_Restore: FixDuplicateBurstKeyInDifferentAlbum failed,"
322 " ret=%{public}d, sql=%{public}s, bindArgs=%{public}s",
323 ret,
324 executeSql.c_str(),
325 this->ToString(bindArgs).c_str());
326 }
327 }
328 return 0;
329 }
330
FindSourcePath(const FileInfo & fileInfo)331 std::string PhotosClone::FindSourcePath(const FileInfo &fileInfo)
332 {
333 if (fileInfo.lPath.empty()) {
334 return fileInfo.sourcePath;
335 }
336 if (!fileInfo.sourcePath.empty()) {
337 return fileInfo.sourcePath;
338 }
339 if (fileInfo.hidden == 0 && fileInfo.recycledTime == 0) {
340 return fileInfo.sourcePath;
341 }
342 return this->SOURCE_PATH_PREFIX + fileInfo.lPath + "/" + fileInfo.displayName;
343 }
344
345 /**
346 * @brief Get Row Count of Photos No Need Migrate.
347 */
GetNoNeedMigrateCount()348 int32_t PhotosClone::GetNoNeedMigrateCount()
349 {
350 std::string querySql = this->SQL_PHOTOS_TABLE_COUNT_NO_NEED_MIGRATE;
351 if (this->mediaLibraryOriginalRdb_ == nullptr) {
352 MEDIA_ERR_LOG("Media_Restore: mediaLibraryOriginalRdb_ is null.");
353 return 0;
354 }
355 auto resultSet = this->mediaLibraryOriginalRdb_->QuerySql(querySql);
356 if (resultSet == nullptr || resultSet->GoToFirstRow() != NativeRdb::E_OK) {
357 return 0;
358 }
359 return GetInt32Val("count", resultSet);
360 }
361 } // namespace OHOS::Media