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