Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit de8afe4c authored by Atneya Nair's avatar Atneya Nair
Browse files

[audio] Add default appops session

Add default appops facade impl which tracks a simple appop.

Test: atest AppOpsSessionTests
Bug: 355498020
Flag: EXEMPT, safe adding utils
Change-Id: I17c3988150b167c141e72c994caf657cea07e1d1
parent 6d4d381b
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@ cc_library {
    name: "libaudiopermission",

    srcs: [
        "AppOpsSession.cpp",
        "NativePermissionController.cpp",
        "ValidatedAttributionSourceState.cpp",
    ],
@@ -35,6 +36,7 @@ cc_library {
        "libbase",
        "libbinder",
        "liblog",
        "libpermission",
        "libutils",
    ],

@@ -98,6 +100,7 @@ cc_test {
        "libbase",
        "libbinder",
        "liblog",
        "libpermission",
        "libutils",
    ],
    srcs: ["tests/*.cpp"],
+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
+57 −2
Original line number Diff line number Diff line
@@ -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>
@@ -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;
};

@@ -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
+1 −1
Original line number Diff line number Diff line
@@ -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
+1 −1
Original line number Diff line number Diff line
@@ -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;