diff --git a/interfaces/innerkits/privacy/test/unittest/src/privacy_kit_test.cpp b/interfaces/innerkits/privacy/test/unittest/src/privacy_kit_test.cpp index 4c5b9de0288e90d9b8a081c8670d51cb127fce48..2c4b8b0bb65f018013a10e486db4bf8e00cffe4b 100644 --- a/interfaces/innerkits/privacy/test/unittest/src/privacy_kit_test.cpp +++ b/interfaces/innerkits/privacy/test/unittest/src/privacy_kit_test.cpp @@ -132,6 +132,52 @@ void PrivacyKitTest::CheckPermissionUsedResult(const PermissionUsedRequest& requ ASSERT_EQ(totalSuccessCount, successCount); ASSERT_EQ(totalFailCount, failCount); } +namespace OHOS { +namespace Security { +namespace AccessToken { +namespace { + void SetTokenID(std::vector& g_InfoParms_List, + std::vector& g_TokenId_List, int32_t number) + { + SetSelfTokenID(g_selfTokenId); + for (int32_t i = 0; i < number; i++) { + HapInfoParams g_InfoParmsTmp = { + .userID = i, + .bundleName = "ohos.privacy_test.bundle" + std::to_string(i), + .instIndex = i, + .appIDDesc = "privacy_test.bundle" + std::to_string(i) + }; + g_InfoParms_List.push_back(g_InfoParmsTmp); + HapPolicyParams g_PolicyPramsTmp = { + .apl = APL_NORMAL, + .domain = "test.domain." + std::to_string(i) + }; + AccessTokenKit::AllocHapToken(g_InfoParmsTmp, g_PolicyPramsTmp); + AccessTokenID g_TokenId_Tmp = AccessTokenKit::GetHapTokenID(g_InfoParmsTmp.userID, + g_InfoParmsTmp.bundleName, + g_InfoParmsTmp.instIndex); + g_TokenId_List.push_back(g_TokenId_Tmp); + } + AccessTokenID tokenId = AccessTokenKit::GetHapTokenID(100, "com.ohos.permissionmanager", 0); + SetSelfTokenID(tokenId); + } + + void DeleteTokenID(std::vector& g_InfoParms_List) + { + SetSelfTokenID(g_selfTokenId); + for (size_t i = 0; i < g_InfoParms_List.size(); i++) { + AccessTokenID g_TokenId_Tmp = AccessTokenKit::GetHapTokenID(g_InfoParms_List[i].userID, + g_InfoParms_List[i].bundleName, + g_InfoParms_List[i].instIndex); + AccessTokenKit::DeleteToken(g_TokenId_Tmp); + } + AccessTokenID tokenId = AccessTokenKit::GetHapTokenID(100, "com.ohos.permissionmanager", 0); + SetSelfTokenID(tokenId); + } +} +} // namespace AccessToken +} // namespace Security +} // namespace OHOS /** * @tc.name: AddPermissionUsedRecord001 @@ -536,4 +582,91 @@ HWTEST_F(PrivacyKitTest, GetPermissionUsedRecordsAsync002, TestSize.Level1) BuildQueryRequest(g_TokenId_A, GetLocalDeviceUdid(), "", permissionList, request); OHOS::sptr callback(new TestCallBack()); ASSERT_EQ(RET_NO_ERROR, PrivacyKit::GetPermissionUsedRecords(request, callback)); +} + +/** + * @tc.name: AddPermissionUsedRecord007 + * @tc.desc: AddPermissionUsedRecord merge more than 10 records + * @tc.type: FUNC + * @tc.require:Issue Number + */ +HWTEST_F(PrivacyKitTest, AddPermissionUsedRecord007, TestSize.Level1) +{ + std::vector addPermissionList = { + "ohos.permission.ANSWER_CALL", + "ohos.permission.READ_CALENDAR", + "ohos.permission.WRITE_CALENDAR", + "ohos.permission.SEND_MESSAGES", + "ohos.permission.WRITE_CALL_LOG", + "ohos.permission.READ_CALL_LOG", + "ohos.permission.READ_CELL_MESSAGES", + "ohos.permission.MICROPHONE", + "ohos.permission.RECEIVE_WAP_MESSAGES", + "ohos.permission.RECEIVE_SMS", + "ohos.permission.RECEIVE_MMS" + }; + for (int32_t i = 0; i < 10; i++) { + ASSERT_EQ(RET_NO_ERROR, PrivacyKit::AddPermissionUsedRecord(g_TokenId_A, addPermissionList[i], 1, 0)); + } + PermissionUsedRequest request; + PermissionUsedResult result; + std::vector permissionList; + BuildQueryRequest(g_TokenId_A, GetLocalDeviceUdid(), g_InfoParmsA.bundleName, permissionList, request); + request.flag = FLAG_PERMISSION_USAGE_DETAIL; + ASSERT_EQ(RET_NO_ERROR, PrivacyKit::GetPermissionUsedRecords(request, result)); + ASSERT_EQ(1, result.bundleRecords.size()); + CheckPermissionUsedResult(request, result, 10, 10, 0); + + ASSERT_EQ(RET_NO_ERROR, PrivacyKit::AddPermissionUsedRecord(g_TokenId_A, addPermissionList[10], 1, 0)); + ASSERT_EQ(RET_NO_ERROR, PrivacyKit::GetPermissionUsedRecords(request, result)); + ASSERT_EQ(1, result.bundleRecords.size()); + ASSERT_EQ(10, result.bundleRecords[0].permissionRecords.size()); + ASSERT_EQ(1, result.bundleRecords[0].permissionRecords[0].accessRecords.size()); + CheckPermissionUsedResult(request, result, 10, 10, 0); +} + +/** + * @tc.name: AddPermissionUsedRecord008 + * @tc.desc: AddPermissionUsedRecord user_grant permission. + * @tc.type: FUNC + * @tc.require:Issue Number + */ +HWTEST_F(PrivacyKitTest, AddPermissionUsedRecord008, TestSize.Level1) +{ + std::vector g_InfoParms_List; + std::vector g_TokenId_List; + SetTokenID(g_InfoParms_List, g_TokenId_List, 100); + std::vector addPermissionList = { + "ohos.permission.ANSWER_CALL", + "ohos.permission.READ_CALENDAR", + }; + for (int32_t i = 0; i < 200; i++) { + ASSERT_EQ(RET_NO_ERROR, PrivacyKit::AddPermissionUsedRecord(g_TokenId_List[i % 100], + addPermissionList[i % 2], 1, 0)); + + PermissionUsedRequest request; + PermissionUsedResult result; + std::vector permissionList; + BuildQueryRequest(g_TokenId_List[i % 100], GetLocalDeviceUdid(), + g_InfoParms_List[i % 100].bundleName, permissionList, request); + request.flag = FLAG_PERMISSION_USAGE_DETAIL; + ASSERT_EQ(RET_NO_ERROR, PrivacyKit::GetPermissionUsedRecords(request, result)); + } + sleep(70); + for (int32_t i = 0; i < 100; i++) { + PermissionUsedRequest request; + PermissionUsedResult result; + std::vector permissionList; + BuildQueryRequest(g_TokenId_List[i], GetLocalDeviceUdid(), + g_InfoParms_List[i].bundleName, permissionList, request); + request.flag = FLAG_PERMISSION_USAGE_DETAIL; + + ASSERT_EQ(RET_NO_ERROR, PrivacyKit::GetPermissionUsedRecords(request, result)); + ASSERT_EQ(1, result.bundleRecords.size()); + ASSERT_EQ(1, result.bundleRecords[0].permissionRecords.size()); + ASSERT_EQ(1, result.bundleRecords[0].permissionRecords[0].accessRecords.size()); + CheckPermissionUsedResult(request, result, 1, 2, 0); + sleep(1); + } + DeleteTokenID(g_InfoParms_List); } \ No newline at end of file diff --git a/services/privacymanager/BUILD.gn b/services/privacymanager/BUILD.gn index 5d00d6c6548f26200e0d992ec0c9d823725763ab..b1fbb726bad5161f491c9065f45e4c9673cc0a07 100644 --- a/services/privacymanager/BUILD.gn +++ b/services/privacymanager/BUILD.gn @@ -49,6 +49,7 @@ if (is_standard_system) { "src/record/permission_record.cpp", "src/record/permission_record_manager.cpp", "src/record/permission_record_repository.cpp", + "src/record/permission_used_record_cache.cpp", "src/record/permission_visitor.cpp", "src/record/permission_visitor_repository.cpp", "src/service/privacy_manager_service.cpp", diff --git a/services/privacymanager/include/common/constant.h b/services/privacymanager/include/common/constant.h index 6e3602ac6997e3bb12487543d7179de4991567ad..1893d77e377034f4fb0b8d13f851a9b7cfccb6c1 100644 --- a/services/privacymanager/include/common/constant.h +++ b/services/privacymanager/include/common/constant.h @@ -62,6 +62,7 @@ public: const static int32_t RECORD_DELETE_TIME = 30 * 86400; const static int32_t PRECISE = 60; const static int32_t LATEST_RECORD_TIME = 7 * 86400; + const static std::string COUNT_CMD; const static std::map PERMISSION_OPCODE_MAP; public: diff --git a/services/privacymanager/include/database/permission_used_record_db.h b/services/privacymanager/include/database/permission_used_record_db.h index 059e0082aac7277038e93678e9b64272f580eb33..f362f958226d6dc2a74eb18ec6f72608638e6c20 100644 --- a/services/privacymanager/include/database/permission_used_record_db.h +++ b/services/privacymanager/include/database/permission_used_record_db.h @@ -48,7 +48,9 @@ public: const GenericValues& orConditions, std::vector& results); int32_t Modify(const DataType type, const GenericValues& modifyValues, const GenericValues& conditions); int32_t RefreshAll(const DataType type, const std::vector& values); - + int32_t Count(const DataType type, GenericValues& result); + int32_t DeleteExpireRecords(const DataType type, const GenericValues& andConditions); + int32_t DeleteExcessiveRecords(const DataType type, int32_t excessiveSize); void OnCreate() override; void OnUpdate() override; @@ -70,6 +72,10 @@ private: const std::vector& andColumns, const std::vector& orColumns) const; std::string CreateUpdatePrepareSqlCmd(const DataType type, const std::vector& modifyColumns, const std::vector& conditionColumns) const; + std::string CreateCountPrepareSqlCmd(const DataType type) const; + std::string CreateDeleteExpireRecordsPrepareSqlCmd(const DataType type, + const std::vector& andColumns) const; + std::string CreateDeleteExcessiveRecordsPrepareSqlCmd(const DataType type, int32_t excessiveSize) const; private: inline static const std::string PERMISSION_VISITOR_TABLE = "permission_visitor_table"; diff --git a/services/privacymanager/include/record/permission_record_node.h b/services/privacymanager/include/record/permission_record_node.h new file mode 100644 index 0000000000000000000000000000000000000000..23276f72ecde74d8cfc6bde4821f411fbe2a3f33 --- /dev/null +++ b/services/privacymanager/include/record/permission_record_node.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PERMISSION_UESD_RECORD_NODE_H +#define PERMISSION_UESD_RECORD_NODE_H + +#include +#include "permission_record.h" +#include "rwlock.h" +namespace OHOS { +namespace Security { +namespace AccessToken { +struct PermissionUsedRecordNode { + std::weak_ptr pre; + std::shared_ptr next; + PermissionRecord record; + OHOS::Utils::RWLock nodeLock; + + PermissionUsedRecordNode() = default; +}; +} // namespace AccessToken +} // namespace Security +} // namespace OHOS +#endif // PERMISSION_UESD_RECORD_NODE_H diff --git a/services/privacymanager/include/record/permission_record_repository.h b/services/privacymanager/include/record/permission_record_repository.h index ffda94fb527f1ed4164f39216d641c3ff16bbaa9..2d05a54c5f7a1e6ae374d6556998766cb2e2a34f 100644 --- a/services/privacymanager/include/record/permission_record_repository.h +++ b/services/privacymanager/include/record/permission_record_repository.h @@ -33,6 +33,10 @@ public: bool FindRecordValues(const GenericValues& andConditionValues, const GenericValues& orConditionValues, std::vector& recordValues); bool RemoveRecordValues(const GenericValues& conditionValues); + + bool CountRecordValues(GenericValues& resultValues); + bool DeleteExpireRecordsValues(const GenericValues& andConditions); + bool DeleteExcessiveSizeRecordValues(int32_t excessiveSize); }; } // namespace AccessToken } // namespace Security diff --git a/services/privacymanager/include/record/permission_used_record_cache.h b/services/privacymanager/include/record/permission_used_record_cache.h new file mode 100644 index 0000000000000000000000000000000000000000..da5c56c9e6cb05a4939371f84fc10dd2cf279707 --- /dev/null +++ b/services/privacymanager/include/record/permission_used_record_cache.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PERMISSION_USED_RECORD_CACHE_H +#define PERMISSION_USED_RECORD_CACHE_H + +#include +#include +#include +#include +#include +#include "nocopyable.h" +#include "permission_record.h" +#include "permission_record_node.h" +#include "rwlock.h" +#include "thread_pool.h" +namespace OHOS { +namespace Security { +namespace AccessToken { +class PermissionUsedRecordCache { +public: + static PermissionUsedRecordCache& GetInstance(); + int32_t AddRecord(PermissionRecord& record); + size_t MergeTenRecords(const PermissionRecord& record, + const std::vector>& existedRecords); + void MergeSameRecord(const PermissionRecord& record, std::shared_ptr existedRecord); + bool MergeRecordsFromDB(PermissionRecord& record); + void ExecuteReadRecordBufferTask(); + int32_t PersistPendingRecords(); + void GetPersistPendingRecordsAndReset(); + int32_t RemoveRecords(int32_t visitorId); + void GetRecords(int32_t visitor, const std::vector& permissionList, + const GenericValues& andConditionValues, std::vector& findRecordsValues); + bool RecordCompare(int32_t visitor, const std::set& opCodeList, + const GenericValues& andConditionValues, const PermissionRecord& record); + void AddRecordNode(const PermissionRecord& record, + std::shared_ptr curRecordNode); + void DeleteRecordNode(std::shared_ptr deleteRecordNode); + +private: + int32_t readableSize; + std::shared_ptr recordBufferHead = std::make_shared(); + std::shared_ptr curRecordBufferPos = recordBufferHead; + std::shared_ptr persistPendingBufferHead = std::make_shared(); + std::map>> visitorRecord; + int64_t beginTimestamp = 0L; + int64_t endTimestamp = 0L; + int64_t nextPersistTimestamp = 0L; + int64_t lastEndTimestamp = 0L; + + const int32_t MAX_PERSIST_SIZE = 100; + const size_t MAX_RECORD_SIZE = 10; + OHOS::Utils::RWLock cacheLock; + OHOS::ThreadPool readRecordBufferTaskWorker; +}; +} // namespace AccessToken +} // namespace Security +} // namespace OHOS +#endif // PERMISSION_USED_RECORD_CACHE_H diff --git a/services/privacymanager/src/common/constant.cpp b/services/privacymanager/src/common/constant.cpp index 34fda467f258b1d64a42f07a37f98c7203b51854..1a9b748559ad8801a76bbe294d7c0d37c169debc 100644 --- a/services/privacymanager/src/common/constant.cpp +++ b/services/privacymanager/src/common/constant.cpp @@ -18,6 +18,7 @@ namespace OHOS { namespace Security { namespace AccessToken { +const std::string Constant::COUNT_CMD = "COUNT"; const std::map Constant::PERMISSION_OPCODE_MAP = { std::map::value_type("ohos.permission.ANSWER_CALL", Constant::OP_ANSWER_CALL), std::map::value_type("ohos.permission.READ_CALENDAR", Constant::OP_READ_CALENDAR), diff --git a/services/privacymanager/src/database/permission_used_record_db.cpp b/services/privacymanager/src/database/permission_used_record_db.cpp index 60b82be4b1912f29cf22533fb71733ab3e29d17c..72316b057f6a4b5638ac5feb0a0a67691f99fb48 100644 --- a/services/privacymanager/src/database/permission_used_record_db.cpp +++ b/services/privacymanager/src/database/permission_used_record_db.cpp @@ -16,6 +16,7 @@ #include "permission_used_record_db.h" #include "accesstoken_log.h" +#include "constant.h" #include "field_const.h" namespace OHOS { @@ -224,6 +225,49 @@ int32_t PermissionUsedRecordDb::RefreshAll(const DataType type, const std::vecto CommitTransaction(); return SUCCESS; } +int32_t PermissionUsedRecordDb::Count(const DataType type, GenericValues& result) +{ + OHOS::Utils::UniqueWriteGuard lock(this->rwLock_); + std::string countSql = CreateCountPrepareSqlCmd(type); + auto countStatement = Prepare(countSql); + if (countStatement.Step() == Statement::State::ROW) { + int32_t column = 0; + result.Put(Constant::COUNT_CMD, countStatement.GetValue(column, true)); + } + return SUCCESS; +} + +int32_t PermissionUsedRecordDb::DeleteExpireRecords(const DataType type, + const GenericValues& andConditions) +{ + OHOS::Utils::UniqueWriteGuard lock(this->rwLock_); + std::vector andColumns = andConditions.GetAllKeys(); + if (!andColumns.empty()) { + std::string deleteExpireSql = CreateDeleteExpireRecordsPrepareSqlCmd(type, andColumns); + auto deleteExpireStatement = Prepare(deleteExpireSql); + for (const auto& columnName : andColumns) { + deleteExpireStatement.Bind(columnName, andConditions.Get(columnName)); + } + int32_t ret = deleteExpireStatement.Step(); + if (ret != Statement::State::DONE) { + return FAILURE; + } + } + return SUCCESS; +} + +int32_t PermissionUsedRecordDb::DeleteExcessiveRecords(const DataType type, int32_t excessiveSize) +{ + if (excessiveSize > 0) { + OHOS::Utils::UniqueWriteGuard lock(this->rwLock_); + std::string deleteExcessiveSql = CreateDeleteExcessiveRecordsPrepareSqlCmd(type, excessiveSize); + auto deleteExcessiveStatement = Prepare(deleteExcessiveSql); + if (deleteExcessiveStatement.Step() != Statement::State::DONE) { + return FAILURE; + } + } + return SUCCESS; +} std::string PermissionUsedRecordDb::CreateInsertPrepareSqlCmd(const DataType type) const { @@ -335,6 +379,58 @@ std::string PermissionUsedRecordDb::CreateSelectByConditionPrepareSqlCmd(const D return sql; } +std::string PermissionUsedRecordDb::CreateCountPrepareSqlCmd(const DataType type) const +{ + auto it = dataTypeToSqlTable_.find(type); + if (it == dataTypeToSqlTable_.end()) { + return std::string(); + } + std::string sql = "select count(*) from " + it->second.tableName_; + return sql; +} + +std::string PermissionUsedRecordDb::CreateDeleteExpireRecordsPrepareSqlCmd(const DataType type, + const std::vector& andColumns) const +{ + auto it = dataTypeToSqlTable_.find(type); + if (it == dataTypeToSqlTable_.end()) { + return std::string(); + } + std::string sql = "delete from " + it->second.tableName_ + " where "; + sql.append(FIELD_TIMESTAMP + " in (select "); + sql.append(FIELD_TIMESTAMP + " from " + it->second.tableName_ + " where 1 = 1"); + for (const auto& andColName : andColumns) { + if (andColName == FIELD_TIMESTAMP_BEGIN) { + sql.append(" and "); + sql.append(FIELD_TIMESTAMP + " >=:" + andColName); + } else if (andColName == FIELD_TIMESTAMP_END) { + sql.append(" and "); + sql.append(FIELD_TIMESTAMP + " <=:" + andColName); + } else { + sql.append(" and "); + sql.append(andColName + "=:" + andColName); + } + } + sql.append(" )"); + return sql; +} + +std::string PermissionUsedRecordDb::CreateDeleteExcessiveRecordsPrepareSqlCmd(const DataType type, + int32_t excessiveSize) const +{ + auto it = dataTypeToSqlTable_.find(type); + if (it == dataTypeToSqlTable_.end()) { + return std::string(); + } + std::string sql = "delete from " + it->second.tableName_ + " where "; + sql.append(FIELD_TIMESTAMP + " in (select "); + sql.append(FIELD_TIMESTAMP + " from " + it->second.tableName_ + " order by "); + sql.append(FIELD_TIMESTAMP + " limit "); + sql.append(std::to_string(excessiveSize) + " )"); + return sql; +} + + int32_t PermissionUsedRecordDb::CreatePermissionVisitorTable() const { auto it = dataTypeToSqlTable_.find(DataType::PERMISSION_VISITOR); diff --git a/services/privacymanager/src/record/permission_record_manager.cpp b/services/privacymanager/src/record/permission_record_manager.cpp index 024790c7d1f230b01a13fe4c1ca016359d66253c..b63bf45075c42ed9cfe22a0ec4663a78bddef4a9 100644 --- a/services/privacymanager/src/record/permission_record_manager.cpp +++ b/services/privacymanager/src/record/permission_record_manager.cpp @@ -22,6 +22,7 @@ #include "data_translator.h" #include "field_const.h" #include "permission_record_repository.h" +#include "permission_used_record_cache.h" #include "permission_visitor_repository.h" #include "time_util.h" #include "to_string.h" @@ -105,50 +106,10 @@ bool PermissionRecordManager::AddRecord( if (!GetPermissionsRecord(visitorId, permissionName, successCount, failCount, record)) { return false; } - - GenericValues nullValues; - GenericValues recordValues; - std::vector insertValues; - std::vector findValues; - PermissionRecord::TranslationIntoGenericValues(record, recordValues); - - int64_t insertTimestamp = record.timestamp; - int64_t insertAccessDuration = record.accessDuration; - int32_t insertAccessCount = record.accessCount; - int32_t insertRejectCount = record.rejectCount; - recordValues.Remove(FIELD_TIMESTAMP); - recordValues.Remove(FIELD_ACCESS_DURATION); - recordValues.Remove(FIELD_ACCESS_COUNT); - recordValues.Remove(FIELD_REJECT_COUNT); - if (!PermissionRecordRepository::GetInstance().FindRecordValues(recordValues, nullValues, findValues)) { - return false; - } - - recordValues.Put(FIELD_TIMESTAMP, insertTimestamp); - recordValues.Put(FIELD_ACCESS_DURATION, insertAccessDuration); - recordValues.Put(FIELD_ACCESS_COUNT, insertAccessCount); - recordValues.Put(FIELD_REJECT_COUNT, insertRejectCount); - for (const auto& rec : findValues) { - if (insertTimestamp - rec.GetInt64(FIELD_TIMESTAMP) < Constant::PRECISE) { - insertAccessDuration += rec.GetInt64(FIELD_ACCESS_DURATION); - insertAccessCount += rec.GetInt(FIELD_ACCESS_COUNT); - insertRejectCount += rec.GetInt(FIELD_REJECT_COUNT); - recordValues.Remove(FIELD_ACCESS_DURATION); - recordValues.Remove(FIELD_ACCESS_COUNT); - recordValues.Remove(FIELD_REJECT_COUNT); - - recordValues.Put(FIELD_ACCESS_DURATION, insertAccessDuration); - recordValues.Put(FIELD_ACCESS_COUNT, insertAccessCount); - recordValues.Put(FIELD_REJECT_COUNT, insertRejectCount); - - if (!PermissionRecordRepository::GetInstance().RemoveRecordValues(rec)) { - return false; - } - break; - } + if (PermissionUsedRecordCache::GetInstance().AddRecord(record) == Constant::SUCCESS) { + return true; } - insertValues.emplace_back(recordValues); - return PermissionRecordRepository::GetInstance().AddRecordValues(insertValues); + return false; } bool PermissionRecordManager::GetPermissionsRecord(int32_t visitorId, const std::string& permissionName, @@ -184,15 +145,23 @@ int32_t PermissionRecordManager::AddPermissionUsedRecord(AccessTokenID tokenID, ACCESSTOKEN_LOG_DEBUG(LABEL, "Invalid token type"); return Constant::SUCCESS; } + int32_t opCode = -1; + if (!Constant::TransferPermissionToOpcode(permissionName, opCode)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Cannot Transfer permission to opcode"); + return Constant::FAILURE; + } Utils::UniqueWriteGuard lk(this->rwLock_); int32_t visitorId; if (!AddVisitor(tokenID, visitorId)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Add visitor failed"); return Constant::FAILURE; } if (!AddRecord(visitorId, permissionName, successCount, failCount)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Add record failed"); return Constant::FAILURE; } + ACCESSTOKEN_LOG_DEBUG(LABEL, "Add record successful"); return Constant::SUCCESS; } @@ -220,7 +189,8 @@ void PermissionRecordManager::RemovePermissionUsedRecords(AccessTokenID tokenID, for (const auto& visitor : findVisitorValues) { GenericValues record; record.Put(FIELD_VISITOR_ID, visitor.GetInt(FIELD_ID)); - PermissionRecordRepository::GetInstance().RemoveRecordValues(record); + PermissionRecordRepository::GetInstance().RemoveRecordValues(record); // remove from database + PermissionUsedRecordCache::GetInstance().RemoveRecords(visitor.GetInt(FIELD_ID)); // remove from cache } PermissionVisitorRepository::GetInstance().RemoveVisitorValues(visitorValues); } @@ -279,7 +249,8 @@ bool PermissionRecordManager::GetRecordsFromDB(const PermissionUsedRequest& requ std::vector findRecordsValues; BundleUsedRecord bundleRecord; if (!PermissionRecordRepository::GetInstance().FindRecordValues( - andConditionValues, orConditionValues, findRecordsValues)) { + andConditionValues, orConditionValues, findRecordsValues)) { // find records from database + ACCESSTOKEN_LOG_ERROR(LABEL, "find records from database failed"); return false; } andConditionValues.Remove(FIELD_VISITOR_ID); @@ -287,9 +258,13 @@ bool PermissionRecordManager::GetRecordsFromDB(const PermissionUsedRequest& requ bundleRecord.isRemote = visitor.GetInt(FIELD_IS_REMOTE_DEVICE); bundleRecord.deviceId = visitor.GetString(FIELD_DEVICE_ID); bundleRecord.bundleName = visitor.GetString(FIELD_BUNDLE_NAME); + + PermissionUsedRecordCache::GetInstance().GetRecords(visitor.GetInt(FIELD_ID), request.permissionList, + andConditionValues, findRecordsValues); // find records from cache if (!findRecordsValues.empty()) { if (!GetRecords(request.flag, findRecordsValues, bundleRecord, result)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "findRecordsValues GetRecords failed"); return false; } } @@ -374,24 +349,22 @@ void PermissionRecordManager::ExecuteDeletePermissionRecordTask() int32_t PermissionRecordManager::DeletePermissionRecord(int32_t days) { Utils::UniqueWriteGuard lk(this->rwLock_); - GenericValues nullValues; - std::vector deleteRecordValues; - if (!PermissionRecordRepository::GetInstance().FindRecordValues(nullValues, nullValues, deleteRecordValues)) { + GenericValues countValue; + if (!PermissionRecordRepository::GetInstance().CountRecordValues(countValue)) { return Constant::FAILURE; } - - size_t deleteSize = 0; - if (deleteRecordValues.size() > Constant::MAX_TOTAL_RECORD) { - deleteSize = deleteRecordValues.size() - Constant::MAX_TOTAL_RECORD; - for (size_t i = 0; i < deleteSize; ++i) { - PermissionRecordRepository::GetInstance().RemoveRecordValues(deleteRecordValues[i]); + int64_t total = countValue.GetInt64(Constant::COUNT_CMD); + if (total > Constant::MAX_TOTAL_RECORD) { + int32_t excessiveSize = total - Constant::MAX_TOTAL_RECORD; + if (!PermissionRecordRepository::GetInstance().DeleteExcessiveSizeRecordValues(excessiveSize)) { + return Constant::FAILURE; } } + GenericValues andConditionValues; int64_t deleteTimestamp = TimeUtil::GetCurrentTimestamp() - days; - for (size_t i = deleteSize; i < deleteRecordValues.size(); ++i) { - if (deleteRecordValues[i].GetInt64(FIELD_TIMESTAMP) < deleteTimestamp) { - PermissionRecordRepository::GetInstance().RemoveRecordValues(deleteRecordValues[i]); - } + andConditionValues.Put(FIELD_TIMESTAMP_END, deleteTimestamp); + if (!PermissionRecordRepository::GetInstance().DeleteExpireRecordsValues(andConditionValues)) { + return Constant::FAILURE; } return Constant::SUCCESS; } diff --git a/services/privacymanager/src/record/permission_record_repository.cpp b/services/privacymanager/src/record/permission_record_repository.cpp index 7f053023bd23ee1c29fd695c4a052911a13da894..fbe65a0d209a16f8a071359d32296da349e9d56f 100644 --- a/services/privacymanager/src/record/permission_record_repository.cpp +++ b/services/privacymanager/src/record/permission_record_repository.cpp @@ -71,6 +71,36 @@ bool PermissionRecordRepository::RemoveRecordValues(const GenericValues& conditi } return true; } + +bool PermissionRecordRepository::CountRecordValues(GenericValues& resultValues) +{ + if (PermissionUsedRecordDb::GetInstance().Count(PermissionUsedRecordDb::PERMISSION_RECORD, resultValues) + != PermissionUsedRecordDb::SUCCESS) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Cannot count PERMISSION_RECORD"); + return false; + } + return true; +} + +bool PermissionRecordRepository::DeleteExpireRecordsValues(const GenericValues& andConditions) +{ + if (PermissionUsedRecordDb::GetInstance().DeleteExpireRecords(PermissionUsedRecordDb::PERMISSION_RECORD, + andConditions) != PermissionUsedRecordDb::SUCCESS) { + ACCESSTOKEN_LOG_ERROR(LABEL, "PERMISSION_RECORD delete fail"); + return false; + } + return true; +} + +bool PermissionRecordRepository::DeleteExcessiveSizeRecordValues(int32_t excessiveSize) +{ + if (PermissionUsedRecordDb::GetInstance().DeleteExcessiveRecords(PermissionUsedRecordDb::PERMISSION_RECORD, + excessiveSize) != PermissionUsedRecordDb::SUCCESS) { + ACCESSTOKEN_LOG_ERROR(LABEL, "PERMISSION_RECORD delete fail"); + return false; + } + return true; +} } // namespace AccessToken } // namespace Security } // namespace OHOS \ No newline at end of file diff --git a/services/privacymanager/src/record/permission_used_record_cache.cpp b/services/privacymanager/src/record/permission_used_record_cache.cpp new file mode 100644 index 0000000000000000000000000000000000000000..58017be5b6b7553a975289ee2a84604832699124 --- /dev/null +++ b/services/privacymanager/src/record/permission_used_record_cache.cpp @@ -0,0 +1,344 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "permission_used_record_cache.h" +#include "accesstoken_log.h" +#include "constant.h" +#include "field_const.h" +#include "generic_values.h" +#include "permission_record.h" +#include "permission_record_manager.h" +#include "permission_record_node.h" +#include "permission_record_repository.h" +#include "permission_used_record_db.h" +#include "time_util.h" + +namespace OHOS { +namespace Security { +namespace AccessToken { +namespace { +static constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { + LOG_CORE, SECURITY_DOMAIN_PRIVACY, "PermissionUsedRecordCache" +}; +} + +PermissionUsedRecordCache& PermissionUsedRecordCache::GetInstance() +{ + static PermissionUsedRecordCache instance; + return instance; +} + +int32_t PermissionUsedRecordCache::AddRecord(PermissionRecord& record) +{ + if (beginTimestamp == 0 && endTimestamp == 0) { + beginTimestamp = record.timestamp; + nextPersistTimestamp = TimeUtil::GetCurrentTimestamp() + Constant::PRECISE; + } + endTimestamp = record.timestamp; + if (TimeUtil::GetCurrentTimestamp() == nextPersistTimestamp || readableSize >= MAX_PERSIST_SIZE) { + // start ExecuteReadRecordBufferTask + ExecuteReadRecordBufferTask(); + } + // VisitorId can be found in the cache, and there is no need to merge records in the database + Utils::UniqueWriteGuard infoGuard(this->cacheLock); + auto findVisitor = visitorRecord.find(record.visitorId); + if (findVisitor != visitorRecord.end()) { // visitorId is existed + std::vector> existedRecords = findVisitor->second; + if (existedRecords.size() >= MAX_RECORD_SIZE) { + size_t earliest = MergeTenRecords(record, existedRecords); + findVisitor->second.erase(findVisitor->second.begin() + earliest); + findVisitor->second.emplace_back(curRecordBufferPos); + ACCESSTOKEN_LOG_DEBUG(LABEL, "More than 10 records, covering the earliest records"); + return Constant::SUCCESS; + } + for (auto existedRecord : existedRecords) { + if (record.opCode == existedRecord->record.opCode && + (record.timestamp - existedRecord->record.timestamp) < Constant::PRECISE) { + MergeSameRecord(record, existedRecord); + ACCESSTOKEN_LOG_DEBUG(LABEL, "Merge the same permission records within 1 minute in cache"); + return Constant::SUCCESS; + } + } + // No merge and no more than 10 records + AddRecordNode(record, curRecordBufferPos); + curRecordBufferPos = curRecordBufferPos->next; + findVisitor->second.emplace_back(curRecordBufferPos); + readableSize++; + ACCESSTOKEN_LOG_DEBUG(LABEL, "No merge and no more than 10 records"); + return Constant::SUCCESS; + } + // The last persistence did not satifies the time interval. Merge records from the database + if ((record.timestamp - lastEndTimestamp) < + Constant::PRECISE && MergeRecordsFromDB(record)) { + ACCESSTOKEN_LOG_DEBUG(LABEL, "Merge the same permission records within 1 minute in database"); + return Constant::SUCCESS; + } + // Add Record to the cache for the first time + AddRecordNode(record, curRecordBufferPos); + curRecordBufferPos = curRecordBufferPos->next; // new record node + std::vector> tmpVisitorRecords; + tmpVisitorRecords.emplace_back(curRecordBufferPos); + visitorRecord.insert(std::make_pair(record.visitorId, tmpVisitorRecords)); + readableSize++; + ACCESSTOKEN_LOG_DEBUG(LABEL, "Add Record to the cache for the first time"); + return Constant::SUCCESS; +} + +size_t PermissionUsedRecordCache::MergeTenRecords(const PermissionRecord& record, + const std::vector>& existedRecords) +{ + size_t earliest = 0; + for (size_t i = 0; i < existedRecords.size(); i++) { + if (existedRecords[earliest]->record.timestamp > existedRecords[i]->record.timestamp) { + earliest = i; + } + } + std::shared_ptr overwriteRecord = existedRecords[earliest]; + // The oldest records will be removed from the two-way linked list + if (curRecordBufferPos == overwriteRecord) { + curRecordBufferPos = curRecordBufferPos->pre.lock(); + } + DeleteRecordNode(overwriteRecord); + AddRecordNode(record, curRecordBufferPos); + curRecordBufferPos = curRecordBufferPos->next; + return earliest; +} + +void PermissionUsedRecordCache::MergeSameRecord(const PermissionRecord& record, + std::shared_ptr existedRecord) +{ + existedRecord->record.timestamp = record.timestamp; + existedRecord->record.accessDuration += record.accessDuration; + existedRecord->record.accessCount += record.accessCount; + existedRecord->record.rejectCount += record.rejectCount; +} + +bool PermissionUsedRecordCache::MergeRecordsFromDB(PermissionRecord& record) +{ + int64_t timestampBegin = record.timestamp - Constant::PRECISE; + GenericValues andConditionValues; + GenericValues nullConditionValues; + std::vector findValues; + andConditionValues.Put(FIELD_VISITOR_ID, record.visitorId); + andConditionValues.Put(FIELD_OP_CODE, record.opCode); + andConditionValues.Put(FIELD_TIMESTAMP_BEGIN, timestampBegin); + if (!PermissionRecordRepository::GetInstance().FindRecordValues( + andConditionValues, nullConditionValues, findValues)) { + return false; // Records are not merged in the database + } + if (!findValues.empty()) { // Need to merge with records in the database + for (const auto& ret : findValues) { + if (record.timestamp - ret.GetInt64(FIELD_TIMESTAMP) < Constant::PRECISE) { + record.accessDuration += ret.GetInt64(FIELD_ACCESS_DURATION); + record.accessCount += ret.GetInt(FIELD_ACCESS_COUNT); + record.rejectCount += ret.GetInt(FIELD_REJECT_COUNT); + if (!PermissionRecordRepository::GetInstance().RemoveRecordValues(ret)) { + // Delete record in the database failed, rollback + record.accessDuration -= ret.GetInt64(FIELD_ACCESS_DURATION); + record.accessCount -= ret.GetInt(FIELD_ACCESS_COUNT); + record.rejectCount -= ret.GetInt(FIELD_REJECT_COUNT); + return false; // Records are not merged in the database + } + AddRecordNode(record, curRecordBufferPos); + curRecordBufferPos = curRecordBufferPos->next; // new record node + std::vector> tmpVisitorRecords; + tmpVisitorRecords.emplace_back(curRecordBufferPos); + visitorRecord.insert(std::make_pair(record.visitorId, tmpVisitorRecords)); + readableSize++; + return true; + } + } + } + return false; // Records are not merged in the database +} + +void PermissionUsedRecordCache::ExecuteReadRecordBufferTask() +{ + if (readRecordBufferTaskWorker.GetCurTaskNum() > 1) { + ACCESSTOKEN_LOG_INFO(LABEL, "Already has read record buffer task!"); + return; + } + auto readRecordBufferTask = [this]() { + ACCESSTOKEN_LOG_INFO(LABEL, "ReadRecordBuffer task called"); + PersistPendingRecords(); + }; + readRecordBufferTaskWorker.AddTask(readRecordBufferTask); +} + +int32_t PermissionUsedRecordCache::PersistPendingRecords() +{ + GetPersistPendingRecordsAndReset(); + Utils::UniqueWriteGuard infoGuard(this->cacheLock); + std::vector insertValues; + std::shared_ptr curPendingRecordNode = persistPendingBufferHead->next; + while (curPendingRecordNode != nullptr) { + std::shared_ptr next = curPendingRecordNode->next; + GenericValues tmpRecordValues; + PermissionRecord tmpRecord = curPendingRecordNode->record; + PermissionRecord::TranslationIntoGenericValues(tmpRecord, tmpRecordValues); + insertValues.emplace_back(tmpRecordValues); + DeleteRecordNode(curPendingRecordNode); + curPendingRecordNode = next; + } + persistPendingBufferHead = std::make_shared(); + if (!insertValues.empty() && !PermissionRecordRepository::GetInstance().AddRecordValues(insertValues)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Failed to persist pending records"); + return false; + } + ACCESSTOKEN_LOG_INFO(LABEL, "Persist pending records successful"); + return true; +} + +void PermissionUsedRecordCache::GetPersistPendingRecordsAndReset() +{ + Utils::UniqueWriteGuard infoGuard(this->cacheLock); + persistPendingBufferHead = recordBufferHead; + recordBufferHead = std::make_shared(); + curRecordBufferPos = recordBufferHead; + std::map>> tmpVisitorRecord; + visitorRecord.swap(tmpVisitorRecord); + // The current persistence time do not satisfied the time interval + lastEndTimestamp = endTimestamp; + readableSize = 0; + beginTimestamp = 0; + endTimestamp = 0; + nextPersistTimestamp = 0; +} + +int32_t PermissionUsedRecordCache::RemoveRecords(int32_t visitorId) +{ + if (TimeUtil::GetCurrentTimestamp() == nextPersistTimestamp || readableSize >= MAX_PERSIST_SIZE) { + // start ExecuteReadRecordBufferTask + ExecuteReadRecordBufferTask(); + } + Utils::UniqueWriteGuard infoGuard(this->cacheLock); + auto findVisitor = visitorRecord.find(visitorId); + if (findVisitor != visitorRecord.end()) { + std::vector> deleteVisitorRecord = findVisitor->second; + for (auto deleteRecordNode : deleteVisitorRecord) { + if (curRecordBufferPos == deleteRecordNode) { + curRecordBufferPos = deleteRecordNode->pre.lock(); + } + DeleteRecordNode(deleteRecordNode); + readableSize--; + } + visitorRecord.erase(findVisitor); + } + std::shared_ptr curPendingRecordNode = persistPendingBufferHead->next; + + while (curPendingRecordNode != nullptr) { + std::shared_ptr nextPendingRecordNode = curPendingRecordNode->next; + if (curPendingRecordNode->record.visitorId == visitorId) { + DeleteRecordNode(curPendingRecordNode); + } + curPendingRecordNode = nextPendingRecordNode; + } + return Constant::SUCCESS; +} + +void PermissionUsedRecordCache::GetRecords(int32_t visitor, const std::vector& permissionList, + const GenericValues& andConditionValues, std::vector& findRecordsValues) +{ + if (TimeUtil::GetCurrentTimestamp() == nextPersistTimestamp || readableSize >= MAX_PERSIST_SIZE) { + // start ExecuteReadRecordBufferTask + ExecuteReadRecordBufferTask(); + } + // Transfer permissionList to opcodeSet + std::set opCodeList; + for (const auto& permission : permissionList) { + int32_t opCode = Constant::OP_INVALID; + Constant::TransferPermissionToOpcode(permission, opCode); + opCodeList.insert(opCode); + } + Utils::UniqueWriteGuard infoGuard(this->cacheLock); + auto findVisitor = visitorRecord.find(visitor); + if (findVisitor != visitorRecord.end()) { + std::vector> findVisitorRecords = findVisitor->second; + for (auto findVisitorRecord : findVisitorRecords) { + PermissionRecord record = findVisitorRecord->record; + if (RecordCompare(visitor, opCodeList, andConditionValues, record)) { + GenericValues recordValues; + PermissionRecord::TranslationIntoGenericValues(record, recordValues); + findRecordsValues.emplace_back(recordValues); + } + } + } + std::shared_ptr curPendingRecordNode = persistPendingBufferHead->next; + while (curPendingRecordNode != nullptr) { + if (curPendingRecordNode->record.visitorId == visitor) { + PermissionRecord record = curPendingRecordNode->record; + if (RecordCompare(visitor, opCodeList, andConditionValues, record)) { + GenericValues recordValues; + PermissionRecord::TranslationIntoGenericValues(record, recordValues); + findRecordsValues.emplace_back(recordValues); + } + curPendingRecordNode = curPendingRecordNode->next; + } + } +} + +bool PermissionUsedRecordCache::RecordCompare(int32_t visitor, const std::set& opCodeList, + const GenericValues& andConditionValues, const PermissionRecord& record) +{ + // compare visitorId + if (visitor != record.visitorId) { + return false; + } + // compare opCode + if (!opCodeList.empty() && opCodeList.find(record.opCode) == opCodeList.end()) { + return false; + } + // compare timestamp + std::vector andColumns = andConditionValues.GetAllKeys(); + if (!andColumns.empty()) { + for (auto andColumn : andColumns) { + if (andColumn == FIELD_TIMESTAMP_BEGIN && + record.timestamp < andConditionValues.GetInt64(andColumn)) { + return false; + } else if (andColumn == FIELD_TIMESTAMP_END && + record.timestamp > andConditionValues.GetInt64(andColumn)) { + return false; + } else if (andColumn == FIELD_TIMESTAMP && + record.timestamp != andConditionValues.GetInt64(andColumn)) { + return false; + } + } + } + return true; +} + +void PermissionUsedRecordCache::AddRecordNode(const PermissionRecord& record, + std::shared_ptr curRecordNode) +{ + std::shared_ptr tmpRecordNode = std::make_shared(); + tmpRecordNode->record = record; + tmpRecordNode->pre = curRecordNode; + curRecordNode->next = tmpRecordNode; +} + +void PermissionUsedRecordCache::DeleteRecordNode(std::shared_ptr deleteRecordNode) +{ + std::shared_ptr pre = deleteRecordNode->pre.lock(); + if (deleteRecordNode->next == nullptr) { // End of the linked list + pre->next = nullptr; + } else { + std::shared_ptr next = deleteRecordNode->next; + pre->next = next; + next->pre = pre; + } +} +} // namespace AccessToken +} // namespace Security +} // namespace OHOS \ No newline at end of file