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

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

Pull AttrSourceIter into lib

Allows for re-use. Add const iterator as well.

Bug: 355498020
Flag: EXEMPT mechanical refactoring
Test: atest libaudiopermission_tests
Change-Id: If28e8d2742bcb05a63e2e6fc78051a6beea1f9d9
parent 7582af8b
Loading
Loading
Loading
Loading
+2 −5
Original line number Diff line number Diff line
@@ -83,7 +83,7 @@ cc_library {
}

cc_test {
    name: "libaudiopermission_test",
    name: "libaudiopermission_tests",
    host_supported: true,
    defaults: [
        "libmediautils_tests_config",
@@ -100,10 +100,7 @@ cc_test {
        "liblog",
        "libutils",
    ],
    srcs: [
        "tests/NativePermissionControllerTest.cpp",
        "tests/ValidatedAttributionSourceStateTest.cpp",
    ],
    srcs: ["tests/*.cpp"],
    test_options: {
        unit_test: true,
    },
+108 −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.
 */
#pragma once

#include <android/content/AttributionSourceState.h>

#include <iterator>

// AttributionSourceState are essentially an intrusive linked list, where the next field carries
// the pointer to the next element. These iterator helpers allow for convenient iteration over the
// entire attribution chain. Usage:
//   std::for_each(AttrSourceIter::begin(mAttributionSourceState), AttrSourceIter::end(), ...)
namespace android::media::permission::AttrSourceIter {

class ConstIter {
  public:
    using iterator_category = std::forward_iterator_tag;
    using difference_type = std::ptrdiff_t;
    using value_type = ::android::content::AttributionSourceState;
    using pointer = const value_type*;
    using reference = const value_type&;

    ConstIter(const ::android::content::AttributionSourceState* attr) : mAttr(attr) {}

    reference operator*() const { return *mAttr; }
    pointer operator->() const { return mAttr; }

    ConstIter& operator++() {
        mAttr = !mAttr->next.empty() ? mAttr->next.data() : nullptr;
        return *this;
    }
    ConstIter operator++(int) {
        ConstIter tmp = *this;
        ++(*this);
        return tmp;
    }

    friend bool operator==(const ConstIter& a, const ConstIter& b) = default;

    static ConstIter end() { return ConstIter(nullptr); }

  private:
    const ::android::content::AttributionSourceState* mAttr;
};

/**
 * Non-const iterator. Note, AttributionSourceState is conceptually a linked list on the next field.
 * Be very careful if `next` is modified over iteration, as it can go wrong easily.
 */
class Iter {
  public:
    using iterator_category = std::forward_iterator_tag;
    using difference_type = std::ptrdiff_t;
    using value_type = ::android::content::AttributionSourceState;
    using pointer = value_type*;
    using reference = value_type&;

    Iter(::android::content::AttributionSourceState* attr) : mAttr(attr) {}

    reference operator*() const { return *mAttr; }
    pointer operator->() const { return mAttr; }

    Iter& operator++() {
        mAttr = !mAttr->next.empty() ? mAttr->next.data() : nullptr;
        return *this;
    }
    Iter operator++(int) {
        Iter tmp = *this;
        ++(*this);
        return tmp;
    }

    friend bool operator==(const Iter& a, const Iter& b) = default;

    operator ConstIter() const { return ConstIter(mAttr); }

    static Iter end() { return Iter(nullptr); }

  private:
    ::android::content::AttributionSourceState* mAttr;
};

inline Iter begin(::android::content::AttributionSourceState& a) {
    return Iter(&a);
}
inline Iter end() {
    return Iter::end();
}
inline ConstIter cbegin(const ::android::content::AttributionSourceState& a) {
    return ConstIter(&a);
}
inline ConstIter cend() {
    return ConstIter::end();
}
}  // namespace com::android::media::permission::AttrSourceIter
+94 −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 <android/content/AttributionSourceState.h>
#include <media/AttrSourceIter.h>

#include <gmock/gmock.h>
#include <gtest/gtest.h>

#include <algorithm>

using ::android::content::AttributionSourceState;
using ::android::media::permission::AttrSourceIter::begin;
using ::android::media::permission::AttrSourceIter::cbegin;
using ::android::media::permission::AttrSourceIter::cend;
using ::android::media::permission::AttrSourceIter::end;

using ::android::media::permission::AttrSourceIter::ConstIter;

using ::testing::ContainerEq;
using ::testing::ElementsAreArray;
using ::testing::Eq;
using ::testing::Return;

class AttrSourceIterTest : public ::testing::Test {
  public:
    AttrSourceIterTest() {
        mAttr.pid = 1;
        mAttr.uid = 1;
        AttributionSourceState next;
        next.pid = 2;
        next.uid = 2;
        AttributionSourceState nextnext;
        nextnext.pid = 3;
        nextnext.uid = 3;
        next.next = {nextnext};
        mAttr.next = {next};
    }

  protected:
    AttributionSourceState mAttr;
};

TEST_F(AttrSourceIterTest, constIter) {
    const AttributionSourceState& ref = mAttr;
    std::vector<int> mPids;
    std::transform(cbegin(ref), cend(), std::back_inserter(mPids),
                   [](const auto& x) { return x.pid; });
    EXPECT_THAT(mPids, ElementsAreArray({1, 2, 3}));
}

TEST_F(AttrSourceIterTest, nonConstIter) {
    AttributionSourceState expected;
    {
        expected.pid = 2;
        expected.uid = 1;
        AttributionSourceState expectedNext;
        expectedNext.pid = 4;
        expectedNext.uid = 2;
        AttributionSourceState expectedNextNext;
        expectedNextNext.pid = 6;
        expectedNextNext.uid = 3;
        expectedNext.next = {expectedNextNext};
        expected.next = {expectedNext};
    }
    std::for_each(begin(mAttr), end(), [](auto& x) { x.pid = x.pid * 2; });

    EXPECT_THAT(mAttr, Eq(expected));
}

TEST_F(AttrSourceIterTest, nonConstIterReferenceEquals) {
    const AttributionSourceState& ref = mAttr;
    std::vector<const AttributionSourceState*> attrs;
    std::transform(cbegin(ref), cend(), std::back_inserter(attrs),
                   [](const auto& x) { return &x; });
    std::for_each(begin(mAttr), end(), [](auto& x) { x.pid = x.pid * 2; });
    std::vector<const AttributionSourceState*> attrsAfter;
    std::transform(cbegin(ref), cend(), std::back_inserter(attrsAfter),
                   [](const auto& x) { return &x; });
    EXPECT_THAT(attrs, ContainerEq(attrsAfter));
}
+29 −51
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@
#include "binder/AppOpsManager.h"
#include "mediautils/ServiceUtilities.h"
#include <android_media_audiopolicy.h>
#include <media/AttrSourceIter.h>

#include <algorithm>

@@ -58,39 +59,6 @@ int getTargetSdkForPackageName(std::string_view packageName) {
bool doesPackageTargetAtLeastU(std::string_view packageName) {
    return getTargetSdkForPackageName(packageName) >= __ANDROID_API_U__;
}

class AttrSourceItr {
  public:
    using iterator_category = std::forward_iterator_tag;
    using difference_type = std::ptrdiff_t;
    using value_type = AttributionSourceState;
    using pointer = value_type*;
    using reference = value_type&;

    AttrSourceItr() : mAttr(nullptr) {}

    AttrSourceItr(AttributionSourceState& attr) : mAttr(&attr) {}

    reference operator*() const { return *mAttr; }
    pointer operator->() const { return mAttr; }

    AttrSourceItr& operator++() {
        mAttr = !mAttr->next.empty() ? mAttr->next.data() : nullptr;
        return *this;
    }

    AttrSourceItr operator++(int) {
        AttrSourceItr tmp = *this;
        ++(*this);
        return tmp;
    }

    friend bool operator==(const AttrSourceItr& a, const AttrSourceItr& b) = default;

    static AttrSourceItr end() { return AttrSourceItr{}; }
private:
    AttributionSourceState * mAttr;
};
} // anonymous

// static
@@ -124,28 +92,33 @@ OpRecordAudioMonitor::createIfNeeded(
                                    commandThread);
}

OpRecordAudioMonitor::OpRecordAudioMonitor(
        const AttributionSourceState &attributionSource,
        const uint32_t virtualDeviceId, const audio_attributes_t &attr,
        int32_t appOp,
        bool shouldMonitorRecord,
        wp<AudioPolicyService::AudioCommandThread> commandThread) :
        mHasOp(true), mAttributionSource(attributionSource),
        mVirtualDeviceId(virtualDeviceId), mAttr(attr), mAppOp(appOp),
        mShouldMonitorRecord(shouldMonitorRecord),
        mCommandThread(commandThread) {
// The vdi is carried in the attribution source for appops perm checks.
// Overwrite the entire chain with the vdi associated with the mix this client is attached to
// This ensures the checkOps triggered by the listener are correct.
// Note: we still only register for events by package name, so we assume that we get events
// independent of vdi.
    if (mVirtualDeviceId != 0 /* default vdi */) {
        // TODO (atneya@) lift for const
        std::for_each(AttrSourceItr{mAttributionSource}, AttrSourceItr::end(),
                      [&](auto& attr) { attr.deviceId = mVirtualDeviceId; });
static AttributionSourceState& overwriteVdi(AttributionSourceState& chain, int vdi) {
    using permission::AttrSourceIter::begin;
    using permission::AttrSourceIter::end;
    if (vdi != 0 /* default vdi */) {
        std::for_each(begin(chain), end(), [vdi](auto& attr) { attr.deviceId = vdi; });
    }
    return chain;
}

OpRecordAudioMonitor::OpRecordAudioMonitor(
        AttributionSourceState attributionSource,
        const uint32_t virtualDeviceId, const audio_attributes_t &attr,
        int32_t appOp,
        bool shouldMonitorRecord,
        wp<AudioPolicyService::AudioCommandThread> commandThread) :
        mHasOp(true),
        mAttributionSource(overwriteVdi(attributionSource, virtualDeviceId)),
        mVirtualDeviceId(virtualDeviceId), mAttr(attr),
        mAppOp(appOp),
        mShouldMonitorRecord(shouldMonitorRecord),
        mCommandThread(commandThread) {}

OpRecordAudioMonitor::~OpRecordAudioMonitor()
{
    if (mOpCallback != 0) {
@@ -156,6 +129,9 @@ OpRecordAudioMonitor::~OpRecordAudioMonitor()

void OpRecordAudioMonitor::onFirstRef()
{
    using permission::AttrSourceIter::cbegin;
    using permission::AttrSourceIter::cend;

    checkOp();
    mOpCallback = new RecordAudioOpCallback(this);
    ALOGV("start watching op %d for %s", mAppOp, mAttributionSource.toString().c_str());
@@ -165,7 +141,7 @@ void OpRecordAudioMonitor::onFirstRef()
                        : 0;

    const auto reg = [&](int32_t op) {
        std::for_each(AttrSourceItr{mAttributionSource}, AttrSourceItr::end(),
        std::for_each(cbegin(mAttributionSource), cend(),
                      [&](const auto& attr) {
                          mAppOpsManager.startWatchingMode(
                                  op,
@@ -191,9 +167,11 @@ bool OpRecordAudioMonitor::hasOp() const {
// - not called from constructor,
// - not called from RecordAudioOpCallback because the callback is not installed in this case
void OpRecordAudioMonitor::checkOp(bool updateUidStates) {
    using permission::AttrSourceIter::cbegin;
    using permission::AttrSourceIter::cend;

    const auto check = [&](int32_t op) -> bool {
        return std::all_of(
                AttrSourceItr{mAttributionSource}, AttrSourceItr::end(), [&](const auto& x) {
        return std::all_of(cbegin(mAttributionSource), cend(), [&](const auto& x) {
                    return mAppOpsManager.checkOp(op, x.uid,
                                                  VALUE_OR_FATAL(aidl2legacy_string_view_String16(
                                                          x.packageName.value_or("")))) ==
+2 −2
Original line number Diff line number Diff line
@@ -43,7 +43,7 @@ public:
            wp<AudioPolicyService::AudioCommandThread> commandThread);

private:
    OpRecordAudioMonitor(const AttributionSourceState &attributionSource,
    OpRecordAudioMonitor(AttributionSourceState attributionSource,
                         uint32_t virtualDeviceId,
                         const audio_attributes_t &attr,
                         int32_t appOp,
@@ -71,7 +71,7 @@ private:
    void checkOp(bool updateUidStates = false);

    std::atomic_bool mHasOp;
    AttributionSourceState mAttributionSource;
    const AttributionSourceState mAttributionSource;
    const uint32_t mVirtualDeviceId;
    const audio_attributes_t mAttr;
    const int32_t mAppOp;