blob: 5f313a0677cd58d4f943e51a39f56cf446c2d010 [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;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010050
51using ::aidl::android::companion::virtualcamera::BnVirtualCameraCallback;
52using ::aidl::android::companion::virtualcamera::Format;
Biswarup Pal112458f2023-12-28 19:50:17 +000053using ::aidl::android::companion::virtualcamera::LensFacing;
Biswarup Pal6152a302023-12-19 12:44:09 +000054using ::aidl::android::companion::virtualcamera::SensorOrientation;
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010055using ::aidl::android::companion::virtualcamera::SupportedStreamConfiguration;
Biswarup Pal6152a302023-12-19 12:44:09 +000056using ::aidl::android::companion::virtualcamera::VirtualCameraConfiguration;
Jan Sebechlebsky0bb5e092023-12-08 16:17:54 +010057using ::aidl::android::hardware::camera::common::Status;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010058using ::aidl::android::hardware::camera::device::BnCameraDeviceCallback;
59using ::aidl::android::hardware::camera::device::BufferRequest;
60using ::aidl::android::hardware::camera::device::BufferRequestStatus;
61using ::aidl::android::hardware::camera::device::CaptureRequest;
62using ::aidl::android::hardware::camera::device::CaptureResult;
63using ::aidl::android::hardware::camera::device::HalStream;
64using ::aidl::android::hardware::camera::device::NotifyMsg;
65using ::aidl::android::hardware::camera::device::Stream;
66using ::aidl::android::hardware::camera::device::StreamBuffer;
67using ::aidl::android::hardware::camera::device::StreamBufferRet;
68using ::aidl::android::hardware::camera::device::StreamConfiguration;
69using ::aidl::android::hardware::graphics::common::PixelFormat;
70using ::aidl::android::view::Surface;
71using ::testing::_;
72using ::testing::ElementsAre;
73using ::testing::Eq;
74using ::testing::Return;
75using ::testing::SizeIs;
76
77Stream createStream(int streamId, int width, int height, PixelFormat format) {
78 Stream s;
79 s.id = streamId;
80 s.width = width;
81 s.height = height;
82 s.format = format;
83 return s;
84}
85
86class MockCameraDeviceCallback : public BnCameraDeviceCallback {
87 public:
88 MOCK_METHOD(ndk::ScopedAStatus, notify, (const std::vector<NotifyMsg>&),
89 (override));
90 MOCK_METHOD(ndk::ScopedAStatus, processCaptureResult,
91 (const std::vector<CaptureResult>&), (override));
92 MOCK_METHOD(ndk::ScopedAStatus, requestStreamBuffers,
93 (const std::vector<BufferRequest>&, std::vector<StreamBufferRet>*,
94 BufferRequestStatus*),
95 (override));
96 MOCK_METHOD(ndk::ScopedAStatus, returnStreamBuffers,
97 (const std::vector<StreamBuffer>&), (override));
98};
99
100class MockVirtualCameraCallback : public BnVirtualCameraCallback {
101 public:
102 MOCK_METHOD(ndk::ScopedAStatus, onStreamConfigured,
103 (int, const Surface&, int32_t, int32_t, Format), (override));
104 MOCK_METHOD(ndk::ScopedAStatus, onProcessCaptureRequest, (int, int),
105 (override));
106 MOCK_METHOD(ndk::ScopedAStatus, onStreamClosed, (int), (override));
107};
108
Jan Sebechlebsky4c9bb1e2024-02-28 16:32:39 +0100109class VirtualCameraSessionTestBase : public ::testing::Test {
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100110 public:
Jan Sebechlebsky4c9bb1e2024-02-28 16:32:39 +0100111 virtual void SetUp() override {
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100112 mMockCameraDeviceCallback =
113 ndk::SharedRefBase::make<MockCameraDeviceCallback>();
114 mMockVirtualCameraClientCallback =
115 ndk::SharedRefBase::make<MockVirtualCameraCallback>();
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100116
Jan Sebechlebsky85a2aef2023-11-29 10:35:55 +0100117 // Explicitly defining default actions below to prevent gmock from
118 // default-constructing ndk::ScopedAStatus, because default-constructed
119 // status wraps nullptr AStatus and causes crash when attempting to print
120 // it in gtest report.
121 ON_CALL(*mMockCameraDeviceCallback, notify)
122 .WillByDefault(ndk::ScopedAStatus::ok);
123 ON_CALL(*mMockCameraDeviceCallback, processCaptureResult)
124 .WillByDefault(ndk::ScopedAStatus::ok);
125 ON_CALL(*mMockCameraDeviceCallback, requestStreamBuffers)
126 .WillByDefault(ndk::ScopedAStatus::ok);
127 ON_CALL(*mMockCameraDeviceCallback, returnStreamBuffers)
128 .WillByDefault(ndk::ScopedAStatus::ok);
129
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100130 ON_CALL(*mMockVirtualCameraClientCallback, onStreamConfigured)
131 .WillByDefault(ndk::ScopedAStatus::ok);
132 ON_CALL(*mMockVirtualCameraClientCallback, onProcessCaptureRequest)
133 .WillByDefault(ndk::ScopedAStatus::ok);
Jan Sebechlebsky85a2aef2023-11-29 10:35:55 +0100134 ON_CALL(*mMockVirtualCameraClientCallback, onStreamClosed)
135 .WillByDefault(ndk::ScopedAStatus::ok);
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100136 }
137
138 protected:
139 std::shared_ptr<MockCameraDeviceCallback> mMockCameraDeviceCallback;
140 std::shared_ptr<MockVirtualCameraCallback> mMockVirtualCameraClientCallback;
Jan Sebechlebsky4c9bb1e2024-02-28 16:32:39 +0100141};
142
143class VirtualCameraSessionTest : public VirtualCameraSessionTestBase {
144 public:
145 void SetUp() override {
146 VirtualCameraSessionTestBase::SetUp();
147
148 mVirtualCameraDevice = ndk::SharedRefBase::make<VirtualCameraDevice>(
149 kCameraId,
150 VirtualCameraConfiguration{
151 .supportedStreamConfigs = {SupportedStreamConfiguration{
152 .width = kVgaWidth,
153 .height = kVgaHeight,
154 .pixelFormat = Format::YUV_420_888,
155 .maxFps = kMaxFps},
156 SupportedStreamConfiguration{
157 .width = kSvgaWidth,
158 .height = kSvgaHeight,
159 .pixelFormat = Format::YUV_420_888,
160 .maxFps = kMaxFps}},
161 .virtualCameraCallback = mMockVirtualCameraClientCallback,
162 .sensorOrientation = SensorOrientation::ORIENTATION_0,
163 .lensFacing = LensFacing::FRONT});
164 mVirtualCameraSession = ndk::SharedRefBase::make<VirtualCameraSession>(
165 mVirtualCameraDevice, mMockCameraDeviceCallback,
166 mMockVirtualCameraClientCallback);
167 }
168
169 protected:
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100170 std::shared_ptr<VirtualCameraDevice> mVirtualCameraDevice;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100171 std::shared_ptr<VirtualCameraSession> mVirtualCameraSession;
172};
173
174TEST_F(VirtualCameraSessionTest, ConfigureTriggersClientConfigureCallback) {
175 PixelFormat format = PixelFormat::YCBCR_420_888;
176 StreamConfiguration streamConfiguration;
177 streamConfiguration.streams = {
Jan Sebechlebsky39129f82024-01-19 16:42:11 +0100178 createStream(kStreamId, kVgaWidth, kVgaHeight, format),
179 createStream(kSecondStreamId, kSvgaWidth, kSvgaHeight, format)};
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100180 std::vector<HalStream> halStreams;
Jan Sebechlebsky39129f82024-01-19 16:42:11 +0100181
182 // Expect highest resolution to be picked for the client input.
183 EXPECT_CALL(*mMockVirtualCameraClientCallback,
184 onStreamConfigured(kStreamId, _, kSvgaWidth, kSvgaHeight,
185 Format::YUV_420_888));
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100186
187 ASSERT_TRUE(
188 mVirtualCameraSession->configureStreams(streamConfiguration, &halStreams)
189 .isOk());
190
191 EXPECT_THAT(halStreams, SizeIs(streamConfiguration.streams.size()));
Jan Sebechlebsky39129f82024-01-19 16:42:11 +0100192 EXPECT_THAT(mVirtualCameraSession->getStreamIds(),
193 ElementsAre(kStreamId, kSecondStreamId));
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100194}
195
196TEST_F(VirtualCameraSessionTest, SecondConfigureDropsUnreferencedStreams) {
197 PixelFormat format = PixelFormat::YCBCR_420_888;
198 StreamConfiguration streamConfiguration;
199 std::vector<HalStream> halStreams;
200
Jan Sebechlebsky39129f82024-01-19 16:42:11 +0100201 streamConfiguration.streams = {createStream(0, kVgaWidth, kVgaHeight, format),
202 createStream(1, kVgaWidth, kVgaHeight, format),
203 createStream(2, kVgaWidth, kVgaHeight, format)};
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100204 ASSERT_TRUE(
205 mVirtualCameraSession->configureStreams(streamConfiguration, &halStreams)
206 .isOk());
207
208 EXPECT_THAT(mVirtualCameraSession->getStreamIds(), ElementsAre(0, 1, 2));
209
Jan Sebechlebsky39129f82024-01-19 16:42:11 +0100210 streamConfiguration.streams = {createStream(0, kVgaWidth, kVgaHeight, format),
211 createStream(2, kVgaWidth, kVgaHeight, format),
212 createStream(3, kVgaWidth, kVgaHeight, format)};
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100213 ASSERT_TRUE(
214 mVirtualCameraSession->configureStreams(streamConfiguration, &halStreams)
215 .isOk());
216
217 EXPECT_THAT(mVirtualCameraSession->getStreamIds(), ElementsAre(0, 2, 3));
218}
219
220TEST_F(VirtualCameraSessionTest, CloseTriggersClientTerminateCallback) {
221 EXPECT_CALL(*mMockVirtualCameraClientCallback, onStreamClosed(kStreamId))
222 .WillOnce(Return(ndk::ScopedAStatus::ok()));
223
224 ASSERT_TRUE(mVirtualCameraSession->close().isOk());
225}
226
Jan Sebechlebskyb0d8cab2023-11-28 10:55:04 +0100227TEST_F(VirtualCameraSessionTest, FlushBeforeConfigure) {
228 // Flush request coming before the configure request finished
229 // (so potentially the thread is not yet running) should be
230 // gracefully handled.
231
232 EXPECT_TRUE(mVirtualCameraSession->flush().isOk());
233}
234
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100235TEST_F(VirtualCameraSessionTest, onProcessCaptureRequestTriggersClientCallback) {
236 StreamConfiguration streamConfiguration;
Jan Sebechlebsky39129f82024-01-19 16:42:11 +0100237 streamConfiguration.streams = {createStream(kStreamId, kVgaWidth, kVgaHeight,
238 PixelFormat::YCBCR_420_888)};
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100239 std::vector<CaptureRequest> requests(1);
240 requests[0].frameNumber = 42;
241 requests[0].settings = *(
242 MetadataBuilder().setControlAfMode(ANDROID_CONTROL_AF_MODE_AUTO).build());
243
244 std::vector<HalStream> halStreams;
245 ASSERT_TRUE(
246 mVirtualCameraSession->configureStreams(streamConfiguration, &halStreams)
247 .isOk());
248
249 EXPECT_CALL(*mMockVirtualCameraClientCallback,
250 onProcessCaptureRequest(kStreamId, requests[0].frameNumber))
251 .WillOnce(Return(ndk::ScopedAStatus::ok()));
252 int32_t aidlReturn = 0;
253 ASSERT_TRUE(mVirtualCameraSession
254 ->processCaptureRequest(requests, /*in_cachesToRemove=*/{},
255 &aidlReturn)
256 .isOk());
257 EXPECT_THAT(aidlReturn, Eq(requests.size()));
258}
259
Jan Sebechlebsky0bb5e092023-12-08 16:17:54 +0100260TEST_F(VirtualCameraSessionTest, configureAfterCameraRelease) {
261 StreamConfiguration streamConfiguration;
Jan Sebechlebsky39129f82024-01-19 16:42:11 +0100262 streamConfiguration.streams = {createStream(kStreamId, kVgaWidth, kVgaHeight,
263 PixelFormat::YCBCR_420_888)};
Jan Sebechlebsky0bb5e092023-12-08 16:17:54 +0100264 std::vector<HalStream> halStreams;
265
266 // Release virtual camera.
267 mVirtualCameraDevice.reset();
268
269 // Expect configuration attempt returns CAMERA_DISCONNECTED service specific code.
270 EXPECT_THAT(
271 mVirtualCameraSession->configureStreams(streamConfiguration, &halStreams)
272 .getServiceSpecificError(),
273 Eq(static_cast<int32_t>(Status::CAMERA_DISCONNECTED)));
274}
275
Jan Sebechlebsky39129f82024-01-19 16:42:11 +0100276TEST_F(VirtualCameraSessionTest, ConfigureWithEmptyStreams) {
277 StreamConfiguration streamConfiguration;
278 std::vector<HalStream> halStreams;
279
280 // Expect configuration attempt returns CAMERA_DISCONNECTED service specific code.
281 EXPECT_THAT(
282 mVirtualCameraSession->configureStreams(streamConfiguration, &halStreams)
283 .getServiceSpecificError(),
284 Eq(static_cast<int32_t>(Status::ILLEGAL_ARGUMENT)));
285}
286
Jan Sebechlebsky4c9bb1e2024-02-28 16:32:39 +0100287TEST_F(VirtualCameraSessionTest, ConfigureWithDifferentAspectRatioFails) {
288 StreamConfiguration streamConfiguration;
289 streamConfiguration.streams = {
290 createStream(kStreamId, kVgaWidth, kVgaHeight, PixelFormat::YCBCR_420_888),
291 createStream(kSecondStreamId, kVgaHeight, kVgaWidth,
292 PixelFormat::YCBCR_420_888)};
293
294 std::vector<HalStream> halStreams;
295
296 // Expect configuration attempt returns CAMERA_DISCONNECTED service specific code.
297 EXPECT_THAT(
298 mVirtualCameraSession->configureStreams(streamConfiguration, &halStreams)
299 .getServiceSpecificError(),
300 Eq(static_cast<int32_t>(Status::ILLEGAL_ARGUMENT)));
301}
302
303class VirtualCameraSessionInputChoiceTest : public VirtualCameraSessionTestBase {
304 public:
305 std::shared_ptr<VirtualCameraSession> createSession(
306 const std::vector<SupportedStreamConfiguration>& supportedInputConfigs) {
307 mVirtualCameraDevice = ndk::SharedRefBase::make<VirtualCameraDevice>(
308 kCameraId, VirtualCameraConfiguration{
309 .supportedStreamConfigs = supportedInputConfigs,
310 .virtualCameraCallback = mMockVirtualCameraClientCallback,
311 .sensorOrientation = SensorOrientation::ORIENTATION_0,
312 .lensFacing = LensFacing::FRONT});
313 return ndk::SharedRefBase::make<VirtualCameraSession>(
314 mVirtualCameraDevice, mMockCameraDeviceCallback,
315 mMockVirtualCameraClientCallback);
316 }
317
318 protected:
319 std::shared_ptr<VirtualCameraDevice> mVirtualCameraDevice;
320};
321
322TEST_F(VirtualCameraSessionInputChoiceTest,
323 configureChoosesCorrectInputStreamForDownsampledOutput) {
324 // Create camera configured to support SVGA YUV input and RGB QVGA input.
325 auto virtualCameraSession = createSession(
326 {SupportedStreamConfiguration{.width = kSvgaWidth,
327 .height = kSvgaHeight,
328 .pixelFormat = Format::YUV_420_888,
329 .maxFps = kMaxFps},
330 SupportedStreamConfiguration{.width = kQvgaWidth,
331 .height = kQvgaHeight,
332 .pixelFormat = Format::RGBA_8888,
333 .maxFps = kMaxFps}});
334
335 // Configure VGA stream. Expect SVGA input to be chosen to downscale from.
336 StreamConfiguration streamConfiguration;
337 streamConfiguration.streams = {createStream(
338 kStreamId, kVgaWidth, kVgaHeight, PixelFormat::IMPLEMENTATION_DEFINED)};
339 std::vector<HalStream> halStreams;
340
341 // Expect configuration attempt returns CAMERA_DISCONNECTED service specific code.
342 EXPECT_CALL(*mMockVirtualCameraClientCallback,
343 onStreamConfigured(kStreamId, _, kSvgaWidth, kSvgaHeight,
344 Format::YUV_420_888));
345 EXPECT_TRUE(
346 virtualCameraSession->configureStreams(streamConfiguration, &halStreams)
347 .isOk());
348}
349
350TEST_F(VirtualCameraSessionInputChoiceTest,
351 configureChoosesCorrectInputStreamForMatchingResolution) {
352 // Create camera configured to support SVGA YUV input and RGB QVGA input.
353 auto virtualCameraSession = createSession(
354 {SupportedStreamConfiguration{.width = kSvgaWidth,
355 .height = kSvgaHeight,
356 .pixelFormat = Format::YUV_420_888,
357 .maxFps = kMaxFps},
358 SupportedStreamConfiguration{.width = kQvgaWidth,
359 .height = kQvgaHeight,
360 .pixelFormat = Format::RGBA_8888,
361 .maxFps = kMaxFps}});
362
363 // Configure VGA stream. Expect SVGA input to be chosen to downscale from.
364 StreamConfiguration streamConfiguration;
365 streamConfiguration.streams = {createStream(
366 kStreamId, kQvgaWidth, kQvgaHeight, PixelFormat::IMPLEMENTATION_DEFINED)};
367 std::vector<HalStream> halStreams;
368
369 // Expect configuration attempt returns CAMERA_DISCONNECTED service specific code.
370 EXPECT_CALL(*mMockVirtualCameraClientCallback,
371 onStreamConfigured(kStreamId, _, kQvgaWidth, kQvgaHeight,
372 Format::RGBA_8888));
373 EXPECT_TRUE(
374 virtualCameraSession->configureStreams(streamConfiguration, &halStreams)
375 .isOk());
376}
377
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100378} // namespace
379} // namespace virtualcamera
380} // namespace companion
381} // namespace android