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

Commit b46a7e65 authored by Austin Borger's avatar Austin Borger Committed by Android (Google) Code Review
Browse files

Merge "cameraservice_test: Create a new test for camera permissions."

parents 6659abfa 86a588ef
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -1721,7 +1721,8 @@ Status CameraService::connectDevice(

    // enforce system camera permissions
    if (oomScoreOffset > 0 &&
            !hasPermissionsForSystemCamera(callingPid, CameraThreadState::getCallingUid())) {
            !hasPermissionsForSystemCamera(callingPid, CameraThreadState::getCallingUid()) &&
            !isTrustedCallingUid(CameraThreadState::getCallingUid())) {
        String8 msg =
                String8::format("Cannot change the priority of a client %s pid %d for "
                        "camera id %s without SYSTEM_CAMERA permissions",
+6 −0
Original line number Diff line number Diff line
@@ -27,8 +27,13 @@ cc_test {
        "external/dynamic_depth/internal",
    ],

    header_libs: [
        "libmedia_headers",
    ],

    shared_libs: [
        "libbase",
        "libbinder",
        "libcutils",
        "libcameraservice",
        "libhidlbase",
@@ -57,6 +62,7 @@ cc_test {
    ],

    srcs: [
        "CameraPermissionsTest.cpp",
        "CameraProviderManagerTest.cpp",
        "ClientManagerTest.cpp",
        "DepthProcessorTest.cpp",
+298 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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/hardware/BnCameraServiceListener.h>
#include <android/hardware/BnCameraServiceProxy.h>
#include <android/hardware/camera2/BnCameraDeviceCallbacks.h>
#include <android/hardware/ICameraService.h>

#include <private/android_filesystem_config.h>

#include "../CameraService.h"
#include "../utils/CameraServiceProxyWrapper.h"

#include <gtest/gtest.h>

#include <memory>
#include <vector>

using namespace android;
using namespace android::hardware::camera;

// Empty service listener.
class TestCameraServiceListener : public hardware::BnCameraServiceListener {
public:
    virtual ~TestCameraServiceListener() {};

    virtual binder::Status onStatusChanged(int32_t , const String16&) {
        return binder::Status::ok();
    };

    virtual binder::Status onPhysicalCameraStatusChanged(int32_t /*status*/,
            const String16& /*cameraId*/, const String16& /*physicalCameraId*/) {
        // No op
        return binder::Status::ok();
    };

    virtual binder::Status onTorchStatusChanged(int32_t /*status*/, const String16& /*cameraId*/) {
        return binder::Status::ok();
    };

    virtual binder::Status onCameraAccessPrioritiesChanged() {
        // No op
        return binder::Status::ok();
    }

    virtual binder::Status onCameraOpened(const String16& /*cameraId*/,
            const String16& /*clientPackageName*/) {
        // No op
        return binder::Status::ok();
    }

    virtual binder::Status onCameraClosed(const String16& /*cameraId*/) {
        // No op
        return binder::Status::ok();
    }

    virtual binder::Status onTorchStrengthLevelChanged(const String16& /*cameraId*/,
            int32_t /*torchStrength*/) {
        // No op
        return binder::Status::ok();
    }
};

// Empty device callback.
class TestCameraDeviceCallbacks : public hardware::camera2::BnCameraDeviceCallbacks {
public:
    TestCameraDeviceCallbacks() {}

    virtual ~TestCameraDeviceCallbacks() {}

    virtual binder::Status onDeviceError(int /*errorCode*/,
            const CaptureResultExtras& /*resultExtras*/) {
        return binder::Status::ok();
    }

    virtual binder::Status onDeviceIdle() {
        return binder::Status::ok();
    }

    virtual binder::Status onCaptureStarted(const CaptureResultExtras& /*resultExtras*/,
            int64_t /*timestamp*/) {
        return binder::Status::ok();
    }

    virtual binder::Status onResultReceived(const CameraMetadata& /*metadata*/,
            const CaptureResultExtras& /*resultExtras*/,
            const std::vector<PhysicalCaptureResultInfo>& /*physicalResultInfos*/) {
        return binder::Status::ok();
    }

    virtual binder::Status onPrepared(int /*streamId*/) {
        return binder::Status::ok();
    }

    virtual binder::Status onRepeatingRequestError(
            int64_t /*lastFrameNumber*/, int32_t /*stoppedSequenceId*/) {
        return binder::Status::ok();
    }

    virtual binder::Status onRequestQueueEmpty() {
        return binder::Status::ok();
    }
};

// Override isCameraDisabled from the CameraServiceProxy with a flag.
class CameraServiceProxyOverride : public ::android::hardware::BnCameraServiceProxy {
public:
    CameraServiceProxyOverride() :
            mCameraServiceProxy(CameraServiceProxyWrapper::getDefaultCameraServiceProxy()),
            mCameraDisabled(false)
    { }

    virtual binder::Status getRotateAndCropOverride(const String16& packageName, int lensFacing,
            int userId, int *ret) override {
        return mCameraServiceProxy->getRotateAndCropOverride(packageName, lensFacing,
                userId, ret);
    }

    virtual binder::Status pingForUserUpdate() override {
        return mCameraServiceProxy->pingForUserUpdate();
    }

    virtual binder::Status notifyCameraState(
            const hardware::CameraSessionStats& cameraSessionStats) override {
        return mCameraServiceProxy->notifyCameraState(cameraSessionStats);
    }

    virtual binder::Status isCameraDisabled(bool *ret) override {
        if (mOverrideCameraDisabled) {
            *ret = mCameraDisabled;
            return binder::Status::ok();
        }
        return mCameraServiceProxy->isCameraDisabled(ret);
    }

    void setCameraDisabled(bool cameraDisabled) {
        mCameraDisabled = cameraDisabled;
    }

    void setOverrideCameraDisabled(bool overrideCameraDisabled) {
        mOverrideCameraDisabled = overrideCameraDisabled;
    }

protected:
    sp<hardware::ICameraServiceProxy> mCameraServiceProxy;
    bool mCameraDisabled;
    bool mOverrideCameraDisabled;
};

class AutoDisconnectDevice {
public:
    AutoDisconnectDevice(sp<hardware::camera2::ICameraDeviceUser> device) :
            mDevice(device)
    { }

    ~AutoDisconnectDevice() {
        if (mDevice != nullptr) { 
            mDevice->disconnect();
        }
    }

private:
    sp<hardware::camera2::ICameraDeviceUser> mDevice;
};

class CameraPermissionsTest : public ::testing::Test {
protected:
    static sp<CameraService> sCameraService;
    static sp<CameraServiceProxyOverride> sCameraServiceProxy;
    static std::shared_ptr<CameraServiceProxyWrapper> sCameraServiceProxyWrapper;
    static uid_t sOldUid;

    static void SetUpTestSuite() {
        sOldUid = getuid();
        setuid(AID_CAMERASERVER);
        sCameraServiceProxy = new CameraServiceProxyOverride();
        sCameraServiceProxyWrapper =
            std::make_shared<CameraServiceProxyWrapper>(sCameraServiceProxy);
        sCameraService = new CameraService(sCameraServiceProxyWrapper);
        sCameraService->clearCachedVariables();
    }

    static void TearDownTestSuite() {
        sCameraServiceProxyWrapper = nullptr;
        sCameraServiceProxy = nullptr;
        sCameraService = nullptr;
        setuid(sOldUid);
    }
};

sp<CameraService> CameraPermissionsTest::sCameraService = nullptr;
sp<CameraServiceProxyOverride> CameraPermissionsTest::sCameraServiceProxy = nullptr;
std::shared_ptr<CameraServiceProxyWrapper>
CameraPermissionsTest::sCameraServiceProxyWrapper = nullptr;
uid_t CameraPermissionsTest::sOldUid = 0;

// Test that camera connections fail with ERROR_DISABLED when the camera is disabled via device
// policy, and succeed when it isn't.
TEST_F(CameraPermissionsTest, TestCameraDisabled) {
    std::vector<hardware::CameraStatus> statuses;
    sp<TestCameraServiceListener> serviceListener = new TestCameraServiceListener();
    sCameraService->addListenerTest(serviceListener, &statuses);
    sCameraServiceProxy->setOverrideCameraDisabled(true);

    sCameraServiceProxy->setCameraDisabled(true);
    for (auto s : statuses) {
        sp<TestCameraDeviceCallbacks> callbacks = new TestCameraDeviceCallbacks();
        sp<hardware::camera2::ICameraDeviceUser> device;
        binder::Status status =
                sCameraService->connectDevice(callbacks, String16(s.cameraId), String16(), {},
                android::CameraService::USE_CALLING_UID, 0/*oomScoreDiff*/,
                /*targetSdkVersion*/__ANDROID_API_FUTURE__, &device);
        AutoDisconnectDevice autoDisconnect(device);
        ASSERT_TRUE(!status.isOk()) << "connectDevice returned OK status";
        ASSERT_EQ(status.serviceSpecificErrorCode(), hardware::ICameraService::ERROR_DISABLED)
                << "connectDevice returned exception code " << status.exceptionCode();
    }

    sCameraServiceProxy->setCameraDisabled(false);
    for (auto s : statuses) {
        sp<TestCameraDeviceCallbacks> callbacks = new TestCameraDeviceCallbacks();
        sp<hardware::camera2::ICameraDeviceUser> device;
        binder::Status status =
                sCameraService->connectDevice(callbacks, String16(s.cameraId), String16(), {},
                android::CameraService::USE_CALLING_UID, 0/*oomScoreDiff*/,
                /*targetSdkVersion*/__ANDROID_API_FUTURE__, &device);
        AutoDisconnectDevice autoDisconnect(device);
        ASSERT_TRUE(status.isOk());
    }
}

// Test that consecutive camera connections succeed.
TEST_F(CameraPermissionsTest, TestConsecutiveConnections) {
    std::vector<hardware::CameraStatus> statuses;
    sp<TestCameraServiceListener> serviceListener = new TestCameraServiceListener();
    sCameraService->addListenerTest(serviceListener, &statuses);
    sCameraServiceProxy->setOverrideCameraDisabled(false);

    for (auto s : statuses) {
        sp<TestCameraDeviceCallbacks> callbacks = new TestCameraDeviceCallbacks();
        sp<hardware::camera2::ICameraDeviceUser> deviceA, deviceB;
        binder::Status status =
                sCameraService->connectDevice(callbacks, String16(s.cameraId), String16(), {},
                android::CameraService::USE_CALLING_UID, 0/*oomScoreDiff*/,
                /*targetSdkVersion*/__ANDROID_API_FUTURE__, &deviceA);
        AutoDisconnectDevice autoDisconnectA(deviceA);
        ASSERT_TRUE(status.isOk()) << "Exception code " << status.exceptionCode() <<
                " service specific error code " << status.serviceSpecificErrorCode();
        status =
                sCameraService->connectDevice(callbacks, String16(s.cameraId), String16(), {},
                android::CameraService::USE_CALLING_UID, 0/*oomScoreDiff*/,
                /*targetSdkVersion*/__ANDROID_API_FUTURE__, &deviceB);
        AutoDisconnectDevice autoDisconnectB(deviceB);
        ASSERT_TRUE(status.isOk()) << "Exception code " << status.exceptionCode() <<
                " service specific error code " << status.serviceSpecificErrorCode();
    }
}

// Test that consecutive camera connections succeed even when a nonzero oomScoreOffset is provided
// in the second call.
TEST_F(CameraPermissionsTest, TestConflictingOomScoreOffset) {
    std::vector<hardware::CameraStatus> statuses;
    sp<TestCameraServiceListener> serviceListener = new TestCameraServiceListener();
    sCameraService->addListenerTest(serviceListener, &statuses);
    sCameraServiceProxy->setOverrideCameraDisabled(false);

    for (auto s : statuses) {
        sp<TestCameraDeviceCallbacks> callbacks = new TestCameraDeviceCallbacks();
        sp<hardware::camera2::ICameraDeviceUser> deviceA, deviceB;
        binder::Status status =
                sCameraService->connectDevice(callbacks, String16(s.cameraId), String16(), {},
                android::CameraService::USE_CALLING_UID, 0/*oomScoreDiff*/,
                /*targetSdkVersion*/__ANDROID_API_FUTURE__, &deviceA);
        AutoDisconnectDevice autoDisconnectA(deviceA);
        ASSERT_TRUE(status.isOk()) << "Exception code " << status.exceptionCode() <<
                " service specific error code " << status.serviceSpecificErrorCode();
        status =
                sCameraService->connectDevice(callbacks, String16(s.cameraId), String16(), {},
                android::CameraService::USE_CALLING_UID, 1/*oomScoreDiff*/,
                /*targetSdkVersion*/__ANDROID_API_FUTURE__, &deviceB);
        AutoDisconnectDevice autoDisconnectB(deviceB);
        ASSERT_TRUE(status.isOk()) << "Exception code " << status.exceptionCode() <<
                " service specific error code " << status.serviceSpecificErrorCode();
    }
}