/* * Copyright 2023 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 "VirtualCameraProvider.h" #include "aidl/android/hardware/camera/common/CameraDeviceStatus.h" #include "aidl/android/hardware/camera/common/Status.h" #include "aidl/android/hardware/camera/common/TorchModeStatus.h" #include "aidl/android/hardware/camera/provider/BnCameraProviderCallback.h" #include "android/binder_auto_utils.h" #include "android/binder_interface_utils.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include "util/Util.h" namespace android { namespace companion { namespace virtualcamera { namespace { using ::aidl::android::companion::virtualcamera::Format; using ::aidl::android::companion::virtualcamera::LensFacing; using ::aidl::android::companion::virtualcamera::SensorOrientation; using ::aidl::android::companion::virtualcamera::SupportedStreamConfiguration; using ::aidl::android::companion::virtualcamera::VirtualCameraConfiguration; using ::aidl::android::hardware::camera::common::CameraDeviceStatus; using ::aidl::android::hardware::camera::common::Status; using ::aidl::android::hardware::camera::common::TorchModeStatus; using ::aidl::android::hardware::camera::provider::BnCameraProviderCallback; using ::testing::_; using ::testing::ElementsAre; using ::testing::Eq; using ::testing::IsEmpty; using ::testing::IsNull; using ::testing::MatchesRegex; using ::testing::Not; using ::testing::Return; constexpr char kCameraId[] = "42"; constexpr int kVgaWidth = 640; constexpr int kVgaHeight = 480; constexpr int kMaxFps = 30; constexpr int kDefaultDeviceId = 0; constexpr char kVirtualCameraNameRegex[] = "device@[0-9]+\\.[0-9]+/virtual/.+"; class MockCameraProviderCallback : public BnCameraProviderCallback { public: MOCK_METHOD(ndk::ScopedAStatus, cameraDeviceStatusChange, (const std::string&, CameraDeviceStatus), (override)); MOCK_METHOD(ndk::ScopedAStatus, torchModeStatusChange, (const std::string&, TorchModeStatus), (override)); MOCK_METHOD(ndk::ScopedAStatus, physicalCameraDeviceStatusChange, (const std::string&, const std::string&, CameraDeviceStatus), (override)); }; class VirtualCameraProviderTest : public ::testing::Test { public: void SetUp() override { mCameraProvider = ndk::SharedRefBase::make(); mMockCameraProviderCallback = ndk::SharedRefBase::make(); ON_CALL(*mMockCameraProviderCallback, cameraDeviceStatusChange) .WillByDefault([](const std::string&, CameraDeviceStatus) { return ndk::ScopedAStatus::ok(); }); } protected: std::shared_ptr mCameraProvider; std::shared_ptr mMockCameraProviderCallback = ndk::SharedRefBase::make(); VirtualCameraConfiguration mInputConfig = VirtualCameraConfiguration{ .supportedStreamConfigs = {SupportedStreamConfiguration{ .width = kVgaWidth, .height = kVgaHeight, .pixelFormat = Format::YUV_420_888, .maxFps = kMaxFps}}, .virtualCameraCallback = nullptr, .sensorOrientation = SensorOrientation::ORIENTATION_0, .lensFacing = LensFacing::FRONT}; }; TEST_F(VirtualCameraProviderTest, SetNullCameraCallbackFails) { // Attempting to set callback to nullptr should fail. EXPECT_FALSE(mCameraProvider->setCallback(nullptr).isOk()); } TEST_F(VirtualCameraProviderTest, NoCamerasInitially) { std::vector cameras; // Initially, the camera provider should return empty list // of cameras. ASSERT_TRUE(mCameraProvider->getCameraIdList(&cameras).isOk()); EXPECT_THAT(cameras, IsEmpty()); } TEST_F(VirtualCameraProviderTest, CreateCamera) { // When new camera is created, we expect // cameraDeviceStatusChange to be called exactly once with // PRESENT status. EXPECT_CALL(*mMockCameraProviderCallback, cameraDeviceStatusChange(_, CameraDeviceStatus::PRESENT)) .WillOnce(Return(ndk::ScopedAStatus::ok())); ASSERT_TRUE(mCameraProvider->setCallback(mMockCameraProviderCallback).isOk()); std::shared_ptr camera = mCameraProvider->createCamera(mInputConfig, kCameraId, kDefaultDeviceId); EXPECT_THAT(camera, Not(IsNull())); EXPECT_THAT(camera->getCameraName(), MatchesRegex(kVirtualCameraNameRegex)); // Created camera should be in the list of cameras. std::vector cameraIds; ASSERT_TRUE(mCameraProvider->getCameraIdList(&cameraIds).isOk()); EXPECT_THAT(cameraIds, ElementsAre(camera->getCameraName())); } TEST_F(VirtualCameraProviderTest, CreateCameraBeforeCallbackIsSet) { // We expect cameraDeviceStatusChange to be invoked even when the // setCallback configures the callback after camera is already created. EXPECT_CALL(*mMockCameraProviderCallback, cameraDeviceStatusChange(_, CameraDeviceStatus::PRESENT)) .WillOnce(Return(ndk::ScopedAStatus::ok())); std::shared_ptr camera = mCameraProvider->createCamera(mInputConfig, kCameraId, kDefaultDeviceId); ASSERT_TRUE(mCameraProvider->setCallback(mMockCameraProviderCallback).isOk()); // Created camera should be in the list of cameras. std::vector cameraIds; EXPECT_TRUE(mCameraProvider->getCameraIdList(&cameraIds).isOk()); EXPECT_THAT(cameraIds, ElementsAre(camera->getCameraName())); } TEST_F(VirtualCameraProviderTest, RemoveCamera) { ASSERT_TRUE(mCameraProvider->setCallback(mMockCameraProviderCallback).isOk()); std::shared_ptr camera = mCameraProvider->createCamera(mInputConfig, kCameraId, kDefaultDeviceId); EXPECT_CALL(*mMockCameraProviderCallback, cameraDeviceStatusChange(Eq(camera->getCameraName()), CameraDeviceStatus::NOT_PRESENT)) .WillOnce(Return(ndk::ScopedAStatus::ok())); EXPECT_TRUE(mCameraProvider->removeCamera(camera->getCameraName())); // There are no cameras present after only camera is removed. std::vector cameraIds; ASSERT_TRUE(mCameraProvider->getCameraIdList(&cameraIds).isOk()); EXPECT_THAT(cameraIds, IsEmpty()); } TEST_F(VirtualCameraProviderTest, RemoveNonExistingCamera) { ASSERT_TRUE(mCameraProvider->setCallback(mMockCameraProviderCallback).isOk()); std::shared_ptr camera = mCameraProvider->createCamera(mInputConfig, kCameraId, kDefaultDeviceId); // Removing non-existing camera should fail. const std::string cameraName = "DefinitelyNoTCamera"; EXPECT_FALSE(mCameraProvider->removeCamera(cameraName)); // Camera should be still present in the camera list. std::vector cameraIds; ASSERT_TRUE(mCameraProvider->getCameraIdList(&cameraIds).isOk()); EXPECT_THAT(cameraIds, ElementsAre(camera->getCameraName())); } } // namespace } // namespace virtualcamera } // namespace companion } // namespace android