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

Commit ff1e5ee6 authored by Atneya Nair's avatar Atneya Nair Committed by Android (Google) Code Review
Browse files

Merge "Add ValidatedAttributionSourceState" into main

parents 24a0aea4 85a07e23
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@ cc_library {

    srcs: [
        "NativePermissionController.cpp",
        "ValidatedAttributionSourceState.cpp",
    ],
    export_include_dirs: [
        "include",
@@ -22,6 +23,7 @@ cc_library {
    ],
    static_libs: [
        "audio-permission-aidl-cpp",
        "framework-permission-aidl-cpp",
    ],
    shared_libs: [
        "libbase",
@@ -94,6 +96,7 @@ cc_test {
    ],
    srcs: [
        "tests/NativePermissionControllerTest.cpp",
        "tests/ValidatedAttributionSourceStateTest.cpp",
    ],
    test_options: {
        unit_test: true,
+53 −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/ValidatedAttributionSourceState.h>

#include <binder/IPCThreadState.h>
#include <error/expected_utils.h>
#include <utils/Log.h>

namespace com::android::media::permission {

using ::android::base::unexpected;

Result<ValidatedAttributionSourceState> ValidatedAttributionSourceState::createFromBinderContext(
        AttributionSourceState attr, const IPermissionProvider& provider) {
    attr.pid = ::android::IPCThreadState::self()->getCallingPid();
    attr.uid = ::android::IPCThreadState::self()->getCallingUid();
    return createFromTrustedUidNoPackage(std::move(attr), provider);
}

Result<ValidatedAttributionSourceState>
ValidatedAttributionSourceState::createFromTrustedUidNoPackage(
        AttributionSourceState attr, const IPermissionProvider& provider) {
    if (attr.packageName.has_value() && attr.packageName->size() != 0) {
        if (VALUE_OR_RETURN(provider.validateUidPackagePair(attr.uid, attr.packageName.value()))) {
            return ValidatedAttributionSourceState{std::move(attr)};
        } else {
            return unexpected{::android::PERMISSION_DENIED};
        }
    } else {
        // For APIs which don't appropriately pass attribution sources or packages, we need
        // to populate the package name with our best guess.
        const auto packageNames = VALUE_OR_RETURN(provider.getPackagesForUid(attr.uid));
        LOG_ALWAYS_FATAL_IF(packageNames.empty());
        attr.packageName = std::move(packageNames[0]);
        return ValidatedAttributionSourceState{std::move(attr)};
    }
}

}  // namespace com::android::media::permission
+71 −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 <error/Result.h>

#include "IPermissionProvider.h"

namespace com::android::media::permission {

using ::android::content::AttributionSourceState;
using ::android::error::Result;

class ValidatedAttributionSourceState {
  public:
    /**
     * Validates an attribution source from within the context of a binder transaction.
     * Overwrites the uid/pid and validates the packageName
     */
    static Result<ValidatedAttributionSourceState> createFromBinderContext(
            AttributionSourceState attr, const IPermissionProvider& provider);

    /**
     * Creates a ValidatedAttributionSourceState in cases where the source is passed from a
     * trusted entity which already performed validation.
     */
    static ValidatedAttributionSourceState createFromTrustedSource(AttributionSourceState attr) {
        return ValidatedAttributionSourceState(attr);
    }

    /**
     * Create a ValidatedAttribubtionSourceState in cases where the uid/pid is trusted, but the
     * packages have not been validated. Proper use of the previous two methods should avoid the
     * necessity of this, but it is useful for migration purposes as well as testing this class.
     */
    static Result<ValidatedAttributionSourceState> createFromTrustedUidNoPackage(
            AttributionSourceState attr, const IPermissionProvider& provider);

    operator AttributionSourceState() const { return state_; }

    operator const AttributionSourceState&() const { return state_; }

    AttributionSourceState unwrapInto() && { return std::move(state_); }

    bool operator==(const ValidatedAttributionSourceState& other) const {
        return operator==(other.state_);
    }

    bool operator==(const AttributionSourceState& other) const { return state_ == other; }

  private:
    ValidatedAttributionSourceState(AttributionSourceState attr) : state_(attr) {}

    AttributionSourceState state_;
};
}  // namespace com::android::media::permission
+125 −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/ValidatedAttributionSourceState.h>

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

#include <android-base/expected.h>
#include <media/IPermissionProvider.h>

using ::android::base::unexpected;
using ::android::binder::Status;
using ::android::content::AttributionSourceState;
using ::android::error::Result;
using ::com::android::media::permission::IPermissionProvider;
using ::com::android::media::permission::ValidatedAttributionSourceState;
using ::testing::Return;

class MockPermissionProvider : public IPermissionProvider {
  public:
    MOCK_METHOD(Result<std::vector<std::string>>, getPackagesForUid, (uid_t uid),
                (override, const));
    MOCK_METHOD(Result<bool>, validateUidPackagePair, (uid_t uid, const std::string&),
                (override, const));
};

class ValidatedAttributionSourceStateTest : public ::testing::Test {
  protected:
    MockPermissionProvider mMockProvider;
    const uid_t mUid = 10001;
    const std::vector<std::string> mPackageList{"com.package1", "com.package2"};
};

#define UNWRAP_EQ(expr, desired_expr)                         \
    do {                                                      \
        auto tmp_ = (expr);                                   \
        EXPECT_TRUE(tmp_.has_value());                        \
        if (tmp_.has_value()) EXPECT_EQ(*tmp_, desired_expr); \
    } while (0)

TEST_F(ValidatedAttributionSourceStateTest, providedPackageValid) {
    const std::string package = "com.package1";
    EXPECT_CALL(mMockProvider, validateUidPackagePair(mUid, package)).WillOnce(Return(true));
    AttributionSourceState attr;
    attr.uid = mUid;
    attr.packageName = package;
    UNWRAP_EQ(ValidatedAttributionSourceState::createFromTrustedUidNoPackage(attr, mMockProvider),
              attr);
}

TEST_F(ValidatedAttributionSourceStateTest, providedPackageInvalid) {
    const std::string package = "com.package.spoof";
    EXPECT_CALL(mMockProvider, validateUidPackagePair(mUid, package)).WillOnce(Return(false));
    AttributionSourceState attr;
    attr.uid = mUid;
    attr.packageName = package;
    const auto res =
            ValidatedAttributionSourceState::createFromTrustedUidNoPackage(attr, mMockProvider);
    ASSERT_FALSE(res.has_value());
    EXPECT_EQ(res.error(), ::android::PERMISSION_DENIED);
}

TEST_F(ValidatedAttributionSourceStateTest, packageLookup_whenMissingPackage) {
    EXPECT_CALL(mMockProvider, getPackagesForUid(mUid)).WillOnce(Return(mPackageList));
    AttributionSourceState attr;
    attr.uid = mUid;
    AttributionSourceState expectedAttr;
    expectedAttr.uid = mUid;
    expectedAttr.packageName = "com.package1";
    UNWRAP_EQ(ValidatedAttributionSourceState::createFromTrustedUidNoPackage(attr, mMockProvider),
              expectedAttr);
}

TEST_F(ValidatedAttributionSourceStateTest, packageLookup_whenEmptyPackage) {
    EXPECT_CALL(mMockProvider, getPackagesForUid(mUid)).WillOnce(Return(mPackageList));
    AttributionSourceState attr;
    attr.uid = mUid;
    attr.packageName = std::string{};
    AttributionSourceState expectedAttr;
    expectedAttr.uid = mUid;
    expectedAttr.packageName = "com.package1";
    UNWRAP_EQ(ValidatedAttributionSourceState::createFromTrustedUidNoPackage(attr, mMockProvider),
              expectedAttr);
}

TEST_F(ValidatedAttributionSourceStateTest, controllerNotInitialized) {
    EXPECT_CALL(mMockProvider, getPackagesForUid(mUid))
            .WillOnce(Return(unexpected{::android::NO_INIT}));
    AttributionSourceState attr;
    attr.uid = mUid;
    attr.packageName = std::string{};
    AttributionSourceState expectedAttr;
    expectedAttr.uid = mUid;
    expectedAttr.packageName = "com.package1";
    const auto res =
            ValidatedAttributionSourceState::createFromTrustedUidNoPackage(attr, mMockProvider);
    ASSERT_FALSE(res.has_value());
    EXPECT_EQ(res.error(), ::android::NO_INIT);
}

TEST_F(ValidatedAttributionSourceStateTest, uidNotFound) {
    EXPECT_CALL(mMockProvider, getPackagesForUid(mUid))
            .WillOnce(Return(unexpected{::android::BAD_VALUE}));
    AttributionSourceState attr;
    attr.uid = mUid;
    attr.packageName = std::string{};
    const auto res =
            ValidatedAttributionSourceState::createFromTrustedUidNoPackage(attr, mMockProvider);
    ASSERT_FALSE(res.has_value());
    EXPECT_EQ(res.error(), ::android::BAD_VALUE);
}