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/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 {