/* * 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 #include #include #include #include #include #include #include "../CameraService.h" #include "../utils/CameraServiceProxyWrapper.h" #include #include #include using namespace android; using namespace android::hardware::camera; using android::hardware::camera2::CameraMetadataInfo; // Empty service listener. class TestCameraServiceListener : public hardware::BnCameraServiceListener { public: virtual ~TestCameraServiceListener() {} virtual binder::Status onStatusChanged(int32_t , const std::string&, int32_t) { return binder::Status::ok(); } virtual binder::Status onPhysicalCameraStatusChanged(int32_t /*status*/, const std::string& /*cameraId*/, const std::string& /*physicalCameraId*/, int32_t /*deviceId*/) { // No op return binder::Status::ok(); } virtual binder::Status onTorchStatusChanged(int32_t /*status*/, const std::string& /*cameraId*/, int32_t /*deviceId*/) { return binder::Status::ok(); } virtual binder::Status onCameraAccessPrioritiesChanged() { // No op return binder::Status::ok(); } virtual binder::Status onCameraOpened(const std::string& /*cameraId*/, const std::string& /*clientPackageName*/, int32_t /*deviceId*/) { // No op return binder::Status::ok(); } virtual binder::Status onCameraClosed(const std::string& /*cameraId*/, int32_t /*deviceId*/) { // No op return binder::Status::ok(); } virtual binder::Status onTorchStrengthLevelChanged(const std::string& /*cameraId*/, int32_t /*torchStrength*/, int32_t /*deviceId*/) { // No op return binder::Status::ok(); } virtual binder::Status onCameraOpenedInSharedMode(const std::string& /*cameraId*/, const std::string& /*clientPackageName*/, int32_t /*deviceId*/, bool /*isPrimaryClient*/) { // 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 CameraMetadataInfo& /*metadata*/, const CaptureResultExtras& /*resultExtras*/, const std::vector& /*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(); } virtual binder::Status onClientSharedAccessPriorityChanged(bool /*isPrimaryClient*/) { 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 std::string& packageName, int lensFacing, int userId, int *ret) override { return mCameraServiceProxy->getRotateAndCropOverride(packageName, lensFacing, userId, ret); } virtual binder::Status getAutoframingOverride(const std::string& 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 notifyFeatureCombinationStats( const hardware::CameraFeatureCombinationStats& featureCombStats) override { return mCameraServiceProxy->notifyFeatureCombinationStats(featureCombStats); } 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 mCameraServiceProxy; bool mCameraDisabled; bool mOverrideCameraDisabled; }; class AutoDisconnectDevice { public: AutoDisconnectDevice(sp device) : mDevice(device) { } ~AutoDisconnectDevice() { if (mDevice != nullptr) { mDevice->disconnect(); } } private: sp mDevice; }; class CameraPermissionsTest : public ::testing::Test { protected: static sp sCameraService; static sp sCameraServiceProxy; static std::shared_ptr sCameraServiceProxyWrapper; static uid_t sOldUid; static void SetUpTestSuite() { sOldUid = getuid(); setuid(AID_CAMERASERVER); sCameraServiceProxy = new CameraServiceProxyOverride(); sCameraServiceProxyWrapper = std::make_shared(sCameraServiceProxy); sCameraService = new CameraService(sCameraServiceProxyWrapper); sCameraService->clearCachedVariables(); } static void TearDownTestSuite() { sCameraServiceProxyWrapper = nullptr; sCameraServiceProxy = nullptr; sCameraService = nullptr; setuid(sOldUid); } }; sp CameraPermissionsTest::sCameraService = nullptr; sp CameraPermissionsTest::sCameraServiceProxy = nullptr; std::shared_ptr 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) { AttributionSourceState clientAttribution; clientAttribution.deviceId = kDefaultDeviceId; clientAttribution.uid = android::CameraService::USE_CALLING_UID; clientAttribution.pid = android::CameraService::USE_CALLING_PID; std::vector statuses; sp serviceListener = new TestCameraServiceListener(); sCameraService->addListenerTest(serviceListener, &statuses); sCameraServiceProxy->setOverrideCameraDisabled(true); sCameraServiceProxy->setCameraDisabled(true); for (auto s : statuses) { sp callbacks = new TestCameraDeviceCallbacks(); sp device; binder::Status status = sCameraService->connectDevice(callbacks, s.cameraId, 0/*oomScoreDiff*/, /*targetSdkVersion*/__ANDROID_API_FUTURE__, hardware::ICameraService::ROTATION_OVERRIDE_NONE, clientAttribution, /*devicePolicy*/0, /*sharedMode*/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 callbacks = new TestCameraDeviceCallbacks(); sp device; binder::Status status = sCameraService->connectDevice(callbacks, s.cameraId, 0/*oomScoreDiff*/, /*targetSdkVersion*/__ANDROID_API_FUTURE__, hardware::ICameraService::ROTATION_OVERRIDE_NONE, clientAttribution, /*devicePolicy*/0, /*sharedMode*/false, &device); AutoDisconnectDevice autoDisconnect(device); ASSERT_TRUE(status.isOk()); } } // Test that consecutive camera connections succeed. TEST_F(CameraPermissionsTest, TestConsecutiveConnections) { AttributionSourceState clientAttribution; clientAttribution.deviceId = kDefaultDeviceId; clientAttribution.uid = android::CameraService::USE_CALLING_UID; std::vector statuses; sp serviceListener = new TestCameraServiceListener(); sCameraService->addListenerTest(serviceListener, &statuses); sCameraServiceProxy->setOverrideCameraDisabled(false); for (auto s : statuses) { sp callbacks = new TestCameraDeviceCallbacks(); sp deviceA, deviceB; binder::Status status = sCameraService->connectDevice(callbacks, s.cameraId, 0/*oomScoreDiff*/, /*targetSdkVersion*/__ANDROID_API_FUTURE__, hardware::ICameraService::ROTATION_OVERRIDE_NONE, clientAttribution, /*devicePolicy*/0, /*sharedMode*/false, &deviceA); AutoDisconnectDevice autoDisconnectA(deviceA); ASSERT_TRUE(status.isOk()) << "Exception code " << status.exceptionCode() << " service specific error code " << status.serviceSpecificErrorCode(); status = sCameraService->connectDevice(callbacks, s.cameraId, 0/*oomScoreDiff*/, /*targetSdkVersion*/__ANDROID_API_FUTURE__, hardware::ICameraService::ROTATION_OVERRIDE_NONE, clientAttribution, /*devicePolicy*/0, /*sharedMode*/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) { AttributionSourceState clientAttribution; clientAttribution.deviceId = kDefaultDeviceId; clientAttribution.uid = android::CameraService::USE_CALLING_UID; std::vector statuses; sp serviceListener = new TestCameraServiceListener(); sCameraService->addListenerTest(serviceListener, &statuses); sCameraServiceProxy->setOverrideCameraDisabled(false); for (auto s : statuses) { sp callbacks = new TestCameraDeviceCallbacks(); sp deviceA, deviceB; binder::Status status = sCameraService->connectDevice(callbacks, s.cameraId, 0/*oomScoreDiff*/, /*targetSdkVersion*/__ANDROID_API_FUTURE__, hardware::ICameraService::ROTATION_OVERRIDE_NONE, clientAttribution, /*devicePolicy*/0, /*sharedMode*/false, &deviceA); AutoDisconnectDevice autoDisconnectA(deviceA); ASSERT_TRUE(status.isOk()) << "Exception code " << status.exceptionCode() << " service specific error code " << status.serviceSpecificErrorCode(); status = sCameraService->connectDevice(callbacks, s.cameraId, 1/*oomScoreDiff*/, /*targetSdkVersion*/__ANDROID_API_FUTURE__, hardware::ICameraService::ROTATION_OVERRIDE_NONE, clientAttribution, /*devicePolicy*/0, /*sharedMode*/false, &deviceB); AutoDisconnectDevice autoDisconnectB(deviceB); ASSERT_TRUE(status.isOk()) << "Exception code " << status.exceptionCode() << " service specific error code " << status.serviceSpecificErrorCode(); } }