303 lines
12 KiB
C++
303 lines
12 KiB
C++
/*
|
|
* 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), mOverrideCameraDisabled(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 getAutoframingOverride(const String16& packageName, int *ret) override {
|
|
return mCameraServiceProxy->getAutoframingOverride(packageName, 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(int userId, bool *ret) override {
|
|
if (mOverrideCameraDisabled) {
|
|
*ret = mCameraDisabled;
|
|
return binder::Status::ok();
|
|
}
|
|
return mCameraServiceProxy->isCameraDisabled(userId, 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__, /*overrideToPortrait*/false, &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__, /*overrideToPortrait*/false, &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__, /*overrideToPortrait*/false, &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__, /*overrideToPortrait*/false, &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__, /*overrideToPortrait*/false, &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__, /*overrideToPortrait*/false, &deviceB);
|
|
AutoDisconnectDevice autoDisconnectB(deviceB);
|
|
ASSERT_TRUE(status.isOk()) << "Exception code " << status.exceptionCode() <<
|
|
" service specific error code " << status.serviceSpecificErrorCode();
|
|
}
|
|
}
|