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
16 #include "media_asset_rdbstore.h"
17
18 #include <unordered_set>
19
20 #include "media_file_uri.h"
21 #include "media_file_utils.h"
22 #include "media_log.h"
23 #include "medialibrary_tracer.h"
24 #include "parameter.h"
25 #include "parameters.h"
26 #include "photo_album_column.h"
27 #include "photo_map_column.h"
28 #include "vision_column.h"
29
30 using namespace std;
31 using namespace OHOS::NativeRdb;
32 using namespace OHOS::RdbDataShareAdapter;
33 using namespace OHOS::Media::MediaOperation;
34
35 namespace OHOS {
36 namespace Media {
37
38 const std::string MEDIA_LIBRARY_STARTUP_PARAM_PREFIX = "multimedia.medialibrary.startup.";
39 constexpr uint32_t BASE_USER_RANGE = 200000;
40 const std::unordered_set<OperationObject> OPERATION_OBJECT_SET = {
41 OperationObject::UFM_PHOTO,
42 OperationObject::UFM_AUDIO,
43 OperationObject::PAH_PHOTO,
44 OperationObject::PAH_MAP,
45 };
46 const std::unordered_set<OperationType> OPERATION_TYPE_SET = {
47 OperationType::QUERY,
48 };
49
GetTableNameFromOprnObject(const OperationObject & object)50 std::string GetTableNameFromOprnObject(const OperationObject& object)
51 {
52 if (object == OperationObject::PAH_MAP) {
53 return PhotoColumn::PHOTOS_TABLE;
54 }
55 if (TABLE_NAME_MAP.find(object) != TABLE_NAME_MAP.end()) {
56 auto cmdObj = TABLE_NAME_MAP.at(object);
57 return cmdObj.begin()->second;
58 } else {
59 return MEDIALIBRARY_TABLE;
60 }
61 }
62
GetOprnTypeFromUri(Uri & uri)63 OperationType GetOprnTypeFromUri(Uri& uri)
64 {
65 const std::string opType = MediaFileUri::GetPathSecondDentry(uri);
66 if (OPRN_TYPE_MAP.find(opType) != OPRN_TYPE_MAP.end()) {
67 return OPRN_TYPE_MAP.at(opType);
68 } else {
69 return OperationType::QUERY;
70 }
71 }
72
GetOprnObjectFromUri(Uri & uri)73 OperationObject GetOprnObjectFromUri(Uri& uri)
74 {
75 const string opObject = MediaFileUri::GetPathFirstDentry(uri);
76 if (OPRN_OBJ_MAP.find(opObject) != OPRN_OBJ_MAP.end()) {
77 return OPRN_OBJ_MAP.at(opObject);
78 }
79 std::string uriString = uri.ToString();
80 if (MediaFileUtils::StartsWith(uriString, PhotoColumn::PHOTO_CACHE_URI_PREFIX)) {
81 return OperationObject::PAH_PHOTO;
82 }
83
84 for (const auto &item : OPRN_MAP) {
85 if (MediaFileUtils::StartsWith(uriString, item.first)) {
86 return item.second;
87 }
88 }
89 return OperationObject::UNKNOWN_OBJECT;
90 }
91
GetInstance()92 MediaAssetRdbStore* MediaAssetRdbStore::GetInstance()
93 {
94 static MediaAssetRdbStore instance;
95 return &instance;
96 }
97
CloudSyncTriggerFunc(const std::vector<std::string> & args)98 const std::string MediaAssetRdbStore::CloudSyncTriggerFunc(const std::vector<std::string>& args)
99 {
100 return "true";
101 }
102
IsCallerSelfFunc(const std::vector<std::string> & args)103 const std::string MediaAssetRdbStore::IsCallerSelfFunc(const std::vector<std::string>& args)
104 {
105 return "false";
106 }
107
PhotoAlbumNotifyFunc(const std::vector<std::string> & args)108 const std::string MediaAssetRdbStore::PhotoAlbumNotifyFunc(const std::vector<std::string> &args)
109 {
110 return "";
111 }
112
MediaAssetRdbStore()113 MediaAssetRdbStore::MediaAssetRdbStore()
114 {
115 MEDIA_INFO_LOG("init visitor rdb");
116 if (rdbStore_ != nullptr) {
117 MEDIA_INFO_LOG("visitor rdb exists");
118 return;
119 }
120 if (TryGetRdbStore() != NativeRdb::E_OK) {
121 return;
122 }
123 MEDIA_INFO_LOG("success to init visitor rdb");
124 }
125
TryGetRdbStore(bool isIgnoreSELinux)126 int32_t MediaAssetRdbStore::TryGetRdbStore(bool isIgnoreSELinux)
127 {
128 auto context = AbilityRuntime::Context::GetApplicationContext();
129 if (context == nullptr) {
130 MEDIA_ERR_LOG("fail to acquire application Context");
131 return NativeRdb::E_ERROR;
132 }
133 uid_t uid = getuid() / BASE_USER_RANGE;
134 const string key = MEDIA_LIBRARY_STARTUP_PARAM_PREFIX + to_string(uid);
135 auto rdbInitFlag = system::GetBoolParameter(key, false);
136 if (!rdbInitFlag && !isIgnoreSELinux) {
137 MEDIA_ERR_LOG("media library db update not complete, key:%{public}s", key.c_str());
138 return NativeRdb::E_ERROR;
139 }
140 string name = MEDIA_DATA_ABILITY_DB_NAME;
141 string databaseDir = MEDIA_DB_DIR + "/rdb";
142 if (access(databaseDir.c_str(), E_OK) != 0) {
143 MEDIA_WARN_LOG("can not get rdb through sandbox");
144 return NativeRdb::E_ERROR;
145 }
146 string dbPath = databaseDir.append("/").append(name);
147 int32_t errCode = 0;
148 NativeRdb::RdbStoreConfig config {""};
149 config.SetName(name);
150 config.SetVisitorDir(dbPath);
151 config.SetBundleName(context->GetBundleName());
152 config.SetArea(context->GetArea());
153 config.SetSecurityLevel(SecurityLevel::S3);
154 config.SetRoleType(RoleType::VISITOR);
155 config.SetScalarFunction("cloud_sync_func", 0, CloudSyncTriggerFunc);
156 config.SetScalarFunction("is_caller_self_func", 0, IsCallerSelfFunc);
157 config.SetScalarFunction("photo_album_notify_func", 1, PhotoAlbumNotifyFunc);
158
159 MediaLibraryDataCallBack rdbDataCallBack;
160 rdbStore_ = RdbHelper::GetRdbStore(config, MEDIA_RDB_VERSION, rdbDataCallBack, errCode);
161 if (rdbStore_ == nullptr || errCode != NativeRdb::E_OK) {
162 MEDIA_ERR_LOG("Get visitor RdbStore is failed, errCode: %{public}d", errCode);
163 rdbStore_ = nullptr;
164 return errCode;
165 }
166 return NativeRdb::E_OK;
167 }
168
AddVirtualColumnsOfDateType(vector<string> & columns)169 void AddVirtualColumnsOfDateType(vector<string>& columns)
170 {
171 vector<string> dateTypes = { MEDIA_DATA_DB_DATE_ADDED, MEDIA_DATA_DB_DATE_TRASHED, MEDIA_DATA_DB_DATE_MODIFIED,
172 MEDIA_DATA_DB_DATE_TAKEN };
173 vector<string> dateTypeSeconds = { MEDIA_DATA_DB_DATE_ADDED_TO_SECOND,
174 MEDIA_DATA_DB_DATE_TRASHED_TO_SECOND, MEDIA_DATA_DB_DATE_MODIFIED_TO_SECOND,
175 MEDIA_DATA_DB_DATE_TAKEN_TO_SECOND };
176 for (size_t i = 0; i < dateTypes.size(); i++) {
177 auto it = find(columns.begin(), columns.end(), dateTypes[i]);
178 if (it != columns.end()) {
179 columns.push_back(dateTypeSeconds[i]);
180 }
181 }
182 }
183
AddQueryIndex(AbsPredicates & predicates,const vector<string> & columns)184 void AddQueryIndex(AbsPredicates& predicates, const vector<string>& columns)
185 {
186 auto it = find(columns.begin(), columns.end(), MEDIA_COLUMN_COUNT);
187 if (it == columns.end()) {
188 return;
189 }
190 const string &group = predicates.GetGroup();
191 const string &whereInfo = predicates.GetWhereClause();
192 if (group.empty()) {
193 predicates.GroupBy({ PhotoColumn::PHOTO_DATE_DAY });
194 predicates.IndexedBy(PhotoColumn::PHOTO_SCHPT_DAY_INDEX);
195 return;
196 }
197 if (group == PhotoColumn::MEDIA_TYPE && (whereInfo.find(PhotoColumn::PHOTO_THUMBNAIL_VISIBLE) == string::npos)) {
198 predicates.IndexedBy(PhotoColumn::PHOTO_SCHPT_MEDIA_TYPE_INDEX);
199 return;
200 }
201 if (group == PhotoColumn::PHOTO_DATE_DAY) {
202 predicates.IndexedBy(PhotoColumn::PHOTO_SCHPT_DAY_INDEX);
203 return;
204 }
205 }
206
GetQueryFilter(const string & tableName)207 static string GetQueryFilter(const string &tableName)
208 {
209 if (tableName == MEDIALIBRARY_TABLE) {
210 return MEDIALIBRARY_TABLE + "." + MEDIA_DATA_DB_SYNC_STATUS + " = " +
211 to_string(static_cast<int32_t>(SyncStatusType::TYPE_VISIBLE));
212 }
213 if (tableName == PhotoColumn::PHOTOS_TABLE) {
214 return PhotoColumn::PHOTOS_TABLE + "." + PhotoColumn::PHOTO_SYNC_STATUS + " = " +
215 to_string(static_cast<int32_t>(SyncStatusType::TYPE_VISIBLE)) + " AND " +
216 PhotoColumn::PHOTOS_TABLE + "." + PhotoColumn::PHOTO_CLEAN_FLAG + " = " +
217 to_string(static_cast<int32_t>(CleanType::TYPE_NOT_CLEAN));
218 }
219 if (tableName == PhotoAlbumColumns::TABLE) {
220 return PhotoAlbumColumns::TABLE + "." + PhotoAlbumColumns::ALBUM_DIRTY + " != " +
221 to_string(static_cast<int32_t>(DirtyTypes::TYPE_DELETED));
222 }
223 if (tableName == PhotoMap::TABLE) {
224 return PhotoMap::TABLE + "." + PhotoMap::DIRTY + " != " + to_string(static_cast<int32_t>(
225 DirtyTypes::TYPE_DELETED));
226 }
227 return "";
228 }
229
AddQueryFilter(AbsRdbPredicates & predicates)230 void AddQueryFilter(AbsRdbPredicates &predicates)
231 {
232 /* build all-table vector */
233 string tableName = predicates.GetTableName();
234 vector<string> joinTables = predicates.GetJoinTableNames();
235 joinTables.push_back(tableName);
236 /* add filters */
237 string filters;
238 for (auto &t : joinTables) {
239 string filter = GetQueryFilter(t);
240 if (filter.empty()) {
241 continue;
242 }
243 if (filters.empty()) {
244 filters += filter;
245 } else {
246 filters += " AND " + filter;
247 }
248 }
249 if (filters.empty()) {
250 return;
251 }
252
253 /* rebuild */
254 string queryCondition = predicates.GetWhereClause();
255 queryCondition = queryCondition.empty() ? filters : filters + " AND " + queryCondition;
256 predicates.SetWhereClause(queryCondition);
257 }
258
Query(const DataShare::DataSharePredicates & predicates,std::vector<std::string> & columns,OperationObject & object,int & errCode)259 std::shared_ptr<DataShare::DataShareResultSet> MediaAssetRdbStore::Query(
260 const DataShare::DataSharePredicates& predicates,
261 std::vector<std::string>& columns, OperationObject& object, int& errCode)
262 {
263 auto resultSet = QueryRdb(predicates, columns, object);
264 if (resultSet == nullptr) {
265 MEDIA_ERR_LOG("fail to acquire result from visitor query");
266 return nullptr;
267 }
268 auto resultSetBridge = RdbUtils::ToResultSetBridge(resultSet);
269 return make_shared<DataShare::DataShareResultSet>(resultSetBridge);
270 }
271
IsNumber(const string & str)272 bool IsNumber(const string& str)
273 {
274 if (str.empty()) {
275 MEDIA_ERR_LOG("IsNumber input is empty");
276 return false;
277 }
278 for (char const& c : str) {
279 if (isdigit(c) == 0) {
280 return false;
281 }
282 }
283 return true;
284 }
285
GetInt32Val(const string & column,std::shared_ptr<NativeRdb::AbsSharedResultSet> & resultSet)286 int32_t GetInt32Val(const string& column, std::shared_ptr<NativeRdb::AbsSharedResultSet>& resultSet)
287 {
288 int index;
289 int32_t value = -1;
290 int err = resultSet->GetColumnIndex(column, index);
291 if (err == E_OK) {
292 err = resultSet->GetInt(index, value);
293 }
294 return value;
295 }
296
IsQueryGroupPhotoAlbumAssets(const string & albumId)297 bool MediaAssetRdbStore::IsQueryGroupPhotoAlbumAssets(const string &albumId)
298 {
299 if (albumId.empty() || !IsNumber(albumId)) {
300 return false;
301 }
302 RdbPredicates predicates(ANALYSIS_ALBUM_TABLE);
303 predicates.EqualTo(PhotoAlbumColumns::ALBUM_ID, albumId);
304 vector<string> columns = {PhotoAlbumColumns::ALBUM_TYPE, PhotoAlbumColumns::ALBUM_SUBTYPE};
305 auto resultSet = rdbStore_->Query(predicates, columns);
306 if (resultSet == nullptr || resultSet->GoToFirstRow() != E_OK) {
307 return false;
308 }
309 int32_t albumType = GetInt32Val(PhotoAlbumColumns::ALBUM_TYPE, resultSet);
310 int32_t albumSubtype = GetInt32Val(PhotoAlbumColumns::ALBUM_SUBTYPE, resultSet);
311 return albumType == PhotoAlbumType::SMART && albumSubtype == PhotoAlbumSubType::GROUP_PHOTO;
312 }
313
IsQueryAccessibleViaSandBox(Uri & uri,OperationObject & object,const DataShare::DataSharePredicates & predicates,bool isIgnoreSELinux)314 bool MediaAssetRdbStore::IsQueryAccessibleViaSandBox(Uri& uri, OperationObject& object,
315 const DataShare::DataSharePredicates& predicates, bool isIgnoreSELinux)
316 {
317 if (access(MEDIA_DB_DIR.c_str(), E_OK) != 0) {
318 return false;
319 }
320 if (rdbStore_ == nullptr) {
321 if (TryGetRdbStore(isIgnoreSELinux) != NativeRdb::E_OK) {
322 MEDIA_ERR_LOG("fail to acquire rdb when query");
323 return false;
324 }
325 }
326 object = GetOprnObjectFromUri(uri);
327 if (OPERATION_OBJECT_SET.count(object) == 0) {
328 return false;
329 }
330 OperationType type = GetOprnTypeFromUri(uri);
331 if (OPERATION_TYPE_SET.count(type) == 0) {
332 return false;
333 }
334 if (object != OperationObject::PAH_MAP) {
335 return true;
336 }
337 std::string tableName = GetTableNameFromOprnObject(object);
338 NativeRdb::RdbPredicates rdbPredicates = RdbUtils::ToPredicates(predicates, tableName);
339 auto whereArgs = rdbPredicates.GetWhereArgs();
340 if (!whereArgs.empty()) {
341 string albumId = whereArgs[0];
342 if (IsQueryGroupPhotoAlbumAssets(albumId)) {
343 return false;
344 }
345 }
346 return true;
347 }
348
AddQueryDateTakenTime(std::vector<std::string> & columns)349 std::shared_ptr<NativeRdb::AbsSharedResultSet> MediaAssetRdbStore::AddQueryDateTakenTime(
350 std::vector<std::string>& columns)
351 {
352 auto it = find(columns.begin(), columns.end(), MEDIA_COLUMN_COUNT);
353 if (it == columns.end()) {
354 return nullptr;
355 }
356 auto itData = find(columns.begin(), columns.end(), MEDIA_DATA_DB_DATE_TAKEN);
357 if (itData == columns.end()) {
358 return nullptr;
359 }
360 std::string extraWhereSql = "";
361 auto itForThumbnailVisible = find(columns.begin(), columns.end(), PhotoColumn::PHOTO_THUMBNAIL_VISIBLE);
362 if (itForThumbnailVisible != columns.end()) {
363 extraWhereSql = " AND thumbnail_visible = 1 ";
364 }
365
366 std::string sql = ""
367 "SELECT"
368 " count( * ) AS count,"
369 " date_taken,"
370 " date_day,"
371 " burst_key,"
372 " display_name,"
373 " file_id,"
374 " media_type,"
375 " subtype "
376 "FROM"
377 " Photos "
378 "WHERE"
379 " sync_status = 0 "
380 " AND clean_flag = 0 " +
381 extraWhereSql +
382 " AND date_trashed = 0 "
383 " AND time_pending = 0 "
384 " AND hidden = 0 "
385 " AND is_temp = 0 "
386 " AND burst_cover_level = 1 "
387 "GROUP BY"
388 " date_day "
389 "ORDER BY"
390 " date_day DESC;";
391
392 auto resultSet = rdbStore_->QuerySql(sql);
393 if (resultSet == nullptr) {
394 MEDIA_ERR_LOG("fail to acquire result from visitor query");
395 return nullptr;
396 }
397 return resultSet;
398 }
399
QueryRdb(const DataShare::DataSharePredicates & predicates,std::vector<std::string> & columns,OperationObject & object)400 std::shared_ptr<NativeRdb::ResultSet> MediaAssetRdbStore::QueryRdb(
401 const DataShare::DataSharePredicates& predicates, std::vector<std::string>& columns, OperationObject& object)
402 {
403 if (rdbStore_ == nullptr) {
404 MEDIA_ERR_LOG("fail to acquire rdb when query");
405 return nullptr;
406 }
407 auto ret = AddQueryDateTakenTime(columns);
408 if (ret != nullptr) {
409 return ret;
410 }
411 std::string tableName = GetTableNameFromOprnObject(object);
412 NativeRdb::RdbPredicates rdbPredicates = RdbUtils::ToPredicates(predicates, tableName);
413 AddVirtualColumnsOfDateType(const_cast<vector<string> &>(columns));
414 if (object == OperationObject::UFM_PHOTO || object == OperationObject::PAH_PHOTO) {
415 AddQueryIndex(rdbPredicates, columns);
416 }
417 AddQueryFilter(rdbPredicates);
418 auto resultSet = rdbStore_->QueryByStep(rdbPredicates, columns, false);
419 if (resultSet == nullptr) {
420 MEDIA_ERR_LOG("fail to acquire result from visitor query");
421 return nullptr;
422 }
423 return resultSet;
424 }
425
IsSupportSharedAssetQuery(Uri & uri,OperationObject & object,bool isIgnoreSELinux)426 bool MediaAssetRdbStore::IsSupportSharedAssetQuery(Uri& uri, OperationObject& object, bool isIgnoreSELinux)
427 {
428 if (access(MEDIA_DB_DIR.c_str(), E_OK) != 0) {
429 return false;
430 }
431 if (rdbStore_ == nullptr) {
432 if (TryGetRdbStore(isIgnoreSELinux) != NativeRdb::E_OK) {
433 MEDIA_ERR_LOG("fail to acquire rdb when query");
434 return false;
435 }
436 }
437 OperationType type = GetOprnTypeFromUri(uri);
438 if (OPERATION_TYPE_SET.count(type) == 0) {
439 return false;
440 }
441 object = GetOprnObjectFromUri(uri);
442 return true;
443 }
444
QueryTimeIdBatch(int32_t start,int32_t count,std::vector<std::string> & batchKeys)445 int32_t MediaAssetRdbStore::QueryTimeIdBatch(int32_t start, int32_t count, std::vector<std::string> &batchKeys)
446 {
447 MediaLibraryTracer tracer;
448 tracer.Start("MediaAssetRdbStore::QueryTimeIdBatch");
449 if (rdbStore_ == nullptr) {
450 MEDIA_ERR_LOG("rdbStore_ is nullptr when query");
451 return NativeRdb::E_DB_NOT_EXIST;
452 }
453 DataShare::DataSharePredicates predicates;
454 predicates.And()->OrderByDesc(MediaColumn::MEDIA_DATE_TAKEN)
455 ->Limit(count, start)
456 ->EqualTo(PhotoColumn::PHOTO_THUMBNAIL_VISIBLE, "1")
457 ->EqualTo(MediaColumn::MEDIA_DATE_TRASHED, "0")
458 ->EqualTo(MediaColumn::MEDIA_TIME_PENDING, "0")
459 ->EqualTo(MediaColumn::MEDIA_HIDDEN, "0")
460 ->EqualTo(PhotoColumn::PHOTO_IS_TEMP, "0")
461 ->EqualTo(PhotoColumn::PHOTO_BURST_COVER_LEVEL,
462 to_string(static_cast<int32_t>(BurstCoverLevelType::COVER)));
463 std::vector<std::string> columns = {MediaColumn::MEDIA_ID, MediaColumn::MEDIA_DATE_TAKEN};
464 NativeRdb::RdbPredicates rdbPredicates = RdbUtils::ToPredicates(predicates, PhotoColumn::PHOTOS_TABLE);
465 AddQueryFilter(rdbPredicates);
466 auto resultSet = rdbStore_->Query(rdbPredicates, columns);
467 if (resultSet == nullptr) {
468 MEDIA_ERR_LOG("fail to acquire result from visitor query");
469 return NativeRdb::E_ERROR;
470 }
471
472 while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
473 int columnIndex = 0;
474 int64_t dateTakenTime = 0;
475 int fileId = 0;
476 if (resultSet->GetColumnIndex(MediaColumn::MEDIA_DATE_TAKEN, columnIndex) != NativeRdb::E_OK ||
477 resultSet->GetLong(columnIndex, dateTakenTime) != NativeRdb::E_OK) {
478 MEDIA_ERR_LOG("Fail to get dateTaken");
479 return NativeRdb::E_ERROR;
480 }
481 if (resultSet->GetColumnIndex(MediaColumn::MEDIA_ID, columnIndex) != NativeRdb::E_OK ||
482 resultSet->GetInt(columnIndex, fileId) != NativeRdb::E_OK) {
483 MEDIA_ERR_LOG("Fail to get fileId");
484 return NativeRdb::E_ERROR;
485 }
486
487 std::string timeId;
488 if (!MediaFileUtils::GenerateKvStoreKey(to_string(fileId), to_string(dateTakenTime), timeId)) {
489 MEDIA_ERR_LOG("Fail to generate kvStore key, fileId:%{public}d", fileId);
490 continue;
491 }
492 batchKeys.emplace_back(std::move(timeId));
493 }
494 return NativeRdb::E_OK;
495 }
496
OnCreate(NativeRdb::RdbStore & rdbStore)497 int32_t MediaLibraryDataCallBack::OnCreate(NativeRdb::RdbStore& rdbStore)
498 {
499 return 0;
500 }
501
OnUpgrade(NativeRdb::RdbStore & rdbStore,int32_t oldVersion,int32_t newVersion)502 int32_t MediaLibraryDataCallBack::OnUpgrade(NativeRdb::RdbStore& rdbStore, int32_t oldVersion, int32_t newVersion)
503 {
504 return 0;
505 }
506
507 } // namespace Media
508 } // namespace OHOS