Allow to specify list of supported input configurations.

... and populate corresponding metadata entries / perform
validation based on these.

Bug: 301023410
Test: atest virtual_camera_tests
Change-Id: I66f3cf2b013d5845b6fa7429294a1ed2157318f8
diff --git a/services/camera/virtualcamera/tests/VirtualCameraDeviceTest.cc b/services/camera/virtualcamera/tests/VirtualCameraDeviceTest.cc
new file mode 100644
index 0000000..140ae65
--- /dev/null
+++ b/services/camera/virtualcamera/tests/VirtualCameraDeviceTest.cc
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 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 <memory>
+
+#include "VirtualCameraDevice.h"
+#include "aidl/android/companion/virtualcamera/Format.h"
+#include "aidl/android/companion/virtualcamera/SupportedStreamConfiguration.h"
+#include "aidl/android/hardware/camera/device/CameraMetadata.h"
+#include "aidl/android/hardware/camera/device/StreamConfiguration.h"
+#include "android/binder_interface_utils.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "log/log_main.h"
+#include "system/camera_metadata.h"
+#include "utils/Errors.h"
+
+namespace android {
+namespace companion {
+namespace virtualcamera {
+namespace {
+
+using ::aidl::android::companion::virtualcamera::Format;
+using ::aidl::android::companion::virtualcamera::SupportedStreamConfiguration;
+using ::aidl::android::hardware::camera::device::CameraMetadata;
+using ::aidl::android::hardware::camera::device::Stream;
+using ::aidl::android::hardware::camera::device::StreamConfiguration;
+using ::aidl::android::hardware::camera::device::StreamType;
+using ::aidl::android::hardware::graphics::common::PixelFormat;
+using ::testing::UnorderedElementsAreArray;
+using metadata_stream_t =
+    camera_metadata_enum_android_scaler_available_stream_configurations_t;
+
+constexpr int kCameraId = 42;
+constexpr int kVgaWidth = 640;
+constexpr int kVgaHeight = 480;
+constexpr int kHdWidth = 1280;
+constexpr int kHdHeight = 720;
+
+struct AvailableStreamConfiguration {
+  const int width;
+  const int height;
+  const int pixelFormat;
+  const metadata_stream_t streamConfiguration;
+};
+
+bool operator==(const AvailableStreamConfiguration& a,
+                const AvailableStreamConfiguration& b) {
+  return a.width == b.width && a.height == b.height &&
+         a.pixelFormat == b.pixelFormat &&
+         a.streamConfiguration == b.streamConfiguration;
+}
+
+std::ostream& operator<<(std::ostream& os,
+                         const AvailableStreamConfiguration& config) {
+  os << config.width << "x" << config.height << " (pixfmt "
+     << config.pixelFormat << ", streamConfiguration "
+     << config.streamConfiguration << ")";
+  return os;
+}
+
+std::vector<AvailableStreamConfiguration> getAvailableStreamConfigurations(
+    const CameraMetadata& metadata) {
+  const camera_metadata_t* const raw =
+      reinterpret_cast<const camera_metadata_t*>(metadata.metadata.data());
+  camera_metadata_ro_entry_t entry;
+  if (find_camera_metadata_ro_entry(
+          raw, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, &entry) !=
+      NO_ERROR) {
+    return {};
+  }
+
+  std::vector<AvailableStreamConfiguration> res;
+  for (int i = 0; i < entry.count; i += 4) {
+    res.push_back(AvailableStreamConfiguration{
+        .width = entry.data.i32[i + 1],
+        .height = entry.data.i32[i + 2],
+        .pixelFormat = entry.data.i32[i],
+        .streamConfiguration =
+            static_cast<metadata_stream_t>(entry.data.i32[i + 3])});
+  }
+  return res;
+}
+
+struct VirtualCameraConfigTestParam {
+  std::vector<SupportedStreamConfiguration> inputConfig;
+  std::vector<AvailableStreamConfiguration> expectedAvailableStreamConfigs;
+};
+
+class VirtualCameraDeviceTest
+    : public testing::TestWithParam<VirtualCameraConfigTestParam> {};
+
+TEST_P(VirtualCameraDeviceTest, cameraCharacteristicsForInputFormat) {
+  const VirtualCameraConfigTestParam& param = GetParam();
+  std::shared_ptr<VirtualCameraDevice> camera =
+      ndk::SharedRefBase::make<VirtualCameraDevice>(
+          kCameraId, param.inputConfig, /*virtualCameraClientCallback=*/nullptr);
+
+  CameraMetadata metadata;
+  ASSERT_TRUE(camera->getCameraCharacteristics(&metadata).isOk());
+  EXPECT_THAT(getAvailableStreamConfigurations(metadata),
+              UnorderedElementsAreArray(param.expectedAvailableStreamConfigs));
+
+  // Configuration needs to succeed for every available stream configuration
+  for (const AvailableStreamConfiguration& config :
+       param.expectedAvailableStreamConfigs) {
+    StreamConfiguration configuration{
+        .streams = std::vector<Stream>{Stream{
+            .streamType = StreamType::OUTPUT,
+            .width = config.width,
+            .height = config.height,
+            .format = static_cast<PixelFormat>(config.pixelFormat),
+        }}};
+    bool aidl_ret;
+    ASSERT_TRUE(
+        camera->isStreamCombinationSupported(configuration, &aidl_ret).isOk());
+    EXPECT_TRUE(aidl_ret);
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    cameraCharacteristicsForInputFormat, VirtualCameraDeviceTest,
+    testing::Values(
+        VirtualCameraConfigTestParam{
+            .inputConfig = {SupportedStreamConfiguration{
+                .width = kVgaWidth,
+                .height = kVgaHeight,
+                .pixelFormat = Format::YUV_420_888}},
+            .expectedAvailableStreamConfigs =
+                {AvailableStreamConfiguration{
+                     .width = kVgaWidth,
+                     .height = kVgaHeight,
+                     .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888,
+                     .streamConfiguration =
+                         ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT},
+                 AvailableStreamConfiguration{
+                     .width = kVgaWidth,
+                     .height = kVgaHeight,
+                     .pixelFormat =
+                         ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED,
+                     .streamConfiguration =
+                         ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT},
+                 AvailableStreamConfiguration{
+                     .width = kVgaWidth,
+                     .height = kVgaHeight,
+                     .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_BLOB,
+                     .streamConfiguration =
+                         ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT}}},
+        VirtualCameraConfigTestParam{
+            .inputConfig = {SupportedStreamConfiguration{
+                                .width = kVgaWidth,
+                                .height = kVgaHeight,
+                                .pixelFormat = Format::YUV_420_888},
+                            SupportedStreamConfiguration{
+                                .width = kHdWidth,
+                                .height = kHdHeight,
+                                .pixelFormat = Format::YUV_420_888}},
+            .expectedAvailableStreamConfigs = {
+                AvailableStreamConfiguration{
+                    .width = kVgaWidth,
+                    .height = kVgaHeight,
+                    .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888,
+                    .streamConfiguration =
+                        ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT},
+                AvailableStreamConfiguration{
+                    .width = kVgaWidth,
+                    .height = kVgaHeight,
+                    .pixelFormat =
+                        ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED,
+                    .streamConfiguration =
+                        ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT},
+                AvailableStreamConfiguration{
+                    .width = kVgaWidth,
+                    .height = kVgaHeight,
+                    .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_BLOB,
+                    .streamConfiguration =
+                        ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT},
+                AvailableStreamConfiguration{
+                    .width = kHdWidth,
+                    .height = kHdHeight,
+                    .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888,
+                    .streamConfiguration =
+                        ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT},
+                AvailableStreamConfiguration{
+                    .width = kHdWidth,
+                    .height = kHdHeight,
+                    .pixelFormat =
+                        ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED,
+                    .streamConfiguration =
+                        ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT},
+                AvailableStreamConfiguration{
+                    .width = kHdWidth,
+                    .height = kHdHeight,
+                    .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_BLOB,
+                    .streamConfiguration =
+                        ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT}}}));
+
+}  // namespace
+}  // namespace virtualcamera
+}  // namespace companion
+}  // namespace android