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