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