Add permission caching to PermissionController
Audioserver currently calls upwards to system_server for all permission
checks.
Invert the call direction, and maintain mapping of uids which hold a
particular permission. Add AIDL interface for system_server and enum
representing relevant permissions.
Test: atest audiopermissioncontroller_test
Bug: 338089555
Flag: com.android.media.audio.audioserver_permissions
Change-Id: I45be63217fa58544e6dd9530a5d9e29b99f31ade
diff --git a/aidl/com/android/media/permission/INativePermissionController.aidl b/aidl/com/android/media/permission/INativePermissionController.aidl
index 5766e33..a14092d 100644
--- a/aidl/com/android/media/permission/INativePermissionController.aidl
+++ b/aidl/com/android/media/permission/INativePermissionController.aidl
@@ -16,6 +16,7 @@
package com.android.media.permission;
+import com.android.media.permission.PermissionEnum;
import com.android.media.permission.UidPackageState;
/**
@@ -33,4 +34,13 @@
* If the list is empty, the package no longer exists.
*/
void updatePackagesForUid(in UidPackageState newPackageState);
+ /**
+ * Populate or replace the list of uids which holds a particular permission.
+ * Runtime permissions will need additional checks, and should not use the cache as-is.
+ * Not virtual device aware.
+ * Is is possible for updates to the permission state to be delayed during high traffic.
+ * @param perm - Enum representing the permission for which holders are being supplied
+ * @param uids - Uids (not app-ids) which hold the permission. Should be sorted
+ */
+ void populatePermissionState(in PermissionEnum perm, in int[] uids);
}
diff --git a/aidl/com/android/media/permission/PermissionEnum.aidl b/aidl/com/android/media/permission/PermissionEnum.aidl
new file mode 100644
index 0000000..834a275
--- /dev/null
+++ b/aidl/com/android/media/permission/PermissionEnum.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * 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.
+ */
+
+package com.android.media.permission;
+
+/**
+ * Enumerates permissions which are tracked/pushed by NativePermissionController
+ * {@hide}
+ */
+enum PermissionEnum {
+ MODIFY_AUDIO_ROUTING = 0,
+ MODIFY_PHONE_STATE = 1,
+ CALL_AUDIO_INTERCEPTION = 2,
+ // This is a runtime + WIU permission, which means data delivery should be protected by AppOps
+ // We query the controller only for early fails/hard errors
+ RECORD_AUDIO = 3,
+ ENUM_SIZE = 4, // Not for actual usage
+}
diff --git a/services/audiopolicy/permission/NativePermissionController.cpp b/services/audiopolicy/permission/NativePermissionController.cpp
index d88c69f..9fcf22d 100644
--- a/services/audiopolicy/permission/NativePermissionController.cpp
+++ b/services/audiopolicy/permission/NativePermissionController.cpp
@@ -88,6 +88,19 @@
return Status::ok();
}
+Status NativePermissionController::populatePermissionState(PermissionEnum perm,
+ const std::vector<int>& uids) {
+ if (perm >= PermissionEnum::ENUM_SIZE || static_cast<int>(perm) < 0) {
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
+ }
+ std::lock_guard l{m_};
+ auto& cursor = permission_map_[static_cast<size_t>(perm)];
+ cursor = std::vector<uid_t>{uids.begin(), uids.end()};
+ // should be sorted
+ std::sort(cursor.begin(), cursor.end());
+ return Status::ok();
+}
+
// -- End Binder methods
Result<std::vector<std::string>> NativePermissionController::getPackagesForUid(uid_t uid) const {
@@ -120,4 +133,15 @@
(std::find(cursor->second.begin(), cursor->second.end(), packageName) !=
cursor->second.end());
}
+
+Result<bool> NativePermissionController::checkPermission(PermissionEnum perm, uid_t uid) const {
+ std::lock_guard l{m_};
+ const auto& uids = permission_map_[static_cast<size_t>(perm)];
+ if (!uids.empty()) {
+ return std::binary_search(uids.begin(), uids.end(), uid);
+ } else {
+ return unexpected{::android::NO_INIT};
+ }
+}
+
} // namespace com::android::media::permission
diff --git a/services/audiopolicy/permission/include/media/IPermissionProvider.h b/services/audiopolicy/permission/include/media/IPermissionProvider.h
index 9184ffb..27a61ea 100644
--- a/services/audiopolicy/permission/include/media/IPermissionProvider.h
+++ b/services/audiopolicy/permission/include/media/IPermissionProvider.h
@@ -21,6 +21,7 @@
#include <optional>
#include <vector>
+#include <com/android/media/permission/PermissionEnum.h>
#include <error/Result.h>
namespace com::android::media::permission {
@@ -38,6 +39,11 @@
// Fails if the provider does not know about the app-id.
virtual ::android::error::Result<bool> validateUidPackagePair(
uid_t uid, const std::string& packageName) const = 0;
+
+ // True iff the uid holds the permission (user aware).
+ // Fails with NO_INIT if cache hasn't been populated.
+ virtual ::android::error::Result<bool> checkPermission(PermissionEnum permission,
+ uid_t uid) const = 0;
virtual ~IPermissionProvider() = default;
};
} // namespace com::android::media::permission
diff --git a/services/audiopolicy/permission/include/media/NativePermissionController.h b/services/audiopolicy/permission/include/media/NativePermissionController.h
index c0e717c..d464023 100644
--- a/services/audiopolicy/permission/include/media/NativePermissionController.h
+++ b/services/audiopolicy/permission/include/media/NativePermissionController.h
@@ -33,16 +33,22 @@
public:
Status populatePackagesForUids(const std::vector<UidPackageState>& initialPackageStates) final;
Status updatePackagesForUid(const UidPackageState& newPackageState) final;
+ Status populatePermissionState(PermissionEnum permission, const std::vector<int>& uids) final;
// end binder methods
::android::error::Result<std::vector<std::string>> getPackagesForUid(uid_t uid) const final;
::android::error::Result<bool> validateUidPackagePair(
uid_t uid, const std::string& packageName) const final;
+ ::android::error::Result<bool> checkPermission(PermissionEnum permission,
+ uid_t uid) const final;
private:
mutable std::mutex m_;
// map of app_ids to the set of packages names which could run in them (should be 1)
std::unordered_map<uid_t, std::vector<std::string>> package_map_ GUARDED_BY(m_);
bool is_package_populated_ GUARDED_BY(m_);
+ // (logical) map of PermissionEnum to list of uids (not appid) which hold the perm
+ std::array<std::vector<uid_t>, static_cast<size_t>(PermissionEnum::ENUM_SIZE)> permission_map_
+ GUARDED_BY(m_);
};
} // namespace com::android::media::permission
diff --git a/services/audiopolicy/permission/tests/NativePermissionControllerTest.cpp b/services/audiopolicy/permission/tests/NativePermissionControllerTest.cpp
index f23a8b3..3f6b787 100644
--- a/services/audiopolicy/permission/tests/NativePermissionControllerTest.cpp
+++ b/services/audiopolicy/permission/tests/NativePermissionControllerTest.cpp
@@ -24,6 +24,7 @@
using ::android::base::unexpected;
using ::android::binder::Status;
using com::android::media::permission::NativePermissionController;
+using com::android::media::permission::PermissionEnum;
using com::android::media::permission::UidPackageState;
class NativePermissionControllerTest : public ::testing::Test {
@@ -177,3 +178,41 @@
UNWRAP_EQ(controller_.validateUidPackagePair(12000, "any.package"), false);
}
+
+TEST_F(NativePermissionControllerTest, populatePermissionState_InvalidPermission) {
+ EXPECT_EQ(controller_.populatePermissionState(PermissionEnum::ENUM_SIZE, {}).exceptionCode(),
+ Status::EX_ILLEGAL_ARGUMENT);
+ EXPECT_EQ(controller_
+ .populatePermissionState(
+ static_cast<PermissionEnum>(
+ static_cast<int>(PermissionEnum::ENUM_SIZE) + 1),
+ {})
+ .exceptionCode(),
+ Status::EX_ILLEGAL_ARGUMENT);
+}
+
+TEST_F(NativePermissionControllerTest, populatePermissionState_HoldsPermission) {
+ // Unsorted
+ std::vector<int> uids{3, 1, 2, 4, 5};
+
+ EXPECT_TRUE(
+ controller_.populatePermissionState(PermissionEnum::MODIFY_AUDIO_ROUTING, uids).isOk());
+
+ EXPECT_TRUE(*controller_.checkPermission(PermissionEnum::MODIFY_AUDIO_ROUTING, 3));
+}
+
+TEST_F(NativePermissionControllerTest, populatePermissionState_DoesNotHoldPermission) {
+ // Unsorted
+ std::vector<int> uids{3, 1, 2, 4, 5};
+
+ EXPECT_TRUE(
+ controller_.populatePermissionState(PermissionEnum::MODIFY_AUDIO_ROUTING, uids).isOk());
+
+ EXPECT_FALSE(*controller_.checkPermission(PermissionEnum::MODIFY_AUDIO_ROUTING, 6));
+}
+
+TEST_F(NativePermissionControllerTest, populatePermissionState_NotInitialized) {
+ const auto res = controller_.checkPermission(PermissionEnum::MODIFY_AUDIO_ROUTING, 3);
+ ASSERT_FALSE(res.has_value());
+ EXPECT_EQ(res.error(), ::android::NO_INIT);
+}
diff --git a/services/audiopolicy/permission/tests/ValidatedAttributionSourceStateTest.cpp b/services/audiopolicy/permission/tests/ValidatedAttributionSourceStateTest.cpp
index f4d6556..efc318b 100644
--- a/services/audiopolicy/permission/tests/ValidatedAttributionSourceStateTest.cpp
+++ b/services/audiopolicy/permission/tests/ValidatedAttributionSourceStateTest.cpp
@@ -27,6 +27,7 @@
using ::android::content::AttributionSourceState;
using ::android::error::Result;
using ::com::android::media::permission::IPermissionProvider;
+using ::com::android::media::permission::PermissionEnum;
using ::com::android::media::permission::ValidatedAttributionSourceState;
using ::testing::Return;
@@ -36,6 +37,7 @@
(override, const));
MOCK_METHOD(Result<bool>, validateUidPackagePair, (uid_t uid, const std::string&),
(override, const));
+ MOCK_METHOD(Result<bool>, checkPermission, (PermissionEnum perm, uid_t), (override, const));
};
class ValidatedAttributionSourceStateTest : public ::testing::Test {