Loading media/libaudiopermission/Android.bp +3 −0 Original line number Diff line number Diff line Loading @@ -13,6 +13,7 @@ cc_library { name: "libaudiopermission", srcs: [ "AppOpsSession.cpp", "NativePermissionController.cpp", "ValidatedAttributionSourceState.cpp", ], Loading @@ -35,6 +36,7 @@ cc_library { "libbase", "libbinder", "liblog", "libpermission", "libutils", ], Loading Loading @@ -98,6 +100,7 @@ cc_test { "libbase", "libbinder", "liblog", "libpermission", "libutils", ], srcs: ["tests/*.cpp"], Loading media/libaudiopermission/AppOpsSession.cpp 0 → 100644 +111 −0 Original line number Diff line number Diff line /* * 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. */ #include <media/AppOpsSession.h> #include <media/AttrSourceIter.h> #include <binder/AppOpsManager.h> #include <binder/PermissionController.h> using ::android::content::AttributionSourceState; namespace android::media::permission { // params are unreliable at the moment void DefaultAppOpsFacade::OpMonitor::opChanged(int32_t, const String16&) { DefaultAppOpsFacade x{}; const auto allowed = x.checkAccess(mAttr, mOps); std::lock_guard l_{mLock}; if (mCb == nullptr) return; mCb(allowed); } bool DefaultAppOpsFacade::startAccess(const ValidatedAttributionSourceState& attr_, Ops ops) { const AttributionSourceState& attr = attr_; // TODO no support for additional op at the moment if (ops.attributedOp == AppOpsManager::OP_NONE) return true; // nothing to do // TODO caching and sync up-call marking AppOpsManager ap{}; return ap.startOpNoThrow( /*op=*/ ops.attributedOp, /*uid=*/ attr.uid, /*packageName=*/ String16{attr.packageName.value_or("").c_str()}, /*startModeIfDefault=*/ false, /*attributionTag=*/ attr.attributionTag.has_value() ? String16{attr.attributionTag.value().c_str()} : String16{}, /*msg=*/ String16{"AppOpsSession start"}); } void DefaultAppOpsFacade::stopAccess(const ValidatedAttributionSourceState& attr_, Ops ops) { const AttributionSourceState& attr = attr_; // TODO caching and sync up-call marking AppOpsManager ap{}; return ap.finishOp( /*op=*/ ops.attributedOp, /*uid=*/ attr.uid, /*callingPackage=*/ String16{attr.packageName.value_or("").c_str()}, /*attributionTag=*/ attr.attributionTag.has_value() ? String16{attr.attributionTag.value().c_str()} : String16{}); } bool DefaultAppOpsFacade::checkAccess(const ValidatedAttributionSourceState& attr, Ops ops) { const auto check = [&](int32_t op) -> bool { if (op == AppOpsManager::OP_NONE) return true; return std::all_of( AttrSourceIter::cbegin(attr), AttrSourceIter::cend(), [&](const auto& x) { return AppOpsManager{}.checkOp(op, x.uid, String16{x.packageName.value_or("").c_str()}) == AppOpsManager::MODE_ALLOWED; }); }; return check(ops.attributedOp) && check(ops.additionalOp); } uintptr_t DefaultAppOpsFacade::addChangeCallback(const ValidatedAttributionSourceState& attr, Ops ops, std::function<void(bool)> cb) { const auto listener = sp<OpMonitor>::make(attr, ops, std::move(cb)); const auto reg = [&](int32_t op) { std::for_each(AttrSourceIter::cbegin(attr), AttrSourceIter::cend(), [&listener, op](const auto& x) { AppOpsManager{}.startWatchingMode( op, String16{x.packageName.value_or("").c_str()}, AppOpsManager::WATCH_FOREGROUND_CHANGES, listener); }); }; if (ops.attributedOp != AppOpsManager::OP_NONE) reg(ops.attributedOp); if (ops.additionalOp != AppOpsManager::OP_NONE) reg(ops.additionalOp); std::lock_guard l_{sMapLock}; const auto cookie = reinterpret_cast<uintptr_t>(listener.get()); sCbMap[cookie] = std::move(listener); return cookie; } void DefaultAppOpsFacade::removeChangeCallback(uintptr_t ptr) { sp<OpMonitor> monitor; { std::lock_guard l_{sMapLock}; if (const auto iter = sCbMap.find(ptr); iter != sCbMap.end()) { monitor = std::move(iter->second); sCbMap.erase(iter); } } LOG_ALWAYS_FATAL_IF(monitor == nullptr, "Unexpected nullptr in cb map"); monitor->stopListening(); } } // namespace android::media::permission media/libaudiopermission/include/media/AppOpsSession.h +57 −2 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ #pragma once #include <android-base/thread_annotations.h> #include <binder/IAppOpsCallback.h> #include <cutils/android_filesystem_config.h> #include <log/log.h> #include <media/ValidatedAttributionSourceState.h> #include <utils/RefBase.h> Loading @@ -28,8 +30,28 @@ namespace android::media::permission { using ValidatedAttributionSourceState = com::android::media::permission::ValidatedAttributionSourceState; /** * Tracking ops for the following uids are pointless -- system always has ops and isn't tracked, * and native only services don't have packages which is what appops tracks over. * So, we skip tracking, and always permit access. * Notable omissions are AID_SHELL, AID_RADIO, and AID_BLUETOOTH, which are non-app uids which * interface with us, but are associated with packages so can still be attributed to. */ inline bool skipOpsForUid(uid_t uid) { switch (uid % AID_USER_OFFSET) { case AID_ROOT: case AID_SYSTEM: case AID_MEDIA: case AID_AUDIOSERVER: case AID_CAMERASERVER: return true; default: return false; } } struct Ops { int attributedOp = -1; int attributedOp = -1; // same as OP_NONE int additionalOp = -1; }; Loading Loading @@ -154,4 +176,37 @@ class AppOpsSession { bool mDeliveryPermitted GUARDED_BY(mLock); }; } // namespace com::android::media::permission class DefaultAppOpsFacade { public: bool startAccess(const ValidatedAttributionSourceState&, Ops); void stopAccess(const ValidatedAttributionSourceState&, Ops); bool checkAccess(const ValidatedAttributionSourceState&, Ops); uintptr_t addChangeCallback(const ValidatedAttributionSourceState&, Ops, std::function<void(bool)> cb); void removeChangeCallback(uintptr_t); class OpMonitor : public BnAppOpsCallback { public: OpMonitor(ValidatedAttributionSourceState attr, Ops ops, std::function<void(bool)> cb) : mAttr(attr), mOps(ops), mCb(cb) {} void opChanged(int32_t op, const String16& packageName) override; void stopListening() { std::lock_guard l_{mLock}; mCb = nullptr; } private: const ValidatedAttributionSourceState mAttr; const Ops mOps; std::mutex mLock; std::function<void(bool)> mCb GUARDED_BY(mLock); }; private: static inline std::mutex sMapLock{}; static inline std::unordered_map<uintptr_t, sp<OpMonitor>> sCbMap{}; }; } // namespace android::media::permission media/libaudiopermission/include/media/AttrSourceIter.h +1 −1 Original line number Diff line number Diff line Loading @@ -105,4 +105,4 @@ inline ConstIter cbegin(const ::android::content::AttributionSourceState& a) { inline ConstIter cend() { return ConstIter::end(); } } // namespace com::android::media::permission::AttrSourceIter } // namespace android::media::permission::AttrSourceIter media/libaudiopermission/tests/AppOpsSessionTests.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -29,8 +29,8 @@ using ::com::android::media::permission::ValidatedAttributionSourceState; using ::testing::ElementsAreArray; using ::testing::Eq; using ::testing::Ne; using ::testing::IsEmpty; using ::testing::Ne; class AppOpsSessionTests; Loading Loading
media/libaudiopermission/Android.bp +3 −0 Original line number Diff line number Diff line Loading @@ -13,6 +13,7 @@ cc_library { name: "libaudiopermission", srcs: [ "AppOpsSession.cpp", "NativePermissionController.cpp", "ValidatedAttributionSourceState.cpp", ], Loading @@ -35,6 +36,7 @@ cc_library { "libbase", "libbinder", "liblog", "libpermission", "libutils", ], Loading Loading @@ -98,6 +100,7 @@ cc_test { "libbase", "libbinder", "liblog", "libpermission", "libutils", ], srcs: ["tests/*.cpp"], Loading
media/libaudiopermission/AppOpsSession.cpp 0 → 100644 +111 −0 Original line number Diff line number Diff line /* * 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. */ #include <media/AppOpsSession.h> #include <media/AttrSourceIter.h> #include <binder/AppOpsManager.h> #include <binder/PermissionController.h> using ::android::content::AttributionSourceState; namespace android::media::permission { // params are unreliable at the moment void DefaultAppOpsFacade::OpMonitor::opChanged(int32_t, const String16&) { DefaultAppOpsFacade x{}; const auto allowed = x.checkAccess(mAttr, mOps); std::lock_guard l_{mLock}; if (mCb == nullptr) return; mCb(allowed); } bool DefaultAppOpsFacade::startAccess(const ValidatedAttributionSourceState& attr_, Ops ops) { const AttributionSourceState& attr = attr_; // TODO no support for additional op at the moment if (ops.attributedOp == AppOpsManager::OP_NONE) return true; // nothing to do // TODO caching and sync up-call marking AppOpsManager ap{}; return ap.startOpNoThrow( /*op=*/ ops.attributedOp, /*uid=*/ attr.uid, /*packageName=*/ String16{attr.packageName.value_or("").c_str()}, /*startModeIfDefault=*/ false, /*attributionTag=*/ attr.attributionTag.has_value() ? String16{attr.attributionTag.value().c_str()} : String16{}, /*msg=*/ String16{"AppOpsSession start"}); } void DefaultAppOpsFacade::stopAccess(const ValidatedAttributionSourceState& attr_, Ops ops) { const AttributionSourceState& attr = attr_; // TODO caching and sync up-call marking AppOpsManager ap{}; return ap.finishOp( /*op=*/ ops.attributedOp, /*uid=*/ attr.uid, /*callingPackage=*/ String16{attr.packageName.value_or("").c_str()}, /*attributionTag=*/ attr.attributionTag.has_value() ? String16{attr.attributionTag.value().c_str()} : String16{}); } bool DefaultAppOpsFacade::checkAccess(const ValidatedAttributionSourceState& attr, Ops ops) { const auto check = [&](int32_t op) -> bool { if (op == AppOpsManager::OP_NONE) return true; return std::all_of( AttrSourceIter::cbegin(attr), AttrSourceIter::cend(), [&](const auto& x) { return AppOpsManager{}.checkOp(op, x.uid, String16{x.packageName.value_or("").c_str()}) == AppOpsManager::MODE_ALLOWED; }); }; return check(ops.attributedOp) && check(ops.additionalOp); } uintptr_t DefaultAppOpsFacade::addChangeCallback(const ValidatedAttributionSourceState& attr, Ops ops, std::function<void(bool)> cb) { const auto listener = sp<OpMonitor>::make(attr, ops, std::move(cb)); const auto reg = [&](int32_t op) { std::for_each(AttrSourceIter::cbegin(attr), AttrSourceIter::cend(), [&listener, op](const auto& x) { AppOpsManager{}.startWatchingMode( op, String16{x.packageName.value_or("").c_str()}, AppOpsManager::WATCH_FOREGROUND_CHANGES, listener); }); }; if (ops.attributedOp != AppOpsManager::OP_NONE) reg(ops.attributedOp); if (ops.additionalOp != AppOpsManager::OP_NONE) reg(ops.additionalOp); std::lock_guard l_{sMapLock}; const auto cookie = reinterpret_cast<uintptr_t>(listener.get()); sCbMap[cookie] = std::move(listener); return cookie; } void DefaultAppOpsFacade::removeChangeCallback(uintptr_t ptr) { sp<OpMonitor> monitor; { std::lock_guard l_{sMapLock}; if (const auto iter = sCbMap.find(ptr); iter != sCbMap.end()) { monitor = std::move(iter->second); sCbMap.erase(iter); } } LOG_ALWAYS_FATAL_IF(monitor == nullptr, "Unexpected nullptr in cb map"); monitor->stopListening(); } } // namespace android::media::permission
media/libaudiopermission/include/media/AppOpsSession.h +57 −2 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ #pragma once #include <android-base/thread_annotations.h> #include <binder/IAppOpsCallback.h> #include <cutils/android_filesystem_config.h> #include <log/log.h> #include <media/ValidatedAttributionSourceState.h> #include <utils/RefBase.h> Loading @@ -28,8 +30,28 @@ namespace android::media::permission { using ValidatedAttributionSourceState = com::android::media::permission::ValidatedAttributionSourceState; /** * Tracking ops for the following uids are pointless -- system always has ops and isn't tracked, * and native only services don't have packages which is what appops tracks over. * So, we skip tracking, and always permit access. * Notable omissions are AID_SHELL, AID_RADIO, and AID_BLUETOOTH, which are non-app uids which * interface with us, but are associated with packages so can still be attributed to. */ inline bool skipOpsForUid(uid_t uid) { switch (uid % AID_USER_OFFSET) { case AID_ROOT: case AID_SYSTEM: case AID_MEDIA: case AID_AUDIOSERVER: case AID_CAMERASERVER: return true; default: return false; } } struct Ops { int attributedOp = -1; int attributedOp = -1; // same as OP_NONE int additionalOp = -1; }; Loading Loading @@ -154,4 +176,37 @@ class AppOpsSession { bool mDeliveryPermitted GUARDED_BY(mLock); }; } // namespace com::android::media::permission class DefaultAppOpsFacade { public: bool startAccess(const ValidatedAttributionSourceState&, Ops); void stopAccess(const ValidatedAttributionSourceState&, Ops); bool checkAccess(const ValidatedAttributionSourceState&, Ops); uintptr_t addChangeCallback(const ValidatedAttributionSourceState&, Ops, std::function<void(bool)> cb); void removeChangeCallback(uintptr_t); class OpMonitor : public BnAppOpsCallback { public: OpMonitor(ValidatedAttributionSourceState attr, Ops ops, std::function<void(bool)> cb) : mAttr(attr), mOps(ops), mCb(cb) {} void opChanged(int32_t op, const String16& packageName) override; void stopListening() { std::lock_guard l_{mLock}; mCb = nullptr; } private: const ValidatedAttributionSourceState mAttr; const Ops mOps; std::mutex mLock; std::function<void(bool)> mCb GUARDED_BY(mLock); }; private: static inline std::mutex sMapLock{}; static inline std::unordered_map<uintptr_t, sp<OpMonitor>> sCbMap{}; }; } // namespace android::media::permission
media/libaudiopermission/include/media/AttrSourceIter.h +1 −1 Original line number Diff line number Diff line Loading @@ -105,4 +105,4 @@ inline ConstIter cbegin(const ::android::content::AttributionSourceState& a) { inline ConstIter cend() { return ConstIter::end(); } } // namespace com::android::media::permission::AttrSourceIter } // namespace android::media::permission::AttrSourceIter
media/libaudiopermission/tests/AppOpsSessionTests.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -29,8 +29,8 @@ using ::com::android::media::permission::ValidatedAttributionSourceState; using ::testing::ElementsAreArray; using ::testing::Eq; using ::testing::Ne; using ::testing::IsEmpty; using ::testing::Ne; class AppOpsSessionTests; Loading