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 <vector>
17 #include <map>
18 #include "medialibrary_app_uri_permission_operations.h"
19 #include "medialibrary_unistore_manager.h"
20 #include "medialibrary_rdbstore.h"
21 #include "medialibrary_rdb_utils.h"
22 #include "media_column.h"
23 #include "datashare_predicates.h"
24 #include "rdb_utils.h"
25 #include "media_file_uri.h"
26 #include "medialibrary_asset_operations.h"
27 #include "medialibrary_rdb_transaction.h"
28 #include "media_file_utils.h"
29 #include "medialibrary_appstate_observer.h"
30 #include "datashare_predicates_objects.h"
31 
32 using namespace OHOS::DataShare;
33 using namespace std;
34 using namespace OHOS::NativeRdb;
35 using namespace OHOS::RdbDataShareAdapter;
36 
37 namespace OHOS {
38 namespace Media {
39 
40 const int MediaLibraryAppUriPermissionOperations::ERROR = -1;
41 const int MediaLibraryAppUriPermissionOperations::SUCCEED = 0;
42 const int MediaLibraryAppUriPermissionOperations::ALREADY_EXIST = 1;
43 const int MediaLibraryAppUriPermissionOperations::NO_DATA_EXIST = 0;
44 
HandleInsertOperation(MediaLibraryCommand & cmd)45 int32_t MediaLibraryAppUriPermissionOperations::HandleInsertOperation(MediaLibraryCommand &cmd)
46 {
47     MEDIA_INFO_LOG("insert appUriPermission begin");
48     // permissionType from param
49     int permissionTypeParam = -1;
50     if (!GetIntFromValuesBucket(cmd.GetValueBucket(), AppUriPermissionColumn::PERMISSION_TYPE,
51         permissionTypeParam)) {
52         return ERROR;
53     }
54     if (!IsValidPermissionType(permissionTypeParam)) {
55         return ERROR;
56     }
57     // parse fileId
58     int fileId = -1;
59     if (!GetIntFromValuesBucket(cmd.GetValueBucket(), AppUriPermissionColumn::FILE_ID, fileId)) {
60         return ERROR;
61     }
62     if (!IsPhotoExist(fileId)) {
63         return ERROR;
64     }
65     // query permission data before insert
66     int queryFlag = ERROR;
67     shared_ptr<ResultSet> resultSet = QueryNewData(cmd.GetValueBucket(), queryFlag);
68     // Update the permissionType
69     if (queryFlag > 0) {
70         return UpdatePermissionType(resultSet, permissionTypeParam);
71     }
72     if (queryFlag < 0) {
73         return ERROR;
74     }
75 
76     // delete the temporary permission when the app dies
77     if (AppUriPermissionColumn::PERMISSION_TYPES_TEMPORARY.find(permissionTypeParam) !=
78         AppUriPermissionColumn::PERMISSION_TYPES_TEMPORARY.end()) {
79         MedialibraryAppStateObserverManager::GetInstance().SubscribeAppState();
80     }
81 
82     auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
83     if (rdbStore == nullptr) {
84         MEDIA_ERR_LOG("get rdbStore error");
85         return ERROR;
86     }
87     // insert data
88     int64_t outRowId = -1;
89     cmd.GetValueBucket().PutLong(AppUriPermissionColumn::DATE_MODIFIED,
90         MediaFileUtils::UTCTimeMilliSeconds());
91 
92     cmd.GetValueBucket().Delete(AppUriSensitiveColumn::HIDE_SENSITIVE_TYPE);
93     cmd.SetTableName(AppUriPermissionColumn::APP_URI_PERMISSION_TABLE);
94 
95     int32_t errCode = rdbStore->Insert(cmd, outRowId);
96     if (errCode != NativeRdb::E_OK) {
97         MEDIA_ERR_LOG("insert into db error, errCode=%{public}d", errCode);
98         return ERROR;
99     }
100     MEDIA_INFO_LOG("insert appUriPermission ok");
101     return SUCCEED;
102 }
103 
BatchInsert(MediaLibraryCommand & cmd,const std::vector<DataShare::DataShareValuesBucket> & values)104 int32_t MediaLibraryAppUriPermissionOperations::BatchInsert(
105     MediaLibraryCommand &cmd, const std::vector<DataShare::DataShareValuesBucket> &values)
106 {
107     MEDIA_INFO_LOG("batch insert begin");
108 
109     if (!IsPhotosAllExist(values)) {
110         return ERROR;
111     }
112     std::shared_ptr<TransactionOperations> trans = make_shared<TransactionOperations>(__func__);
113     std::function<int(void)> func = [&]()->int {
114         return BatchInsertInner(cmd, values, trans);
115     };
116     int32_t errCode = trans->RetryTrans(func);
117     if (errCode != E_OK) {
118         MEDIA_ERR_LOG("BatchInsert: trans retry fail!, ret:%{public}d", errCode);
119     }
120     MEDIA_INFO_LOG("batch insert ok");
121     return SUCCEED;
122 }
123 
BatchInsertInner(MediaLibraryCommand & cmd,const std::vector<DataShare::DataShareValuesBucket> & values,std::shared_ptr<TransactionOperations> trans)124 int32_t MediaLibraryAppUriPermissionOperations::BatchInsertInner(
125     MediaLibraryCommand &cmd, const std::vector<DataShare::DataShareValuesBucket> &values,
126     std::shared_ptr<TransactionOperations> trans)
127 {
128     int32_t errCode = NativeRdb::E_OK;
129     std::vector<ValuesBucket> insertVector;
130     for (auto it = values.begin(); it != values.end(); it++) {
131         ValuesBucket value = RdbUtils::ToValuesBucket(*it);
132         int queryFlag = -1;
133         std::shared_ptr<OHOS::NativeRdb::ResultSet> resultSet = QueryNewData(value, queryFlag);
134         if (queryFlag < 0) {
135             return ERROR;
136         }
137         // permissionType from param
138         int permissionTypeParam = -1;
139         if (!GetIntFromValuesBucket(value, AppUriPermissionColumn::PERMISSION_TYPE,
140             permissionTypeParam)) {
141             return ERROR;
142         }
143 
144         value.Delete(AppUriSensitiveColumn::HIDE_SENSITIVE_TYPE);
145 
146         if (queryFlag == 0) {
147             // delete the temporary permission when the app dies
148             if (AppUriPermissionColumn::PERMISSION_TYPES_TEMPORARY.find(permissionTypeParam) !=
149                 AppUriPermissionColumn::PERMISSION_TYPES_TEMPORARY.end()) {
150                 MedialibraryAppStateObserverManager::GetInstance().SubscribeAppState();
151             }
152             value.PutLong(AppUriPermissionColumn::DATE_MODIFIED, MediaFileUtils::UTCTimeMilliSeconds());
153             insertVector.push_back(value);
154         } else if (UpdatePermissionType(resultSet, permissionTypeParam) == ERROR) {
155             return ERROR;
156         }
157     }
158 
159     if (!insertVector.empty()) {
160         if (errCode != E_OK) {
161             MEDIA_ERR_LOG("start transaction error, errCode = %{public}d", errCode);
162             return ERROR;
163         }
164         int64_t outRowId = -1;
165         errCode = trans->BatchInsert(outRowId, AppUriPermissionColumn::APP_URI_PERMISSION_TABLE, insertVector);
166         if (errCode != NativeRdb::E_OK) {
167             MEDIA_ERR_LOG("batch insert err=%{public}d", errCode);
168             return ERROR;
169         }
170     }
171     return errCode;
172 }
173 
DeleteOperation(NativeRdb::RdbPredicates & predicates)174 int32_t MediaLibraryAppUriPermissionOperations::DeleteOperation(NativeRdb::RdbPredicates &predicates)
175 {
176     MEDIA_INFO_LOG("delete begin");
177     int deleteRow = MediaLibraryRdbStore::Delete(predicates);
178     MEDIA_INFO_LOG("deleted row=%{public}d", deleteRow);
179     return deleteRow < 0 ? ERROR : SUCCEED;
180 }
181 
QueryOperation(DataShare::DataSharePredicates & predicates,std::vector<std::string> & fetchColumns)182 std::shared_ptr<OHOS::NativeRdb::ResultSet> MediaLibraryAppUriPermissionOperations::QueryOperation(
183     DataShare::DataSharePredicates &predicates, std::vector<std::string> &fetchColumns)
184 {
185     RdbPredicates rdbPredicates = RdbUtils::ToPredicates(predicates,
186         AppUriPermissionColumn::APP_URI_PERMISSION_TABLE);
187     std::shared_ptr<OHOS::NativeRdb::ResultSet> resultSet =
188         MediaLibraryRdbStore::QueryWithFilter(rdbPredicates, fetchColumns);
189     if (resultSet == nullptr) {
190         return nullptr;
191     }
192     int32_t numRows = 0;
193     resultSet->GetRowCount(numRows);
194     if (numRows == 0) {
195         return nullptr;
196     }
197     resultSet->GoToFirstRow();
198     return resultSet;
199 }
200 
QueryNewData(OHOS::NativeRdb::ValuesBucket & valueBucket,int & resultFlag)201 std::shared_ptr<OHOS::NativeRdb::ResultSet> MediaLibraryAppUriPermissionOperations::QueryNewData(
202     OHOS::NativeRdb::ValuesBucket &valueBucket, int &resultFlag)
203 {
204     // parse appid
205     ValueObject appidVO;
206     bool appIdRet = valueBucket.GetObject(AppUriPermissionColumn::APP_ID, appidVO);
207     if (!appIdRet) {
208         MEDIA_ERR_LOG("param without appId");
209         resultFlag = ERROR;
210         return nullptr;
211     }
212     string appId = appidVO;
213 
214     // parse fileId
215     int fileId = -1;
216     if (!GetIntFromValuesBucket(valueBucket, AppUriPermissionColumn::FILE_ID, fileId)) {
217         resultFlag = ERROR;
218         return nullptr;
219     }
220 
221     // parse uriType
222     int uriType = -1;
223     if (!GetIntFromValuesBucket(valueBucket, AppUriPermissionColumn::URI_TYPE, uriType)) {
224         resultFlag = ERROR;
225         return nullptr;
226     }
227 
228     OHOS::DataShare::DataSharePredicates permissionPredicates;
229     permissionPredicates.And()->EqualTo(AppUriPermissionColumn::FILE_ID, fileId);
230     permissionPredicates.And()->EqualTo(AppUriPermissionColumn::URI_TYPE, uriType);
231     permissionPredicates.And()->EqualTo(AppUriPermissionColumn::APP_ID, appId);
232 
233     vector<string> fetchColumns;
234     fetchColumns.push_back(AppUriPermissionColumn::ID);
235     fetchColumns.push_back(AppUriPermissionColumn::PERMISSION_TYPE);
236 
237     shared_ptr<ResultSet> resultSet = QueryOperation(permissionPredicates, fetchColumns);
238     resultFlag = (resultSet == nullptr ? NO_DATA_EXIST : ALREADY_EXIST);
239     return resultSet;
240 }
241 
GetIntFromValuesBucket(OHOS::NativeRdb::ValuesBucket & valueBucket,const std::string & column,int & result)242 bool MediaLibraryAppUriPermissionOperations::GetIntFromValuesBucket(
243     OHOS::NativeRdb::ValuesBucket &valueBucket, const std::string &column, int &result)
244 {
245     ValueObject valueObject;
246     bool ret = valueBucket.GetObject(column, valueObject);
247     if (!ret) {
248         MEDIA_ERR_LOG("valueBucket param without %{public}s", column.c_str());
249         return false;
250     }
251     result = valueObject;
252     return true;
253 }
254 
UpdatePermissionType(shared_ptr<ResultSet> & resultSetDB,int & permissionTypeParam,std::shared_ptr<TransactionOperations> trans)255 int MediaLibraryAppUriPermissionOperations::UpdatePermissionType(shared_ptr<ResultSet> &resultSetDB,
256     int &permissionTypeParam, std::shared_ptr<TransactionOperations> trans)
257 {
258     int32_t permissionTypeDB =
259         MediaLibraryRdbStore::GetInt(resultSetDB, AppUriPermissionColumn::PERMISSION_TYPE);
260     if (!CanOverride(permissionTypeParam, permissionTypeDB)) {
261         return ALREADY_EXIST;
262     }
263 
264     // delete the temporary permission when the app dies
265     if (AppUriPermissionColumn::PERMISSION_TYPES_TEMPORARY.find(permissionTypeParam) !=
266         AppUriPermissionColumn::PERMISSION_TYPES_TEMPORARY.end()) {
267         MedialibraryAppStateObserverManager::GetInstance().SubscribeAppState();
268     }
269 
270     // update permission type
271     ValuesBucket updateVB;
272     updateVB.PutInt(AppUriPermissionColumn::PERMISSION_TYPE, permissionTypeParam);
273     updateVB.PutLong(AppUriPermissionColumn::DATE_MODIFIED, MediaFileUtils::UTCTimeMilliSeconds());
274     int32_t idDB = MediaLibraryRdbStore::GetInt(resultSetDB, AppUriPermissionColumn::ID);
275 
276     OHOS::DataShare::DataSharePredicates updatePredicates;
277     updatePredicates.EqualTo(AppUriPermissionColumn::ID, idDB);
278     RdbPredicates updateRdbPredicates =
279         RdbUtils::ToPredicates(updatePredicates, AppUriPermissionColumn::APP_URI_PERMISSION_TABLE);
280     int32_t updateRows = 0;
281     if (trans == nullptr) {
282         updateRows = MediaLibraryRdbStore::UpdateWithDateTime(updateVB, updateRdbPredicates);
283     } else {
284         updateRows = trans->Update(updateVB, updateRdbPredicates);
285     }
286     if (updateRows < 1) {
287         MEDIA_ERR_LOG("upgrade permissionType error,idDB=%{public}d", idDB);
288         return ERROR;
289     }
290     MEDIA_INFO_LOG("update ok,Rows=%{public}d", updateRows);
291     return SUCCEED;
292 }
293 
IsValidPermissionType(int & permissionType)294 bool MediaLibraryAppUriPermissionOperations::IsValidPermissionType(int &permissionType)
295 {
296     bool isValid = AppUriPermissionColumn::PERMISSION_TYPES_ALL.find(permissionType)
297         != AppUriPermissionColumn::PERMISSION_TYPES_ALL.end();
298     if (!isValid) {
299         MEDIA_ERR_LOG("invalid permissionType=%{public}d", permissionType);
300     }
301     return isValid;
302 }
303 
CanOverride(int & permissionTypeParam,int & permissionTypeDB)304 bool MediaLibraryAppUriPermissionOperations::CanOverride(int &permissionTypeParam, int &permissionTypeDB)
305 {
306     MEDIA_INFO_LOG("permissionTypeParam=%{public}d,permissionTypeDB=%{public}d",
307         permissionTypeParam, permissionTypeDB);
308     // Equal permissions do not need to be overridden
309     if (permissionTypeParam == permissionTypeDB) {
310         return false;
311     }
312     // temporary permission can't override persist permission
313     if (AppUriPermissionColumn::PERMISSION_TYPES_TEMPORARY.find(permissionTypeParam) !=
314         AppUriPermissionColumn::PERMISSION_TYPES_TEMPORARY.end()
315         && AppUriPermissionColumn::PERMISSION_TYPES_PERSIST.find(permissionTypeDB) !=
316         AppUriPermissionColumn::PERMISSION_TYPES_PERSIST.end()
317     ) {
318         return false;
319     }
320     // persist permission can override temporary permission
321     if (AppUriPermissionColumn::PERMISSION_TYPES_TEMPORARY.find(permissionTypeDB) !=
322         AppUriPermissionColumn::PERMISSION_TYPES_TEMPORARY.end()
323         && AppUriPermissionColumn::PERMISSION_TYPES_PERSIST.find(permissionTypeParam) !=
324         AppUriPermissionColumn::PERMISSION_TYPES_PERSIST.end()
325     ) {
326         return true;
327     }
328     // Temporary permissions can override each other.
329     if (AppUriPermissionColumn::PERMISSION_TYPES_TEMPORARY.find(permissionTypeParam) !=
330         AppUriPermissionColumn::PERMISSION_TYPES_TEMPORARY.end()
331         && AppUriPermissionColumn::PERMISSION_TYPES_TEMPORARY.find(permissionTypeDB) !=
332         AppUriPermissionColumn::PERMISSION_TYPES_PERSIST.end()
333     ) {
334         return true;
335     }
336     // PERMISSION_PERSIST_READ_WRITE can override PERMISSION_PERSIST_READ, but not vice verse.
337     return permissionTypeParam == AppUriPermissionColumn::PERMISSION_PERSIST_READ_WRITE;
338 }
339 
IsPhotoExist(int32_t & photoFileId)340 bool MediaLibraryAppUriPermissionOperations::IsPhotoExist(int32_t &photoFileId)
341 {
342     // query whether photo exists.
343     RdbPredicates rdbPredicates(PhotoColumn::PHOTOS_TABLE);
344     rdbPredicates.And()->EqualTo(PhotoColumn::MEDIA_ID, std::to_string(photoFileId));
345     vector<string> photoColumns;
346     photoColumns.push_back(PhotoColumn::MEDIA_ID);
347     shared_ptr<NativeRdb::ResultSet> photoRestultSet =
348         MediaLibraryRdbStore::QueryWithFilter(rdbPredicates, photoColumns);
349     if (photoRestultSet == nullptr) {
350         MEDIA_ERR_LOG("query photoRestultSet is null,fileId=%{public}d", photoFileId);
351         return false;
352     }
353     int32_t photoNumRows = 0;
354     photoRestultSet->GetRowCount(photoNumRows);
355     if (photoNumRows == 0) {
356         MEDIA_ERR_LOG("query photo not exist,fileId=%{public}d", photoFileId);
357         return false;
358     }
359     return true;
360 }
361 
IsPhotosAllExist(const std::vector<DataShare::DataShareValuesBucket> & values)362 bool MediaLibraryAppUriPermissionOperations::IsPhotosAllExist(
363     const std::vector<DataShare::DataShareValuesBucket> &values)
364 {
365     std::vector<std::string> fileIds;
366     bool isValid = false;
367     for (auto it = values.begin(); it != values.end(); it++) {
368         int fileId = it->Get(AppUriPermissionColumn::FILE_ID, isValid);
369         if (!isValid) {
370             MEDIA_ERR_LOG("get fileId error");
371             return false;
372         }
373         fileIds.push_back(std::to_string(static_cast<int32_t>(fileId)));
374     }
375     // query whether photos exists.
376     RdbPredicates rdbPredicates(PhotoColumn::PHOTOS_TABLE);
377     rdbPredicates.And()->In(MediaColumn::MEDIA_ID, fileIds);
378     vector<string> photoColumns;
379     photoColumns.push_back(PhotoColumn::MEDIA_ID);
380     shared_ptr<NativeRdb::ResultSet> photoRestultSet =
381         MediaLibraryRdbStore::QueryWithFilter(rdbPredicates, photoColumns);
382     if (photoRestultSet == nullptr) {
383         MEDIA_ERR_LOG("query photoRestultSet is null");
384         return false;
385     }
386     int32_t photoNumRows = 0;
387     photoRestultSet->GetRowCount(photoNumRows);
388     size_t photoNum = static_cast<size_t>(photoNumRows);
389     if (photoNum != fileIds.size()) {
390         MEDIA_ERR_LOG("some photo not exist");
391         return false;
392     }
393     return true;
394 }
395 
396 } // namespace Media
397 } // namespace OHOS