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 "photo_album_dao.h"
16
17 #include <vector>
18 #include <string>
19
20 #include "rdb_store.h"
21 #include "media_log.h"
22 #include "result_set_utils.h"
23 #include "album_plugin_config.h"
24 #include "userfile_manager_types.h"
25 #include "medialibrary_errno.h"
26 #include "backup_database_utils.h"
27 #include "medialibrary_rdb_transaction.h"
28
29 namespace OHOS::Media {
ToLower(const std::string & str)30 std::string StringUtils::ToLower(const std::string &str)
31 {
32 std::string lowerStr;
33 std::transform(
34 str.begin(), str.end(), std::back_inserter(lowerStr), [](unsigned char c) { return std::tolower(c); });
35 return lowerStr;
36 }
37
38 /**
39 * @brief Check the AlbumName unique or not. true - unique, false - not unique.
40 */
CheckAlbumNameUnique(const std::string & albumName,const std::string & lPath)41 bool PhotoAlbumDao::CheckAlbumNameUnique(const std::string &albumName, const std::string &lPath)
42 {
43 std::vector<NativeRdb::ValueObject> bindArgs = {albumName, lPath};
44 std::string querySql = this->SQL_PHOTO_ALBUM_CHECK_ALBUM_NAME_UNIQUE;
45 if (this->mediaLibraryRdb_ == nullptr) {
46 MEDIA_ERR_LOG("Media_Restore: mediaLibraryRdb_ is null.");
47 return true;
48 }
49 auto resultSet = this->mediaLibraryRdb_->QuerySql(querySql, bindArgs);
50 if (resultSet == nullptr || resultSet->GoToNextRow() != NativeRdb::E_OK) {
51 MEDIA_ERR_LOG("Media_Restore: Query resultSql is null.");
52 return true;
53 }
54 int32_t count = GetInt32Val("count", resultSet);
55 return count == 0;
56 }
57
58 /**
59 * @brief Find the Unique Album Name.
60 */
FindUniqueAlbumName(const PhotoAlbumDao::PhotoAlbumRowData & photoAlbum)61 std::string PhotoAlbumDao::FindUniqueAlbumName(const PhotoAlbumDao::PhotoAlbumRowData &photoAlbum)
62 {
63 if (photoAlbum.lPath.empty() || photoAlbum.albumName.empty()) {
64 MEDIA_ERR_LOG("Media_Restore: Invalid album data");
65 return "";
66 }
67 const std::string lPath = photoAlbum.lPath;
68 // The PhotoAlbum is cached.
69 PhotoAlbumDao::PhotoAlbumRowData albumDataInCache;
70 if (this->photoAlbumCache_.Find(StringUtils::ToLower(lPath), albumDataInCache)) {
71 return albumDataInCache.albumName;
72 }
73 // Check if the album name is unique.
74 std::string albumName = photoAlbum.albumName;
75 int32_t sequence = 1;
76 bool isUnique = this->CheckAlbumNameUnique(albumName, photoAlbum.lPath);
77 while (!isUnique && sequence < this->MAX_ALBUM_NAME_SEQUENCE) {
78 albumName = photoAlbum.albumName + " " + std::to_string(sequence);
79 sequence++;
80 isUnique = this->CheckAlbumNameUnique(albumName, photoAlbum.lPath);
81 }
82 MEDIA_INFO_LOG("Media_Restore: FindUniqueAlbumName, old name: %{public}s, new name: %{public}s, lPath: %{public}s",
83 photoAlbum.albumName.c_str(),
84 albumName.c_str(),
85 photoAlbum.lPath.c_str());
86 return albumName;
87 }
88
89 /**
90 * @brief get the data of PhotoAlbum table, to find the differenct between the PhotoAlbum and the gallery_album
91 */
GetPhotoAlbums()92 std::vector<PhotoAlbumDao::PhotoAlbumRowData> PhotoAlbumDao::GetPhotoAlbums()
93 {
94 std::vector<PhotoAlbumDao::PhotoAlbumRowData> result;
95 std::string querySql = this->SQL_PHOTO_ALBUM_SELECT;
96 int rowCount = 0;
97 int offset = 0;
98 int pageSize = 200;
99 do {
100 std::vector<NativeRdb::ValueObject> bindArgs = {offset, pageSize};
101 if (this->mediaLibraryRdb_ == nullptr) {
102 MEDIA_ERR_LOG("Media_Restore: mediaLibraryRdb_ is null.");
103 break;
104 }
105 auto resultSet = this->mediaLibraryRdb_->QuerySql(querySql, bindArgs);
106 if (resultSet == nullptr) {
107 MEDIA_ERR_LOG("Media_Restore: Query resultSql is null.");
108 break;
109 }
110 while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
111 PhotoAlbumDao::PhotoAlbumRowData albumRowData;
112 albumRowData.albumId = GetInt32Val(this->FIELD_NAME_ALBUM_ID, resultSet);
113 albumRowData.albumName = GetStringVal(this->FIELD_NAME_ALBUM_NAME, resultSet);
114 albumRowData.bundleName = GetStringVal(this->FIELD_NAME_BUNDLE_NAME, resultSet);
115 albumRowData.albumType = GetInt32Val(this->FIELD_NAME_ALBUM_TYPE, resultSet);
116 albumRowData.albumSubType = GetInt32Val(this->FIELD_NAME_ALBUM_SUBTYPE, resultSet);
117 albumRowData.lPath = GetStringVal(this->FIELD_NAME_LPATH, resultSet);
118 albumRowData.priority = GetInt32Val(this->FIELD_NAME_PRIORITY, resultSet);
119 result.emplace_back(albumRowData);
120 }
121 // Check if there are more rows to fetch.
122 resultSet->GetRowCount(rowCount);
123 offset += pageSize;
124 } while (rowCount > 0);
125 return result;
126 }
127
128 /**
129 * @brief Get and cache PhotoAlbum info by lPath from PhotoAlbum table.
130 */
GetPhotoAlbum(const std::string & lPath)131 PhotoAlbumDao::PhotoAlbumRowData PhotoAlbumDao::GetPhotoAlbum(const std::string &lPath)
132 {
133 std::unique_lock<std::mutex> lock(this->cacheLock_);
134 // find the PhotoAlbum info by lPath in cache
135 PhotoAlbumDao::PhotoAlbumRowData albumRowData;
136 if (this->photoAlbumCache_.Find(StringUtils::ToLower(lPath), albumRowData)) {
137 return albumRowData;
138 }
139 MEDIA_INFO_LOG("Media_Restore: can not find the PhotoAlbum info by lPath in cache."
140 " lPath=%{public}s, lPath in cache=%{public}s",
141 lPath.c_str(),
142 StringUtils::ToLower(lPath).c_str());
143 // query the PhotoAlbum info by lPath from PhotoAlbum table
144 std::vector<NativeRdb::ValueObject> bindArgs = {lPath};
145 std::string querySql = this->SQL_PHOTO_ALBUM_SELECT_BY_LPATH;
146 if (this->mediaLibraryRdb_ == nullptr) {
147 MEDIA_ERR_LOG("Media_Restore: mediaLibraryRdb_ is null.");
148 return albumRowData;
149 }
150 auto resultSet = this->mediaLibraryRdb_->QuerySql(querySql, bindArgs);
151 if (resultSet == nullptr || resultSet->GoToFirstRow() != NativeRdb::E_OK) {
152 MEDIA_WARN_LOG("Media_Restore: can not find the PhotoAlbum info by lPath [%{public}s] in PhotoAlbum table.",
153 lPath.c_str());
154 return albumRowData;
155 }
156 albumRowData.albumId = GetInt32Val(this->FIELD_NAME_ALBUM_ID, resultSet);
157 albumRowData.albumName = GetStringVal(this->FIELD_NAME_ALBUM_NAME, resultSet);
158 albumRowData.bundleName = GetStringVal(this->FIELD_NAME_BUNDLE_NAME, resultSet);
159 albumRowData.albumType = GetInt32Val(this->FIELD_NAME_ALBUM_TYPE, resultSet);
160 albumRowData.albumSubType = GetInt32Val(this->FIELD_NAME_ALBUM_SUBTYPE, resultSet);
161 albumRowData.lPath = GetStringVal(this->FIELD_NAME_LPATH, resultSet);
162 albumRowData.priority = GetInt32Val(this->FIELD_NAME_PRIORITY, resultSet);
163 // cache the PhotoAlbum info by lPath
164 this->photoAlbumCache_.Insert(StringUtils::ToLower(lPath), albumRowData);
165 MEDIA_INFO_LOG("Media_Restore: add the PhotoAlbum info by lPath into cache."
166 " lPath=%{public}s, lPath in cache=%{public}s",
167 lPath.c_str(),
168 StringUtils::ToLower(albumRowData.lPath).c_str());
169 return albumRowData;
170 }
171
172 /**
173 * @brief Get and cache PhotoAlbum info by lPath, if PhotoAlbum not exists, create it.
174 */
GetOrCreatePhotoAlbum(const PhotoAlbumRowData & album)175 PhotoAlbumDao::PhotoAlbumRowData PhotoAlbumDao::GetOrCreatePhotoAlbum(const PhotoAlbumRowData &album)
176 {
177 // validate inputs
178 if (album.lPath.empty()) {
179 MEDIA_ERR_LOG(
180 "Media_Restore: Invalid album data, lPath is empty. Object: %{public}s", this->ToString(album).c_str());
181 return album;
182 }
183 std::unique_lock<std::mutex> lock(this->photoAlbumCreateLock_);
184 // try to get from cache
185 PhotoAlbumDao::PhotoAlbumRowData albumRowData = this->GetPhotoAlbum(album.lPath);
186 if (!albumRowData.lPath.empty()) {
187 return albumRowData;
188 }
189 std::string uniqueAlbumName = this->FindUniqueAlbumName(album);
190 std::vector<NativeRdb::ValueObject> bindArgs = {
191 album.albumType, album.albumSubType, uniqueAlbumName, album.bundleName, album.lPath, album.priority};
192 if (this->mediaLibraryRdb_ == nullptr) {
193 MEDIA_ERR_LOG("Media_Restore: mediaLibraryRdb_ is null.");
194 return album;
195 }
196 auto err = BackupDatabaseUtils::ExecuteSQL(this->mediaLibraryRdb_, this->SQL_PHOTO_ALBUM_INSERT, bindArgs);
197 if (err != NativeRdb::E_OK) {
198 MEDIA_ERR_LOG("Media_Restore: INSERT INTO PhotoAlbum failed, err = %{public}d, executeSql = %{public}s, "
199 "bindArgs = %{public}s",
200 err,
201 this->SQL_PHOTO_ALBUM_INSERT.c_str(),
202 this->ToString(bindArgs).c_str());
203 return album;
204 }
205 MEDIA_INFO_LOG("Media_Restore: INSERT INTO PhotoAlbum success, Object: %{public}s", this->ToString(album).c_str());
206 return this->GetPhotoAlbum(album.lPath);
207 }
208
ToString(const std::vector<NativeRdb::ValueObject> & bindArgs)209 std::string PhotoAlbumDao::ToString(const std::vector<NativeRdb::ValueObject> &bindArgs)
210 {
211 std::string args;
212 for (auto &arg : bindArgs) {
213 std::string tempStr;
214 arg.GetString(tempStr);
215 args += tempStr + ", ";
216 }
217 return args;
218 }
219
220 /**
221 * @brief restore the PhotoAlbum table
222 */
RestoreAlbums(std::vector<PhotoAlbumDao::PhotoAlbumRowData> & photoAlbums)223 int32_t PhotoAlbumDao::RestoreAlbums(std::vector<PhotoAlbumDao::PhotoAlbumRowData> &photoAlbums)
224 {
225 if (photoAlbums.empty()) {
226 MEDIA_INFO_LOG("Media_Restore: albumInfos are empty");
227 return NativeRdb::E_OK;
228 }
229 int32_t err = NativeRdb::E_OK;
230 if (this->mediaLibraryRdb_ == nullptr) {
231 MEDIA_ERR_LOG("Media_Restore: mediaLibraryRdb_ is null.");
232 return E_FAIL;
233 }
234 int32_t count = 0;
235 for (const PhotoAlbumDao::PhotoAlbumRowData &data : photoAlbums) {
236 if (data.lPath.empty() || data.albumName.empty()) {
237 MEDIA_ERR_LOG("Media_Restore: restore albums failed, lPath or albumName is empty. Object: %{public}s",
238 this->ToString(data).c_str());
239 continue;
240 }
241 std::vector<NativeRdb::ValueObject> bindArgs = {
242 data.albumType, data.albumSubType, data.albumName, data.bundleName, data.lPath, data.priority};
243 err = BackupDatabaseUtils::ExecuteSQL(this->mediaLibraryRdb_, this->SQL_PHOTO_ALBUM_INSERT, bindArgs);
244 if (err != NativeRdb::E_OK) {
245 MEDIA_ERR_LOG("Media_Restore: restore albums failed, "
246 "err = %{public}d, executeSql = %{public}s, bindArgs = %{public}s",
247 err,
248 this->SQL_PHOTO_ALBUM_INSERT.c_str(),
249 this->ToString(bindArgs).c_str());
250 continue;
251 }
252 count++;
253 }
254 MEDIA_INFO_LOG("Media_Restore: restore albums success, total %{public}d, restored %{public}d",
255 static_cast<int32_t>(photoAlbums.size()),
256 count);
257 return NativeRdb::E_OK;
258 }
259
260 /**
261 * @brief Build PhotoAlbumRowData for ScreenRecorder.
262 */
BuildAlbumInfoOfRecorders()263 PhotoAlbumDao::PhotoAlbumRowData PhotoAlbumDao::BuildAlbumInfoOfRecorders()
264 {
265 PhotoAlbumDao::PhotoAlbumRowData albumInfo;
266 // bind albumName and bundleName by lPath.
267 albumInfo.albumName = AlbumPlugin::ALBUM_NAME_SCREEN_RECORDS;
268 albumInfo.bundleName = AlbumPlugin::BUNDLE_NAME_SCREEN_RECORDS;
269 albumInfo.lPath = AlbumPlugin::LPATH_SCREEN_RECORDS;
270 albumInfo.albumType = static_cast<int32_t>(PhotoAlbumType::SOURCE);
271 albumInfo.albumSubType = static_cast<int32_t>(PhotoAlbumSubType::SOURCE_GENERIC);
272 albumInfo.priority = 1;
273 return albumInfo;
274 }
275
LoadPhotoAlbums()276 void PhotoAlbumDao::LoadPhotoAlbums()
277 {
278 std::vector<PhotoAlbumDao::PhotoAlbumRowData> photoAlbums = this->GetPhotoAlbums();
279 for (const auto &album : photoAlbums) {
280 if (album.lPath.empty()) {
281 continue;
282 }
283 this->photoAlbumCache_.Insert(StringUtils::ToLower(album.lPath), album);
284 }
285 MEDIA_INFO_LOG(
286 "Media_Restore: LoadPhotoAlbums success, %{public}d albums", static_cast<int32_t>(photoAlbums.size()));
287 }
288
289 /**
290 * @brief Parse the sourcePath to lPath.
291 * example, sourcePath=/storage/emulated/0/DCIM/Camera/IMG_20240829_072213.jpg, lPath=/DCIM/Camera
292 * if the sourcePath can not be parsed, return /Pictures/其它.
293 */
ParseSourcePathToLPath(const std::string & sourcePath)294 std::string PhotoAlbumDao::ParseSourcePathToLPath(const std::string &sourcePath)
295 {
296 size_t start_pos = sourcePath.find(GALLERT_ROOT_PATH);
297 size_t end_pos = sourcePath.find_last_of("/");
298
299 std::string result = "/Pictures/其它";
300 if (start_pos != std::string::npos && end_pos != std::string::npos) {
301 start_pos += GALLERT_ROOT_PATH.length();
302 result = sourcePath.substr(start_pos, end_pos - start_pos);
303 start_pos = result.find_first_of("/");
304 if (start_pos != std::string::npos) {
305 result = result.substr(start_pos);
306 }
307 }
308 return result;
309 }
310
311 /**
312 * @brief Build PhotoAlbumRowData from lPath.
313 */
BuildAlbumInfoByLPath(const std::string & lPath,const int32_t albumType,const int32_t albumSubType)314 PhotoAlbumDao::PhotoAlbumRowData PhotoAlbumDao::BuildAlbumInfoByLPath(
315 const std::string &lPath, const int32_t albumType, const int32_t albumSubType)
316 {
317 PhotoAlbumDao::PhotoAlbumRowData albumInfo;
318 // find albumName from lPath
319 std::string albumName = "其它";
320 std::string albumlPath = lPath;
321 int32_t albumTypeTmp = albumType;
322 int32_t albumSubTypeTmp = albumSubType;
323 size_t fileIndex = albumlPath.find_last_of(FILE_SEPARATOR);
324 if (fileIndex != string::npos) {
325 albumName = albumlPath.substr(fileIndex + 1);
326 } else {
327 albumlPath = "/Pictures/其它";
328 albumTypeTmp = static_cast<int32_t>(PhotoAlbumType::SOURCE);
329 albumSubTypeTmp = static_cast<int32_t>(PhotoAlbumSubType::SOURCE_GENERIC);
330 }
331 albumInfo.albumName = albumName;
332 albumInfo.lPath = albumlPath;
333 albumInfo.albumType = albumTypeTmp;
334 albumInfo.albumSubType = albumSubTypeTmp;
335 albumInfo.priority = 1;
336 return albumInfo;
337 }
338
339 /**
340 * @brief Build PhotoAlbumRowData from lPath.
341 */
BuildAlbumInfoByLPath(const std::string & lPath)342 PhotoAlbumDao::PhotoAlbumRowData PhotoAlbumDao::BuildAlbumInfoByLPath(const std::string &lPath)
343 {
344 int32_t albumType = static_cast<int32_t>(PhotoAlbumType::SOURCE);
345 int32_t albumSubType = static_cast<int32_t>(PhotoAlbumSubType::SOURCE_GENERIC);
346
347 std::string target = "/Pictures/Users/";
348 std::transform(target.begin(), target.end(), target.begin(), ::tolower);
349 std::string lPathLower = lPath;
350 std::transform(lPathLower.begin(), lPathLower.end(), lPathLower.begin(), ::tolower);
351 if (lPathLower.find(target) == 0) {
352 albumType = static_cast<int32_t>(PhotoAlbumType::USER);
353 albumSubType = static_cast<int32_t>(PhotoAlbumSubType::USER_GENERIC);
354 }
355 return this->BuildAlbumInfoByLPath(lPath, albumType, albumSubType);
356 }
357 } // namespace OHOS::Media