blob: 671e031de8c4ad5871a1a126ef78d1a9fecebae2 [file] [log] [blame]
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +01001/*
Biswarup Pal6152a302023-12-19 12:44:09 +00002 * Copyright 2023 The Android Open Source Project
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +01003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <cstdint>
18#include <memory>
19
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010020#include "VirtualCameraDevice.h"
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010021#include "VirtualCameraSession.h"
22#include "aidl/android/companion/virtualcamera/BnVirtualCameraCallback.h"
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010023#include "aidl/android/companion/virtualcamera/SupportedStreamConfiguration.h"
Biswarup Pal6152a302023-12-19 12:44:09 +000024#include "aidl/android/companion/virtualcamera/VirtualCameraConfiguration.h"
Jan Sebechlebsky0bb5e092023-12-08 16:17:54 +010025#include "aidl/android/hardware/camera/common/Status.h"
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010026#include "aidl/android/hardware/camera/device/BnCameraDeviceCallback.h"
27#include "aidl/android/hardware/camera/device/StreamConfiguration.h"
28#include "aidl/android/hardware/graphics/common/PixelFormat.h"
29#include "android/binder_auto_utils.h"
30#include "android/binder_interface_utils.h"
31#include "gmock/gmock.h"
32#include "gtest/gtest.h"
Jan Sebechlebsky4ce32082024-02-14 16:02:11 +010033#include "util/MetadataUtil.h"
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010034
35namespace android {
36namespace companion {
37namespace virtualcamera {
38namespace {
39
Jan Sebechlebsky4c9bb1e2024-02-28 16:32:39 +010040constexpr int kQvgaWidth = 320;
41constexpr int kQvgaHeight = 240;
Jan Sebechlebsky39129f82024-01-19 16:42:11 +010042constexpr int kVgaWidth = 640;
43constexpr int kVgaHeight = 480;
44constexpr int kSvgaWidth = 800;
45constexpr int kSvgaHeight = 600;
Biswarup Pal6152a302023-12-19 12:44:09 +000046constexpr int kMaxFps = 30;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010047constexpr int kStreamId = 0;
Jan Sebechlebsky39129f82024-01-19 16:42:11 +010048constexpr int kSecondStreamId = 1;
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010049constexpr int kCameraId = 42;
Biswarup Pal37a75182024-01-16 15:53:35 +000050constexpr int kDefaultDeviceId = 0;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010051
52using ::aidl::android::companion::virtualcamera::BnVirtualCameraCallback;
53using ::aidl::android::companion::virtualcamera::Format;
Biswarup Pal112458f2023-12-28 19:50:17 +000054using ::aidl::android::companion::virtualcamera::LensFacing;
Biswarup Pal6152a302023-12-19 12:44:09 +000055using ::aidl::android::companion::virtualcamera::SensorOrientation;
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010056using ::aidl::android::companion::virtualcamera::SupportedStreamConfiguration;
Biswarup Pal6152a302023-12-19 12:44:09 +000057using ::aidl::android::companion::virtualcamera::VirtualCameraConfiguration;
Jan Sebechlebsky0bb5e092023-12-08 16:17:54 +010058using ::aidl::android::hardware::camera::common::Status;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010059using ::aidl::android::hardware::camera::device::BnCameraDeviceCallback;
60using ::aidl::android::hardware::camera::device::BufferRequest;
61using ::aidl::android::hardware::camera::device::BufferRequestStatus;
62using ::aidl::android::hardware::camera::device::CaptureRequest;
63using ::aidl::android::hardware::camera::device::CaptureResult;
64using ::aidl::android::hardware::camera::device::HalStream;
65using ::aidl::android::hardware::camera::device::NotifyMsg;
66using ::aidl::android::hardware::camera::device::Stream;
67using ::aidl::android::hardware::camera::device::StreamBuffer;
68using ::aidl::android::hardware::camera::device::StreamBufferRet;
69using ::aidl::android::hardware::camera::device::StreamConfiguration;
70using ::aidl::android::hardware::graphics::common::PixelFormat;
71using ::aidl::android::view::Surface;
72using ::testing::_;
73using ::testing::ElementsAre;
74using ::testing::Eq;
75using ::testing::Return;
76using ::testing::SizeIs;
77
78Stream createStream(int streamId, int width, int height, PixelFormat format) {
79 Stream s;
80 s.id = streamId;
81 s.width = width;
82 s.height = height;
83 s.format = format;
84 return s;
85}
86
87class MockCameraDeviceCallback : public BnCameraDeviceCallback {
88 public:
89 MOCK_METHOD(ndk::ScopedAStatus, notify, (const std::vector<NotifyMsg>&),
90 (override));
91 MOCK_METHOD(ndk::ScopedAStatus, processCaptureResult,
92 (const std::vector<CaptureResult>&), (override));
93 MOCK_METHOD(ndk::ScopedAStatus, requestStreamBuffers,
94 (const std::vector<BufferRequest>&, std::vector<StreamBufferRet>*,
95 BufferRequestStatus*),
96 (override));
97 MOCK_METHOD(ndk::ScopedAStatus, returnStreamBuffers,
98 (const std::vector<StreamBuffer>&), (override));
99};
100
101class MockVirtualCameraCallback : public BnVirtualCameraCallback {
102 public:
103 MOCK_METHOD(ndk::ScopedAStatus, onStreamConfigured,
104 (int, const Surface&, int32_t, int32_t, Format), (override));
105 MOCK_METHOD(ndk::ScopedAStatus, onProcessCaptureRequest, (int, int),
106 (override));
107 MOCK_METHOD(ndk::ScopedAStatus, onStreamClosed, (int), (override));
108};
109
Jan Sebechlebsky4c9bb1e2024-02-28 16:32:39 +0100110class VirtualCameraSessionTestBase : public ::testing::Test {
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100111 public:
Jan Sebechlebsky4c9bb1e2024-02-28 16:32:39 +0100112 virtual void SetUp() override {
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100113 mMockCameraDeviceCallback =
114 ndk::SharedRefBase::make<MockCameraDeviceCallback>();
115 mMockVirtualCameraClientCallback =
116 ndk::SharedRefBase::make<MockVirtualCameraCallback>();
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100117
Jan Sebechlebsky85a2aef2023-11-29 10:35:55 +0100118 // Explicitly defining default actions below to prevent gmock from
119 // default-constructing ndk::ScopedAStatus, because default-constructed
120 // status wraps nullptr AStatus and causes crash when attempting to print
121 // it in gtest report.
122 ON_CALL(*mMockCameraDeviceCallback, notify)
123 .WillByDefault(ndk::ScopedAStatus::ok);
124 ON_CALL(*mMockCameraDeviceCallback, processCaptureResult)
125 .WillByDefault(ndk::ScopedAStatus::ok);
126 ON_CALL(*mMockCameraDeviceCallback, requestStreamBuffers)
127 .WillByDefault(ndk::ScopedAStatus::ok);
128 ON_CALL(*mMockCameraDeviceCallback, returnStreamBuffers)
129 .WillByDefault(ndk::ScopedAStatus::ok);
130
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100131 ON_CALL(*mMockVirtualCameraClientCallback, onStreamConfigured)
132 .WillByDefault(ndk::ScopedAStatus::ok);
133 ON_CALL(*mMockVirtualCameraClientCallback, onProcessCaptureRequest)
134 .WillByDefault(ndk::ScopedAStatus::ok);
Jan Sebechlebsky85a2aef2023-11-29 10:35:55 +0100135 ON_CALL(*mMockVirtualCameraClientCallback, onStreamClosed)
136 .WillByDefault(ndk::ScopedAStatus::ok);
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100137 }
138
139 protected:
140 std::shared_ptr<MockCameraDeviceCallback> mMockCameraDeviceCallback;
141 std::shared_ptr<MockVirtualCameraCallback> mMockVirtualCameraClientCallback;
Jan Sebechlebsky4c9bb1e2024-02-28 16:32:39 +0100142};
143
144class VirtualCameraSessionTest : public VirtualCameraSessionTestBase {
145 public:
146 void SetUp() override {
147 VirtualCameraSessionTestBase::SetUp();
148
149 mVirtualCameraDevice = ndk::SharedRefBase::make<VirtualCameraDevice>(
150 kCameraId,
151 VirtualCameraConfiguration{
152 .supportedStreamConfigs = {SupportedStreamConfiguration{
153 .width = kVgaWidth,
154 .height = kVgaHeight,
155 .pixelFormat = Format::YUV_420_888,
156 .maxFps = kMaxFps},
157 SupportedStreamConfiguration{
158 .width = kSvgaWidth,
159 .height = kSvgaHeight,
160 .pixelFormat = Format::YUV_420_888,
161 .maxFps = kMaxFps}},
162 .virtualCameraCallback = mMockVirtualCameraClientCallback,
163 .sensorOrientation = SensorOrientation::ORIENTATION_0,
Biswarup Pal37a75182024-01-16 15:53:35 +0000164 .lensFacing = LensFacing::FRONT},
165 kDefaultDeviceId);
Jan Sebechlebsky4c9bb1e2024-02-28 16:32:39 +0100166 mVirtualCameraSession = ndk::SharedRefBase::make<VirtualCameraSession>(
167 mVirtualCameraDevice, mMockCameraDeviceCallback,
168 mMockVirtualCameraClientCallback);
169 }
170
171 protected:
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100172 std::shared_ptr<VirtualCameraDevice> mVirtualCameraDevice;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100173 std::shared_ptr<VirtualCameraSession> mVirtualCameraSession;
174};
175
176TEST_F(VirtualCameraSessionTest, ConfigureTriggersClientConfigureCallback) {
177 PixelFormat format = PixelFormat::YCBCR_420_888;
178 StreamConfiguration streamConfiguration;
179 streamConfiguration.streams = {
Jan Sebechlebsky39129f82024-01-19 16:42:11 +0100180 createStream(kStreamId, kVgaWidth, kVgaHeight, format),
181 createStream(kSecondStreamId, kSvgaWidth, kSvgaHeight, format)};
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100182 std::vector<HalStream> halStreams;
Jan Sebechlebsky39129f82024-01-19 16:42:11 +0100183
184 // Expect highest resolution to be picked for the client input.
185 EXPECT_CALL(*mMockVirtualCameraClientCallback,
186 onStreamConfigured(kStreamId, _, kSvgaWidth, kSvgaHeight,
187 Format::YUV_420_888));
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100188
189 ASSERT_TRUE(
190 mVirtualCameraSession->configureStreams(streamConfiguration, &halStreams)
191 .isOk());
192
193 EXPECT_THAT(halStreams, SizeIs(streamConfiguration.streams.size()));
Jan Sebechlebsky39129f82024-01-19 16:42:11 +0100194 EXPECT_THAT(mVirtualCameraSession->getStreamIds(),
195 ElementsAre(kStreamId, kSecondStreamId));
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100196}
197
198TEST_F(VirtualCameraSessionTest, SecondConfigureDropsUnreferencedStreams) {
199 PixelFormat format = PixelFormat::YCBCR_420_888;
200 StreamConfiguration streamConfiguration;
201 std::vector<HalStream> halStreams;
202
Jan Sebechlebsky39129f82024-01-19 16:42:11 +0100203 streamConfiguration.streams = {createStream(0, kVgaWidth, kVgaHeight, format),
204 createStream(1, kVgaWidth, kVgaHeight, format),
205 createStream(2, kVgaWidth, kVgaHeight, format)};
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100206 ASSERT_TRUE(
207 mVirtualCameraSession->configureStreams(streamConfiguration, &halStreams)
208 .isOk());
209
210 EXPECT_THAT(mVirtualCameraSession->getStreamIds(), ElementsAre(0, 1, 2));
211
Jan Sebechlebsky39129f82024-01-19 16:42:11 +0100212 streamConfiguration.streams = {createStream(0, kVgaWidth, kVgaHeight, format),
213 createStream(2, kVgaWidth, kVgaHeight, format),
214 createStream(3, kVgaWidth, kVgaHeight, format)};
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100215 ASSERT_TRUE(
216 mVirtualCameraSession->configureStreams(streamConfiguration, &halStreams)
217 .isOk());
218
219 EXPECT_THAT(mVirtualCameraSession->getStreamIds(), ElementsAre(0, 2, 3));
220}
221
222TEST_F(VirtualCameraSessionTest, CloseTriggersClientTerminateCallback) {
223 EXPECT_CALL(*mMockVirtualCameraClientCallback, onStreamClosed(kStreamId))
224 .WillOnce(Return(ndk::ScopedAStatus::ok()));
225
226 ASSERT_TRUE(mVirtualCameraSession->close().isOk());
227}
228
Jan Sebechlebskyb0d8cab2023-11-28 10:55:04 +0100229TEST_F(VirtualCameraSessionTest, FlushBeforeConfigure) {
230 // Flush request coming before the configure request finished
231 // (so potentially the thread is not yet running) should be
232 // gracefully handled.
233
234 EXPECT_TRUE(mVirtualCameraSession->flush().isOk());
235}
236
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100237TEST_F(VirtualCameraSessionTest, onProcessCaptureRequestTriggersClientCallback) {
238 StreamConfiguration streamConfiguration;
Jan Sebechlebsky39129f82024-01-19 16:42:11 +0100239 streamConfiguration.streams = {createStream(kStreamId, kVgaWidth, kVgaHeight,
240 PixelFormat::YCBCR_420_888)};
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100241 std::vector<CaptureRequest> requests(1);
242 requests[0].frameNumber = 42;
243 requests[0].settings = *(
244 MetadataBuilder().setControlAfMode(ANDROID_CONTROL_AF_MODE_AUTO).build());
245
246 std::vector<HalStream> halStreams;
247 ASSERT_TRUE(
248 mVirtualCameraSession->configureStreams(streamConfiguration, &halStreams)
249 .isOk());
250
251 EXPECT_CALL(*mMockVirtualCameraClientCallback,
252 onProcessCaptureRequest(kStreamId, requests[0].frameNumber))
253 .WillOnce(Return(ndk::ScopedAStatus::ok()));
254 int32_t aidlReturn = 0;
255 ASSERT_TRUE(mVirtualCameraSession
256 ->processCaptureRequest(requests, /*in_cachesToRemove=*/{},
257 &aidlReturn)
258 .isOk());
259 EXPECT_THAT(aidlReturn, Eq(requests.size()));
260}
261
Jan Sebechlebsky0bb5e092023-12-08 16:17:54 +0100262TEST_F(VirtualCameraSessionTest, configureAfterCameraRelease) {
263 StreamConfiguration streamConfiguration;
Jan Sebechlebsky39129f82024-01-19 16:42:11 +0100264 streamConfiguration.streams = {createStream(kStreamId, kVgaWidth, kVgaHeight,
265 PixelFormat::YCBCR_420_888)};
Jan Sebechlebsky0bb5e092023-12-08 16:17:54 +0100266 std::vector<HalStream> halStreams;
267
268 // Release virtual camera.
269 mVirtualCameraDevice.reset();
270
271 // Expect configuration attempt returns CAMERA_DISCONNECTED service specific code.
272 EXPECT_THAT(
273 mVirtualCameraSession->configureStreams(streamConfiguration, &halStreams)
274 .getServiceSpecificError(),
275 Eq(static_cast<int32_t>(Status::CAMERA_DISCONNECTED)));
276}
277
Jan Sebechlebsky39129f82024-01-19 16:42:11 +0100278TEST_F(VirtualCameraSessionTest, ConfigureWithEmptyStreams) {
279 StreamConfiguration streamConfiguration;
280 std::vector<HalStream> halStreams;
281
282 // Expect configuration attempt returns CAMERA_DISCONNECTED service specific code.
283 EXPECT_THAT(
284 mVirtualCameraSession->configureStreams(streamConfiguration, &halStreams)
285 .getServiceSpecificError(),
286 Eq(static_cast<int32_t>(Status::ILLEGAL_ARGUMENT)));
287}
288
Jan Sebechlebsky4c9bb1e2024-02-28 16:32:39 +0100289TEST_F(VirtualCameraSessionTest, ConfigureWithDifferentAspectRatioFails) {
290 StreamConfiguration streamConfiguration;
291 streamConfiguration.streams = {
292 createStream(kStreamId, kVgaWidth, kVgaHeight, PixelFormat::YCBCR_420_888),
293 createStream(kSecondStreamId, kVgaHeight, kVgaWidth,
294 PixelFormat::YCBCR_420_888)};
295
296 std::vector<HalStream> halStreams;
297
298 // Expect configuration attempt returns CAMERA_DISCONNECTED service specific code.
299 EXPECT_THAT(
300 mVirtualCameraSession->configureStreams(streamConfiguration, &halStreams)
301 .getServiceSpecificError(),
302 Eq(static_cast<int32_t>(Status::ILLEGAL_ARGUMENT)));
303}
304
305class VirtualCameraSessionInputChoiceTest : public VirtualCameraSessionTestBase {
306 public:
307 std::shared_ptr<VirtualCameraSession> createSession(
308 const std::vector<SupportedStreamConfiguration>& supportedInputConfigs) {
309 mVirtualCameraDevice = ndk::SharedRefBase::make<VirtualCameraDevice>(
Biswarup Pal37a75182024-01-16 15:53:35 +0000310 kCameraId,
311 VirtualCameraConfiguration{
312 .supportedStreamConfigs = supportedInputConfigs,
313 .virtualCameraCallback = mMockVirtualCameraClientCallback,
314 .sensorOrientation = SensorOrientation::ORIENTATION_0,
315 .lensFacing = LensFacing::FRONT},
316 kDefaultDeviceId);
Jan Sebechlebsky4c9bb1e2024-02-28 16:32:39 +0100317 return ndk::SharedRefBase::make<VirtualCameraSession>(
318 mVirtualCameraDevice, mMockCameraDeviceCallback,
319 mMockVirtualCameraClientCallback);
320 }
321
322 protected:
323 std::shared_ptr<VirtualCameraDevice> mVirtualCameraDevice;
324};
325
326TEST_F(VirtualCameraSessionInputChoiceTest,
327 configureChoosesCorrectInputStreamForDownsampledOutput) {
328 // Create camera configured to support SVGA YUV input and RGB QVGA input.
329 auto virtualCameraSession = createSession(
330 {SupportedStreamConfiguration{.width = kSvgaWidth,
331 .height = kSvgaHeight,
332 .pixelFormat = Format::YUV_420_888,
333 .maxFps = kMaxFps},
334 SupportedStreamConfiguration{.width = kQvgaWidth,
335 .height = kQvgaHeight,
336 .pixelFormat = Format::RGBA_8888,
337 .maxFps = kMaxFps}});
338
339 // Configure VGA stream. Expect SVGA input to be chosen to downscale from.
340 StreamConfiguration streamConfiguration;
341 streamConfiguration.streams = {createStream(
342 kStreamId, kVgaWidth, kVgaHeight, PixelFormat::IMPLEMENTATION_DEFINED)};
343 std::vector<HalStream> halStreams;
344
345 // Expect configuration attempt returns CAMERA_DISCONNECTED service specific code.
346 EXPECT_CALL(*mMockVirtualCameraClientCallback,
347 onStreamConfigured(kStreamId, _, kSvgaWidth, kSvgaHeight,
348 Format::YUV_420_888));
349 EXPECT_TRUE(
350 virtualCameraSession->configureStreams(streamConfiguration, &halStreams)
351 .isOk());
352}
353
354TEST_F(VirtualCameraSessionInputChoiceTest,
355 configureChoosesCorrectInputStreamForMatchingResolution) {
356 // Create camera configured to support SVGA YUV input and RGB QVGA input.
357 auto virtualCameraSession = createSession(
358 {SupportedStreamConfiguration{.width = kSvgaWidth,
359 .height = kSvgaHeight,
360 .pixelFormat = Format::YUV_420_888,
361 .maxFps = kMaxFps},
362 SupportedStreamConfiguration{.width = kQvgaWidth,
363 .height = kQvgaHeight,
364 .pixelFormat = Format::RGBA_8888,
365 .maxFps = kMaxFps}});
366
367 // Configure VGA stream. Expect SVGA input to be chosen to downscale from.
368 StreamConfiguration streamConfiguration;
369 streamConfiguration.streams = {createStream(
370 kStreamId, kQvgaWidth, kQvgaHeight, PixelFormat::IMPLEMENTATION_DEFINED)};
371 std::vector<HalStream> halStreams;
372
373 // Expect configuration attempt returns CAMERA_DISCONNECTED service specific code.
374 EXPECT_CALL(*mMockVirtualCameraClientCallback,
375 onStreamConfigured(kStreamId, _, kQvgaWidth, kQvgaHeight,
376 Format::RGBA_8888));
377 EXPECT_TRUE(
378 virtualCameraSession->configureStreams(streamConfiguration, &halStreams)
379 .isOk());
380}
381
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100382} // namespace
383} // namespace virtualcamera
384} // namespace companion
385} // namespace android