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