Loading services/camera/libcameraservice/CameraService.cpp +2 −1 Original line number Diff line number Diff line Loading @@ -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", Loading services/camera/libcameraservice/tests/Android.bp +6 −0 Original line number Diff line number Diff line Loading @@ -27,8 +27,13 @@ cc_test { "external/dynamic_depth/internal", ], header_libs: [ "libmedia_headers", ], shared_libs: [ "libbase", "libbinder", "libcutils", "libcameraservice", "libhidlbase", Loading Loading @@ -57,6 +62,7 @@ cc_test { ], srcs: [ "CameraPermissionsTest.cpp", "CameraProviderManagerTest.cpp", "ClientManagerTest.cpp", "DepthProcessorTest.cpp", Loading services/camera/libcameraservice/tests/CameraPermissionsTest.cpp 0 → 100644 +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(); } } Loading
services/camera/libcameraservice/CameraService.cpp +2 −1 Original line number Diff line number Diff line Loading @@ -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", Loading
services/camera/libcameraservice/tests/Android.bp +6 −0 Original line number Diff line number Diff line Loading @@ -27,8 +27,13 @@ cc_test { "external/dynamic_depth/internal", ], header_libs: [ "libmedia_headers", ], shared_libs: [ "libbase", "libbinder", "libcutils", "libcameraservice", "libhidlbase", Loading Loading @@ -57,6 +62,7 @@ cc_test { ], srcs: [ "CameraPermissionsTest.cpp", "CameraProviderManagerTest.cpp", "ClientManagerTest.cpp", "DepthProcessorTest.cpp", Loading
services/camera/libcameraservice/tests/CameraPermissionsTest.cpp 0 → 100644 +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(); } }