1 /*
2 * Copyright (C) 2025 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_ani_native_impl.h"
17
18 #include <string>
19 #include <memory>
20
21 #include "medialibrary_ani_enum_comm.h"
22 #include "medialibrary_ani_log.h"
23 #include "media_device_column.h"
24 #include "photo_album_column.h"
25 #include "media_file_uri.h"
26 #include "media_file_utils.h"
27 #include "medialibrary_errno.h"
28 #include "medialibrary_db_const.h"
29 #include "userfile_client.h"
30 #include "medialibrary_tracer.h"
31
32 using namespace std;
33 using namespace OHOS::DataShare;
34
35 namespace OHOS {
36 namespace Media {
GetFileAssetsInfo(const std::vector<std::string> & fetchColumns,const DataShare::DataSharePredicates * predicate)37 std::vector<std::unique_ptr<FileAsset>> MediaAniNativeImpl::GetFileAssetsInfo(
38 const std::vector<std::string> &fetchColumns,
39 const DataShare::DataSharePredicates *predicate)
40 {
41 MediaLibraryTracer tracer;
42 tracer.Start("GetFileAssetsInfo Excute");
43
44 std::vector<std::unique_ptr<FileAsset>> result;
45 std::shared_ptr<MediaLibraryAsyncContext> context = GetAssetsContext(fetchColumns, predicate);
46 if (context == nullptr) {
47 ANI_ERR_LOG("GetAssetsContext failed");
48 return result;
49 }
50
51 if (!PhotoAccessGetFileAssetsInfoExecute(context, result)) {
52 ANI_ERR_LOG("PhotoAccessGetFileAssetsInfoExecute failed");
53 }
54
55 return result;
56 }
57
GetAssetsSync(const std::vector<std::string> & fetchColumns,const DataSharePredicates * predicate)58 std::vector<std::unique_ptr<FileAsset>> MediaAniNativeImpl::GetAssetsSync(
59 const std::vector<std::string> &fetchColumns, const DataSharePredicates *predicate)
60 {
61 MediaLibraryTracer tracer;
62 tracer.Start("GetAssetsSync Excute");
63
64 std::vector<std::unique_ptr<FileAsset>> result;
65 std::shared_ptr<MediaLibraryAsyncContext> context = GetAssetsContext(fetchColumns, predicate);
66 if (context == nullptr) {
67 ANI_ERR_LOG("GetAssetsContext failed");
68 return result;
69 }
70
71 if (!PhotoAccessGetAssetsExecuteSync(context, result)) {
72 ANI_ERR_LOG("PhotoAccessGetAssetsExecuteSync failed");
73 }
74
75 return result;
76 }
77
GetAssets(const std::vector<std::string> & fetchColumns,const DataSharePredicates * predicate)78 std::unique_ptr<FetchResult<FileAsset>> MediaAniNativeImpl::GetAssets(
79 const std::vector<std::string> &fetchColumns, const DataSharePredicates *predicate)
80 {
81 MediaLibraryTracer tracer;
82 tracer.Start("GetAssets Excute");
83
84 std::shared_ptr<MediaLibraryAsyncContext> context = GetAssetsContext(fetchColumns, predicate);
85 if (context == nullptr) {
86 ANI_ERR_LOG("GetAssetsContext failed");
87 return nullptr;
88 }
89
90 if (!PhotoAccessGetAssetsExecute(context)) {
91 ANI_ERR_LOG("PhotoAccessGetAssetsExecute failed");
92 return nullptr;
93 }
94
95 return std::move(context->fetchFileResult);
96 }
97
GetAssetsContext(const std::vector<std::string> & fetchColumns,const DataSharePredicates * predicate)98 std::shared_ptr<MediaLibraryAsyncContext> MediaAniNativeImpl::GetAssetsContext(
99 const std::vector<std::string> &fetchColumns, const DataSharePredicates *predicate)
100 {
101 std::shared_ptr<MediaLibraryAsyncContext> context = std::make_shared<MediaLibraryAsyncContext>();
102 context->assetType = TYPE_PHOTO;
103 if (!HandleSpecialPredicate(context, predicate, ASSET_FETCH_OPT)) {
104 ANI_ERR_LOG("HandleSpecialPredicate failed");
105 return nullptr;
106 }
107 if (!GetLocationPredicate(context, predicate)) {
108 ANI_ERR_LOG("GetLocationPredicate failed");
109 return nullptr;
110 }
111
112 context->fetchColumn = fetchColumns;
113 if (!AddDefaultAssetColumns(context->fetchColumn, PhotoColumn::IsPhotoColumn)) {
114 ANI_ERR_LOG("AddDefaultAssetColumns failed");
115 return nullptr;
116 }
117
118 auto &predicates = context->predicates;
119 predicates.And()->EqualTo(MediaColumn::MEDIA_DATE_TRASHED, to_string(0));
120 predicates.And()->EqualTo(MediaColumn::MEDIA_TIME_PENDING, to_string(0));
121 predicates.And()->EqualTo(MediaColumn::MEDIA_HIDDEN, to_string(0));
122 predicates.And()->EqualTo(PhotoColumn::PHOTO_IS_TEMP, to_string(false));
123 predicates.EqualTo(PhotoColumn::PHOTO_BURST_COVER_LEVEL,
124 to_string(static_cast<int32_t>(BurstCoverLevelType::COVER)));
125
126 return context;
127 }
128
HandleSpecialDateTypePredicate(const OperationItem & item,vector<OperationItem> & operations,const FetchOptionType & fetchOptType)129 static bool HandleSpecialDateTypePredicate(const OperationItem &item,
130 vector<OperationItem> &operations, const FetchOptionType &fetchOptType)
131 {
132 constexpr int32_t fieldIdx = 0;
133 constexpr int32_t valueIdx = 1;
134 vector<string>dateTypes = { MEDIA_DATA_DB_DATE_ADDED, MEDIA_DATA_DB_DATE_TRASHED, MEDIA_DATA_DB_DATE_MODIFIED,
135 MEDIA_DATA_DB_DATE_TAKEN};
136 string dateType = item.GetSingle(fieldIdx);
137 auto it = find(dateTypes.begin(), dateTypes.end(), dateType);
138 if (it != dateTypes.end() && item.operation != DataShare::ORDER_BY_ASC &&
139 item.operation != DataShare::ORDER_BY_DESC) {
140 dateType += "_s";
141 operations.push_back({ item.operation, { dateType, static_cast<double>(item.GetSingle(valueIdx)) } });
142 return true;
143 }
144 if (DATE_TRANSITION_MAP.count(dateType) != 0) {
145 dateType = DATE_TRANSITION_MAP.at(dateType);
146 operations.push_back({ item.operation, { dateType, static_cast<double>(item.GetSingle(valueIdx)) } });
147 return true;
148 }
149 return false;
150 }
151
HandleSpecialPredicate(std::shared_ptr<MediaLibraryAsyncContext> context,const DataSharePredicates * predicate,const FetchOptionType & fetchOptType)152 bool MediaAniNativeImpl::HandleSpecialPredicate(std::shared_ptr<MediaLibraryAsyncContext> context,
153 const DataSharePredicates *predicate, const FetchOptionType &fetchOptType)
154 {
155 constexpr int32_t fieldIdx = 0;
156 constexpr int32_t valueIdx = 1;
157 std::vector<OperationItem> operations;
158 auto &items = predicate->GetOperationList();
159 for (auto &item : items) {
160 if (item.singleParams.empty()) {
161 operations.push_back(item);
162 continue;
163 }
164 if (HandleSpecialDateTypePredicate(item, operations, fetchOptType)) {
165 continue;
166 }
167 // change uri ->file id
168 // get networkid
169 // replace networkid with file id
170 if (static_cast<string>(item.GetSingle(fieldIdx)) == DEVICE_DB_NETWORK_ID) {
171 if (item.operation != DataShare::EQUAL_TO || static_cast<string>(item.GetSingle(valueIdx)).empty()) {
172 ANI_ERR_LOG("DEVICE_DB_NETWORK_ID predicates not support %{public}d", item.operation);
173 return false;
174 }
175 context->networkId = static_cast<string>(item.GetSingle(valueIdx));
176 continue;
177 }
178 if (static_cast<string>(item.GetSingle(fieldIdx)) == MEDIA_DATA_DB_URI) {
179 if (item.operation != DataShare::EQUAL_TO) {
180 ANI_ERR_LOG("MEDIA_DATA_DB_URI predicates not support %{public}d", item.operation);
181 return false;
182 }
183 string uri = static_cast<string>(item.GetSingle(valueIdx));
184 MediaFileUri::RemoveAllFragment(uri);
185 MediaFileUri fileUri(uri);
186 context->uri = uri;
187 if ((fetchOptType != ALBUM_FETCH_OPT) && (!fileUri.IsApi10())) {
188 fileUri = MediaFileUri(MediaFileUtils::GetRealUriFromVirtualUri(uri));
189 }
190 context->networkId = fileUri.GetNetworkId();
191 string field = (fetchOptType == ALBUM_FETCH_OPT) ? PhotoAlbumColumns::ALBUM_ID : MEDIA_DATA_DB_ID;
192 operations.push_back({ item.operation, { field, fileUri.GetFileId() } });
193 continue;
194 }
195 if (static_cast<string>(item.GetSingle(fieldIdx)) == PENDING_STATUS) {
196 // do not query pending files below API11
197 continue;
198 }
199 if (LOCATION_PARAM_MAP.find(static_cast<string>(item.GetSingle(fieldIdx))) != LOCATION_PARAM_MAP.end()) {
200 continue;
201 }
202 operations.push_back(item);
203 }
204 context->predicates = DataSharePredicates(move(operations));
205 return true;
206 }
207
GetLocationPredicate(std::shared_ptr<MediaLibraryAsyncContext> context,const DataSharePredicates * predicate)208 bool MediaAniNativeImpl::GetLocationPredicate(std::shared_ptr<MediaLibraryAsyncContext> context,
209 const DataSharePredicates *predicate)
210 {
211 constexpr int32_t fieldIdx = 0;
212 constexpr int32_t valueIdx = 1;
213 map<string, string> locationMap;
214 auto &items = predicate->GetOperationList();
215 for (auto &item : items) {
216 if (item.singleParams.empty()) {
217 continue;
218 }
219 if (LOCATION_PARAM_MAP.find(static_cast<string>(item.GetSingle(fieldIdx))) != LOCATION_PARAM_MAP.end()) {
220 if (item.operation != DataShare::EQUAL_TO) {
221 ANI_ERR_LOG("location predicates not support %{public}d", item.operation);
222 return false;
223 }
224 string param = static_cast<string>(item.GetSingle(fieldIdx));
225 string value = static_cast<string>(item.GetSingle(valueIdx));
226 locationMap.insert(make_pair(param, value));
227 if (param == DIAMETER) {
228 continue;
229 }
230 if (LOCATION_PARAM_MAP.at(param).second == DataShare::GREATER_THAN_OR_EQUAL_TO) {
231 context->predicates.GreaterThanOrEqualTo(LOCATION_PARAM_MAP.at(param).first, value);
232 continue;
233 }
234 if (LOCATION_PARAM_MAP.at(param).second == DataShare::LESS_THAN) {
235 context->predicates.LessThan(LOCATION_PARAM_MAP.at(param).first, value);
236 continue;
237 }
238 if (LOCATION_PARAM_MAP.at(param).second == DataShare::EQUAL_TO) {
239 context->predicates.EqualTo(LOCATION_PARAM_MAP.at(param).first, value);
240 continue;
241 }
242 }
243 }
244
245 if (locationMap.count(DIAMETER) == 1 && locationMap.count(START_LATITUDE) == 1
246 && locationMap.count(START_LONGITUDE) == 1) {
247 // 0.5:Used for rounding down
248 string latitudeIndex = "round((latitude - " + locationMap.at(START_LATITUDE) + ") / " +
249 locationMap.at(DIAMETER) + " - 0.5)";
250 string longitudeIndex = "round((longitude - " + locationMap.at(START_LONGITUDE) + ") / " +
251 locationMap.at(DIAMETER) + " - 0.5)";
252 string albumName = LATITUDE + "||'_'||" + LONGITUDE + "||'_'||" + latitudeIndex + "||'_'||" +
253 longitudeIndex + " AS " + ALBUM_NAME;
254 context->fetchColumn.push_back(albumName);
255 string locationGroup = latitudeIndex + "," + longitudeIndex;
256 context->predicates.GroupBy({ locationGroup });
257 }
258 return true;
259 }
260
AddDefaultAssetColumns(vector<string> & fetchColumn,function<bool (const string & columnName)> isValidColumn,const PhotoAlbumSubType subType)261 bool MediaAniNativeImpl::AddDefaultAssetColumns(vector<string> &fetchColumn,
262 function<bool(const string &columnName)> isValidColumn, const PhotoAlbumSubType subType)
263 {
264 auto validFetchColumns = MediaColumn::DEFAULT_FETCH_COLUMNS;
265 validFetchColumns.insert(PhotoColumn::DEFAULT_FETCH_COLUMNS.begin(), PhotoColumn::DEFAULT_FETCH_COLUMNS.end());
266
267 switch (subType) {
268 case PhotoAlbumSubType::FAVORITE:
269 validFetchColumns.insert(MediaColumn::MEDIA_IS_FAV);
270 break;
271 case PhotoAlbumSubType::VIDEO:
272 validFetchColumns.insert(MediaColumn::MEDIA_TYPE);
273 break;
274 case PhotoAlbumSubType::HIDDEN:
275 validFetchColumns.insert(MediaColumn::MEDIA_HIDDEN);
276 break;
277 case PhotoAlbumSubType::TRASH:
278 validFetchColumns.insert(MediaColumn::MEDIA_DATE_TRASHED);
279 break;
280 case PhotoAlbumSubType::SCREENSHOT:
281 case PhotoAlbumSubType::CAMERA:
282 validFetchColumns.insert(PhotoColumn::PHOTO_SUBTYPE);
283 break;
284 default:
285 break;
286 }
287 for (const auto &column : fetchColumn) {
288 if (column == PENDING_STATUS) {
289 validFetchColumns.insert(MediaColumn::MEDIA_TIME_PENDING);
290 } else if (isValidColumn(column)) {
291 validFetchColumns.insert(column);
292 } else if (column == MEDIA_DATA_DB_URI) {
293 continue;
294 } else if (DATE_TRANSITION_MAP.count(column) != 0) {
295 validFetchColumns.insert(DATE_TRANSITION_MAP.at(column));
296 } else {
297 ANI_ERR_LOG("error");
298 return false;
299 }
300 }
301 fetchColumn.assign(validFetchColumns.begin(), validFetchColumns.end());
302
303 return true;
304 }
305
UriAppendKeyValue(string & uri,const string & key,const string & value)306 static void UriAppendKeyValue(string &uri, const string &key, const string &value)
307 {
308 string uriKey = key + '=';
309 if (uri.find(uriKey) != string::npos) {
310 return;
311 }
312
313 char queryMark = (uri.find('?') == string::npos) ? '?' : '&';
314 string append = queryMark + key + '=' + value;
315
316 size_t posJ = uri.find('#');
317 if (posJ == string::npos) {
318 uri += append;
319 } else {
320 uri.insert(posJ, append);
321 }
322 }
323
PhotoAccessGetAssetsExecuteSync(std::shared_ptr<MediaLibraryAsyncContext> context,std::vector<std::unique_ptr<FileAsset>> & fileAssetArray)324 bool MediaAniNativeImpl::PhotoAccessGetAssetsExecuteSync(std::shared_ptr<MediaLibraryAsyncContext> context,
325 std::vector<std::unique_ptr<FileAsset>>& fileAssetArray)
326 {
327 string queryUri = PAH_QUERY_PHOTO;
328 UriAppendKeyValue(queryUri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
329
330 Uri uri(queryUri);
331 int errCode = 0;
332 shared_ptr<DataShare::DataShareResultSet> resultSet = UserFileClient::Query(uri, context->predicates,
333 context->fetchColumn, errCode);
334 if (resultSet == nullptr && !context->uri.empty() && errCode == E_PERMISSION_DENIED) {
335 Uri queryWithUri(context->uri);
336 resultSet = UserFileClient::Query(queryWithUri, context->predicates, context->fetchColumn, errCode);
337 }
338 if (resultSet == nullptr) {
339 ANI_ERR_LOG("resultSet is nullptr");
340 return false;
341 }
342
343 auto fetchResult = make_unique<FetchResult<FileAsset>>(move(resultSet));
344 if (fetchResult == nullptr) {
345 ANI_ERR_LOG("fetchResult is nullptr");
346 return false;
347 }
348 fetchResult->SetResultNapiType(ResultNapiType::TYPE_PHOTOACCESS_HELPER);
349
350 auto file = fetchResult->GetFirstObject();
351 while (file != nullptr) {
352 fileAssetArray.push_back(move(file));
353 file = fetchResult->GetNextObject();
354 }
355 return true;
356 }
357
PhotoAccessGetAssetsExecute(std::shared_ptr<MediaLibraryAsyncContext> context)358 bool MediaAniNativeImpl::PhotoAccessGetAssetsExecute(std::shared_ptr<MediaLibraryAsyncContext> context)
359 {
360 string queryUri = PAH_QUERY_PHOTO;
361 UriAppendKeyValue(queryUri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
362
363 Uri uri(queryUri);
364 int errCode = 0;
365 shared_ptr<DataShare::DataShareResultSet> resultSet = UserFileClient::Query(uri, context->predicates,
366 context->fetchColumn, errCode);
367 if (resultSet == nullptr && !context->uri.empty() && errCode == E_PERMISSION_DENIED) {
368 Uri queryWithUri(context->uri);
369 resultSet = UserFileClient::Query(queryWithUri, context->predicates, context->fetchColumn, errCode);
370 }
371 if (resultSet == nullptr) {
372 ANI_ERR_LOG("resultSet is nullptr");
373 return false;
374 }
375
376 context->fetchFileResult = make_unique<FetchResult<FileAsset>>(move(resultSet));
377 context->fetchFileResult->SetResultNapiType(ResultNapiType::TYPE_PHOTOACCESS_HELPER);
378 return true;
379 }
380
PhotoAccessGetFileAssetsInfoExecute(std::shared_ptr<MediaLibraryAsyncContext> context,std::vector<std::unique_ptr<FileAsset>> & fileAssetArray)381 bool MediaAniNativeImpl::PhotoAccessGetFileAssetsInfoExecute(std::shared_ptr<MediaLibraryAsyncContext> context,
382 std::vector<std::unique_ptr<FileAsset>>& fileAssetArray)
383 {
384 if (context->assetType != TYPE_PHOTO) {
385 return false;
386 }
387 string queryUri = PAH_QUERY_PHOTO;
388 UriAppendKeyValue(queryUri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
389
390 Uri uri(queryUri);
391 shared_ptr<NativeRdb::ResultSet> resultSet = UserFileClient::QueryRdb(uri,
392 context->predicates, context->fetchColumn);
393 if (resultSet == nullptr) {
394 ANI_ERR_LOG("QueryRdb failed");
395 return false;
396 }
397
398 while (!resultSet->GoToNextRow()) {
399 std::unique_ptr<FileAsset> fileAsset = GetNextRowFileAsset(resultSet);
400 if (fileAsset == nullptr) {
401 ANI_ERR_LOG("get fileAsset failed");
402 continue;
403 }
404 fileAssetArray.emplace_back(std::move(fileAsset));
405 }
406 return true;
407 }
408
GetNextRowFileAsset(shared_ptr<NativeRdb::ResultSet> resultSet)409 std::unique_ptr<FileAsset> MediaAniNativeImpl::GetNextRowFileAsset(shared_ptr<NativeRdb::ResultSet> resultSet)
410 {
411 if (resultSet == nullptr) {
412 ANI_ERR_LOG("resultSet is nullptr");
413 return nullptr;
414 }
415 vector<string> columnNames;
416 resultSet->GetAllColumnNames(columnNames);
417
418 std::unique_ptr<FileAsset> fileAsset;
419 int32_t index = -1;
420 for (const auto &name : columnNames) {
421 index++;
422
423 // Check if the column name exists in the type map
424 if (MediaLibraryAniUtils::GetTypeMap().count(name) == 0) {
425 continue;
426 }
427 GetFileAssetField(index, name, resultSet, fileAsset);
428 }
429 string extrUri = MediaFileUtils::GetExtraUri(fileAsset->GetDisplayName(), fileAsset->GetPath(), false);
430 MediaFileUri fileUri(fileAsset->GetMediaType(), to_string(fileAsset->GetId()), "", MEDIA_API_VERSION_V10, extrUri);
431 fileAsset->SetUri(move(fileUri.ToString()));
432
433 return fileAsset;
434 }
435
GetFileAssetField(int32_t index,string name,const shared_ptr<NativeRdb::ResultSet> resultSet,std::unique_ptr<FileAsset> & fileAsset)436 void MediaAniNativeImpl::GetFileAssetField(int32_t index, string name, const shared_ptr<NativeRdb::ResultSet> resultSet,
437 std::unique_ptr<FileAsset> &fileAsset)
438 {
439 if (!fileAsset) {
440 ANI_ERR_LOG("fileAsset is null");
441 return;
442 }
443
444 int status;
445 int integerVal = 0;
446 string stringVal = "";
447 int64_t longVal = 0;
448 double doubleVal = 0.0;
449
450 const auto& typeMap = MediaLibraryAniUtils::GetTypeMap();
451 auto it = typeMap.find(name);
452 if (it == typeMap.end()) {
453 ANI_ERR_LOG("Unknown field name: %{public}s", name.c_str());
454 return;
455 }
456 auto dataType = it->second;
457
458 switch (dataType.first) {
459 case TYPE_STRING:
460 status = resultSet->GetString(index, stringVal);
461 ANI_DEBUG_LOG("GetFileAssetField TYPE_STRING: status: %{public}d", status);
462 fileAsset->GetMemberMap().emplace(name, stringVal);
463 break;
464 case TYPE_INT32:
465 status = resultSet->GetInt(index, integerVal);
466 ANI_DEBUG_LOG("GetFileAssetField TYPE_INT32: status: %{public}d", status);
467 fileAsset->GetMemberMap().emplace(name, integerVal);
468 break;
469 case TYPE_INT64:
470 status = resultSet->GetLong(index, longVal);
471 ANI_DEBUG_LOG("GetFileAssetField TYPE_INT64: status: %{public}d", status);
472 fileAsset->GetMemberMap().emplace(name, longVal);
473 break;
474 case TYPE_DOUBLE:
475 status = resultSet->GetDouble(index, doubleVal);
476 ANI_DEBUG_LOG("GetFileAssetField TYPE_DOUBLE: status: %{public}d", status);
477 fileAsset->GetMemberMap().emplace(name, doubleVal);
478 break;
479 default:
480 ANI_ERR_LOG("not match dataType %{public}d", dataType.first);
481 break;
482 }
483 }
484 } // namespace Media
485 } // namespace OHOS