• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 #define MLOG_TAG "PhotoMapOperation"
16 
17 #include "photo_map_operations.h"
18 
19 #include "media_column.h"
20 #include "media_file_uri.h"
21 #include "media_file_utils.h"
22 #include "medialibrary_album_operations.h"
23 #include "medialibrary_analysis_album_operations.h"
24 #include "medialibrary_asset_operations.h"
25 #include "medialibrary_db_const.h"
26 #include "medialibrary_data_manager.h"
27 #include "medialibrary_errno.h"
28 #include "medialibrary_notify.h"
29 #include "medialibrary_rdb_transaction.h"
30 #include "medialibrary_rdb_utils.h"
31 #include "medialibrary_rdbstore.h"
32 #include "medialibrary_unistore_manager.h"
33 #include "photo_album_column.h"
34 #include "photo_map_column.h"
35 #include "value_object.h"
36 #include "vision_column.h"
37 #include "rdb_utils.h"
38 #include "result_set_utils.h"
39 #include "vision_album_column.h"
40 #include "vision_face_tag_column.h"
41 #include "vision_image_face_column.h"
42 #include "vision_photo_map_column.h"
43 #include "dfx_manager.h"
44 #include "dfx_const.h"
45 
46 namespace OHOS::Media {
47 using namespace std;
48 using namespace OHOS::NativeRdb;
49 using namespace OHOS::DataShare;
50 
51 constexpr int32_t ALBUM_IS_REMOVED = 1;
52 
InsertAnalysisAsset(const DataShareValuesBucket & value,std::shared_ptr<TransactionOperations> trans)53 static int32_t InsertAnalysisAsset(const DataShareValuesBucket &value,
54     std::shared_ptr<TransactionOperations> trans)
55 {
56     if (trans == nullptr) {
57         MEDIA_ERR_LOG("transactionOperations is null");
58         return -EINVAL;
59     }
60     /**
61      * Build insert sql:
62      * INSERT INTO AnalysisPhotoMap (map_album, map_asset) SELECT
63      * ?, ?
64      * WHERE
65      *     (NOT EXISTS (SELECT * FROM AnalysisPhotoMap WHERE map_album = ? AND map_asset = ?))
66      *     AND (EXISTS (SELECT file_id FROM Photos WHERE file_id = ?))
67      *     AND (EXISTS (SELECT album_id FROM AnalysisAlbum WHERE album_id = ?));
68      */
69     static const std::string INSERT_MAP_SQL = "INSERT OR IGNORE INTO " + ANALYSIS_PHOTO_MAP_TABLE +
70         " (" + PhotoMap::ALBUM_ID + ", " + PhotoMap::ASSET_ID + ") " +
71         "SELECT ?, ? WHERE " +
72         "(NOT EXISTS (SELECT 1 FROM " + ANALYSIS_PHOTO_MAP_TABLE + " WHERE " +
73             PhotoMap::ALBUM_ID + " = ? AND " + PhotoMap::ASSET_ID + " = ?)) " +
74         "AND (EXISTS (SELECT 1 FROM " + PhotoColumn::PHOTOS_TABLE + " WHERE " +
75             MediaColumn::MEDIA_ID + " = ?)) " +
76         "AND (EXISTS (SELECT 1 FROM " + ANALYSIS_ALBUM_TABLE +
77             " WHERE " + PhotoAlbumColumns::ALBUM_ID + " = ? ));";
78     bool isValid = false;
79     int32_t albumId = value.Get(PhotoMap::ALBUM_ID, isValid);
80     if (!isValid) {
81         return -EINVAL;
82     }
83     int32_t assetId = value.Get(PhotoMap::ASSET_ID, isValid);
84     if (!isValid) {
85         return -EINVAL;
86     }
87     vector<ValueObject> bindArgs = { albumId, assetId, albumId, assetId, assetId, albumId};
88     return trans->ExecuteForLastInsertedRowId(INSERT_MAP_SQL, bindArgs);
89 }
90 
AddPhotoAssets(const vector<DataShareValuesBucket> & values)91 int32_t PhotoMapOperations::AddPhotoAssets(const vector<DataShareValuesBucket> &values)
92 {
93     auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
94     if (rdbStore == nullptr) {
95         return E_HAS_DB_ERROR;
96     }
97 
98     int32_t changedRows = 0;
99     vector<int32_t> updateIds;
100     if (!values.empty()) {
101         bool isValid = false;
102         int32_t albumId = values[0].Get(PhotoColumn::PHOTO_OWNER_ALBUM_ID, isValid);
103         if (!isValid || albumId <= 0) {
104             MEDIA_WARN_LOG("Ignore failure on get album id when add assets. isValid: %{public}d, albumId: %{public}d",
105                 isValid, albumId);
106             return changedRows;
107         }
108 
109         changedRows = MediaLibraryRdbUtils::UpdateOwnerAlbumId(rdbStore, values, updateIds);
110         MediaLibraryRdbUtils::UpdateUserAlbumInternal(rdbStore, { to_string(albumId) });
111         MediaLibraryRdbUtils::UpdateSystemAlbumInternal(rdbStore, {
112             to_string(PhotoAlbumSubType::IMAGE), to_string(PhotoAlbumSubType::VIDEO),
113             to_string(PhotoAlbumSubType::FAVORITE)
114         });
115         auto watch = MediaLibraryNotify::GetInstance();
116         for (const auto &id : updateIds) {
117             string notifyUri = PhotoColumn::PHOTO_URI_PREFIX + to_string(id);
118             watch->Notify(MediaFileUtils::Encode(notifyUri), NotifyType::NOTIFY_ALBUM_ADD_ASSET, albumId);
119             watch->Notify(MediaFileUtils::Encode(notifyUri), NotifyType::NOTIFY_ALBUM_ADD_ASSET,
120                 watch->GetAlbumIdBySubType(PhotoAlbumSubType::IMAGE));
121             watch->Notify(MediaFileUtils::Encode(notifyUri), NotifyType::NOTIFY_ALBUM_ADD_ASSET,
122                 watch->GetAlbumIdBySubType(PhotoAlbumSubType::VIDEO));
123             watch->Notify(MediaFileUtils::Encode(notifyUri), NotifyType::NOTIFY_ALBUM_ADD_ASSET,
124                 watch->GetAlbumIdBySubType(PhotoAlbumSubType::FAVORITE));
125             watch->Notify(MediaFileUtils::Encode(notifyUri), NotifyType::NOTIFY_ADD);
126         }
127     }
128     return changedRows;
129 }
130 
GetPortraitAlbumIds(const string & albumId,vector<string> & portraitAlbumIds)131 static int32_t GetPortraitAlbumIds(const string &albumId, vector<string> &portraitAlbumIds)
132 {
133     auto uniStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
134     if (uniStore == nullptr) {
135         MEDIA_ERR_LOG("uniStore is nullptr! failed query album order");
136         return E_HAS_DB_ERROR;
137     }
138     const std::string queryPortraitAlbumIds = "SELECT " + ALBUM_ID + " FROM " + ANALYSIS_ALBUM_TABLE + " WHERE " +
139         GROUP_TAG + " IN(SELECT " + GROUP_TAG + " FROM " + ANALYSIS_ALBUM_TABLE +
140         " WHERE " + ALBUM_ID + " = " + albumId + " AND " + ALBUM_SUBTYPE + " = " + to_string(PORTRAIT) +")";
141 
142     auto resultSet = uniStore->QuerySql(queryPortraitAlbumIds);
143     if (resultSet == nullptr) {
144         return E_DB_FAIL;
145     }
146     while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
147         portraitAlbumIds.push_back(to_string(GetInt32Val(ALBUM_ID, resultSet)));
148     }
149     return E_OK;
150 }
151 
AddAnaLysisPhotoAssets(const vector<DataShareValuesBucket> & values)152 int32_t PhotoMapOperations::AddAnaLysisPhotoAssets(const vector<DataShareValuesBucket> &values)
153 {
154     auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
155     if (rdbStore == nullptr) {
156         return E_HAS_DB_ERROR;
157     }
158     if (values.empty()) {
159         return 0;
160     }
161     std::shared_ptr<TransactionOperations> trans = make_shared<TransactionOperations>();
162     int32_t changedRows = 0;
163     int32_t err = NativeRdb::E_OK;
164     std::function<int(void)> func = [&]()->int {
165         for (const auto &value : values) {
166             err =  InsertAnalysisAsset(value, trans);
167             if (err == E_HAS_DB_ERROR) {
168                 MEDIA_WARN_LOG("InsertAnalysisAsset for db error, changedRows now: %{public}d", changedRows);
169                 return err;
170             }
171             if (err > 0) {
172                 changedRows++;
173             }
174         }
175         return err;
176     };
177     err = trans->RetryTrans(func, __func__);
178     if (err != E_OK) {
179         MEDIA_ERR_LOG("AddAnaLysisPhotoAssets: trans retry fail!, ret:%{public}d", err);
180     }
181     bool isValid = false;
182     std::vector<string> albumIdList;
183     for (const auto &value : values) {
184         int32_t albumId = value.Get(PhotoMap::ALBUM_ID, isValid);
185         if (!isValid || albumId <= 0) {
186             MEDIA_WARN_LOG("Ignore failure on get album id when add assets. isValid: %{public}d, albumId: %{public}d",
187                 isValid, albumId);
188             continue;
189         }
190         albumIdList.push_back(to_string(albumId));
191     }
192     MediaLibraryRdbUtils::UpdateAnalysisAlbumInternal(rdbStore, albumIdList);
193     return changedRows;
194 }
195 
GetDismissAssetsPredicates(NativeRdb::RdbPredicates & rdbPredicate,vector<string> & updateAlbumIds,PhotoAlbumSubType subtype,const string & albumId,const vector<string> & assetsArray)196 static void GetDismissAssetsPredicates(NativeRdb::RdbPredicates &rdbPredicate, vector<string> &updateAlbumIds,
197     PhotoAlbumSubType subtype, const string &albumId, const vector<string> &assetsArray)
198 {
199     if (subtype == PhotoAlbumSubType::PORTRAIT) {
200         GetPortraitAlbumIds(albumId, updateAlbumIds);
201         rdbPredicate.In(MAP_ALBUM, updateAlbumIds);
202         rdbPredicate.And()->In(MAP_ASSET, assetsArray);
203     } else {
204         rdbPredicate.EqualTo(MAP_ALBUM, albumId);
205         rdbPredicate.And()->In(MAP_ASSET, assetsArray);
206         updateAlbumIds.push_back(albumId);
207     }
208 }
209 
DoDismissAssets(int32_t subtype,const string & albumId,const vector<string> & assetIds)210 int32_t DoDismissAssets(int32_t subtype, const string &albumId, const vector<string> &assetIds)
211 {
212     int32_t deleteRow = 0;
213     if (subtype == PhotoAlbumSubType::GROUP_PHOTO) {
214         NativeRdb::RdbPredicates rdbPredicate { VISION_IMAGE_FACE_TABLE };
215         rdbPredicate.In(MediaColumn::MEDIA_ID, assetIds);
216         deleteRow = MediaLibraryRdbStore::Delete(rdbPredicate);
217         if (deleteRow != 0 && MediaLibraryDataManagerUtils::IsNumber(albumId)) {
218             MediaLibraryAnalysisAlbumOperations::UpdateGroupPhotoAlbumById(atoi(albumId.c_str()));
219         }
220         return deleteRow;
221     }
222 
223     vector<string> updateAlbumIds;
224     NativeRdb::RdbPredicates rdbPredicate { ANALYSIS_PHOTO_MAP_TABLE };
225     GetDismissAssetsPredicates(rdbPredicate, updateAlbumIds,
226         static_cast<PhotoAlbumSubType>(subtype), albumId, assetIds);
227     deleteRow = MediaLibraryRdbStore::Delete(rdbPredicate);
228     if (deleteRow <= 0) {
229         return deleteRow;
230     }
231     MediaLibraryRdbUtils::UpdateAnalysisAlbumInternal(
232         MediaLibraryUnistoreManager::GetInstance().GetRdbStore(), updateAlbumIds, assetIds);
233     return deleteRow;
234 }
235 
DismissAssets(NativeRdb::RdbPredicates & predicates)236 int32_t PhotoMapOperations::DismissAssets(NativeRdb::RdbPredicates &predicates)
237 {
238     vector<string> whereArgsUri = predicates.GetWhereArgs();
239     MediaLibraryRdbStore::ReplacePredicatesUriToId(predicates);
240 
241     const vector<string> &whereArgsId = predicates.GetWhereArgs();
242     if (whereArgsId.size() == 0) {
243         MEDIA_ERR_LOG("No fileAsset to delete");
244         return E_INVALID_ARGUMENTS;
245     }
246     string strAlbumId = whereArgsId[0];
247     if (strAlbumId.empty()) {
248         MEDIA_ERR_LOG("Failed to get albumId");
249         return E_INVALID_ARGUMENTS;
250     }
251 
252     int32_t albumId = atoi(strAlbumId.c_str());
253     if (albumId <= 0) {
254         MEDIA_WARN_LOG("Ignore failure on get album id when remove assets, album updating would be lost");
255         return E_INVALID_ARGUMENTS;
256     }
257     string strSubtype = whereArgsId[whereArgsId.size() - 1];
258     int32_t subtype = atoi(strSubtype.c_str());
259     if (subtype != PhotoAlbumSubType::CLASSIFY && subtype != PhotoAlbumSubType::PORTRAIT &&
260         subtype != PhotoAlbumSubType::GROUP_PHOTO) {
261         MEDIA_ERR_LOG("Invalid album subtype: %{public}d", subtype);
262         return E_INVALID_ARGUMENTS;
263     }
264 
265     vector<string> assetsArray;
266     for (size_t i = 1; i < whereArgsId.size() - 1; i++) {
267         assetsArray.push_back(whereArgsId[i]);
268     }
269 
270     int32_t deleteRow = DoDismissAssets(subtype, strAlbumId, assetsArray);
271     if (deleteRow > 0) {
272         auto watch = MediaLibraryNotify::GetInstance();
273         for (size_t i = 1; i < whereArgsUri.size() - 1; i++) {
274             watch->Notify(MediaFileUtils::Encode(whereArgsUri[i]), NotifyType::NOTIFY_ALBUM_DISMISS_ASSET, albumId);
275         }
276     }
277     return deleteRow;
278 }
279 
RemovePhotoAssets(RdbPredicates & predicates)280 int32_t PhotoMapOperations::RemovePhotoAssets(RdbPredicates &predicates)
281 {
282     vector<string> whereArgs = predicates.GetWhereArgs();
283     MediaLibraryRdbStore::ReplacePredicatesUriToId(predicates);
284     int32_t deleteRow = 0;
285     vector<string> whereIdArgs = predicates.GetWhereArgs();
286     if (whereIdArgs.empty()) {
287         return deleteRow;
288     }
289     whereIdArgs.erase(whereIdArgs.begin());
290     deleteRow = MediaLibraryRdbUtils::UpdateRemoveAsset(
291         MediaLibraryUnistoreManager::GetInstance().GetRdbStore(), whereIdArgs);
292 
293     string strAlbumId = predicates.GetWhereArgs()[0];
294     if (strAlbumId.empty()) {
295         MEDIA_ERR_LOG("Failed to get albumId");
296         return deleteRow;
297     }
298     int32_t albumId = atoi(strAlbumId.c_str());
299     MediaLibraryRdbUtils::UpdateUserAlbumInternal(
300         MediaLibraryUnistoreManager::GetInstance().GetRdbStore(), { strAlbumId });
301     auto watch = MediaLibraryNotify::GetInstance();
302     for (size_t i = 1; i < whereArgs.size(); i++) {
303         watch->Notify(MediaFileUtils::Encode(whereArgs[i]), NotifyType::NOTIFY_ALBUM_REMOVE_ASSET, albumId);
304     }
305     DfxManager::GetInstance()->HandleDeleteBehavior(DfxType::ALBUM_REMOVE_PHOTOS, deleteRow, whereArgs);
306     return deleteRow;
307 }
308 
IsQueryGroupPhotoAlbumAssets(const string & albumId,string & tagId,int32_t & isRemoved)309 bool IsQueryGroupPhotoAlbumAssets(const string &albumId, string &tagId, int32_t &isRemoved)
310 {
311     if (albumId.empty() || !MediaLibraryDataManagerUtils::IsNumber(albumId)) {
312         return false;
313     }
314     RdbPredicates predicates(ANALYSIS_ALBUM_TABLE);
315     predicates.EqualTo(PhotoAlbumColumns::ALBUM_ID, albumId);
316     vector<string> columns = {PhotoAlbumColumns::ALBUM_TYPE, PhotoAlbumColumns::ALBUM_SUBTYPE, TAG_ID, IS_REMOVED};
317     auto resultSet = MediaLibraryRdbStore::QueryWithFilter(predicates, columns);
318     if (resultSet == nullptr || resultSet->GoToFirstRow() != E_OK) {
319         return false;
320     }
321     int32_t albumType = GetInt32Val(PhotoAlbumColumns::ALBUM_TYPE, resultSet);
322     int32_t albumSubtype = GetInt32Val(PhotoAlbumColumns::ALBUM_SUBTYPE, resultSet);
323     tagId = GetStringVal(TAG_ID, resultSet);
324     isRemoved = GetInt32Val(IS_REMOVED, resultSet);
325     return albumType == PhotoAlbumType::SMART && albumSubtype == PhotoAlbumSubType::GROUP_PHOTO;
326 }
327 
QueryGroupPhotoAlbumAssets(const string & albumId,const string & tagId,const vector<string> & columns)328 shared_ptr<OHOS::NativeRdb::ResultSet> QueryGroupPhotoAlbumAssets(const string &albumId, const string &tagId,
329     const vector<string> &columns)
330 {
331     string strColumns;
332     for (size_t i = 0; i < columns.size(); i++) {
333         strColumns.append("P." + columns[i]);
334         if (i != columns.size() - 1) {
335             strColumns.append(", ");
336         }
337     }
338     string strTags = "'";
339     int32_t albumTagCount = 1;
340     for (char c : tagId) {
341         if (c == ',') {
342             strTags.append("', '");
343             albumTagCount++;
344         } else {
345             strTags.push_back(c);
346         }
347     }
348     strTags.append("'");
349     string sql = "SELECT " + strColumns + " FROM " + VISION_IMAGE_FACE_TABLE + " F INNER JOIN " +
350         ANALYSIS_ALBUM_TABLE + " AA ON F." + TAG_ID + " = AA." + TAG_ID + " AND AA." + GROUP_TAG + " IN (" + strTags +
351         ") INNER JOIN " + ANALYSIS_PHOTO_MAP_TABLE + " ON " + MAP_ALBUM + " = AA." + PhotoAlbumColumns::ALBUM_ID +
352         " AND " + MAP_ASSET + " = F." + MediaColumn::MEDIA_ID + " INNER JOIN " + PhotoColumn::PHOTOS_TABLE +
353         " P ON P." + MediaColumn::MEDIA_ID + " = F." + MediaColumn::MEDIA_ID + " AND " +
354         MediaColumn::MEDIA_DATE_TRASHED + " = 0 AND " + MediaColumn::MEDIA_HIDDEN + " = 0 AND " +
355         MediaColumn::MEDIA_TIME_PENDING + " = 0 GROUP BY P." + MediaColumn::MEDIA_ID +
356         " HAVING COUNT(" + GROUP_TAG + ") = " + TOTAL_FACES + " AND " +
357         " COUNT(DISTINCT " + GROUP_TAG +") = " + to_string(albumTagCount) + ";";
358     auto resultSet = MediaLibraryUnistoreManager::GetInstance().GetRdbStore()->QuerySql(sql);
359     if (resultSet != nullptr) {
360         return resultSet;
361     }
362     return nullptr;
363 }
364 
QueryPhotoAssets(const RdbPredicates & rdbPredicate,const vector<string> & columns)365 shared_ptr<OHOS::NativeRdb::ResultSet> PhotoMapOperations::QueryPhotoAssets(const RdbPredicates &rdbPredicate,
366     const vector<string> &columns)
367 {
368     string albumId = rdbPredicate.GetWhereArgs()[0];
369     string tagId;
370     int32_t isRemoved;
371     if (IsQueryGroupPhotoAlbumAssets(albumId, tagId, isRemoved)) {
372         if (isRemoved == ALBUM_IS_REMOVED) {
373             return nullptr;
374         }
375         return QueryGroupPhotoAlbumAssets(albumId, tagId, columns);
376     }
377     return MediaLibraryRdbStore::QueryWithFilter(rdbPredicate, columns);
378 }
379 } // namespace OHOS::Media