[libcamera-devel,2/2] android: Introduce CameraCapabilties class
diff mbox series

Message ID 20210621152954.40299-3-jacopo@jmondi.org
State Accepted
Delegated to: Jacopo Mondi
Headers show
Series
  • android: Introduce CameraCapabilities
Related show

Commit Message

Jacopo Mondi June 21, 2021, 3:29 p.m. UTC
The camera_device.cpp has grown a little too much, and it has quickly
become hard to maintain. Break out the handling of the static
information collected at camera initialization time to a new
CameraCapabilities class.

Break out from the camera_device.cpp file all the functions relative to:
- Initialization of supported stream configurations
- Initialization of static metadata
- Initialization of request templates

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Acked-by: Paul Elder <paul.elder@ideasonboard.com>
Tested-by: Paul Elder <paul.elder@ideasonboard.com>
---
 src/android/camera_capabilities.cpp | 1164 +++++++++++++++++++++++++++
 src/android/camera_capabilities.h   |   65 ++
 src/android/camera_device.cpp       | 1147 +-------------------------
 src/android/camera_device.h         |   27 +-
 src/android/meson.build             |    1 +
 5 files changed, 1245 insertions(+), 1159 deletions(-)
 create mode 100644 src/android/camera_capabilities.cpp
 create mode 100644 src/android/camera_capabilities.h

Comments

Hirokazu Honda June 22, 2021, 1:34 a.m. UTC | #1
Hi Jacopo, thank you for the patch.

I failed to apply the patch on the top of the latest tree to review.
Could you tell me the parent commit which I can apply this patch?

-Hiro
On Tue, Jun 22, 2021 at 12:29 AM Jacopo Mondi <jacopo@jmondi.org> wrote:

> The camera_device.cpp has grown a little too much, and it has quickly
> become hard to maintain. Break out the handling of the static
> information collected at camera initialization time to a new
> CameraCapabilities class.
>
> Break out from the camera_device.cpp file all the functions relative to:
> - Initialization of supported stream configurations
> - Initialization of static metadata
> - Initialization of request templates
>
> Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
> Acked-by: Paul Elder <paul.elder@ideasonboard.com>
> Tested-by: Paul Elder <paul.elder@ideasonboard.com>
> ---
>  src/android/camera_capabilities.cpp | 1164 +++++++++++++++++++++++++++
>  src/android/camera_capabilities.h   |   65 ++
>  src/android/camera_device.cpp       | 1147 +-------------------------
>  src/android/camera_device.h         |   27 +-
>  src/android/meson.build             |    1 +
>  5 files changed, 1245 insertions(+), 1159 deletions(-)
>  create mode 100644 src/android/camera_capabilities.cpp
>  create mode 100644 src/android/camera_capabilities.h
>
> diff --git a/src/android/camera_capabilities.cpp
> b/src/android/camera_capabilities.cpp
> new file mode 100644
> index 000000000000..311a2c839586
> --- /dev/null
> +++ b/src/android/camera_capabilities.cpp
> @@ -0,0 +1,1164 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2021, Google Inc.
> + *
> + * camera_capabilities.cpp - Camera static properties manager
> + */
> +
> +#include "camera_capabilities.h"
> +
> +#include <array>
> +#include <cmath>
> +
> +#include <hardware/camera3.h>
> +
> +#include <libcamera/control_ids.h>
> +#include <libcamera/controls.h>
> +#include <libcamera/property_ids.h>
> +
> +#include "libcamera/internal/formats.h"
> +#include "libcamera/internal/log.h"
> +
> +using namespace libcamera;
> +
> +LOG_DECLARE_CATEGORY(HAL)
> +
> +namespace {
> +
> +/*
> + * \var camera3Resolutions
> + * \brief The list of image resolutions defined as mandatory to be
> supported by
> + * the Android Camera3 specification
> + */
> +const std::vector<Size> camera3Resolutions = {
> +       { 320, 240 },
> +       { 640, 480 },
> +       { 1280, 720 },
> +       { 1920, 1080 }
> +};
> +
> +/*
> + * \struct Camera3Format
> + * \brief Data associated with an Android format identifier
> + * \var libcameraFormats List of libcamera pixel formats compatible with
> the
> + * Android format
> + * \var name The human-readable representation of the Android format code
> + */
> +struct Camera3Format {
> +       std::vector<PixelFormat> libcameraFormats;
> +       bool mandatory;
> +       const char *name;
> +};
> +
> +/*
> + * \var camera3FormatsMap
> + * \brief Associate Android format code with ancillary data
> + */
> +const std::map<int, const Camera3Format> camera3FormatsMap = {
> +       {
> +               HAL_PIXEL_FORMAT_BLOB, {
> +                       { formats::MJPEG },
> +                       true,
> +                       "BLOB"
> +               }
> +       }, {
> +               HAL_PIXEL_FORMAT_YCbCr_420_888, {
> +                       { formats::NV12, formats::NV21 },
> +                       true,
> +                       "YCbCr_420_888"
> +               }
> +       }, {
> +               /*
> +                * \todo Translate IMPLEMENTATION_DEFINED inspecting the
> gralloc
> +                * usage flag. For now, copy the YCbCr_420 configuration.
> +                */
> +               HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, {
> +                       { formats::NV12, formats::NV21 },
> +                       true,
> +                       "IMPLEMENTATION_DEFINED"
> +               }
> +       }, {
> +               HAL_PIXEL_FORMAT_RAW10, {
> +                       {
> +                               formats::SBGGR10_CSI2P,
> +                               formats::SGBRG10_CSI2P,
> +                               formats::SGRBG10_CSI2P,
> +                               formats::SRGGB10_CSI2P
> +                       },
> +                       false,
> +                       "RAW10"
> +               }
> +       }, {
> +               HAL_PIXEL_FORMAT_RAW12, {
> +                       {
> +                               formats::SBGGR12_CSI2P,
> +                               formats::SGBRG12_CSI2P,
> +                               formats::SGRBG12_CSI2P,
> +                               formats::SRGGB12_CSI2P
> +                       },
> +                       false,
> +                       "RAW12"
> +               }
> +       }, {
> +               HAL_PIXEL_FORMAT_RAW16, {
> +                       {
> +                               formats::SBGGR16,
> +                               formats::SGBRG16,
> +                               formats::SGRBG16,
> +                               formats::SRGGB16
> +                       },
> +                       false,
> +                       "RAW16"
> +               }
> +       },
> +};
> +
> +} /* namespace */
> +
> +int CameraCapabilities::initialize(std::shared_ptr<libcamera::Camera>
> camera,
> +                                  int orientation, int facing)
> +{
> +       camera_ = camera;
> +       orientation_ = orientation;
> +       facing_ = facing;
> +
> +       /* Acquire the camera and initialize available stream
> configurations. */
> +       int ret = camera_->acquire();
> +       if (ret) {
> +               LOG(HAL, Error) << "Failed to temporarily acquire the
> camera";
> +               return ret;
> +       }
> +
> +       ret = initializeStreamConfigurations();
> +       camera_->release();
> +       if (ret)
> +               return ret;
> +
> +       return initializeStaticMetadata();
> +}
> +
> +std::vector<Size>
> CameraCapabilities::getYUVResolutions(CameraConfiguration *cameraConfig,
> +                                                       const PixelFormat
> &pixelFormat,
> +                                                       const
> std::vector<Size> &resolutions)
> +{
> +       std::vector<Size> supportedResolutions;
> +
> +       StreamConfiguration &cfg = cameraConfig->at(0);
> +       for (const Size &res : resolutions) {
> +               cfg.pixelFormat = pixelFormat;
> +               cfg.size = res;
> +
> +               CameraConfiguration::Status status =
> cameraConfig->validate();
> +               if (status != CameraConfiguration::Valid) {
> +                       LOG(HAL, Debug) << cfg.toString() << " not
> supported";
> +                       continue;
> +               }
> +
> +               LOG(HAL, Debug) << cfg.toString() << " supported";
> +
> +               supportedResolutions.push_back(res);
> +       }
> +
> +       return supportedResolutions;
> +}
> +
> +std::vector<Size> CameraCapabilities::getRawResolutions(const
> libcamera::PixelFormat &pixelFormat)
> +{
> +       std::unique_ptr<CameraConfiguration> cameraConfig =
> +               camera_->generateConfiguration({ StreamRole::Raw });
> +       StreamConfiguration &cfg = cameraConfig->at(0);
> +       const StreamFormats &formats = cfg.formats();
> +       std::vector<Size> supportedResolutions =
> formats.sizes(pixelFormat);
> +
> +       return supportedResolutions;
> +}
> +
> +/*
> + * Initialize the format conversion map to translate from Android format
> + * identifier to libcamera pixel formats and fill in the list of supported
> + * stream configurations to be reported to the Android camera framework
> through
> + * the Camera static metadata.
> + */
> +int CameraCapabilities::initializeStreamConfigurations()
> +{
> +       /*
> +        * Get the maximum output resolutions
> +        * \todo Get this from the camera properties once defined
> +        */
> +       std::unique_ptr<CameraConfiguration> cameraConfig =
> +               camera_->generateConfiguration({ StillCapture });
> +       if (!cameraConfig) {
> +               LOG(HAL, Error) << "Failed to get maximum resolution";
> +               return -EINVAL;
> +       }
> +       StreamConfiguration &cfg = cameraConfig->at(0);
> +
> +       /*
> +        * \todo JPEG - Adjust the maximum available resolution by taking
> the
> +        * JPEG encoder requirements into account (alignment and aspect
> ratio).
> +        */
> +       const Size maxRes = cfg.size;
> +       LOG(HAL, Debug) << "Maximum supported resolution: " <<
> maxRes.toString();
> +
> +       /*
> +        * Build the list of supported image resolutions.
> +        *
> +        * The resolutions listed in camera3Resolution are mandatory to be
> +        * supported, up to the camera maximum resolution.
> +        *
> +        * Augment the list by adding resolutions calculated from the
> camera
> +        * maximum one.
> +        */
> +       std::vector<Size> cameraResolutions;
> +       std::copy_if(camera3Resolutions.begin(), camera3Resolutions.end(),
> +                    std::back_inserter(cameraResolutions),
> +                    [&](const Size &res) { return res < maxRes; });
> +
> +       /*
> +        * The Camera3 specification suggests adding 1/2 and 1/4 of the
> maximum
> +        * resolution.
> +        */
> +       for (unsigned int divider = 2;; divider <<= 1) {
> +               Size derivedSize{
> +                       maxRes.width / divider,
> +                       maxRes.height / divider,
> +               };
> +
> +               if (derivedSize.width < 320 ||
> +                   derivedSize.height < 240)
> +                       break;
> +
> +               cameraResolutions.push_back(derivedSize);
> +       }
> +       cameraResolutions.push_back(maxRes);
> +
> +       /* Remove duplicated entries from the list of supported
> resolutions. */
> +       std::sort(cameraResolutions.begin(), cameraResolutions.end());
> +       auto last = std::unique(cameraResolutions.begin(),
> cameraResolutions.end());
> +       cameraResolutions.erase(last, cameraResolutions.end());
> +
> +       /*
> +        * Build the list of supported camera formats.
> +        *
> +        * To each Android format a list of compatible libcamera formats is
> +        * associated. The first libcamera format that tests successful is
> added
> +        * to the format translation map used when configuring the streams.
> +        * It is then tested against the list of supported camera
> resolutions to
> +        * build the stream configuration map reported through the camera
> static
> +        * metadata.
> +        */
> +       Size maxJpegSize;
> +       for (const auto &format : camera3FormatsMap) {
> +               int androidFormat = format.first;
> +               const Camera3Format &camera3Format = format.second;
> +               const std::vector<PixelFormat> &libcameraFormats =
> +                       camera3Format.libcameraFormats;
> +
> +               LOG(HAL, Debug) << "Trying to map Android format "
> +                               << camera3Format.name;
> +
> +               /*
> +                * JPEG is always supported, either produced directly by
> the
> +                * camera, or encoded in the HAL.
> +                */
> +               if (androidFormat == HAL_PIXEL_FORMAT_BLOB) {
> +                       formatsMap_[androidFormat] = formats::MJPEG;
> +                       LOG(HAL, Debug) << "Mapped Android format "
> +                                       << camera3Format.name << " to "
> +                                       << formats::MJPEG.toString()
> +                                       << " (fixed mapping)";
> +                       continue;
> +               }
> +
> +               /*
> +                * Test the libcamera formats that can produce images
> +                * compatible with the format defined by Android.
> +                */
> +               PixelFormat mappedFormat;
> +               for (const PixelFormat &pixelFormat : libcameraFormats) {
> +
> +                       LOG(HAL, Debug) << "Testing " <<
> pixelFormat.toString();
> +
> +                       /*
> +                        * The stream configuration size can be adjusted,
> +                        * not the pixel format.
> +                        *
> +                        * \todo This could be simplified once all pipeline
> +                        * handlers will report the StreamFormats list of
> +                        * supported formats.
> +                        */
> +                       cfg.pixelFormat = pixelFormat;
> +
> +                       CameraConfiguration::Status status =
> cameraConfig->validate();
> +                       if (status != CameraConfiguration::Invalid &&
> +                           cfg.pixelFormat == pixelFormat) {
> +                               mappedFormat = pixelFormat;
> +                               break;
> +                       }
> +               }
> +
> +               if (!mappedFormat.isValid()) {
> +                       /* If the format is not mandatory, skip it. */
> +                       if (!camera3Format.mandatory)
> +                               continue;
> +
> +                       LOG(HAL, Error)
> +                               << "Failed to map mandatory Android format
> "
> +                               << camera3Format.name << " ("
> +                               << utils::hex(androidFormat) << "):
> aborting";
> +                       return -EINVAL;
> +               }
> +
> +               /*
> +                * Record the mapping and then proceed to generate the
> +                * stream configurations map, by testing the image
> resolutions.
> +                */
> +               formatsMap_[androidFormat] = mappedFormat;
> +               LOG(HAL, Debug) << "Mapped Android format "
> +                               << camera3Format.name << " to "
> +                               << mappedFormat.toString();
> +
> +               std::vector<Size> resolutions;
> +               const PixelFormatInfo &info =
> PixelFormatInfo::info(mappedFormat);
> +               if (info.colourEncoding ==
> PixelFormatInfo::ColourEncodingRAW)
> +                       resolutions = getRawResolutions(mappedFormat);
> +               else
> +                       resolutions = getYUVResolutions(cameraConfig.get(),
> +                                                       mappedFormat,
> +                                                       cameraResolutions);
> +
> +               for (const Size &res : resolutions) {
> +                       streamConfigurations_.push_back({ res,
> androidFormat });
> +
> +                       /*
> +                        * If the format is HAL_PIXEL_FORMAT_YCbCr_420_888
> +                        * from which JPEG is produced, add an entry for
> +                        * the JPEG stream.
> +                        *
> +                        * \todo Wire the JPEG encoder to query the
> supported
> +                        * sizes provided a list of formats it can encode.
> +                        *
> +                        * \todo Support JPEG streams produced by the
> Camera
> +                        * natively.
> +                        */
> +                       if (androidFormat ==
> HAL_PIXEL_FORMAT_YCbCr_420_888) {
> +                               streamConfigurations_.push_back(
> +                                       { res, HAL_PIXEL_FORMAT_BLOB });
> +                               maxJpegSize = std::max(maxJpegSize, res);
> +                       }
> +               }
> +
> +               /*
> +                * \todo Calculate the maximum JPEG buffer size by asking
> the
> +                * encoder giving the maximum frame size required.
> +                */
> +               maxJpegBufferSize_ = maxJpegSize.width *
> maxJpegSize.height * 1.5;
> +       }
> +
> +       LOG(HAL, Debug) << "Collected stream configuration map: ";
> +       for (const auto &entry : streamConfigurations_)
> +               LOG(HAL, Debug) << "{ " << entry.resolution.toString() <<
> " - "
> +                               << utils::hex(entry.androidFormat) << " }";
> +
> +       return 0;
> +}
> +
> +int CameraCapabilities::initializeStaticMetadata()
> +{
> +       staticMetadata_ = std::make_unique<CameraMetadata>(64, 1024);
> +       if (!staticMetadata_->isValid()) {
> +               LOG(HAL, Error) << "Failed to allocate static metadata";
> +               staticMetadata_.reset();
> +               return -EINVAL;
> +       }
> +
> +       const ControlInfoMap &controlsInfo = camera_->controls();
> +       const ControlList &properties = camera_->properties();
> +
> +       /* Color correction static metadata. */
> +       {
> +               std::vector<uint8_t> data;
> +               data.reserve(3);
> +               const auto &infoMap =
> controlsInfo.find(&controls::draft::ColorCorrectionAberrationMode);
> +               if (infoMap != controlsInfo.end()) {
> +                       for (const auto &value : infoMap->second.values())
> +                               data.push_back(value.get<int32_t>());
> +               } else {
> +
>  data.push_back(ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF);
> +               }
> +
>  staticMetadata_->addEntry(ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
> +                                         data);
> +       }
> +
> +       /* Control static metadata. */
> +       std::vector<uint8_t> aeAvailableAntiBandingModes = {
> +               ANDROID_CONTROL_AE_ANTIBANDING_MODE_OFF,
> +               ANDROID_CONTROL_AE_ANTIBANDING_MODE_50HZ,
> +               ANDROID_CONTROL_AE_ANTIBANDING_MODE_60HZ,
> +               ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO,
> +       };
> +
>  staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,
> +                                 aeAvailableAntiBandingModes);
> +
> +       std::vector<uint8_t> aeAvailableModes = {
> +               ANDROID_CONTROL_AE_MODE_ON,
> +       };
> +       staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_MODES,
> +                                 aeAvailableModes);
> +
> +       int64_t minFrameDurationNsec = -1;
> +       int64_t maxFrameDurationNsec = -1;
> +       const auto frameDurationsInfo =
> controlsInfo.find(&controls::FrameDurationLimits);
> +       if (frameDurationsInfo != controlsInfo.end()) {
> +               minFrameDurationNsec =
> frameDurationsInfo->second.min().get<int64_t>() * 1000;
> +               maxFrameDurationNsec =
> frameDurationsInfo->second.max().get<int64_t>() * 1000;
> +
> +               /*
> +                * Adjust the minimum frame duration to comply with Android
> +                * requirements. The camera service mandates all
> preview/record
> +                * streams to have a minimum frame duration < 33,366
> milliseconds
> +                * (see MAX_PREVIEW_RECORD_DURATION_NS in the camera
> service
> +                * implementation).
> +                *
> +                * If we're close enough (+ 500 useconds) to that value,
> round
> +                * the minimum frame duration of the camera to an accepted
> +                * value.
> +                */
> +               static constexpr int64_t MAX_PREVIEW_RECORD_DURATION_NS =
> 1e9 / 29.97;
> +               if (minFrameDurationNsec > MAX_PREVIEW_RECORD_DURATION_NS
> &&
> +                   minFrameDurationNsec < MAX_PREVIEW_RECORD_DURATION_NS
> + 500000)
> +                       minFrameDurationNsec =
> MAX_PREVIEW_RECORD_DURATION_NS - 1000;
> +
> +               /*
> +                * The AE routine frame rate limits are computed using the
> frame
> +                * duration limits, as libcamera clips the AE routine to
> the
> +                * frame durations.
> +                */
> +               int32_t maxFps = std::round(1e9 / minFrameDurationNsec);
> +               int32_t minFps = std::round(1e9 / maxFrameDurationNsec);
> +               minFps = std::max(1, minFps);
> +
> +               /*
> +                * Force rounding errors so that we have the proper frame
> +                * durations for when we reuse these variables later
> +                */
> +               minFrameDurationNsec = 1e9 / maxFps;
> +               maxFrameDurationNsec = 1e9 / minFps;
> +
> +               /*
> +                * Register to the camera service {min, max} and {max, max}
> +                * intervals as requested by the metadata documentation.
> +                */
> +               int32_t availableAeFpsTarget[] = {
> +                       minFps, maxFps, maxFps, maxFps
> +               };
> +
>  staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> +                                         availableAeFpsTarget);
> +       }
> +
> +       std::vector<int32_t> aeCompensationRange = {
> +               0, 0,
> +       };
> +       staticMetadata_->addEntry(ANDROID_CONTROL_AE_COMPENSATION_RANGE,
> +                                 aeCompensationRange);
> +
> +       const camera_metadata_rational_t aeCompensationStep[] = {
> +               { 0, 1 }
> +       };
> +       staticMetadata_->addEntry(ANDROID_CONTROL_AE_COMPENSATION_STEP,
> +                                 aeCompensationStep);
> +
> +       std::vector<uint8_t> availableAfModes = {
> +               ANDROID_CONTROL_AF_MODE_OFF,
> +       };
> +       staticMetadata_->addEntry(ANDROID_CONTROL_AF_AVAILABLE_MODES,
> +                                 availableAfModes);
> +
> +       std::vector<uint8_t> availableEffects = {
> +               ANDROID_CONTROL_EFFECT_MODE_OFF,
> +       };
> +       staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_EFFECTS,
> +                                 availableEffects);
> +
> +       std::vector<uint8_t> availableSceneModes = {
> +               ANDROID_CONTROL_SCENE_MODE_DISABLED,
> +       };
> +       staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_SCENE_MODES,
> +                                 availableSceneModes);
> +
> +       std::vector<uint8_t> availableStabilizationModes = {
> +               ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF,
> +       };
> +
>  staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
> +                                 availableStabilizationModes);
> +
> +       /*
> +        * \todo Inspect the Camera capabilities to report the available
> +        * AWB modes. Default to AUTO as CTS tests require it.
> +        */
> +       std::vector<uint8_t> availableAwbModes = {
> +               ANDROID_CONTROL_AWB_MODE_AUTO,
> +       };
> +       staticMetadata_->addEntry(ANDROID_CONTROL_AWB_AVAILABLE_MODES,
> +                                 availableAwbModes);
> +
> +       std::vector<int32_t> availableMaxRegions = {
> +               0, 0, 0,
> +       };
> +       staticMetadata_->addEntry(ANDROID_CONTROL_MAX_REGIONS,
> +                                 availableMaxRegions);
> +
> +       std::vector<uint8_t> sceneModesOverride = {
> +               ANDROID_CONTROL_AE_MODE_ON,
> +               ANDROID_CONTROL_AWB_MODE_AUTO,
> +               ANDROID_CONTROL_AF_MODE_OFF,
> +       };
> +       staticMetadata_->addEntry(ANDROID_CONTROL_SCENE_MODE_OVERRIDES,
> +                                 sceneModesOverride);
> +
> +       uint8_t aeLockAvailable = ANDROID_CONTROL_AE_LOCK_AVAILABLE_FALSE;
> +       staticMetadata_->addEntry(ANDROID_CONTROL_AE_LOCK_AVAILABLE,
> +                                 aeLockAvailable);
> +
> +       uint8_t awbLockAvailable =
> ANDROID_CONTROL_AWB_LOCK_AVAILABLE_FALSE;
> +       staticMetadata_->addEntry(ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
> +                                 awbLockAvailable);
> +
> +       char availableControlModes = ANDROID_CONTROL_MODE_AUTO;
> +       staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_MODES,
> +                                 availableControlModes);
> +
> +       /* JPEG static metadata. */
> +
> +       /*
> +        * Create the list of supported thumbnail sizes by inspecting the
> +        * available JPEG resolutions collected in streamConfigurations_
> and
> +        * generate one entry for each aspect ratio.
> +        *
> +        * The JPEG thumbnailer can freely scale, so pick an arbitrary
> +        * (160, 160) size as the bounding rectangle, which is then
> cropped to
> +        * the different supported aspect ratios.
> +        */
> +       constexpr Size maxJpegThumbnail(160, 160);
> +       std::vector<Size> thumbnailSizes;
> +       thumbnailSizes.push_back({ 0, 0 });
> +       for (const auto &entry : streamConfigurations_) {
> +               if (entry.androidFormat != HAL_PIXEL_FORMAT_BLOB)
> +                       continue;
> +
> +               Size thumbnailSize = maxJpegThumbnail
> +                                    .boundedToAspectRatio({
> entry.resolution.width,
> +
> entry.resolution.height });
> +               thumbnailSizes.push_back(thumbnailSize);
> +       }
> +
> +       std::sort(thumbnailSizes.begin(), thumbnailSizes.end());
> +       auto last = std::unique(thumbnailSizes.begin(),
> thumbnailSizes.end());
> +       thumbnailSizes.erase(last, thumbnailSizes.end());
> +
> +       /* Transform sizes in to a list of integers that can be consumed.
> */
> +       std::vector<int32_t> thumbnailEntries;
> +       thumbnailEntries.reserve(thumbnailSizes.size() * 2);
> +       for (const auto &size : thumbnailSizes) {
> +               thumbnailEntries.push_back(size.width);
> +               thumbnailEntries.push_back(size.height);
> +       }
> +       staticMetadata_->addEntry(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,
> +                                 thumbnailEntries);
> +
> +       staticMetadata_->addEntry(ANDROID_JPEG_MAX_SIZE,
> maxJpegBufferSize_);
> +
> +       /* Sensor static metadata. */
> +       std::array<int32_t, 2> pixelArraySize;
> +       {
> +               const Size &size =
> properties.get(properties::PixelArraySize);
> +               pixelArraySize[0] = size.width;
> +               pixelArraySize[1] = size.height;
> +
>  staticMetadata_->addEntry(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
> +                                         pixelArraySize);
> +       }
> +
> +       if (properties.contains(properties::UnitCellSize)) {
> +               const Size &cellSize =
> properties.get<Size>(properties::UnitCellSize);
> +               std::array<float, 2> physicalSize{
> +                       cellSize.width * pixelArraySize[0] / 1e6f,
> +                       cellSize.height * pixelArraySize[1] / 1e6f
> +               };
> +
>  staticMetadata_->addEntry(ANDROID_SENSOR_INFO_PHYSICAL_SIZE,
> +                                         physicalSize);
> +       }
> +
> +       {
> +               const Span<const Rectangle> &rects =
> +                       properties.get(properties::PixelArrayActiveAreas);
> +               std::vector<int32_t> data{
> +                       static_cast<int32_t>(rects[0].x),
> +                       static_cast<int32_t>(rects[0].y),
> +                       static_cast<int32_t>(rects[0].width),
> +                       static_cast<int32_t>(rects[0].height),
> +               };
> +
>  staticMetadata_->addEntry(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
> +                                         data);
> +       }
> +
> +       int32_t sensitivityRange[] = {
> +               32, 2400,
> +       };
> +       staticMetadata_->addEntry(ANDROID_SENSOR_INFO_SENSITIVITY_RANGE,
> +                                 sensitivityRange);
> +
> +       /* Report the color filter arrangement if the camera reports it. */
> +       if
> (properties.contains(properties::draft::ColorFilterArrangement)) {
> +               uint8_t filterArr =
> properties.get(properties::draft::ColorFilterArrangement);
> +
>  staticMetadata_->addEntry(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,
> +                                         filterArr);
> +       }
> +
> +       const auto &exposureInfo =
> controlsInfo.find(&controls::ExposureTime);
> +       if (exposureInfo != controlsInfo.end()) {
> +               int64_t exposureTimeRange[2] = {
> +                       exposureInfo->second.min().get<int32_t>() * 1000LL,
> +                       exposureInfo->second.max().get<int32_t>() * 1000LL,
> +               };
> +
>  staticMetadata_->addEntry(ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE,
> +                                         exposureTimeRange, 2);
> +       }
> +
> +       staticMetadata_->addEntry(ANDROID_SENSOR_ORIENTATION,
> orientation_);
> +
> +       std::vector<int32_t> testPatternModes = {
> +               ANDROID_SENSOR_TEST_PATTERN_MODE_OFF
> +       };
> +       const auto &testPatternsInfo =
> +               controlsInfo.find(&controls::draft::TestPatternMode);
> +       if (testPatternsInfo != controlsInfo.end()) {
> +               const auto &values = testPatternsInfo->second.values();
> +               ASSERT(!values.empty());
> +               for (const auto &value : values) {
> +                       switch (value.get<int32_t>()) {
> +                       case controls::draft::TestPatternModeOff:
> +                               /*
> +                                * ANDROID_SENSOR_TEST_PATTERN_MODE_OFF is
> +                                * already in testPatternModes.
> +                                */
> +                               break;
> +
> +                       case controls::draft::TestPatternModeSolidColor:
> +                               testPatternModes.push_back(
> +
>  ANDROID_SENSOR_TEST_PATTERN_MODE_SOLID_COLOR);
> +                               break;
> +
> +                       case controls::draft::TestPatternModeColorBars:
> +                               testPatternModes.push_back(
> +
>  ANDROID_SENSOR_TEST_PATTERN_MODE_COLOR_BARS);
> +                               break;
> +
> +                       case
> controls::draft::TestPatternModeColorBarsFadeToGray:
> +                               testPatternModes.push_back(
> +
>  ANDROID_SENSOR_TEST_PATTERN_MODE_COLOR_BARS_FADE_TO_GRAY);
> +                               break;
> +
> +                       case controls::draft::TestPatternModePn9:
> +                               testPatternModes.push_back(
> +
>  ANDROID_SENSOR_TEST_PATTERN_MODE_PN9);
> +                               break;
> +
> +                       case controls::draft::TestPatternModeCustom1:
> +                               /* We don't support this yet. */
> +                               break;
> +
> +                       default:
> +                               LOG(HAL, Error) << "Unknown test pattern
> mode: "
> +                                               << value.get<int32_t>();
> +                               continue;
> +                       }
> +               }
> +       }
> +
>  staticMetadata_->addEntry(ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES,
> +                                 testPatternModes);
> +
> +       uint8_t timestampSource =
> ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN;
> +       staticMetadata_->addEntry(ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE,
> +                                 timestampSource);
> +
> +       if (maxFrameDurationNsec > 0)
> +
>  staticMetadata_->addEntry(ANDROID_SENSOR_INFO_MAX_FRAME_DURATION,
> +                                         maxFrameDurationNsec);
> +
> +       /* Statistics static metadata. */
> +       uint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
> +
>  staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
> +                                 faceDetectMode);
> +
> +       int32_t maxFaceCount = 0;
> +       staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
> +                                 maxFaceCount);
> +
> +       {
> +               std::vector<uint8_t> data;
> +               data.reserve(2);
> +               const auto &infoMap =
> controlsInfo.find(&controls::draft::LensShadingMapMode);
> +               if (infoMap != controlsInfo.end()) {
> +                       for (const auto &value : infoMap->second.values())
> +                               data.push_back(value.get<int32_t>());
> +               } else {
> +
>  data.push_back(ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF);
> +               }
> +
>  staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES,
> +                                         data);
> +       }
> +
> +       /* Sync static metadata. */
> +       int32_t maxLatency = ANDROID_SYNC_MAX_LATENCY_UNKNOWN;
> +       staticMetadata_->addEntry(ANDROID_SYNC_MAX_LATENCY, maxLatency);
> +
> +       /* Flash static metadata. */
> +       char flashAvailable = ANDROID_FLASH_INFO_AVAILABLE_FALSE;
> +       staticMetadata_->addEntry(ANDROID_FLASH_INFO_AVAILABLE,
> +                                 flashAvailable);
> +
> +       /* Lens static metadata. */
> +       std::vector<float> lensApertures = {
> +               2.53 / 100,
> +       };
> +       staticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_APERTURES,
> +                                 lensApertures);
> +
> +       uint8_t lensFacing;
> +       switch (facing_) {
> +       default:
> +       case CAMERA_FACING_FRONT:
> +               lensFacing = ANDROID_LENS_FACING_FRONT;
> +               break;
> +       case CAMERA_FACING_BACK:
> +               lensFacing = ANDROID_LENS_FACING_BACK;
> +               break;
> +       case CAMERA_FACING_EXTERNAL:
> +               lensFacing = ANDROID_LENS_FACING_EXTERNAL;
> +               break;
> +       }
> +       staticMetadata_->addEntry(ANDROID_LENS_FACING, lensFacing);
> +
> +       std::vector<float> lensFocalLengths = {
> +               1,
> +       };
> +
>  staticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS,
> +                                 lensFocalLengths);
> +
> +       std::vector<uint8_t> opticalStabilizations = {
> +               ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF,
> +       };
> +
>  staticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
> +                                 opticalStabilizations);
> +
> +       float hypeFocalDistance = 0;
> +       staticMetadata_->addEntry(ANDROID_LENS_INFO_HYPERFOCAL_DISTANCE,
> +                                 hypeFocalDistance);
> +
> +       float minFocusDistance = 0;
> +       staticMetadata_->addEntry(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
> +                                 minFocusDistance);
> +
> +       /* Noise reduction modes. */
> +       {
> +               std::vector<uint8_t> data;
> +               data.reserve(5);
> +               const auto &infoMap =
> controlsInfo.find(&controls::draft::NoiseReductionMode);
> +               if (infoMap != controlsInfo.end()) {
> +                       for (const auto &value : infoMap->second.values())
> +                               data.push_back(value.get<int32_t>());
> +               } else {
> +                       data.push_back(ANDROID_NOISE_REDUCTION_MODE_OFF);
> +               }
> +
>  staticMetadata_->addEntry(ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
> +                                         data);
> +       }
> +
> +       /* Scaler static metadata. */
> +
> +       /*
> +        * \todo The digital zoom factor is a property that depends on the
> +        * desired output configuration and the sensor frame size input to
> the
> +        * ISP. This information is not available to the Android HAL, not
> at
> +        * initialization time at least.
> +        *
> +        * As a workaround rely on pipeline handlers initializing the
> +        * ScalerCrop control with the camera default configuration and
> use the
> +        * maximum and minimum crop rectangles to calculate the digital
> zoom
> +        * factor.
> +        */
> +       float maxZoom = 1.0f;
> +       const auto scalerCrop = controlsInfo.find(&controls::ScalerCrop);
> +       if (scalerCrop != controlsInfo.end()) {
> +               Rectangle min = scalerCrop->second.min().get<Rectangle>();
> +               Rectangle max = scalerCrop->second.max().get<Rectangle>();
> +               maxZoom = std::min(1.0f * max.width / min.width,
> +                                  1.0f * max.height / min.height);
> +       }
> +
>  staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
> +                                 maxZoom);
> +
> +       std::vector<uint32_t> availableStreamConfigurations;
> +       availableStreamConfigurations.reserve(streamConfigurations_.size()
> * 4);
> +       for (const auto &entry : streamConfigurations_) {
> +
>  availableStreamConfigurations.push_back(entry.androidFormat);
> +
>  availableStreamConfigurations.push_back(entry.resolution.width);
> +
>  availableStreamConfigurations.push_back(entry.resolution.height);
> +               availableStreamConfigurations.push_back(
> +
>  ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT);
> +       }
> +
>  staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
> +                                 availableStreamConfigurations);
> +
> +       std::vector<int64_t> availableStallDurations = {
> +               ANDROID_SCALER_AVAILABLE_FORMATS_BLOB, 2560, 1920,
> 33333333,
> +       };
> +       staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,
> +                                 availableStallDurations);
> +
> +       /* Use the minimum frame duration for all the YUV/RGB formats. */
> +       if (minFrameDurationNsec > 0) {
> +               std::vector<int64_t> minFrameDurations;
> +               minFrameDurations.reserve(streamConfigurations_.size() *
> 4);
> +               for (const auto &entry : streamConfigurations_) {
> +                       minFrameDurations.push_back(entry.androidFormat);
> +
>  minFrameDurations.push_back(entry.resolution.width);
> +
>  minFrameDurations.push_back(entry.resolution.height);
> +                       minFrameDurations.push_back(minFrameDurationNsec);
> +               }
> +
>  staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
> +                                         minFrameDurations);
> +       }
> +
> +       uint8_t croppingType = ANDROID_SCALER_CROPPING_TYPE_CENTER_ONLY;
> +       staticMetadata_->addEntry(ANDROID_SCALER_CROPPING_TYPE,
> croppingType);
> +
> +       /* Info static metadata. */
> +       uint8_t supportedHWLevel =
> ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
> +       staticMetadata_->addEntry(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
> +                                 supportedHWLevel);
> +
> +       /* Request static metadata. */
> +       int32_t partialResultCount = 1;
> +       staticMetadata_->addEntry(ANDROID_REQUEST_PARTIAL_RESULT_COUNT,
> +                                 partialResultCount);
> +
> +       {
> +               /* Default the value to 2 if not reported by the camera. */
> +               uint8_t maxPipelineDepth = 2;
> +               const auto &infoMap =
> controlsInfo.find(&controls::draft::PipelineDepth);
> +               if (infoMap != controlsInfo.end())
> +                       maxPipelineDepth =
> infoMap->second.max().get<int32_t>();
> +
>  staticMetadata_->addEntry(ANDROID_REQUEST_PIPELINE_MAX_DEPTH,
> +                                         maxPipelineDepth);
> +       }
> +
> +       /* LIMITED does not support reprocessing. */
> +       uint32_t maxNumInputStreams = 0;
> +       staticMetadata_->addEntry(ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,
> +                                 maxNumInputStreams);
> +
> +       std::vector<uint8_t> availableCapabilities = {
> +               ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE,
> +       };
> +
> +       /* Report if camera supports RAW. */
> +       bool rawStreamAvailable = false;
> +       std::unique_ptr<CameraConfiguration> cameraConfig =
> +               camera_->generateConfiguration({ StreamRole::Raw });
> +       if (cameraConfig && !cameraConfig->empty()) {
> +               const PixelFormatInfo &info =
> +
>  PixelFormatInfo::info(cameraConfig->at(0).pixelFormat);
> +               /* Only advertise RAW support if RAW16 is possible. */
> +               if (info.colourEncoding ==
> PixelFormatInfo::ColourEncodingRAW &&
> +                   info.bitsPerPixel == 16) {
> +                       rawStreamAvailable = true;
> +
>  availableCapabilities.push_back(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW);
> +               }
> +       }
> +
> +       /* Number of { RAW, YUV, JPEG } supported output streams */
> +       int32_t numOutStreams[] = { rawStreamAvailable, 2, 1 };
> +       staticMetadata_->addEntry(ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,
> +                                 numOutStreams);
> +
> +       staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
> +                                 availableCapabilities);
> +
> +       std::vector<int32_t> availableCharacteristicsKeys = {
> +               ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
> +               ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,
> +               ANDROID_CONTROL_AE_AVAILABLE_MODES,
> +               ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> +               ANDROID_CONTROL_AE_COMPENSATION_RANGE,
> +               ANDROID_CONTROL_AE_COMPENSATION_STEP,
> +               ANDROID_CONTROL_AE_LOCK_AVAILABLE,
> +               ANDROID_CONTROL_AF_AVAILABLE_MODES,
> +               ANDROID_CONTROL_AVAILABLE_EFFECTS,
> +               ANDROID_CONTROL_AVAILABLE_MODES,
> +               ANDROID_CONTROL_AVAILABLE_SCENE_MODES,
> +               ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
> +               ANDROID_CONTROL_AWB_AVAILABLE_MODES,
> +               ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
> +               ANDROID_CONTROL_MAX_REGIONS,
> +               ANDROID_CONTROL_SCENE_MODE_OVERRIDES,
> +               ANDROID_FLASH_INFO_AVAILABLE,
> +               ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
> +               ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,
> +               ANDROID_JPEG_MAX_SIZE,
> +               ANDROID_LENS_FACING,
> +               ANDROID_LENS_INFO_AVAILABLE_APERTURES,
> +               ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS,
> +               ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
> +               ANDROID_LENS_INFO_HYPERFOCAL_DISTANCE,
> +               ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
> +               ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
> +               ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
> +               ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,
> +               ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,
> +               ANDROID_REQUEST_PARTIAL_RESULT_COUNT,
> +               ANDROID_REQUEST_PIPELINE_MAX_DEPTH,
> +               ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
> +               ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
> +               ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,
> +               ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
> +               ANDROID_SCALER_CROPPING_TYPE,
> +               ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES,
> +               ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
> +               ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,
> +               ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE,
> +               ANDROID_SENSOR_INFO_MAX_FRAME_DURATION,
> +               ANDROID_SENSOR_INFO_PHYSICAL_SIZE,
> +               ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
> +               ANDROID_SENSOR_INFO_SENSITIVITY_RANGE,
> +               ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE,
> +               ANDROID_SENSOR_ORIENTATION,
> +               ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
> +               ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
> +               ANDROID_SYNC_MAX_LATENCY,
> +       };
> +
>  staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS,
> +                                 availableCharacteristicsKeys);
> +
> +       std::vector<int32_t> availableRequestKeys = {
> +               ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
> +               ANDROID_CONTROL_AE_ANTIBANDING_MODE,
> +               ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
> +               ANDROID_CONTROL_AE_LOCK,
> +               ANDROID_CONTROL_AE_MODE,
> +               ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
> +               ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
> +               ANDROID_CONTROL_AF_MODE,
> +               ANDROID_CONTROL_AF_TRIGGER,
> +               ANDROID_CONTROL_AWB_LOCK,
> +               ANDROID_CONTROL_AWB_MODE,
> +               ANDROID_CONTROL_CAPTURE_INTENT,
> +               ANDROID_CONTROL_EFFECT_MODE,
> +               ANDROID_CONTROL_MODE,
> +               ANDROID_CONTROL_SCENE_MODE,
> +               ANDROID_CONTROL_VIDEO_STABILIZATION_MODE,
> +               ANDROID_FLASH_MODE,
> +               ANDROID_JPEG_ORIENTATION,
> +               ANDROID_JPEG_QUALITY,
> +               ANDROID_JPEG_THUMBNAIL_QUALITY,
> +               ANDROID_JPEG_THUMBNAIL_SIZE,
> +               ANDROID_LENS_APERTURE,
> +               ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
> +               ANDROID_NOISE_REDUCTION_MODE,
> +               ANDROID_SCALER_CROP_REGION,
> +               ANDROID_STATISTICS_FACE_DETECT_MODE
> +       };
> +       staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS,
> +                                 availableRequestKeys);
> +
> +       std::vector<int32_t> availableResultKeys = {
> +               ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
> +               ANDROID_CONTROL_AE_ANTIBANDING_MODE,
> +               ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
> +               ANDROID_CONTROL_AE_LOCK,
> +               ANDROID_CONTROL_AE_MODE,
> +               ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
> +               ANDROID_CONTROL_AE_STATE,
> +               ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
> +               ANDROID_CONTROL_AF_MODE,
> +               ANDROID_CONTROL_AF_STATE,
> +               ANDROID_CONTROL_AF_TRIGGER,
> +               ANDROID_CONTROL_AWB_LOCK,
> +               ANDROID_CONTROL_AWB_MODE,
> +               ANDROID_CONTROL_AWB_STATE,
> +               ANDROID_CONTROL_CAPTURE_INTENT,
> +               ANDROID_CONTROL_EFFECT_MODE,
> +               ANDROID_CONTROL_MODE,
> +               ANDROID_CONTROL_SCENE_MODE,
> +               ANDROID_CONTROL_VIDEO_STABILIZATION_MODE,
> +               ANDROID_FLASH_MODE,
> +               ANDROID_FLASH_STATE,
> +               ANDROID_JPEG_GPS_COORDINATES,
> +               ANDROID_JPEG_GPS_PROCESSING_METHOD,
> +               ANDROID_JPEG_GPS_TIMESTAMP,
> +               ANDROID_JPEG_ORIENTATION,
> +               ANDROID_JPEG_QUALITY,
> +               ANDROID_JPEG_SIZE,
> +               ANDROID_JPEG_THUMBNAIL_QUALITY,
> +               ANDROID_JPEG_THUMBNAIL_SIZE,
> +               ANDROID_LENS_APERTURE,
> +               ANDROID_LENS_FOCAL_LENGTH,
> +               ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
> +               ANDROID_LENS_STATE,
> +               ANDROID_NOISE_REDUCTION_MODE,
> +               ANDROID_REQUEST_PIPELINE_DEPTH,
> +               ANDROID_SCALER_CROP_REGION,
> +               ANDROID_SENSOR_EXPOSURE_TIME,
> +               ANDROID_SENSOR_FRAME_DURATION,
> +               ANDROID_SENSOR_ROLLING_SHUTTER_SKEW,
> +               ANDROID_SENSOR_TEST_PATTERN_MODE,
> +               ANDROID_SENSOR_TIMESTAMP,
> +               ANDROID_STATISTICS_FACE_DETECT_MODE,
> +               ANDROID_STATISTICS_LENS_SHADING_MAP_MODE,
> +               ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE,
> +               ANDROID_STATISTICS_SCENE_FLICKER,
> +       };
> +       staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_RESULT_KEYS,
> +                                 availableResultKeys);
> +
> +       if (!staticMetadata_->isValid()) {
> +               LOG(HAL, Error) << "Failed to construct static metadata";
> +               staticMetadata_.reset();
> +               return -EINVAL;
> +       }
> +
> +       if (staticMetadata_->resized()) {
> +               auto [entryCount, dataCount] = staticMetadata_->usage();
> +               LOG(HAL, Info)
> +                       << "Static metadata resized: " << entryCount
> +                       << " entries and " << dataCount << " bytes used";
> +       }
> +
> +       return 0;
> +}
> +
> +/* Translate Android format code to libcamera pixel format. */
> +PixelFormat CameraCapabilities::toPixelFormat(int format) const
> +{
> +       auto it = formatsMap_.find(format);
> +       if (it == formatsMap_.end()) {
> +               LOG(HAL, Error) << "Requested format " <<
> utils::hex(format)
> +                               << " not supported";
> +               return PixelFormat();
> +       }
> +
> +       return it->second;
> +}
> +
> +std::unique_ptr<CameraMetadata>
> CameraCapabilities::requestTemplatePreview() const
> +{
> +       /*
> +        * \todo Keep this in sync with the actual number of entries.
> +        * Currently: 20 entries, 35 bytes
> +        */
> +       auto requestTemplate = std::make_unique<CameraMetadata>(21, 36);
> +       if (!requestTemplate->isValid()) {
> +               return nullptr;
> +       }
> +
> +       /* Get the FPS range registered in the static metadata. */
> +       camera_metadata_ro_entry_t entry;
> +       bool found =
> staticMetadata_->getEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> +                                              &entry);
> +       if (!found) {
> +               LOG(HAL, Error) << "Cannot create capture template without
> FPS range";
> +               return nullptr;
> +       }
> +
> +       /*
> +        * Assume the AE_AVAILABLE_TARGET_FPS_RANGE static metadata
> +        * has been assembled as {{min, max} {max, max}}.
> +        */
> +       requestTemplate->addEntry(ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
> +                                 entry.data.i32, 2);
> +
> +       uint8_t aeMode = ANDROID_CONTROL_AE_MODE_ON;
> +       requestTemplate->addEntry(ANDROID_CONTROL_AE_MODE, aeMode);
> +
> +       int32_t aeExposureCompensation = 0;
> +       requestTemplate->addEntry(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
> +                                 aeExposureCompensation);
> +
> +       uint8_t aePrecaptureTrigger =
> ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_IDLE;
> +       requestTemplate->addEntry(ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
> +                                 aePrecaptureTrigger);
> +
> +       uint8_t aeLock = ANDROID_CONTROL_AE_LOCK_OFF;
> +       requestTemplate->addEntry(ANDROID_CONTROL_AE_LOCK, aeLock);
> +
> +       uint8_t aeAntibandingMode =
> ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO;
> +       requestTemplate->addEntry(ANDROID_CONTROL_AE_ANTIBANDING_MODE,
> +                                 aeAntibandingMode);
> +
> +       uint8_t afMode = ANDROID_CONTROL_AF_MODE_OFF;
> +       requestTemplate->addEntry(ANDROID_CONTROL_AF_MODE, afMode);
> +
> +       uint8_t afTrigger = ANDROID_CONTROL_AF_TRIGGER_IDLE;
> +       requestTemplate->addEntry(ANDROID_CONTROL_AF_TRIGGER, afTrigger);
> +
> +       uint8_t awbMode = ANDROID_CONTROL_AWB_MODE_AUTO;
> +       requestTemplate->addEntry(ANDROID_CONTROL_AWB_MODE, awbMode);
> +
> +       uint8_t awbLock = ANDROID_CONTROL_AWB_LOCK_OFF;
> +       requestTemplate->addEntry(ANDROID_CONTROL_AWB_LOCK, awbLock);
> +
> +       uint8_t flashMode = ANDROID_FLASH_MODE_OFF;
> +       requestTemplate->addEntry(ANDROID_FLASH_MODE, flashMode);
> +
> +       uint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
> +       requestTemplate->addEntry(ANDROID_STATISTICS_FACE_DETECT_MODE,
> +                                 faceDetectMode);
> +
> +       uint8_t noiseReduction = ANDROID_NOISE_REDUCTION_MODE_OFF;
> +       requestTemplate->addEntry(ANDROID_NOISE_REDUCTION_MODE,
> +                                 noiseReduction);
> +
> +       uint8_t aberrationMode =
> ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF;
> +       requestTemplate->addEntry(ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
> +                                 aberrationMode);
> +
> +       uint8_t controlMode = ANDROID_CONTROL_MODE_AUTO;
> +       requestTemplate->addEntry(ANDROID_CONTROL_MODE, controlMode);
> +
> +       float lensAperture = 2.53 / 100;
> +       requestTemplate->addEntry(ANDROID_LENS_APERTURE, lensAperture);
> +
> +       uint8_t opticalStabilization =
> ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF;
> +       requestTemplate->addEntry(ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
> +                                 opticalStabilization);
> +
> +       uint8_t captureIntent = ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW;
> +       requestTemplate->addEntry(ANDROID_CONTROL_CAPTURE_INTENT,
> +                                 captureIntent);
> +
> +       return requestTemplate;
> +}
> +
> +std::unique_ptr<CameraMetadata>
> CameraCapabilities::requestTemplateVideo() const
> +{
> +       std::unique_ptr<CameraMetadata> previewTemplate =
> requestTemplatePreview();
> +       if (!previewTemplate)
> +               return nullptr;
> +
> +       /*
> +        * The video template requires a fixed FPS range. Everything else
> +        * stays the same as the preview template.
> +        */
> +       camera_metadata_ro_entry_t entry;
> +
>  staticMetadata_->getEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> +                                 &entry);
> +
> +       /*
> +        * Assume the AE_AVAILABLE_TARGET_FPS_RANGE static metadata
> +        * has been assembled as {{min, max} {max, max}}.
> +        */
> +       previewTemplate->updateEntry(ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
> +                                    entry.data.i32 + 2, 2);
> +
> +       return previewTemplate;
> +}
> diff --git a/src/android/camera_capabilities.h
> b/src/android/camera_capabilities.h
> new file mode 100644
> index 000000000000..f511607bbd90
> --- /dev/null
> +++ b/src/android/camera_capabilities.h
> @@ -0,0 +1,65 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2021, Google Inc.
> + *
> + * camera_capabilities.h - Camera static properties manager
> + */
> +#ifndef __ANDROID_CAMERA_CAPABILITIES_H__
> +#define __ANDROID_CAMERA_CAPABILITIES_H__
> +
> +#include <map>
> +#include <memory>
> +#include <vector>
> +
> +#include <libcamera/camera.h>
> +#include <libcamera/class.h>
> +#include <libcamera/formats.h>
> +#include <libcamera/geometry.h>
> +
> +#include "camera_metadata.h"
> +
> +class CameraCapabilities
> +{
> +public:
> +       CameraCapabilities() = default;
> +
> +       int initialize(std::shared_ptr<libcamera::Camera> camera,
> +                      int orientation, int facing);
> +
> +       CameraMetadata *staticMetadata() const { return
> staticMetadata_.get(); }
> +       libcamera::PixelFormat toPixelFormat(int format) const;
> +       unsigned int maxJpegBufferSize() const { return
> maxJpegBufferSize_; }
> +
> +       std::unique_ptr<CameraMetadata> requestTemplatePreview() const;
> +       std::unique_ptr<CameraMetadata> requestTemplateVideo() const;
> +
> +private:
> +       LIBCAMERA_DISABLE_COPY_AND_MOVE(CameraCapabilities)
> +
> +       struct Camera3StreamConfiguration {
> +               libcamera::Size resolution;
> +               int androidFormat;
> +       };
> +
> +       std::vector<libcamera::Size>
> +       getYUVResolutions(libcamera::CameraConfiguration *cameraConfig,
> +                         const libcamera::PixelFormat &pixelFormat,
> +                         const std::vector<libcamera::Size> &resolutions);
> +       std::vector<libcamera::Size>
> +       getRawResolutions(const libcamera::PixelFormat &pixelFormat);
> +       int initializeStreamConfigurations();
> +
> +       int initializeStaticMetadata();
> +
> +       std::shared_ptr<libcamera::Camera> camera_;
> +
> +       int facing_;
> +       int orientation_;
> +
> +       std::vector<Camera3StreamConfiguration> streamConfigurations_;
> +       std::map<int, libcamera::PixelFormat> formatsMap_;
> +       std::unique_ptr<CameraMetadata> staticMetadata_;
> +       unsigned int maxJpegBufferSize_;
> +};
> +
> +#endif /* __ANDROID_CAMERA_CAPABILITIES_H__ */
> diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
> index 8c71fd0675d3..4bd125d7020a 100644
> --- a/src/android/camera_device.cpp
> +++ b/src/android/camera_device.cpp
> @@ -10,11 +10,8 @@
>  #include "camera_ops.h"
>  #include "post_processor.h"
>
> -#include <array>
> -#include <cmath>
>  #include <fstream>
>  #include <sys/mman.h>
> -#include <tuple>
>  #include <unistd.h>
>  #include <vector>
>
> @@ -23,7 +20,6 @@
>  #include <libcamera/formats.h>
>  #include <libcamera/property_ids.h>
>
> -#include "libcamera/internal/formats.h"
>  #include "libcamera/internal/log.h"
>  #include "libcamera/internal/thread.h"
>  #include "libcamera/internal/utils.h"
> @@ -36,94 +32,6 @@ LOG_DECLARE_CATEGORY(HAL)
>
>  namespace {
>
> -/*
> - * \var camera3Resolutions
> - * \brief The list of image resolutions defined as mandatory to be
> supported by
> - * the Android Camera3 specification
> - */
> -const std::vector<Size> camera3Resolutions = {
> -       { 320, 240 },
> -       { 640, 480 },
> -       { 1280, 720 },
> -       { 1920, 1080 }
> -};
> -
> -/*
> - * \struct Camera3Format
> - * \brief Data associated with an Android format identifier
> - * \var libcameraFormats List of libcamera pixel formats compatible with
> the
> - * Android format
> - * \var name The human-readable representation of the Android format code
> - */
> -struct Camera3Format {
> -       std::vector<PixelFormat> libcameraFormats;
> -       bool mandatory;
> -       const char *name;
> -};
> -
> -/*
> - * \var camera3FormatsMap
> - * \brief Associate Android format code with ancillary data
> - */
> -const std::map<int, const Camera3Format> camera3FormatsMap = {
> -       {
> -               HAL_PIXEL_FORMAT_BLOB, {
> -                       { formats::MJPEG },
> -                       true,
> -                       "BLOB"
> -               }
> -       }, {
> -               HAL_PIXEL_FORMAT_YCbCr_420_888, {
> -                       { formats::NV12, formats::NV21 },
> -                       true,
> -                       "YCbCr_420_888"
> -               }
> -       }, {
> -               /*
> -                * \todo Translate IMPLEMENTATION_DEFINED inspecting the
> gralloc
> -                * usage flag. For now, copy the YCbCr_420 configuration.
> -                */
> -               HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, {
> -                       { formats::NV12, formats::NV21 },
> -                       true,
> -                       "IMPLEMENTATION_DEFINED"
> -               }
> -       }, {
> -               HAL_PIXEL_FORMAT_RAW10, {
> -                       {
> -                               formats::SBGGR10_CSI2P,
> -                               formats::SGBRG10_CSI2P,
> -                               formats::SGRBG10_CSI2P,
> -                               formats::SRGGB10_CSI2P
> -                       },
> -                       false,
> -                       "RAW10"
> -               }
> -       }, {
> -               HAL_PIXEL_FORMAT_RAW12, {
> -                       {
> -                               formats::SBGGR12_CSI2P,
> -                               formats::SGBRG12_CSI2P,
> -                               formats::SGRBG12_CSI2P,
> -                               formats::SRGGB12_CSI2P
> -                       },
> -                       false,
> -                       "RAW12"
> -               }
> -       }, {
> -               HAL_PIXEL_FORMAT_RAW16, {
> -                       {
> -                               formats::SBGGR16,
> -                               formats::SGBRG16,
> -                               formats::SGRBG16,
> -                               formats::SRGGB16
> -                       },
> -                       false,
> -                       "RAW16"
> -               }
> -       },
> -};
> -
>  /*
>   * \struct Camera3StreamConfig
>   * \brief Data to store StreamConfiguration associated with
> camera3_stream(s)
> @@ -512,242 +420,7 @@ int CameraDevice::initialize(const CameraConfigData
> *cameraConfigData)
>                 orientation_ = 0;
>         }
>
> -       /* Acquire the camera and initialize available stream
> configurations. */
> -       int ret = camera_->acquire();
> -       if (ret) {
> -               LOG(HAL, Error) << "Failed to temporarily acquire the
> camera";
> -               return ret;
> -       }
> -
> -       ret = initializeStreamConfigurations();
> -       camera_->release();
> -       return ret;
> -}
> -
> -std::vector<Size> CameraDevice::getYUVResolutions(CameraConfiguration
> *cameraConfig,
> -                                                 const PixelFormat
> &pixelFormat,
> -                                                 const std::vector<Size>
> &resolutions)
> -{
> -       std::vector<Size> supportedResolutions;
> -
> -       StreamConfiguration &cfg = cameraConfig->at(0);
> -       for (const Size &res : resolutions) {
> -               cfg.pixelFormat = pixelFormat;
> -               cfg.size = res;
> -
> -               CameraConfiguration::Status status =
> cameraConfig->validate();
> -               if (status != CameraConfiguration::Valid) {
> -                       LOG(HAL, Debug) << cfg.toString() << " not
> supported";
> -                       continue;
> -               }
> -
> -               LOG(HAL, Debug) << cfg.toString() << " supported";
> -
> -               supportedResolutions.push_back(res);
> -       }
> -
> -       return supportedResolutions;
> -}
> -
> -std::vector<Size> CameraDevice::getRawResolutions(const
> libcamera::PixelFormat &pixelFormat)
> -{
> -       std::unique_ptr<CameraConfiguration> cameraConfig =
> -               camera_->generateConfiguration({ StreamRole::Raw });
> -       StreamConfiguration &cfg = cameraConfig->at(0);
> -       const StreamFormats &formats = cfg.formats();
> -       std::vector<Size> supportedResolutions =
> formats.sizes(pixelFormat);
> -
> -       return supportedResolutions;
> -}
> -
> -/*
> - * Initialize the format conversion map to translate from Android format
> - * identifier to libcamera pixel formats and fill in the list of supported
> - * stream configurations to be reported to the Android camera framework
> through
> - * the static stream configuration metadata.
> - */
> -int CameraDevice::initializeStreamConfigurations()
> -{
> -       /*
> -        * Get the maximum output resolutions
> -        * \todo Get this from the camera properties once defined
> -        */
> -       std::unique_ptr<CameraConfiguration> cameraConfig =
> -               camera_->generateConfiguration({ StillCapture });
> -       if (!cameraConfig) {
> -               LOG(HAL, Error) << "Failed to get maximum resolution";
> -               return -EINVAL;
> -       }
> -       StreamConfiguration &cfg = cameraConfig->at(0);
> -
> -       /*
> -        * \todo JPEG - Adjust the maximum available resolution by taking
> the
> -        * JPEG encoder requirements into account (alignment and aspect
> ratio).
> -        */
> -       const Size maxRes = cfg.size;
> -       LOG(HAL, Debug) << "Maximum supported resolution: " <<
> maxRes.toString();
> -
> -       /*
> -        * Build the list of supported image resolutions.
> -        *
> -        * The resolutions listed in camera3Resolution are mandatory to be
> -        * supported, up to the camera maximum resolution.
> -        *
> -        * Augment the list by adding resolutions calculated from the
> camera
> -        * maximum one.
> -        */
> -       std::vector<Size> cameraResolutions;
> -       std::copy_if(camera3Resolutions.begin(), camera3Resolutions.end(),
> -                    std::back_inserter(cameraResolutions),
> -                    [&](const Size &res) { return res < maxRes; });
> -
> -       /*
> -        * The Camera3 specification suggests adding 1/2 and 1/4 of the
> maximum
> -        * resolution.
> -        */
> -       for (unsigned int divider = 2;; divider <<= 1) {
> -               Size derivedSize{
> -                       maxRes.width / divider,
> -                       maxRes.height / divider,
> -               };
> -
> -               if (derivedSize.width < 320 ||
> -                   derivedSize.height < 240)
> -                       break;
> -
> -               cameraResolutions.push_back(derivedSize);
> -       }
> -       cameraResolutions.push_back(maxRes);
> -
> -       /* Remove duplicated entries from the list of supported
> resolutions. */
> -       std::sort(cameraResolutions.begin(), cameraResolutions.end());
> -       auto last = std::unique(cameraResolutions.begin(),
> cameraResolutions.end());
> -       cameraResolutions.erase(last, cameraResolutions.end());
> -
> -       /*
> -        * Build the list of supported camera formats.
> -        *
> -        * To each Android format a list of compatible libcamera formats is
> -        * associated. The first libcamera format that tests successful is
> added
> -        * to the format translation map used when configuring the streams.
> -        * It is then tested against the list of supported camera
> resolutions to
> -        * build the stream configuration map reported through the camera
> static
> -        * metadata.
> -        */
> -       Size maxJpegSize;
> -       for (const auto &format : camera3FormatsMap) {
> -               int androidFormat = format.first;
> -               const Camera3Format &camera3Format = format.second;
> -               const std::vector<PixelFormat> &libcameraFormats =
> -                       camera3Format.libcameraFormats;
> -
> -               LOG(HAL, Debug) << "Trying to map Android format "
> -                               << camera3Format.name;
> -
> -               /*
> -                * JPEG is always supported, either produced directly by
> the
> -                * camera, or encoded in the HAL.
> -                */
> -               if (androidFormat == HAL_PIXEL_FORMAT_BLOB) {
> -                       formatsMap_[androidFormat] = formats::MJPEG;
> -                       LOG(HAL, Debug) << "Mapped Android format "
> -                                       << camera3Format.name << " to "
> -                                       << formats::MJPEG.toString()
> -                                       << " (fixed mapping)";
> -                       continue;
> -               }
> -
> -               /*
> -                * Test the libcamera formats that can produce images
> -                * compatible with the format defined by Android.
> -                */
> -               PixelFormat mappedFormat;
> -               for (const PixelFormat &pixelFormat : libcameraFormats) {
> -
> -                       LOG(HAL, Debug) << "Testing " <<
> pixelFormat.toString();
> -
> -                       /*
> -                        * The stream configuration size can be adjusted,
> -                        * not the pixel format.
> -                        *
> -                        * \todo This could be simplified once all pipeline
> -                        * handlers will report the StreamFormats list of
> -                        * supported formats.
> -                        */
> -                       cfg.pixelFormat = pixelFormat;
> -
> -                       CameraConfiguration::Status status =
> cameraConfig->validate();
> -                       if (status != CameraConfiguration::Invalid &&
> -                           cfg.pixelFormat == pixelFormat) {
> -                               mappedFormat = pixelFormat;
> -                               break;
> -                       }
> -               }
> -
> -               if (!mappedFormat.isValid()) {
> -                       /* If the format is not mandatory, skip it. */
> -                       if (!camera3Format.mandatory)
> -                               continue;
> -
> -                       LOG(HAL, Error)
> -                               << "Failed to map mandatory Android format
> "
> -                               << camera3Format.name << " ("
> -                               << utils::hex(androidFormat) << "):
> aborting";
> -                       return -EINVAL;
> -               }
> -
> -               /*
> -                * Record the mapping and then proceed to generate the
> -                * stream configurations map, by testing the image
> resolutions.
> -                */
> -               formatsMap_[androidFormat] = mappedFormat;
> -               LOG(HAL, Debug) << "Mapped Android format "
> -                               << camera3Format.name << " to "
> -                               << mappedFormat.toString();
> -
> -               std::vector<Size> resolutions;
> -               const PixelFormatInfo &info =
> PixelFormatInfo::info(mappedFormat);
> -               if (info.colourEncoding ==
> PixelFormatInfo::ColourEncodingRAW)
> -                       resolutions = getRawResolutions(mappedFormat);
> -               else
> -                       resolutions = getYUVResolutions(cameraConfig.get(),
> -                                                       mappedFormat,
> -                                                       cameraResolutions);
> -
> -               for (const Size &res : resolutions) {
> -                       streamConfigurations_.push_back({ res,
> androidFormat });
> -
> -                       /*
> -                        * If the format is HAL_PIXEL_FORMAT_YCbCr_420_888
> -                        * from which JPEG is produced, add an entry for
> -                        * the JPEG stream.
> -                        *
> -                        * \todo Wire the JPEG encoder to query the
> supported
> -                        * sizes provided a list of formats it can encode.
> -                        *
> -                        * \todo Support JPEG streams produced by the
> Camera
> -                        * natively.
> -                        */
> -                       if (androidFormat ==
> HAL_PIXEL_FORMAT_YCbCr_420_888) {
> -                               streamConfigurations_.push_back(
> -                                       { res, HAL_PIXEL_FORMAT_BLOB });
> -                               maxJpegSize = std::max(maxJpegSize, res);
> -                       }
> -               }
> -
> -               /*
> -                * \todo Calculate the maximum JPEG buffer size by asking
> the
> -                * encoder giving the maximum frame size required.
> -                */
> -               maxJpegBufferSize_ = maxJpegSize.width *
> maxJpegSize.height * 1.5;
> -       }
> -
> -       LOG(HAL, Debug) << "Collected stream configuration map: ";
> -       for (const auto &entry : streamConfigurations_)
> -               LOG(HAL, Debug) << "{ " << entry.resolution.toString() <<
> " - "
> -                               << utils::hex(entry.androidFormat) << " }";
> -
> -       return 0;
> +       return capabilities_.initialize(camera_, orientation_, facing_);
>  }
>
>  /*
> @@ -817,802 +490,19 @@ void CameraDevice::stop()
>         state_ = State::Stopped;
>  }
>
> -void CameraDevice::setCallbacks(const camera3_callback_ops_t *callbacks)
> +unsigned int CameraDevice::maxJpegBufferSize() const
>  {
> -       callbacks_ = callbacks;
> +       return capabilities_.maxJpegBufferSize();
>  }
>
> -/*
> - * Return static information for the camera.
> - */
> -const camera_metadata_t *CameraDevice::getStaticMetadata()
> -{
> -       if (staticMetadata_)
> -               return staticMetadata_->get();
> -
> -       staticMetadata_ = std::make_unique<CameraMetadata>(64, 1024);
> -       if (!staticMetadata_->isValid()) {
> -               LOG(HAL, Error) << "Failed to allocate static metadata";
> -               staticMetadata_.reset();
> -               return nullptr;
> -       }
> -
> -       const ControlInfoMap &controlsInfo = camera_->controls();
> -       const ControlList &properties = camera_->properties();
> -
> -       /* Color correction static metadata. */
> -       {
> -               std::vector<uint8_t> data;
> -               data.reserve(3);
> -               const auto &infoMap =
> controlsInfo.find(&controls::draft::ColorCorrectionAberrationMode);
> -               if (infoMap != controlsInfo.end()) {
> -                       for (const auto &value : infoMap->second.values())
> -                               data.push_back(value.get<int32_t>());
> -               } else {
> -
>  data.push_back(ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF);
> -               }
> -
>  staticMetadata_->addEntry(ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
> -                                         data);
> -       }
> -
> -       /* Control static metadata. */
> -       std::vector<uint8_t> aeAvailableAntiBandingModes = {
> -               ANDROID_CONTROL_AE_ANTIBANDING_MODE_OFF,
> -               ANDROID_CONTROL_AE_ANTIBANDING_MODE_50HZ,
> -               ANDROID_CONTROL_AE_ANTIBANDING_MODE_60HZ,
> -               ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO,
> -       };
> -
>  staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,
> -                                 aeAvailableAntiBandingModes);
> -
> -       std::vector<uint8_t> aeAvailableModes = {
> -               ANDROID_CONTROL_AE_MODE_ON,
> -       };
> -       staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_MODES,
> -                                 aeAvailableModes);
> -
> -       int64_t minFrameDurationNsec = -1;
> -       int64_t maxFrameDurationNsec = -1;
> -       const auto frameDurationsInfo =
> controlsInfo.find(&controls::FrameDurationLimits);
> -       if (frameDurationsInfo != controlsInfo.end()) {
> -               minFrameDurationNsec =
> frameDurationsInfo->second.min().get<int64_t>() * 1000;
> -               maxFrameDurationNsec =
> frameDurationsInfo->second.max().get<int64_t>() * 1000;
> -
> -               /*
> -                * Adjust the minimum frame duration to comply with Android
> -                * requirements. The camera service mandates all
> preview/record
> -                * streams to have a minimum frame duration < 33,366
> milliseconds
> -                * (see MAX_PREVIEW_RECORD_DURATION_NS in the camera
> service
> -                * implementation).
> -                *
> -                * If we're close enough (+ 500 useconds) to that value,
> round
> -                * the minimum frame duration of the camera to an accepted
> -                * value.
> -                */
> -               static constexpr int64_t MAX_PREVIEW_RECORD_DURATION_NS =
> 1e9 / 29.97;
> -               if (minFrameDurationNsec > MAX_PREVIEW_RECORD_DURATION_NS
> &&
> -                   minFrameDurationNsec < MAX_PREVIEW_RECORD_DURATION_NS
> + 500000)
> -                       minFrameDurationNsec =
> MAX_PREVIEW_RECORD_DURATION_NS - 1000;
> -
> -               /*
> -                * The AE routine frame rate limits are computed using the
> frame
> -                * duration limits, as libcamera clips the AE routine to
> the
> -                * frame durations.
> -                */
> -               int32_t maxFps = std::round(1e9 / minFrameDurationNsec);
> -               int32_t minFps = std::round(1e9 / maxFrameDurationNsec);
> -               minFps = std::max(1, minFps);
> -
> -               /*
> -                * Force rounding errors so that we have the proper frame
> -                * durations for when we reuse these variables later
> -                */
> -               minFrameDurationNsec = 1e9 / maxFps;
> -               maxFrameDurationNsec = 1e9 / minFps;
> -
> -               /*
> -                * Register to the camera service {min, max} and {max, max}
> -                * intervals as requested by the metadata documentation.
> -                */
> -               int32_t availableAeFpsTarget[] = {
> -                       minFps, maxFps, maxFps, maxFps
> -               };
> -
>  staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> -                                         availableAeFpsTarget);
> -       }
> -
> -       std::vector<int32_t> aeCompensationRange = {
> -               0, 0,
> -       };
> -       staticMetadata_->addEntry(ANDROID_CONTROL_AE_COMPENSATION_RANGE,
> -                                 aeCompensationRange);
> -
> -       const camera_metadata_rational_t aeCompensationStep[] = {
> -               { 0, 1 }
> -       };
> -       staticMetadata_->addEntry(ANDROID_CONTROL_AE_COMPENSATION_STEP,
> -                                 aeCompensationStep);
> -
> -       std::vector<uint8_t> availableAfModes = {
> -               ANDROID_CONTROL_AF_MODE_OFF,
> -       };
> -       staticMetadata_->addEntry(ANDROID_CONTROL_AF_AVAILABLE_MODES,
> -                                 availableAfModes);
> -
> -       std::vector<uint8_t> availableEffects = {
> -               ANDROID_CONTROL_EFFECT_MODE_OFF,
> -       };
> -       staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_EFFECTS,
> -                                 availableEffects);
> -
> -       std::vector<uint8_t> availableSceneModes = {
> -               ANDROID_CONTROL_SCENE_MODE_DISABLED,
> -       };
> -       staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_SCENE_MODES,
> -                                 availableSceneModes);
> -
> -       std::vector<uint8_t> availableStabilizationModes = {
> -               ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF,
> -       };
> -
>  staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
> -                                 availableStabilizationModes);
> -
> -       /*
> -        * \todo Inspect the Camera capabilities to report the available
> -        * AWB modes. Default to AUTO as CTS tests require it.
> -        */
> -       std::vector<uint8_t> availableAwbModes = {
> -               ANDROID_CONTROL_AWB_MODE_AUTO,
> -       };
> -       staticMetadata_->addEntry(ANDROID_CONTROL_AWB_AVAILABLE_MODES,
> -                                 availableAwbModes);
> -
> -       std::vector<int32_t> availableMaxRegions = {
> -               0, 0, 0,
> -       };
> -       staticMetadata_->addEntry(ANDROID_CONTROL_MAX_REGIONS,
> -                                 availableMaxRegions);
> -
> -       std::vector<uint8_t> sceneModesOverride = {
> -               ANDROID_CONTROL_AE_MODE_ON,
> -               ANDROID_CONTROL_AWB_MODE_AUTO,
> -               ANDROID_CONTROL_AF_MODE_OFF,
> -       };
> -       staticMetadata_->addEntry(ANDROID_CONTROL_SCENE_MODE_OVERRIDES,
> -                                 sceneModesOverride);
> -
> -       uint8_t aeLockAvailable = ANDROID_CONTROL_AE_LOCK_AVAILABLE_FALSE;
> -       staticMetadata_->addEntry(ANDROID_CONTROL_AE_LOCK_AVAILABLE,
> -                                 aeLockAvailable);
> -
> -       uint8_t awbLockAvailable =
> ANDROID_CONTROL_AWB_LOCK_AVAILABLE_FALSE;
> -       staticMetadata_->addEntry(ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
> -                                 awbLockAvailable);
> -
> -       char availableControlModes = ANDROID_CONTROL_MODE_AUTO;
> -       staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_MODES,
> -                                 availableControlModes);
> -
> -       /* JPEG static metadata. */
> -
> -       /*
> -        * Create the list of supported thumbnail sizes by inspecting the
> -        * available JPEG resolutions collected in streamConfigurations_
> and
> -        * generate one entry for each aspect ratio.
> -        *
> -        * The JPEG thumbnailer can freely scale, so pick an arbitrary
> -        * (160, 160) size as the bounding rectangle, which is then
> cropped to
> -        * the different supported aspect ratios.
> -        */
> -       constexpr Size maxJpegThumbnail(160, 160);
> -       std::vector<Size> thumbnailSizes;
> -       thumbnailSizes.push_back({ 0, 0 });
> -       for (const auto &entry : streamConfigurations_) {
> -               if (entry.androidFormat != HAL_PIXEL_FORMAT_BLOB)
> -                       continue;
> -
> -               Size thumbnailSize = maxJpegThumbnail
> -                                    .boundedToAspectRatio({
> entry.resolution.width,
> -
> entry.resolution.height });
> -               thumbnailSizes.push_back(thumbnailSize);
> -       }
> -
> -       std::sort(thumbnailSizes.begin(), thumbnailSizes.end());
> -       auto last = std::unique(thumbnailSizes.begin(),
> thumbnailSizes.end());
> -       thumbnailSizes.erase(last, thumbnailSizes.end());
> -
> -       /* Transform sizes in to a list of integers that can be consumed.
> */
> -       std::vector<int32_t> thumbnailEntries;
> -       thumbnailEntries.reserve(thumbnailSizes.size() * 2);
> -       for (const auto &size : thumbnailSizes) {
> -               thumbnailEntries.push_back(size.width);
> -               thumbnailEntries.push_back(size.height);
> -       }
> -       staticMetadata_->addEntry(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,
> -                                 thumbnailEntries);
> -
> -       staticMetadata_->addEntry(ANDROID_JPEG_MAX_SIZE,
> maxJpegBufferSize_);
> -
> -       /* Sensor static metadata. */
> -       std::array<int32_t, 2> pixelArraySize;
> -       {
> -               const Size &size =
> properties.get(properties::PixelArraySize);
> -               pixelArraySize[0] = size.width;
> -               pixelArraySize[1] = size.height;
> -
>  staticMetadata_->addEntry(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
> -                                         pixelArraySize);
> -       }
> -
> -       if (properties.contains(properties::UnitCellSize)) {
> -               const Size &cellSize =
> properties.get<Size>(properties::UnitCellSize);
> -               std::array<float, 2> physicalSize{
> -                       cellSize.width * pixelArraySize[0] / 1e6f,
> -                       cellSize.height * pixelArraySize[1] / 1e6f
> -               };
> -
>  staticMetadata_->addEntry(ANDROID_SENSOR_INFO_PHYSICAL_SIZE,
> -                                         physicalSize);
> -       }
> -
> -       {
> -               const Span<const Rectangle> &rects =
> -                       properties.get(properties::PixelArrayActiveAreas);
> -               std::vector<int32_t> data{
> -                       static_cast<int32_t>(rects[0].x),
> -                       static_cast<int32_t>(rects[0].y),
> -                       static_cast<int32_t>(rects[0].width),
> -                       static_cast<int32_t>(rects[0].height),
> -               };
> -
>  staticMetadata_->addEntry(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
> -                                         data);
> -       }
> -
> -       int32_t sensitivityRange[] = {
> -               32, 2400,
> -       };
> -       staticMetadata_->addEntry(ANDROID_SENSOR_INFO_SENSITIVITY_RANGE,
> -                                 sensitivityRange);
> -
> -       /* Report the color filter arrangement if the camera reports it. */
> -       if
> (properties.contains(properties::draft::ColorFilterArrangement)) {
> -               uint8_t filterArr =
> properties.get(properties::draft::ColorFilterArrangement);
> -
>  staticMetadata_->addEntry(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,
> -                                         filterArr);
> -       }
> -
> -       const auto &exposureInfo =
> controlsInfo.find(&controls::ExposureTime);
> -       if (exposureInfo != controlsInfo.end()) {
> -               int64_t exposureTimeRange[2] = {
> -                       exposureInfo->second.min().get<int32_t>() * 1000LL,
> -                       exposureInfo->second.max().get<int32_t>() * 1000LL,
> -               };
> -
>  staticMetadata_->addEntry(ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE,
> -                                         exposureTimeRange, 2);
> -       }
> -
> -       staticMetadata_->addEntry(ANDROID_SENSOR_ORIENTATION,
> orientation_);
> -
> -       std::vector<int32_t> testPatternModes = {
> -               ANDROID_SENSOR_TEST_PATTERN_MODE_OFF
> -       };
> -       const auto &testPatternsInfo =
> -               controlsInfo.find(&controls::draft::TestPatternMode);
> -       if (testPatternsInfo != controlsInfo.end()) {
> -               const auto &values = testPatternsInfo->second.values();
> -               ASSERT(!values.empty());
> -               for (const auto &value : values) {
> -                       switch (value.get<int32_t>()) {
> -                       case controls::draft::TestPatternModeOff:
> -                               /*
> -                                * ANDROID_SENSOR_TEST_PATTERN_MODE_OFF is
> -                                * already in testPatternModes.
> -                                */
> -                               break;
> -
> -                       case controls::draft::TestPatternModeSolidColor:
> -                               testPatternModes.push_back(
> -
>  ANDROID_SENSOR_TEST_PATTERN_MODE_SOLID_COLOR);
> -                               break;
> -
> -                       case controls::draft::TestPatternModeColorBars:
> -                               testPatternModes.push_back(
> -
>  ANDROID_SENSOR_TEST_PATTERN_MODE_COLOR_BARS);
> -                               break;
> -
> -                       case
> controls::draft::TestPatternModeColorBarsFadeToGray:
> -                               testPatternModes.push_back(
> -
>  ANDROID_SENSOR_TEST_PATTERN_MODE_COLOR_BARS_FADE_TO_GRAY);
> -                               break;
> -
> -                       case controls::draft::TestPatternModePn9:
> -                               testPatternModes.push_back(
> -
>  ANDROID_SENSOR_TEST_PATTERN_MODE_PN9);
> -                               break;
> -
> -                       case controls::draft::TestPatternModeCustom1:
> -                               /* We don't support this yet. */
> -                               break;
> -
> -                       default:
> -                               LOG(HAL, Error) << "Unknown test pattern
> mode: "
> -                                               << value.get<int32_t>();
> -                               continue;
> -                       }
> -               }
> -       }
> -
>  staticMetadata_->addEntry(ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES,
> -                                 testPatternModes);
> -
> -       uint8_t timestampSource =
> ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN;
> -       staticMetadata_->addEntry(ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE,
> -                                 timestampSource);
> -
> -       if (maxFrameDurationNsec > 0)
> -
>  staticMetadata_->addEntry(ANDROID_SENSOR_INFO_MAX_FRAME_DURATION,
> -                                         maxFrameDurationNsec);
> -
> -       /* Statistics static metadata. */
> -       uint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
> -
>  staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
> -                                 faceDetectMode);
> -
> -       int32_t maxFaceCount = 0;
> -       staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
> -                                 maxFaceCount);
> -
> -       {
> -               std::vector<uint8_t> data;
> -               data.reserve(2);
> -               const auto &infoMap =
> controlsInfo.find(&controls::draft::LensShadingMapMode);
> -               if (infoMap != controlsInfo.end()) {
> -                       for (const auto &value : infoMap->second.values())
> -                               data.push_back(value.get<int32_t>());
> -               } else {
> -
>  data.push_back(ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF);
> -               }
> -
>  staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES,
> -                                         data);
> -       }
> -
> -       /* Sync static metadata. */
> -       int32_t maxLatency = ANDROID_SYNC_MAX_LATENCY_UNKNOWN;
> -       staticMetadata_->addEntry(ANDROID_SYNC_MAX_LATENCY, maxLatency);
> -
> -       /* Flash static metadata. */
> -       char flashAvailable = ANDROID_FLASH_INFO_AVAILABLE_FALSE;
> -       staticMetadata_->addEntry(ANDROID_FLASH_INFO_AVAILABLE,
> -                                 flashAvailable);
> -
> -       /* Lens static metadata. */
> -       std::vector<float> lensApertures = {
> -               2.53 / 100,
> -       };
> -       staticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_APERTURES,
> -                                 lensApertures);
> -
> -       uint8_t lensFacing;
> -       switch (facing_) {
> -       default:
> -       case CAMERA_FACING_FRONT:
> -               lensFacing = ANDROID_LENS_FACING_FRONT;
> -               break;
> -       case CAMERA_FACING_BACK:
> -               lensFacing = ANDROID_LENS_FACING_BACK;
> -               break;
> -       case CAMERA_FACING_EXTERNAL:
> -               lensFacing = ANDROID_LENS_FACING_EXTERNAL;
> -               break;
> -       }
> -       staticMetadata_->addEntry(ANDROID_LENS_FACING, lensFacing);
> -
> -       std::vector<float> lensFocalLengths = {
> -               1,
> -       };
> -
>  staticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS,
> -                                 lensFocalLengths);
> -
> -       std::vector<uint8_t> opticalStabilizations = {
> -               ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF,
> -       };
> -
>  staticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
> -                                 opticalStabilizations);
> -
> -       float hypeFocalDistance = 0;
> -       staticMetadata_->addEntry(ANDROID_LENS_INFO_HYPERFOCAL_DISTANCE,
> -                                 hypeFocalDistance);
> -
> -       float minFocusDistance = 0;
> -       staticMetadata_->addEntry(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
> -                                 minFocusDistance);
> -
> -       /* Noise reduction modes. */
> -       {
> -               std::vector<uint8_t> data;
> -               data.reserve(5);
> -               const auto &infoMap =
> controlsInfo.find(&controls::draft::NoiseReductionMode);
> -               if (infoMap != controlsInfo.end()) {
> -                       for (const auto &value : infoMap->second.values())
> -                               data.push_back(value.get<int32_t>());
> -               } else {
> -                       data.push_back(ANDROID_NOISE_REDUCTION_MODE_OFF);
> -               }
> -
>  staticMetadata_->addEntry(ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
> -                                         data);
> -       }
> -
> -       /* Scaler static metadata. */
> -
> -       /*
> -        * \todo The digital zoom factor is a property that depends on the
> -        * desired output configuration and the sensor frame size input to
> the
> -        * ISP. This information is not available to the Android HAL, not
> at
> -        * initialization time at least.
> -        *
> -        * As a workaround rely on pipeline handlers initializing the
> -        * ScalerCrop control with the camera default configuration and
> use the
> -        * maximum and minimum crop rectangles to calculate the digital
> zoom
> -        * factor.
> -        */
> -       float maxZoom = 1.0f;
> -       const auto scalerCrop = controlsInfo.find(&controls::ScalerCrop);
> -       if (scalerCrop != controlsInfo.end()) {
> -               Rectangle min = scalerCrop->second.min().get<Rectangle>();
> -               Rectangle max = scalerCrop->second.max().get<Rectangle>();
> -               maxZoom = std::min(1.0f * max.width / min.width,
> -                                  1.0f * max.height / min.height);
> -       }
> -
>  staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
> -                                 maxZoom);
> -
> -       std::vector<uint32_t> availableStreamConfigurations;
> -       availableStreamConfigurations.reserve(streamConfigurations_.size()
> * 4);
> -       for (const auto &entry : streamConfigurations_) {
> -
>  availableStreamConfigurations.push_back(entry.androidFormat);
> -
>  availableStreamConfigurations.push_back(entry.resolution.width);
> -
>  availableStreamConfigurations.push_back(entry.resolution.height);
> -               availableStreamConfigurations.push_back(
> -
>  ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT);
> -       }
> -
>  staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
> -                                 availableStreamConfigurations);
> -
> -       std::vector<int64_t> availableStallDurations = {
> -               ANDROID_SCALER_AVAILABLE_FORMATS_BLOB, 2560, 1920,
> 33333333,
> -       };
> -       staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,
> -                                 availableStallDurations);
> -
> -       /* Use the minimum frame duration for all the YUV/RGB formats. */
> -       if (minFrameDurationNsec > 0) {
> -               std::vector<int64_t> minFrameDurations;
> -               minFrameDurations.reserve(streamConfigurations_.size() *
> 4);
> -               for (const auto &entry : streamConfigurations_) {
> -                       minFrameDurations.push_back(entry.androidFormat);
> -
>  minFrameDurations.push_back(entry.resolution.width);
> -
>  minFrameDurations.push_back(entry.resolution.height);
> -                       minFrameDurations.push_back(minFrameDurationNsec);
> -               }
> -
>  staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
> -                                         minFrameDurations);
> -       }
> -
> -       uint8_t croppingType = ANDROID_SCALER_CROPPING_TYPE_CENTER_ONLY;
> -       staticMetadata_->addEntry(ANDROID_SCALER_CROPPING_TYPE,
> croppingType);
> -
> -       /* Info static metadata. */
> -       uint8_t supportedHWLevel =
> ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
> -       staticMetadata_->addEntry(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
> -                                 supportedHWLevel);
> -
> -       /* Request static metadata. */
> -       int32_t partialResultCount = 1;
> -       staticMetadata_->addEntry(ANDROID_REQUEST_PARTIAL_RESULT_COUNT,
> -                                 partialResultCount);
> -
> -       {
> -               /* Default the value to 2 if not reported by the camera. */
> -               uint8_t maxPipelineDepth = 2;
> -               const auto &infoMap =
> controlsInfo.find(&controls::draft::PipelineDepth);
> -               if (infoMap != controlsInfo.end())
> -                       maxPipelineDepth =
> infoMap->second.max().get<int32_t>();
> -
>  staticMetadata_->addEntry(ANDROID_REQUEST_PIPELINE_MAX_DEPTH,
> -                                         maxPipelineDepth);
> -       }
> -
> -       /* LIMITED does not support reprocessing. */
> -       uint32_t maxNumInputStreams = 0;
> -       staticMetadata_->addEntry(ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,
> -                                 maxNumInputStreams);
> -
> -       std::vector<uint8_t> availableCapabilities = {
> -               ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE,
> -       };
> -
> -       /* Report if camera supports RAW. */
> -       bool rawStreamAvailable = false;
> -       std::unique_ptr<CameraConfiguration> cameraConfig =
> -               camera_->generateConfiguration({ StreamRole::Raw });
> -       if (cameraConfig && !cameraConfig->empty()) {
> -               const PixelFormatInfo &info =
> -
>  PixelFormatInfo::info(cameraConfig->at(0).pixelFormat);
> -               /* Only advertise RAW support if RAW16 is possible. */
> -               if (info.colourEncoding ==
> PixelFormatInfo::ColourEncodingRAW &&
> -                   info.bitsPerPixel == 16) {
> -                       rawStreamAvailable = true;
> -
>  availableCapabilities.push_back(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW);
> -               }
> -       }
> -
> -       /* Number of { RAW, YUV, JPEG } supported output streams */
> -       int32_t numOutStreams[] = { rawStreamAvailable, 2, 1 };
> -       staticMetadata_->addEntry(ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,
> -                                 numOutStreams);
> -
> -       staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
> -                                 availableCapabilities);
> -
> -       std::vector<int32_t> availableCharacteristicsKeys = {
> -               ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
> -               ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,
> -               ANDROID_CONTROL_AE_AVAILABLE_MODES,
> -               ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> -               ANDROID_CONTROL_AE_COMPENSATION_RANGE,
> -               ANDROID_CONTROL_AE_COMPENSATION_STEP,
> -               ANDROID_CONTROL_AE_LOCK_AVAILABLE,
> -               ANDROID_CONTROL_AF_AVAILABLE_MODES,
> -               ANDROID_CONTROL_AVAILABLE_EFFECTS,
> -               ANDROID_CONTROL_AVAILABLE_MODES,
> -               ANDROID_CONTROL_AVAILABLE_SCENE_MODES,
> -               ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
> -               ANDROID_CONTROL_AWB_AVAILABLE_MODES,
> -               ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
> -               ANDROID_CONTROL_MAX_REGIONS,
> -               ANDROID_CONTROL_SCENE_MODE_OVERRIDES,
> -               ANDROID_FLASH_INFO_AVAILABLE,
> -               ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
> -               ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,
> -               ANDROID_JPEG_MAX_SIZE,
> -               ANDROID_LENS_FACING,
> -               ANDROID_LENS_INFO_AVAILABLE_APERTURES,
> -               ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS,
> -               ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
> -               ANDROID_LENS_INFO_HYPERFOCAL_DISTANCE,
> -               ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
> -               ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
> -               ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
> -               ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,
> -               ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,
> -               ANDROID_REQUEST_PARTIAL_RESULT_COUNT,
> -               ANDROID_REQUEST_PIPELINE_MAX_DEPTH,
> -               ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
> -               ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
> -               ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,
> -               ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
> -               ANDROID_SCALER_CROPPING_TYPE,
> -               ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES,
> -               ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
> -               ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,
> -               ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE,
> -               ANDROID_SENSOR_INFO_MAX_FRAME_DURATION,
> -               ANDROID_SENSOR_INFO_PHYSICAL_SIZE,
> -               ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
> -               ANDROID_SENSOR_INFO_SENSITIVITY_RANGE,
> -               ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE,
> -               ANDROID_SENSOR_ORIENTATION,
> -               ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
> -               ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
> -               ANDROID_SYNC_MAX_LATENCY,
> -       };
> -
>  staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS,
> -                                 availableCharacteristicsKeys);
> -
> -       std::vector<int32_t> availableRequestKeys = {
> -               ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
> -               ANDROID_CONTROL_AE_ANTIBANDING_MODE,
> -               ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
> -               ANDROID_CONTROL_AE_LOCK,
> -               ANDROID_CONTROL_AE_MODE,
> -               ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
> -               ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
> -               ANDROID_CONTROL_AF_MODE,
> -               ANDROID_CONTROL_AF_TRIGGER,
> -               ANDROID_CONTROL_AWB_LOCK,
> -               ANDROID_CONTROL_AWB_MODE,
> -               ANDROID_CONTROL_CAPTURE_INTENT,
> -               ANDROID_CONTROL_EFFECT_MODE,
> -               ANDROID_CONTROL_MODE,
> -               ANDROID_CONTROL_SCENE_MODE,
> -               ANDROID_CONTROL_VIDEO_STABILIZATION_MODE,
> -               ANDROID_FLASH_MODE,
> -               ANDROID_JPEG_ORIENTATION,
> -               ANDROID_JPEG_QUALITY,
> -               ANDROID_JPEG_THUMBNAIL_QUALITY,
> -               ANDROID_JPEG_THUMBNAIL_SIZE,
> -               ANDROID_LENS_APERTURE,
> -               ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
> -               ANDROID_NOISE_REDUCTION_MODE,
> -               ANDROID_SCALER_CROP_REGION,
> -               ANDROID_STATISTICS_FACE_DETECT_MODE
> -       };
> -       staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS,
> -                                 availableRequestKeys);
> -
> -       std::vector<int32_t> availableResultKeys = {
> -               ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
> -               ANDROID_CONTROL_AE_ANTIBANDING_MODE,
> -               ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
> -               ANDROID_CONTROL_AE_LOCK,
> -               ANDROID_CONTROL_AE_MODE,
> -               ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
> -               ANDROID_CONTROL_AE_STATE,
> -               ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
> -               ANDROID_CONTROL_AF_MODE,
> -               ANDROID_CONTROL_AF_STATE,
> -               ANDROID_CONTROL_AF_TRIGGER,
> -               ANDROID_CONTROL_AWB_LOCK,
> -               ANDROID_CONTROL_AWB_MODE,
> -               ANDROID_CONTROL_AWB_STATE,
> -               ANDROID_CONTROL_CAPTURE_INTENT,
> -               ANDROID_CONTROL_EFFECT_MODE,
> -               ANDROID_CONTROL_MODE,
> -               ANDROID_CONTROL_SCENE_MODE,
> -               ANDROID_CONTROL_VIDEO_STABILIZATION_MODE,
> -               ANDROID_FLASH_MODE,
> -               ANDROID_FLASH_STATE,
> -               ANDROID_JPEG_GPS_COORDINATES,
> -               ANDROID_JPEG_GPS_PROCESSING_METHOD,
> -               ANDROID_JPEG_GPS_TIMESTAMP,
> -               ANDROID_JPEG_ORIENTATION,
> -               ANDROID_JPEG_QUALITY,
> -               ANDROID_JPEG_SIZE,
> -               ANDROID_JPEG_THUMBNAIL_QUALITY,
> -               ANDROID_JPEG_THUMBNAIL_SIZE,
> -               ANDROID_LENS_APERTURE,
> -               ANDROID_LENS_FOCAL_LENGTH,
> -               ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
> -               ANDROID_LENS_STATE,
> -               ANDROID_NOISE_REDUCTION_MODE,
> -               ANDROID_REQUEST_PIPELINE_DEPTH,
> -               ANDROID_SCALER_CROP_REGION,
> -               ANDROID_SENSOR_EXPOSURE_TIME,
> -               ANDROID_SENSOR_FRAME_DURATION,
> -               ANDROID_SENSOR_ROLLING_SHUTTER_SKEW,
> -               ANDROID_SENSOR_TEST_PATTERN_MODE,
> -               ANDROID_SENSOR_TIMESTAMP,
> -               ANDROID_STATISTICS_FACE_DETECT_MODE,
> -               ANDROID_STATISTICS_LENS_SHADING_MAP_MODE,
> -               ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE,
> -               ANDROID_STATISTICS_SCENE_FLICKER,
> -       };
> -       staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_RESULT_KEYS,
> -                                 availableResultKeys);
> -
> -       if (!staticMetadata_->isValid()) {
> -               LOG(HAL, Error) << "Failed to construct static metadata";
> -               staticMetadata_.reset();
> -               return nullptr;
> -       }
> -
> -       if (staticMetadata_->resized()) {
> -               auto [entryCount, dataCount] = staticMetadata_->usage();
> -               LOG(HAL, Info)
> -                       << "Static metadata resized: " << entryCount
> -                       << " entries and " << dataCount << " bytes used";
> -       }
> -
> -       return staticMetadata_->get();
> -}
> -
> -std::unique_ptr<CameraMetadata> CameraDevice::requestTemplatePreview()
> +void CameraDevice::setCallbacks(const camera3_callback_ops_t *callbacks)
>  {
> -       /*
> -        * \todo Keep this in sync with the actual number of entries.
> -        * Currently: 20 entries, 35 bytes
> -        */
> -       auto requestTemplate = std::make_unique<CameraMetadata>(21, 36);
> -       if (!requestTemplate->isValid()) {
> -               return nullptr;
> -       }
> -
> -       /* Get the FPS range registered in the static metadata. */
> -       camera_metadata_ro_entry_t entry;
> -       bool found =
> staticMetadata_->getEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> -                                              &entry);
> -       if (!found) {
> -               LOG(HAL, Error) << "Cannot create capture template without
> FPS range";
> -               return nullptr;
> -       }
> -
> -       /*
> -        * Assume the AE_AVAILABLE_TARGET_FPS_RANGE static metadata
> -        * has been assembled as {{min, max} {max, max}}.
> -        */
> -       requestTemplate->addEntry(ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
> -                                 entry.data.i32, 2);
> -
> -       uint8_t aeMode = ANDROID_CONTROL_AE_MODE_ON;
> -       requestTemplate->addEntry(ANDROID_CONTROL_AE_MODE, aeMode);
> -
> -       int32_t aeExposureCompensation = 0;
> -       requestTemplate->addEntry(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
> -                                 aeExposureCompensation);
> -
> -       uint8_t aePrecaptureTrigger =
> ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_IDLE;
> -       requestTemplate->addEntry(ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
> -                                 aePrecaptureTrigger);
> -
> -       uint8_t aeLock = ANDROID_CONTROL_AE_LOCK_OFF;
> -       requestTemplate->addEntry(ANDROID_CONTROL_AE_LOCK, aeLock);
> -
> -       uint8_t aeAntibandingMode =
> ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO;
> -       requestTemplate->addEntry(ANDROID_CONTROL_AE_ANTIBANDING_MODE,
> -                                 aeAntibandingMode);
> -
> -       uint8_t afMode = ANDROID_CONTROL_AF_MODE_OFF;
> -       requestTemplate->addEntry(ANDROID_CONTROL_AF_MODE, afMode);
> -
> -       uint8_t afTrigger = ANDROID_CONTROL_AF_TRIGGER_IDLE;
> -       requestTemplate->addEntry(ANDROID_CONTROL_AF_TRIGGER, afTrigger);
> -
> -       uint8_t awbMode = ANDROID_CONTROL_AWB_MODE_AUTO;
> -       requestTemplate->addEntry(ANDROID_CONTROL_AWB_MODE, awbMode);
> -
> -       uint8_t awbLock = ANDROID_CONTROL_AWB_LOCK_OFF;
> -       requestTemplate->addEntry(ANDROID_CONTROL_AWB_LOCK, awbLock);
> -
> -       uint8_t flashMode = ANDROID_FLASH_MODE_OFF;
> -       requestTemplate->addEntry(ANDROID_FLASH_MODE, flashMode);
> -
> -       uint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
> -       requestTemplate->addEntry(ANDROID_STATISTICS_FACE_DETECT_MODE,
> -                                 faceDetectMode);
> -
> -       uint8_t noiseReduction = ANDROID_NOISE_REDUCTION_MODE_OFF;
> -       requestTemplate->addEntry(ANDROID_NOISE_REDUCTION_MODE,
> -                                 noiseReduction);
> -
> -       uint8_t aberrationMode =
> ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF;
> -       requestTemplate->addEntry(ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
> -                                 aberrationMode);
> -
> -       uint8_t controlMode = ANDROID_CONTROL_MODE_AUTO;
> -       requestTemplate->addEntry(ANDROID_CONTROL_MODE, controlMode);
> -
> -       float lensAperture = 2.53 / 100;
> -       requestTemplate->addEntry(ANDROID_LENS_APERTURE, lensAperture);
> -
> -       uint8_t opticalStabilization =
> ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF;
> -       requestTemplate->addEntry(ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
> -                                 opticalStabilization);
> -
> -       uint8_t captureIntent = ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW;
> -       requestTemplate->addEntry(ANDROID_CONTROL_CAPTURE_INTENT,
> -                                 captureIntent);
> -
> -       return requestTemplate;
> +       callbacks_ = callbacks;
>  }
>
> -std::unique_ptr<CameraMetadata> CameraDevice::requestTemplateVideo()
> +const camera_metadata_t *CameraDevice::getStaticMetadata()
>  {
> -       std::unique_ptr<CameraMetadata> previewTemplate =
> requestTemplatePreview();
> -       if (!previewTemplate)
> -               return nullptr;
> -
> -       /*
> -        * The video template requires a fixed FPS range. Everything else
> -        * stays the same as the preview template.
> -        */
> -       camera_metadata_ro_entry_t entry;
> -
>  staticMetadata_->getEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> -                                 &entry);
> -
> -       /*
> -        * Assume the AE_AVAILABLE_TARGET_FPS_RANGE static metadata
> -        * has been assembled as {{min, max} {max, max}}.
> -        */
> -       previewTemplate->updateEntry(ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
> -                                    entry.data.i32 + 2, 2);
> -
> -       return previewTemplate;
> +       return capabilities_.staticMetadata()->get();
>  }
>
>  /*
> @@ -1630,7 +520,7 @@ const camera_metadata_t
> *CameraDevice::constructDefaultRequestSettings(int type)
>         switch (type) {
>         case CAMERA3_TEMPLATE_PREVIEW:
>                 captureIntent = ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW;
> -               requestTemplate = requestTemplatePreview();
> +               requestTemplate = capabilities_.requestTemplatePreview();
>                 break;
>         case CAMERA3_TEMPLATE_STILL_CAPTURE:
>                 /*
> @@ -1638,15 +528,15 @@ const camera_metadata_t
> *CameraDevice::constructDefaultRequestSettings(int type)
>                  * for the torch mode we currently do not support.
>                  */
>                 captureIntent =
> ANDROID_CONTROL_CAPTURE_INTENT_STILL_CAPTURE;
> -               requestTemplate = requestTemplatePreview();
> +               requestTemplate = capabilities_.requestTemplatePreview();
>                 break;
>         case CAMERA3_TEMPLATE_VIDEO_RECORD:
>                 captureIntent =
> ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_RECORD;
> -               requestTemplate = requestTemplateVideo();
> +               requestTemplate = capabilities_.requestTemplateVideo();
>                 break;
>         case CAMERA3_TEMPLATE_VIDEO_SNAPSHOT:
>                 captureIntent =
> ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT;
> -               requestTemplate = requestTemplateVideo();
> +               requestTemplate = capabilities_.requestTemplateVideo();
>                 break;
>         /* \todo Implement templates generation for the remaining use
> cases. */
>         case CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG:
> @@ -1668,19 +558,6 @@ const camera_metadata_t
> *CameraDevice::constructDefaultRequestSettings(int type)
>         return requestTemplates_[type]->get();
>  }
>
> -PixelFormat CameraDevice::toPixelFormat(int format) const
> -{
> -       /* Translate Android format code to libcamera pixel format. */
> -       auto it = formatsMap_.find(format);
> -       if (it == formatsMap_.end()) {
> -               LOG(HAL, Error) << "Requested format " <<
> utils::hex(format)
> -                               << " not supported";
> -               return PixelFormat();
> -       }
> -
> -       return it->second;
> -}
> -
>  /*
>   * Inspect the stream_list to produce a list of StreamConfiguration to
>   * be use to configure the Camera.
> @@ -1727,7 +604,7 @@ int
> CameraDevice::configureStreams(camera3_stream_configuration_t *stream_list)
>                 camera3_stream_t *stream = stream_list->streams[i];
>                 Size size(stream->width, stream->height);
>
> -               PixelFormat format = toPixelFormat(stream->format);
> +               PixelFormat format =
> capabilities_.toPixelFormat(stream->format);
>
>                 LOG(HAL, Info) << "Stream #" << i
>                                << ", direction: " << stream->stream_type
> diff --git a/src/android/camera_device.h b/src/android/camera_device.h
> index 4aadb27c562c..090fe28a551e 100644
> --- a/src/android/camera_device.h
> +++ b/src/android/camera_device.h
> @@ -10,14 +10,12 @@
>  #include <map>
>  #include <memory>
>  #include <mutex>
> -#include <tuple>
>  #include <vector>
>
>  #include <hardware/camera3.h>
>
>  #include <libcamera/buffer.h>
>  #include <libcamera/camera.h>
> -#include <libcamera/geometry.h>
>  #include <libcamera/request.h>
>  #include <libcamera/stream.h>
>
> @@ -26,6 +24,7 @@
>  #include "libcamera/internal/message.h"
>  #include "libcamera/internal/thread.h"
>
> +#include "camera_capabilities.h"
>  #include "camera_metadata.h"
>  #include "camera_stream.h"
>  #include "camera_worker.h"
> @@ -57,7 +56,7 @@ public:
>         const std::string &model() const { return model_; }
>         int facing() const { return facing_; }
>         int orientation() const { return orientation_; }
> -       unsigned int maxJpegBufferSize() const { return
> maxJpegBufferSize_; }
> +       unsigned int maxJpegBufferSize() const;
>
>         void setCallbacks(const camera3_callback_ops_t *callbacks);
>         const camera_metadata_t *getStaticMetadata();
> @@ -86,11 +85,6 @@ private:
>                 std::unique_ptr<CaptureRequest> request_;
>         };
>
> -       struct Camera3StreamConfiguration {
> -               libcamera::Size resolution;
> -               int androidFormat;
> -       };
> -
>         enum class State {
>                 Stopped,
>                 Flushing,
> @@ -99,22 +93,11 @@ private:
>
>         void stop();
>
> -       int initializeStreamConfigurations();
> -       std::vector<libcamera::Size>
> -       getYUVResolutions(libcamera::CameraConfiguration *cameraConfig,
> -                         const libcamera::PixelFormat &pixelFormat,
> -                         const std::vector<libcamera::Size> &resolutions);
> -       std::vector<libcamera::Size>
> -       getRawResolutions(const libcamera::PixelFormat &pixelFormat);
> -
>         libcamera::FrameBuffer *createFrameBuffer(const buffer_handle_t
> camera3buffer);
>         void abortRequest(camera3_capture_request_t *request);
>         void notifyShutter(uint32_t frameNumber, uint64_t timestamp);
>         void notifyError(uint32_t frameNumber, camera3_stream_t *stream,
>                          camera3_error_msg_code code);
> -       std::unique_ptr<CameraMetadata> requestTemplatePreview();
> -       std::unique_ptr<CameraMetadata> requestTemplateVideo();
> -       libcamera::PixelFormat toPixelFormat(int format) const;
>         int processControls(Camera3RequestDescriptor *descriptor);
>         std::unique_ptr<CameraMetadata> getResultMetadata(
>                 const Camera3RequestDescriptor &descriptor) const;
> @@ -129,13 +112,11 @@ private:
>
>         std::shared_ptr<libcamera::Camera> camera_;
>         std::unique_ptr<libcamera::CameraConfiguration> config_;
> +       CameraCapabilities capabilities_;
>
> -       std::unique_ptr<CameraMetadata> staticMetadata_;
>         std::map<unsigned int, std::unique_ptr<CameraMetadata>>
> requestTemplates_;
>         const camera3_callback_ops_t *callbacks_;
>
> -       std::vector<Camera3StreamConfiguration> streamConfigurations_;
> -       std::map<int, libcamera::PixelFormat> formatsMap_;
>         std::vector<CameraStream> streams_;
>
>         libcamera::Mutex descriptorsMutex_; /* Protects descriptors_. */
> @@ -147,8 +128,6 @@ private:
>         int facing_;
>         int orientation_;
>
> -       unsigned int maxJpegBufferSize_;
> -
>         CameraMetadata lastSettings_;
>  };
>
> diff --git a/src/android/meson.build b/src/android/meson.build
> index f27fd5316705..6270fb201338 100644
> --- a/src/android/meson.build
> +++ b/src/android/meson.build
> @@ -44,6 +44,7 @@ subdir('cros')
>
>  android_hal_sources = files([
>      'camera3_hal.cpp',
> +    'camera_capabilities.cpp',
>      'camera_device.cpp',
>      'camera_hal_config.cpp',
>      'camera_hal_manager.cpp',
> --
> 2.31.1
>
>
Jacopo Mondi June 22, 2021, 7:41 a.m. UTC | #2
Hi Hiro,

On Tue, Jun 22, 2021 at 10:34:27AM +0900, Hirokazu Honda wrote:
> Hi Jacopo, thank you for the patch.
>
> I failed to apply the patch on the top of the latest tree to review.
> Could you tell me the parent commit which I can apply this patch?

weird, I just applied these two patches cleanly on the latest
master which for me is
969da3189439 ("libcamera: utils: Support systems that lack secure_getenv and issetugid"

>
> -Hiro
> On Tue, Jun 22, 2021 at 12:29 AM Jacopo Mondi <jacopo@jmondi.org> wrote:
>
> > The camera_device.cpp has grown a little too much, and it has quickly
> > become hard to maintain. Break out the handling of the static
> > information collected at camera initialization time to a new
> > CameraCapabilities class.
> >
> > Break out from the camera_device.cpp file all the functions relative to:
> > - Initialization of supported stream configurations
> > - Initialization of static metadata
> > - Initialization of request templates
> >
> > Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
> > Acked-by: Paul Elder <paul.elder@ideasonboard.com>
> > Tested-by: Paul Elder <paul.elder@ideasonboard.com>
> > ---
> >  src/android/camera_capabilities.cpp | 1164 +++++++++++++++++++++++++++
> >  src/android/camera_capabilities.h   |   65 ++
> >  src/android/camera_device.cpp       | 1147 +-------------------------
> >  src/android/camera_device.h         |   27 +-
> >  src/android/meson.build             |    1 +
> >  5 files changed, 1245 insertions(+), 1159 deletions(-)
> >  create mode 100644 src/android/camera_capabilities.cpp
> >  create mode 100644 src/android/camera_capabilities.h
> >
> > diff --git a/src/android/camera_capabilities.cpp
> > b/src/android/camera_capabilities.cpp
> > new file mode 100644
> > index 000000000000..311a2c839586
> > --- /dev/null
> > +++ b/src/android/camera_capabilities.cpp
> > @@ -0,0 +1,1164 @@
> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > +/*
> > + * Copyright (C) 2021, Google Inc.
> > + *
> > + * camera_capabilities.cpp - Camera static properties manager
> > + */
> > +
> > +#include "camera_capabilities.h"
> > +
> > +#include <array>
> > +#include <cmath>
> > +
> > +#include <hardware/camera3.h>
> > +
> > +#include <libcamera/control_ids.h>
> > +#include <libcamera/controls.h>
> > +#include <libcamera/property_ids.h>
> > +
> > +#include "libcamera/internal/formats.h"
> > +#include "libcamera/internal/log.h"
> > +
> > +using namespace libcamera;
> > +
> > +LOG_DECLARE_CATEGORY(HAL)
> > +
> > +namespace {
> > +
> > +/*
> > + * \var camera3Resolutions
> > + * \brief The list of image resolutions defined as mandatory to be
> > supported by
> > + * the Android Camera3 specification
> > + */
> > +const std::vector<Size> camera3Resolutions = {
> > +       { 320, 240 },
> > +       { 640, 480 },
> > +       { 1280, 720 },
> > +       { 1920, 1080 }
> > +};
> > +
> > +/*
> > + * \struct Camera3Format
> > + * \brief Data associated with an Android format identifier
> > + * \var libcameraFormats List of libcamera pixel formats compatible with
> > the
> > + * Android format
> > + * \var name The human-readable representation of the Android format code
> > + */
> > +struct Camera3Format {
> > +       std::vector<PixelFormat> libcameraFormats;
> > +       bool mandatory;
> > +       const char *name;
> > +};
> > +
> > +/*
> > + * \var camera3FormatsMap
> > + * \brief Associate Android format code with ancillary data
> > + */
> > +const std::map<int, const Camera3Format> camera3FormatsMap = {
> > +       {
> > +               HAL_PIXEL_FORMAT_BLOB, {
> > +                       { formats::MJPEG },
> > +                       true,
> > +                       "BLOB"
> > +               }
> > +       }, {
> > +               HAL_PIXEL_FORMAT_YCbCr_420_888, {
> > +                       { formats::NV12, formats::NV21 },
> > +                       true,
> > +                       "YCbCr_420_888"
> > +               }
> > +       }, {
> > +               /*
> > +                * \todo Translate IMPLEMENTATION_DEFINED inspecting the
> > gralloc
> > +                * usage flag. For now, copy the YCbCr_420 configuration.
> > +                */
> > +               HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, {
> > +                       { formats::NV12, formats::NV21 },
> > +                       true,
> > +                       "IMPLEMENTATION_DEFINED"
> > +               }
> > +       }, {
> > +               HAL_PIXEL_FORMAT_RAW10, {
> > +                       {
> > +                               formats::SBGGR10_CSI2P,
> > +                               formats::SGBRG10_CSI2P,
> > +                               formats::SGRBG10_CSI2P,
> > +                               formats::SRGGB10_CSI2P
> > +                       },
> > +                       false,
> > +                       "RAW10"
> > +               }
> > +       }, {
> > +               HAL_PIXEL_FORMAT_RAW12, {
> > +                       {
> > +                               formats::SBGGR12_CSI2P,
> > +                               formats::SGBRG12_CSI2P,
> > +                               formats::SGRBG12_CSI2P,
> > +                               formats::SRGGB12_CSI2P
> > +                       },
> > +                       false,
> > +                       "RAW12"
> > +               }
> > +       }, {
> > +               HAL_PIXEL_FORMAT_RAW16, {
> > +                       {
> > +                               formats::SBGGR16,
> > +                               formats::SGBRG16,
> > +                               formats::SGRBG16,
> > +                               formats::SRGGB16
> > +                       },
> > +                       false,
> > +                       "RAW16"
> > +               }
> > +       },
> > +};
> > +
> > +} /* namespace */
> > +
> > +int CameraCapabilities::initialize(std::shared_ptr<libcamera::Camera>
> > camera,
> > +                                  int orientation, int facing)
> > +{
> > +       camera_ = camera;
> > +       orientation_ = orientation;
> > +       facing_ = facing;
> > +
> > +       /* Acquire the camera and initialize available stream
> > configurations. */
> > +       int ret = camera_->acquire();
> > +       if (ret) {
> > +               LOG(HAL, Error) << "Failed to temporarily acquire the
> > camera";
> > +               return ret;
> > +       }
> > +
> > +       ret = initializeStreamConfigurations();
> > +       camera_->release();
> > +       if (ret)
> > +               return ret;
> > +
> > +       return initializeStaticMetadata();
> > +}
> > +
> > +std::vector<Size>
> > CameraCapabilities::getYUVResolutions(CameraConfiguration *cameraConfig,
> > +                                                       const PixelFormat
> > &pixelFormat,
> > +                                                       const
> > std::vector<Size> &resolutions)
> > +{
> > +       std::vector<Size> supportedResolutions;
> > +
> > +       StreamConfiguration &cfg = cameraConfig->at(0);
> > +       for (const Size &res : resolutions) {
> > +               cfg.pixelFormat = pixelFormat;
> > +               cfg.size = res;
> > +
> > +               CameraConfiguration::Status status =
> > cameraConfig->validate();
> > +               if (status != CameraConfiguration::Valid) {
> > +                       LOG(HAL, Debug) << cfg.toString() << " not
> > supported";
> > +                       continue;
> > +               }
> > +
> > +               LOG(HAL, Debug) << cfg.toString() << " supported";
> > +
> > +               supportedResolutions.push_back(res);
> > +       }
> > +
> > +       return supportedResolutions;
> > +}
> > +
> > +std::vector<Size> CameraCapabilities::getRawResolutions(const
> > libcamera::PixelFormat &pixelFormat)
> > +{
> > +       std::unique_ptr<CameraConfiguration> cameraConfig =
> > +               camera_->generateConfiguration({ StreamRole::Raw });
> > +       StreamConfiguration &cfg = cameraConfig->at(0);
> > +       const StreamFormats &formats = cfg.formats();
> > +       std::vector<Size> supportedResolutions =
> > formats.sizes(pixelFormat);
> > +
> > +       return supportedResolutions;
> > +}
> > +
> > +/*
> > + * Initialize the format conversion map to translate from Android format
> > + * identifier to libcamera pixel formats and fill in the list of supported
> > + * stream configurations to be reported to the Android camera framework
> > through
> > + * the Camera static metadata.
> > + */
> > +int CameraCapabilities::initializeStreamConfigurations()
> > +{
> > +       /*
> > +        * Get the maximum output resolutions
> > +        * \todo Get this from the camera properties once defined
> > +        */
> > +       std::unique_ptr<CameraConfiguration> cameraConfig =
> > +               camera_->generateConfiguration({ StillCapture });
> > +       if (!cameraConfig) {
> > +               LOG(HAL, Error) << "Failed to get maximum resolution";
> > +               return -EINVAL;
> > +       }
> > +       StreamConfiguration &cfg = cameraConfig->at(0);
> > +
> > +       /*
> > +        * \todo JPEG - Adjust the maximum available resolution by taking
> > the
> > +        * JPEG encoder requirements into account (alignment and aspect
> > ratio).
> > +        */
> > +       const Size maxRes = cfg.size;
> > +       LOG(HAL, Debug) << "Maximum supported resolution: " <<
> > maxRes.toString();
> > +
> > +       /*
> > +        * Build the list of supported image resolutions.
> > +        *
> > +        * The resolutions listed in camera3Resolution are mandatory to be
> > +        * supported, up to the camera maximum resolution.
> > +        *
> > +        * Augment the list by adding resolutions calculated from the
> > camera
> > +        * maximum one.
> > +        */
> > +       std::vector<Size> cameraResolutions;
> > +       std::copy_if(camera3Resolutions.begin(), camera3Resolutions.end(),
> > +                    std::back_inserter(cameraResolutions),
> > +                    [&](const Size &res) { return res < maxRes; });
> > +
> > +       /*
> > +        * The Camera3 specification suggests adding 1/2 and 1/4 of the
> > maximum
> > +        * resolution.
> > +        */
> > +       for (unsigned int divider = 2;; divider <<= 1) {
> > +               Size derivedSize{
> > +                       maxRes.width / divider,
> > +                       maxRes.height / divider,
> > +               };
> > +
> > +               if (derivedSize.width < 320 ||
> > +                   derivedSize.height < 240)
> > +                       break;
> > +
> > +               cameraResolutions.push_back(derivedSize);
> > +       }
> > +       cameraResolutions.push_back(maxRes);
> > +
> > +       /* Remove duplicated entries from the list of supported
> > resolutions. */
> > +       std::sort(cameraResolutions.begin(), cameraResolutions.end());
> > +       auto last = std::unique(cameraResolutions.begin(),
> > cameraResolutions.end());
> > +       cameraResolutions.erase(last, cameraResolutions.end());
> > +
> > +       /*
> > +        * Build the list of supported camera formats.
> > +        *
> > +        * To each Android format a list of compatible libcamera formats is
> > +        * associated. The first libcamera format that tests successful is
> > added
> > +        * to the format translation map used when configuring the streams.
> > +        * It is then tested against the list of supported camera
> > resolutions to
> > +        * build the stream configuration map reported through the camera
> > static
> > +        * metadata.
> > +        */
> > +       Size maxJpegSize;
> > +       for (const auto &format : camera3FormatsMap) {
> > +               int androidFormat = format.first;
> > +               const Camera3Format &camera3Format = format.second;
> > +               const std::vector<PixelFormat> &libcameraFormats =
> > +                       camera3Format.libcameraFormats;
> > +
> > +               LOG(HAL, Debug) << "Trying to map Android format "
> > +                               << camera3Format.name;
> > +
> > +               /*
> > +                * JPEG is always supported, either produced directly by
> > the
> > +                * camera, or encoded in the HAL.
> > +                */
> > +               if (androidFormat == HAL_PIXEL_FORMAT_BLOB) {
> > +                       formatsMap_[androidFormat] = formats::MJPEG;
> > +                       LOG(HAL, Debug) << "Mapped Android format "
> > +                                       << camera3Format.name << " to "
> > +                                       << formats::MJPEG.toString()
> > +                                       << " (fixed mapping)";
> > +                       continue;
> > +               }
> > +
> > +               /*
> > +                * Test the libcamera formats that can produce images
> > +                * compatible with the format defined by Android.
> > +                */
> > +               PixelFormat mappedFormat;
> > +               for (const PixelFormat &pixelFormat : libcameraFormats) {
> > +
> > +                       LOG(HAL, Debug) << "Testing " <<
> > pixelFormat.toString();
> > +
> > +                       /*
> > +                        * The stream configuration size can be adjusted,
> > +                        * not the pixel format.
> > +                        *
> > +                        * \todo This could be simplified once all pipeline
> > +                        * handlers will report the StreamFormats list of
> > +                        * supported formats.
> > +                        */
> > +                       cfg.pixelFormat = pixelFormat;
> > +
> > +                       CameraConfiguration::Status status =
> > cameraConfig->validate();
> > +                       if (status != CameraConfiguration::Invalid &&
> > +                           cfg.pixelFormat == pixelFormat) {
> > +                               mappedFormat = pixelFormat;
> > +                               break;
> > +                       }
> > +               }
> > +
> > +               if (!mappedFormat.isValid()) {
> > +                       /* If the format is not mandatory, skip it. */
> > +                       if (!camera3Format.mandatory)
> > +                               continue;
> > +
> > +                       LOG(HAL, Error)
> > +                               << "Failed to map mandatory Android format
> > "
> > +                               << camera3Format.name << " ("
> > +                               << utils::hex(androidFormat) << "):
> > aborting";
> > +                       return -EINVAL;
> > +               }
> > +
> > +               /*
> > +                * Record the mapping and then proceed to generate the
> > +                * stream configurations map, by testing the image
> > resolutions.
> > +                */
> > +               formatsMap_[androidFormat] = mappedFormat;
> > +               LOG(HAL, Debug) << "Mapped Android format "
> > +                               << camera3Format.name << " to "
> > +                               << mappedFormat.toString();
> > +
> > +               std::vector<Size> resolutions;
> > +               const PixelFormatInfo &info =
> > PixelFormatInfo::info(mappedFormat);
> > +               if (info.colourEncoding ==
> > PixelFormatInfo::ColourEncodingRAW)
> > +                       resolutions = getRawResolutions(mappedFormat);
> > +               else
> > +                       resolutions = getYUVResolutions(cameraConfig.get(),
> > +                                                       mappedFormat,
> > +                                                       cameraResolutions);
> > +
> > +               for (const Size &res : resolutions) {
> > +                       streamConfigurations_.push_back({ res,
> > androidFormat });
> > +
> > +                       /*
> > +                        * If the format is HAL_PIXEL_FORMAT_YCbCr_420_888
> > +                        * from which JPEG is produced, add an entry for
> > +                        * the JPEG stream.
> > +                        *
> > +                        * \todo Wire the JPEG encoder to query the
> > supported
> > +                        * sizes provided a list of formats it can encode.
> > +                        *
> > +                        * \todo Support JPEG streams produced by the
> > Camera
> > +                        * natively.
> > +                        */
> > +                       if (androidFormat ==
> > HAL_PIXEL_FORMAT_YCbCr_420_888) {
> > +                               streamConfigurations_.push_back(
> > +                                       { res, HAL_PIXEL_FORMAT_BLOB });
> > +                               maxJpegSize = std::max(maxJpegSize, res);
> > +                       }
> > +               }
> > +
> > +               /*
> > +                * \todo Calculate the maximum JPEG buffer size by asking
> > the
> > +                * encoder giving the maximum frame size required.
> > +                */
> > +               maxJpegBufferSize_ = maxJpegSize.width *
> > maxJpegSize.height * 1.5;
> > +       }
> > +
> > +       LOG(HAL, Debug) << "Collected stream configuration map: ";
> > +       for (const auto &entry : streamConfigurations_)
> > +               LOG(HAL, Debug) << "{ " << entry.resolution.toString() <<
> > " - "
> > +                               << utils::hex(entry.androidFormat) << " }";
> > +
> > +       return 0;
> > +}
> > +
> > +int CameraCapabilities::initializeStaticMetadata()
> > +{
> > +       staticMetadata_ = std::make_unique<CameraMetadata>(64, 1024);
> > +       if (!staticMetadata_->isValid()) {
> > +               LOG(HAL, Error) << "Failed to allocate static metadata";
> > +               staticMetadata_.reset();
> > +               return -EINVAL;
> > +       }
> > +
> > +       const ControlInfoMap &controlsInfo = camera_->controls();
> > +       const ControlList &properties = camera_->properties();
> > +
> > +       /* Color correction static metadata. */
> > +       {
> > +               std::vector<uint8_t> data;
> > +               data.reserve(3);
> > +               const auto &infoMap =
> > controlsInfo.find(&controls::draft::ColorCorrectionAberrationMode);
> > +               if (infoMap != controlsInfo.end()) {
> > +                       for (const auto &value : infoMap->second.values())
> > +                               data.push_back(value.get<int32_t>());
> > +               } else {
> > +
> >  data.push_back(ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF);
> > +               }
> > +
> >  staticMetadata_->addEntry(ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
> > +                                         data);
> > +       }
> > +
> > +       /* Control static metadata. */
> > +       std::vector<uint8_t> aeAvailableAntiBandingModes = {
> > +               ANDROID_CONTROL_AE_ANTIBANDING_MODE_OFF,
> > +               ANDROID_CONTROL_AE_ANTIBANDING_MODE_50HZ,
> > +               ANDROID_CONTROL_AE_ANTIBANDING_MODE_60HZ,
> > +               ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO,
> > +       };
> > +
> >  staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,
> > +                                 aeAvailableAntiBandingModes);
> > +
> > +       std::vector<uint8_t> aeAvailableModes = {
> > +               ANDROID_CONTROL_AE_MODE_ON,
> > +       };
> > +       staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_MODES,
> > +                                 aeAvailableModes);
> > +
> > +       int64_t minFrameDurationNsec = -1;
> > +       int64_t maxFrameDurationNsec = -1;
> > +       const auto frameDurationsInfo =
> > controlsInfo.find(&controls::FrameDurationLimits);
> > +       if (frameDurationsInfo != controlsInfo.end()) {
> > +               minFrameDurationNsec =
> > frameDurationsInfo->second.min().get<int64_t>() * 1000;
> > +               maxFrameDurationNsec =
> > frameDurationsInfo->second.max().get<int64_t>() * 1000;
> > +
> > +               /*
> > +                * Adjust the minimum frame duration to comply with Android
> > +                * requirements. The camera service mandates all
> > preview/record
> > +                * streams to have a minimum frame duration < 33,366
> > milliseconds
> > +                * (see MAX_PREVIEW_RECORD_DURATION_NS in the camera
> > service
> > +                * implementation).
> > +                *
> > +                * If we're close enough (+ 500 useconds) to that value,
> > round
> > +                * the minimum frame duration of the camera to an accepted
> > +                * value.
> > +                */
> > +               static constexpr int64_t MAX_PREVIEW_RECORD_DURATION_NS =
> > 1e9 / 29.97;
> > +               if (minFrameDurationNsec > MAX_PREVIEW_RECORD_DURATION_NS
> > &&
> > +                   minFrameDurationNsec < MAX_PREVIEW_RECORD_DURATION_NS
> > + 500000)
> > +                       minFrameDurationNsec =
> > MAX_PREVIEW_RECORD_DURATION_NS - 1000;
> > +
> > +               /*
> > +                * The AE routine frame rate limits are computed using the
> > frame
> > +                * duration limits, as libcamera clips the AE routine to
> > the
> > +                * frame durations.
> > +                */
> > +               int32_t maxFps = std::round(1e9 / minFrameDurationNsec);
> > +               int32_t minFps = std::round(1e9 / maxFrameDurationNsec);
> > +               minFps = std::max(1, minFps);
> > +
> > +               /*
> > +                * Force rounding errors so that we have the proper frame
> > +                * durations for when we reuse these variables later
> > +                */
> > +               minFrameDurationNsec = 1e9 / maxFps;
> > +               maxFrameDurationNsec = 1e9 / minFps;
> > +
> > +               /*
> > +                * Register to the camera service {min, max} and {max, max}
> > +                * intervals as requested by the metadata documentation.
> > +                */
> > +               int32_t availableAeFpsTarget[] = {
> > +                       minFps, maxFps, maxFps, maxFps
> > +               };
> > +
> >  staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> > +                                         availableAeFpsTarget);
> > +       }
> > +
> > +       std::vector<int32_t> aeCompensationRange = {
> > +               0, 0,
> > +       };
> > +       staticMetadata_->addEntry(ANDROID_CONTROL_AE_COMPENSATION_RANGE,
> > +                                 aeCompensationRange);
> > +
> > +       const camera_metadata_rational_t aeCompensationStep[] = {
> > +               { 0, 1 }
> > +       };
> > +       staticMetadata_->addEntry(ANDROID_CONTROL_AE_COMPENSATION_STEP,
> > +                                 aeCompensationStep);
> > +
> > +       std::vector<uint8_t> availableAfModes = {
> > +               ANDROID_CONTROL_AF_MODE_OFF,
> > +       };
> > +       staticMetadata_->addEntry(ANDROID_CONTROL_AF_AVAILABLE_MODES,
> > +                                 availableAfModes);
> > +
> > +       std::vector<uint8_t> availableEffects = {
> > +               ANDROID_CONTROL_EFFECT_MODE_OFF,
> > +       };
> > +       staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_EFFECTS,
> > +                                 availableEffects);
> > +
> > +       std::vector<uint8_t> availableSceneModes = {
> > +               ANDROID_CONTROL_SCENE_MODE_DISABLED,
> > +       };
> > +       staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_SCENE_MODES,
> > +                                 availableSceneModes);
> > +
> > +       std::vector<uint8_t> availableStabilizationModes = {
> > +               ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF,
> > +       };
> > +
> >  staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
> > +                                 availableStabilizationModes);
> > +
> > +       /*
> > +        * \todo Inspect the Camera capabilities to report the available
> > +        * AWB modes. Default to AUTO as CTS tests require it.
> > +        */
> > +       std::vector<uint8_t> availableAwbModes = {
> > +               ANDROID_CONTROL_AWB_MODE_AUTO,
> > +       };
> > +       staticMetadata_->addEntry(ANDROID_CONTROL_AWB_AVAILABLE_MODES,
> > +                                 availableAwbModes);
> > +
> > +       std::vector<int32_t> availableMaxRegions = {
> > +               0, 0, 0,
> > +       };
> > +       staticMetadata_->addEntry(ANDROID_CONTROL_MAX_REGIONS,
> > +                                 availableMaxRegions);
> > +
> > +       std::vector<uint8_t> sceneModesOverride = {
> > +               ANDROID_CONTROL_AE_MODE_ON,
> > +               ANDROID_CONTROL_AWB_MODE_AUTO,
> > +               ANDROID_CONTROL_AF_MODE_OFF,
> > +       };
> > +       staticMetadata_->addEntry(ANDROID_CONTROL_SCENE_MODE_OVERRIDES,
> > +                                 sceneModesOverride);
> > +
> > +       uint8_t aeLockAvailable = ANDROID_CONTROL_AE_LOCK_AVAILABLE_FALSE;
> > +       staticMetadata_->addEntry(ANDROID_CONTROL_AE_LOCK_AVAILABLE,
> > +                                 aeLockAvailable);
> > +
> > +       uint8_t awbLockAvailable =
> > ANDROID_CONTROL_AWB_LOCK_AVAILABLE_FALSE;
> > +       staticMetadata_->addEntry(ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
> > +                                 awbLockAvailable);
> > +
> > +       char availableControlModes = ANDROID_CONTROL_MODE_AUTO;
> > +       staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_MODES,
> > +                                 availableControlModes);
> > +
> > +       /* JPEG static metadata. */
> > +
> > +       /*
> > +        * Create the list of supported thumbnail sizes by inspecting the
> > +        * available JPEG resolutions collected in streamConfigurations_
> > and
> > +        * generate one entry for each aspect ratio.
> > +        *
> > +        * The JPEG thumbnailer can freely scale, so pick an arbitrary
> > +        * (160, 160) size as the bounding rectangle, which is then
> > cropped to
> > +        * the different supported aspect ratios.
> > +        */
> > +       constexpr Size maxJpegThumbnail(160, 160);
> > +       std::vector<Size> thumbnailSizes;
> > +       thumbnailSizes.push_back({ 0, 0 });
> > +       for (const auto &entry : streamConfigurations_) {
> > +               if (entry.androidFormat != HAL_PIXEL_FORMAT_BLOB)
> > +                       continue;
> > +
> > +               Size thumbnailSize = maxJpegThumbnail
> > +                                    .boundedToAspectRatio({
> > entry.resolution.width,
> > +
> > entry.resolution.height });
> > +               thumbnailSizes.push_back(thumbnailSize);
> > +       }
> > +
> > +       std::sort(thumbnailSizes.begin(), thumbnailSizes.end());
> > +       auto last = std::unique(thumbnailSizes.begin(),
> > thumbnailSizes.end());
> > +       thumbnailSizes.erase(last, thumbnailSizes.end());
> > +
> > +       /* Transform sizes in to a list of integers that can be consumed.
> > */
> > +       std::vector<int32_t> thumbnailEntries;
> > +       thumbnailEntries.reserve(thumbnailSizes.size() * 2);
> > +       for (const auto &size : thumbnailSizes) {
> > +               thumbnailEntries.push_back(size.width);
> > +               thumbnailEntries.push_back(size.height);
> > +       }
> > +       staticMetadata_->addEntry(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,
> > +                                 thumbnailEntries);
> > +
> > +       staticMetadata_->addEntry(ANDROID_JPEG_MAX_SIZE,
> > maxJpegBufferSize_);
> > +
> > +       /* Sensor static metadata. */
> > +       std::array<int32_t, 2> pixelArraySize;
> > +       {
> > +               const Size &size =
> > properties.get(properties::PixelArraySize);
> > +               pixelArraySize[0] = size.width;
> > +               pixelArraySize[1] = size.height;
> > +
> >  staticMetadata_->addEntry(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
> > +                                         pixelArraySize);
> > +       }
> > +
> > +       if (properties.contains(properties::UnitCellSize)) {
> > +               const Size &cellSize =
> > properties.get<Size>(properties::UnitCellSize);
> > +               std::array<float, 2> physicalSize{
> > +                       cellSize.width * pixelArraySize[0] / 1e6f,
> > +                       cellSize.height * pixelArraySize[1] / 1e6f
> > +               };
> > +
> >  staticMetadata_->addEntry(ANDROID_SENSOR_INFO_PHYSICAL_SIZE,
> > +                                         physicalSize);
> > +       }
> > +
> > +       {
> > +               const Span<const Rectangle> &rects =
> > +                       properties.get(properties::PixelArrayActiveAreas);
> > +               std::vector<int32_t> data{
> > +                       static_cast<int32_t>(rects[0].x),
> > +                       static_cast<int32_t>(rects[0].y),
> > +                       static_cast<int32_t>(rects[0].width),
> > +                       static_cast<int32_t>(rects[0].height),
> > +               };
> > +
> >  staticMetadata_->addEntry(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
> > +                                         data);
> > +       }
> > +
> > +       int32_t sensitivityRange[] = {
> > +               32, 2400,
> > +       };
> > +       staticMetadata_->addEntry(ANDROID_SENSOR_INFO_SENSITIVITY_RANGE,
> > +                                 sensitivityRange);
> > +
> > +       /* Report the color filter arrangement if the camera reports it. */
> > +       if
> > (properties.contains(properties::draft::ColorFilterArrangement)) {
> > +               uint8_t filterArr =
> > properties.get(properties::draft::ColorFilterArrangement);
> > +
> >  staticMetadata_->addEntry(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,
> > +                                         filterArr);
> > +       }
> > +
> > +       const auto &exposureInfo =
> > controlsInfo.find(&controls::ExposureTime);
> > +       if (exposureInfo != controlsInfo.end()) {
> > +               int64_t exposureTimeRange[2] = {
> > +                       exposureInfo->second.min().get<int32_t>() * 1000LL,
> > +                       exposureInfo->second.max().get<int32_t>() * 1000LL,
> > +               };
> > +
> >  staticMetadata_->addEntry(ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE,
> > +                                         exposureTimeRange, 2);
> > +       }
> > +
> > +       staticMetadata_->addEntry(ANDROID_SENSOR_ORIENTATION,
> > orientation_);
> > +
> > +       std::vector<int32_t> testPatternModes = {
> > +               ANDROID_SENSOR_TEST_PATTERN_MODE_OFF
> > +       };
> > +       const auto &testPatternsInfo =
> > +               controlsInfo.find(&controls::draft::TestPatternMode);
> > +       if (testPatternsInfo != controlsInfo.end()) {
> > +               const auto &values = testPatternsInfo->second.values();
> > +               ASSERT(!values.empty());
> > +               for (const auto &value : values) {
> > +                       switch (value.get<int32_t>()) {
> > +                       case controls::draft::TestPatternModeOff:
> > +                               /*
> > +                                * ANDROID_SENSOR_TEST_PATTERN_MODE_OFF is
> > +                                * already in testPatternModes.
> > +                                */
> > +                               break;
> > +
> > +                       case controls::draft::TestPatternModeSolidColor:
> > +                               testPatternModes.push_back(
> > +
> >  ANDROID_SENSOR_TEST_PATTERN_MODE_SOLID_COLOR);
> > +                               break;
> > +
> > +                       case controls::draft::TestPatternModeColorBars:
> > +                               testPatternModes.push_back(
> > +
> >  ANDROID_SENSOR_TEST_PATTERN_MODE_COLOR_BARS);
> > +                               break;
> > +
> > +                       case
> > controls::draft::TestPatternModeColorBarsFadeToGray:
> > +                               testPatternModes.push_back(
> > +
> >  ANDROID_SENSOR_TEST_PATTERN_MODE_COLOR_BARS_FADE_TO_GRAY);
> > +                               break;
> > +
> > +                       case controls::draft::TestPatternModePn9:
> > +                               testPatternModes.push_back(
> > +
> >  ANDROID_SENSOR_TEST_PATTERN_MODE_PN9);
> > +                               break;
> > +
> > +                       case controls::draft::TestPatternModeCustom1:
> > +                               /* We don't support this yet. */
> > +                               break;
> > +
> > +                       default:
> > +                               LOG(HAL, Error) << "Unknown test pattern
> > mode: "
> > +                                               << value.get<int32_t>();
> > +                               continue;
> > +                       }
> > +               }
> > +       }
> > +
> >  staticMetadata_->addEntry(ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES,
> > +                                 testPatternModes);
> > +
> > +       uint8_t timestampSource =
> > ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN;
> > +       staticMetadata_->addEntry(ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE,
> > +                                 timestampSource);
> > +
> > +       if (maxFrameDurationNsec > 0)
> > +
> >  staticMetadata_->addEntry(ANDROID_SENSOR_INFO_MAX_FRAME_DURATION,
> > +                                         maxFrameDurationNsec);
> > +
> > +       /* Statistics static metadata. */
> > +       uint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
> > +
> >  staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
> > +                                 faceDetectMode);
> > +
> > +       int32_t maxFaceCount = 0;
> > +       staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
> > +                                 maxFaceCount);
> > +
> > +       {
> > +               std::vector<uint8_t> data;
> > +               data.reserve(2);
> > +               const auto &infoMap =
> > controlsInfo.find(&controls::draft::LensShadingMapMode);
> > +               if (infoMap != controlsInfo.end()) {
> > +                       for (const auto &value : infoMap->second.values())
> > +                               data.push_back(value.get<int32_t>());
> > +               } else {
> > +
> >  data.push_back(ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF);
> > +               }
> > +
> >  staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES,
> > +                                         data);
> > +       }
> > +
> > +       /* Sync static metadata. */
> > +       int32_t maxLatency = ANDROID_SYNC_MAX_LATENCY_UNKNOWN;
> > +       staticMetadata_->addEntry(ANDROID_SYNC_MAX_LATENCY, maxLatency);
> > +
> > +       /* Flash static metadata. */
> > +       char flashAvailable = ANDROID_FLASH_INFO_AVAILABLE_FALSE;
> > +       staticMetadata_->addEntry(ANDROID_FLASH_INFO_AVAILABLE,
> > +                                 flashAvailable);
> > +
> > +       /* Lens static metadata. */
> > +       std::vector<float> lensApertures = {
> > +               2.53 / 100,
> > +       };
> > +       staticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_APERTURES,
> > +                                 lensApertures);
> > +
> > +       uint8_t lensFacing;
> > +       switch (facing_) {
> > +       default:
> > +       case CAMERA_FACING_FRONT:
> > +               lensFacing = ANDROID_LENS_FACING_FRONT;
> > +               break;
> > +       case CAMERA_FACING_BACK:
> > +               lensFacing = ANDROID_LENS_FACING_BACK;
> > +               break;
> > +       case CAMERA_FACING_EXTERNAL:
> > +               lensFacing = ANDROID_LENS_FACING_EXTERNAL;
> > +               break;
> > +       }
> > +       staticMetadata_->addEntry(ANDROID_LENS_FACING, lensFacing);
> > +
> > +       std::vector<float> lensFocalLengths = {
> > +               1,
> > +       };
> > +
> >  staticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS,
> > +                                 lensFocalLengths);
> > +
> > +       std::vector<uint8_t> opticalStabilizations = {
> > +               ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF,
> > +       };
> > +
> >  staticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
> > +                                 opticalStabilizations);
> > +
> > +       float hypeFocalDistance = 0;
> > +       staticMetadata_->addEntry(ANDROID_LENS_INFO_HYPERFOCAL_DISTANCE,
> > +                                 hypeFocalDistance);
> > +
> > +       float minFocusDistance = 0;
> > +       staticMetadata_->addEntry(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
> > +                                 minFocusDistance);
> > +
> > +       /* Noise reduction modes. */
> > +       {
> > +               std::vector<uint8_t> data;
> > +               data.reserve(5);
> > +               const auto &infoMap =
> > controlsInfo.find(&controls::draft::NoiseReductionMode);
> > +               if (infoMap != controlsInfo.end()) {
> > +                       for (const auto &value : infoMap->second.values())
> > +                               data.push_back(value.get<int32_t>());
> > +               } else {
> > +                       data.push_back(ANDROID_NOISE_REDUCTION_MODE_OFF);
> > +               }
> > +
> >  staticMetadata_->addEntry(ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
> > +                                         data);
> > +       }
> > +
> > +       /* Scaler static metadata. */
> > +
> > +       /*
> > +        * \todo The digital zoom factor is a property that depends on the
> > +        * desired output configuration and the sensor frame size input to
> > the
> > +        * ISP. This information is not available to the Android HAL, not
> > at
> > +        * initialization time at least.
> > +        *
> > +        * As a workaround rely on pipeline handlers initializing the
> > +        * ScalerCrop control with the camera default configuration and
> > use the
> > +        * maximum and minimum crop rectangles to calculate the digital
> > zoom
> > +        * factor.
> > +        */
> > +       float maxZoom = 1.0f;
> > +       const auto scalerCrop = controlsInfo.find(&controls::ScalerCrop);
> > +       if (scalerCrop != controlsInfo.end()) {
> > +               Rectangle min = scalerCrop->second.min().get<Rectangle>();
> > +               Rectangle max = scalerCrop->second.max().get<Rectangle>();
> > +               maxZoom = std::min(1.0f * max.width / min.width,
> > +                                  1.0f * max.height / min.height);
> > +       }
> > +
> >  staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
> > +                                 maxZoom);
> > +
> > +       std::vector<uint32_t> availableStreamConfigurations;
> > +       availableStreamConfigurations.reserve(streamConfigurations_.size()
> > * 4);
> > +       for (const auto &entry : streamConfigurations_) {
> > +
> >  availableStreamConfigurations.push_back(entry.androidFormat);
> > +
> >  availableStreamConfigurations.push_back(entry.resolution.width);
> > +
> >  availableStreamConfigurations.push_back(entry.resolution.height);
> > +               availableStreamConfigurations.push_back(
> > +
> >  ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT);
> > +       }
> > +
> >  staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
> > +                                 availableStreamConfigurations);
> > +
> > +       std::vector<int64_t> availableStallDurations = {
> > +               ANDROID_SCALER_AVAILABLE_FORMATS_BLOB, 2560, 1920,
> > 33333333,
> > +       };
> > +       staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,
> > +                                 availableStallDurations);
> > +
> > +       /* Use the minimum frame duration for all the YUV/RGB formats. */
> > +       if (minFrameDurationNsec > 0) {
> > +               std::vector<int64_t> minFrameDurations;
> > +               minFrameDurations.reserve(streamConfigurations_.size() *
> > 4);
> > +               for (const auto &entry : streamConfigurations_) {
> > +                       minFrameDurations.push_back(entry.androidFormat);
> > +
> >  minFrameDurations.push_back(entry.resolution.width);
> > +
> >  minFrameDurations.push_back(entry.resolution.height);
> > +                       minFrameDurations.push_back(minFrameDurationNsec);
> > +               }
> > +
> >  staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
> > +                                         minFrameDurations);
> > +       }
> > +
> > +       uint8_t croppingType = ANDROID_SCALER_CROPPING_TYPE_CENTER_ONLY;
> > +       staticMetadata_->addEntry(ANDROID_SCALER_CROPPING_TYPE,
> > croppingType);
> > +
> > +       /* Info static metadata. */
> > +       uint8_t supportedHWLevel =
> > ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
> > +       staticMetadata_->addEntry(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
> > +                                 supportedHWLevel);
> > +
> > +       /* Request static metadata. */
> > +       int32_t partialResultCount = 1;
> > +       staticMetadata_->addEntry(ANDROID_REQUEST_PARTIAL_RESULT_COUNT,
> > +                                 partialResultCount);
> > +
> > +       {
> > +               /* Default the value to 2 if not reported by the camera. */
> > +               uint8_t maxPipelineDepth = 2;
> > +               const auto &infoMap =
> > controlsInfo.find(&controls::draft::PipelineDepth);
> > +               if (infoMap != controlsInfo.end())
> > +                       maxPipelineDepth =
> > infoMap->second.max().get<int32_t>();
> > +
> >  staticMetadata_->addEntry(ANDROID_REQUEST_PIPELINE_MAX_DEPTH,
> > +                                         maxPipelineDepth);
> > +       }
> > +
> > +       /* LIMITED does not support reprocessing. */
> > +       uint32_t maxNumInputStreams = 0;
> > +       staticMetadata_->addEntry(ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,
> > +                                 maxNumInputStreams);
> > +
> > +       std::vector<uint8_t> availableCapabilities = {
> > +               ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE,
> > +       };
> > +
> > +       /* Report if camera supports RAW. */
> > +       bool rawStreamAvailable = false;
> > +       std::unique_ptr<CameraConfiguration> cameraConfig =
> > +               camera_->generateConfiguration({ StreamRole::Raw });
> > +       if (cameraConfig && !cameraConfig->empty()) {
> > +               const PixelFormatInfo &info =
> > +
> >  PixelFormatInfo::info(cameraConfig->at(0).pixelFormat);
> > +               /* Only advertise RAW support if RAW16 is possible. */
> > +               if (info.colourEncoding ==
> > PixelFormatInfo::ColourEncodingRAW &&
> > +                   info.bitsPerPixel == 16) {
> > +                       rawStreamAvailable = true;
> > +
> >  availableCapabilities.push_back(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW);
> > +               }
> > +       }
> > +
> > +       /* Number of { RAW, YUV, JPEG } supported output streams */
> > +       int32_t numOutStreams[] = { rawStreamAvailable, 2, 1 };
> > +       staticMetadata_->addEntry(ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,
> > +                                 numOutStreams);
> > +
> > +       staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
> > +                                 availableCapabilities);
> > +
> > +       std::vector<int32_t> availableCharacteristicsKeys = {
> > +               ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
> > +               ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,
> > +               ANDROID_CONTROL_AE_AVAILABLE_MODES,
> > +               ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> > +               ANDROID_CONTROL_AE_COMPENSATION_RANGE,
> > +               ANDROID_CONTROL_AE_COMPENSATION_STEP,
> > +               ANDROID_CONTROL_AE_LOCK_AVAILABLE,
> > +               ANDROID_CONTROL_AF_AVAILABLE_MODES,
> > +               ANDROID_CONTROL_AVAILABLE_EFFECTS,
> > +               ANDROID_CONTROL_AVAILABLE_MODES,
> > +               ANDROID_CONTROL_AVAILABLE_SCENE_MODES,
> > +               ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
> > +               ANDROID_CONTROL_AWB_AVAILABLE_MODES,
> > +               ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
> > +               ANDROID_CONTROL_MAX_REGIONS,
> > +               ANDROID_CONTROL_SCENE_MODE_OVERRIDES,
> > +               ANDROID_FLASH_INFO_AVAILABLE,
> > +               ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
> > +               ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,
> > +               ANDROID_JPEG_MAX_SIZE,
> > +               ANDROID_LENS_FACING,
> > +               ANDROID_LENS_INFO_AVAILABLE_APERTURES,
> > +               ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS,
> > +               ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
> > +               ANDROID_LENS_INFO_HYPERFOCAL_DISTANCE,
> > +               ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
> > +               ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
> > +               ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
> > +               ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,
> > +               ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,
> > +               ANDROID_REQUEST_PARTIAL_RESULT_COUNT,
> > +               ANDROID_REQUEST_PIPELINE_MAX_DEPTH,
> > +               ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
> > +               ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
> > +               ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,
> > +               ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
> > +               ANDROID_SCALER_CROPPING_TYPE,
> > +               ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES,
> > +               ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
> > +               ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,
> > +               ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE,
> > +               ANDROID_SENSOR_INFO_MAX_FRAME_DURATION,
> > +               ANDROID_SENSOR_INFO_PHYSICAL_SIZE,
> > +               ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
> > +               ANDROID_SENSOR_INFO_SENSITIVITY_RANGE,
> > +               ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE,
> > +               ANDROID_SENSOR_ORIENTATION,
> > +               ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
> > +               ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
> > +               ANDROID_SYNC_MAX_LATENCY,
> > +       };
> > +
> >  staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS,
> > +                                 availableCharacteristicsKeys);
> > +
> > +       std::vector<int32_t> availableRequestKeys = {
> > +               ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
> > +               ANDROID_CONTROL_AE_ANTIBANDING_MODE,
> > +               ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
> > +               ANDROID_CONTROL_AE_LOCK,
> > +               ANDROID_CONTROL_AE_MODE,
> > +               ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
> > +               ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
> > +               ANDROID_CONTROL_AF_MODE,
> > +               ANDROID_CONTROL_AF_TRIGGER,
> > +               ANDROID_CONTROL_AWB_LOCK,
> > +               ANDROID_CONTROL_AWB_MODE,
> > +               ANDROID_CONTROL_CAPTURE_INTENT,
> > +               ANDROID_CONTROL_EFFECT_MODE,
> > +               ANDROID_CONTROL_MODE,
> > +               ANDROID_CONTROL_SCENE_MODE,
> > +               ANDROID_CONTROL_VIDEO_STABILIZATION_MODE,
> > +               ANDROID_FLASH_MODE,
> > +               ANDROID_JPEG_ORIENTATION,
> > +               ANDROID_JPEG_QUALITY,
> > +               ANDROID_JPEG_THUMBNAIL_QUALITY,
> > +               ANDROID_JPEG_THUMBNAIL_SIZE,
> > +               ANDROID_LENS_APERTURE,
> > +               ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
> > +               ANDROID_NOISE_REDUCTION_MODE,
> > +               ANDROID_SCALER_CROP_REGION,
> > +               ANDROID_STATISTICS_FACE_DETECT_MODE
> > +       };
> > +       staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS,
> > +                                 availableRequestKeys);
> > +
> > +       std::vector<int32_t> availableResultKeys = {
> > +               ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
> > +               ANDROID_CONTROL_AE_ANTIBANDING_MODE,
> > +               ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
> > +               ANDROID_CONTROL_AE_LOCK,
> > +               ANDROID_CONTROL_AE_MODE,
> > +               ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
> > +               ANDROID_CONTROL_AE_STATE,
> > +               ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
> > +               ANDROID_CONTROL_AF_MODE,
> > +               ANDROID_CONTROL_AF_STATE,
> > +               ANDROID_CONTROL_AF_TRIGGER,
> > +               ANDROID_CONTROL_AWB_LOCK,
> > +               ANDROID_CONTROL_AWB_MODE,
> > +               ANDROID_CONTROL_AWB_STATE,
> > +               ANDROID_CONTROL_CAPTURE_INTENT,
> > +               ANDROID_CONTROL_EFFECT_MODE,
> > +               ANDROID_CONTROL_MODE,
> > +               ANDROID_CONTROL_SCENE_MODE,
> > +               ANDROID_CONTROL_VIDEO_STABILIZATION_MODE,
> > +               ANDROID_FLASH_MODE,
> > +               ANDROID_FLASH_STATE,
> > +               ANDROID_JPEG_GPS_COORDINATES,
> > +               ANDROID_JPEG_GPS_PROCESSING_METHOD,
> > +               ANDROID_JPEG_GPS_TIMESTAMP,
> > +               ANDROID_JPEG_ORIENTATION,
> > +               ANDROID_JPEG_QUALITY,
> > +               ANDROID_JPEG_SIZE,
> > +               ANDROID_JPEG_THUMBNAIL_QUALITY,
> > +               ANDROID_JPEG_THUMBNAIL_SIZE,
> > +               ANDROID_LENS_APERTURE,
> > +               ANDROID_LENS_FOCAL_LENGTH,
> > +               ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
> > +               ANDROID_LENS_STATE,
> > +               ANDROID_NOISE_REDUCTION_MODE,
> > +               ANDROID_REQUEST_PIPELINE_DEPTH,
> > +               ANDROID_SCALER_CROP_REGION,
> > +               ANDROID_SENSOR_EXPOSURE_TIME,
> > +               ANDROID_SENSOR_FRAME_DURATION,
> > +               ANDROID_SENSOR_ROLLING_SHUTTER_SKEW,
> > +               ANDROID_SENSOR_TEST_PATTERN_MODE,
> > +               ANDROID_SENSOR_TIMESTAMP,
> > +               ANDROID_STATISTICS_FACE_DETECT_MODE,
> > +               ANDROID_STATISTICS_LENS_SHADING_MAP_MODE,
> > +               ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE,
> > +               ANDROID_STATISTICS_SCENE_FLICKER,
> > +       };
> > +       staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_RESULT_KEYS,
> > +                                 availableResultKeys);
> > +
> > +       if (!staticMetadata_->isValid()) {
> > +               LOG(HAL, Error) << "Failed to construct static metadata";
> > +               staticMetadata_.reset();
> > +               return -EINVAL;
> > +       }
> > +
> > +       if (staticMetadata_->resized()) {
> > +               auto [entryCount, dataCount] = staticMetadata_->usage();
> > +               LOG(HAL, Info)
> > +                       << "Static metadata resized: " << entryCount
> > +                       << " entries and " << dataCount << " bytes used";
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +/* Translate Android format code to libcamera pixel format. */
> > +PixelFormat CameraCapabilities::toPixelFormat(int format) const
> > +{
> > +       auto it = formatsMap_.find(format);
> > +       if (it == formatsMap_.end()) {
> > +               LOG(HAL, Error) << "Requested format " <<
> > utils::hex(format)
> > +                               << " not supported";
> > +               return PixelFormat();
> > +       }
> > +
> > +       return it->second;
> > +}
> > +
> > +std::unique_ptr<CameraMetadata>
> > CameraCapabilities::requestTemplatePreview() const
> > +{
> > +       /*
> > +        * \todo Keep this in sync with the actual number of entries.
> > +        * Currently: 20 entries, 35 bytes
> > +        */
> > +       auto requestTemplate = std::make_unique<CameraMetadata>(21, 36);
> > +       if (!requestTemplate->isValid()) {
> > +               return nullptr;
> > +       }
> > +
> > +       /* Get the FPS range registered in the static metadata. */
> > +       camera_metadata_ro_entry_t entry;
> > +       bool found =
> > staticMetadata_->getEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> > +                                              &entry);
> > +       if (!found) {
> > +               LOG(HAL, Error) << "Cannot create capture template without
> > FPS range";
> > +               return nullptr;
> > +       }
> > +
> > +       /*
> > +        * Assume the AE_AVAILABLE_TARGET_FPS_RANGE static metadata
> > +        * has been assembled as {{min, max} {max, max}}.
> > +        */
> > +       requestTemplate->addEntry(ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
> > +                                 entry.data.i32, 2);
> > +
> > +       uint8_t aeMode = ANDROID_CONTROL_AE_MODE_ON;
> > +       requestTemplate->addEntry(ANDROID_CONTROL_AE_MODE, aeMode);
> > +
> > +       int32_t aeExposureCompensation = 0;
> > +       requestTemplate->addEntry(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
> > +                                 aeExposureCompensation);
> > +
> > +       uint8_t aePrecaptureTrigger =
> > ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_IDLE;
> > +       requestTemplate->addEntry(ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
> > +                                 aePrecaptureTrigger);
> > +
> > +       uint8_t aeLock = ANDROID_CONTROL_AE_LOCK_OFF;
> > +       requestTemplate->addEntry(ANDROID_CONTROL_AE_LOCK, aeLock);
> > +
> > +       uint8_t aeAntibandingMode =
> > ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO;
> > +       requestTemplate->addEntry(ANDROID_CONTROL_AE_ANTIBANDING_MODE,
> > +                                 aeAntibandingMode);
> > +
> > +       uint8_t afMode = ANDROID_CONTROL_AF_MODE_OFF;
> > +       requestTemplate->addEntry(ANDROID_CONTROL_AF_MODE, afMode);
> > +
> > +       uint8_t afTrigger = ANDROID_CONTROL_AF_TRIGGER_IDLE;
> > +       requestTemplate->addEntry(ANDROID_CONTROL_AF_TRIGGER, afTrigger);
> > +
> > +       uint8_t awbMode = ANDROID_CONTROL_AWB_MODE_AUTO;
> > +       requestTemplate->addEntry(ANDROID_CONTROL_AWB_MODE, awbMode);
> > +
> > +       uint8_t awbLock = ANDROID_CONTROL_AWB_LOCK_OFF;
> > +       requestTemplate->addEntry(ANDROID_CONTROL_AWB_LOCK, awbLock);
> > +
> > +       uint8_t flashMode = ANDROID_FLASH_MODE_OFF;
> > +       requestTemplate->addEntry(ANDROID_FLASH_MODE, flashMode);
> > +
> > +       uint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
> > +       requestTemplate->addEntry(ANDROID_STATISTICS_FACE_DETECT_MODE,
> > +                                 faceDetectMode);
> > +
> > +       uint8_t noiseReduction = ANDROID_NOISE_REDUCTION_MODE_OFF;
> > +       requestTemplate->addEntry(ANDROID_NOISE_REDUCTION_MODE,
> > +                                 noiseReduction);
> > +
> > +       uint8_t aberrationMode =
> > ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF;
> > +       requestTemplate->addEntry(ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
> > +                                 aberrationMode);
> > +
> > +       uint8_t controlMode = ANDROID_CONTROL_MODE_AUTO;
> > +       requestTemplate->addEntry(ANDROID_CONTROL_MODE, controlMode);
> > +
> > +       float lensAperture = 2.53 / 100;
> > +       requestTemplate->addEntry(ANDROID_LENS_APERTURE, lensAperture);
> > +
> > +       uint8_t opticalStabilization =
> > ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF;
> > +       requestTemplate->addEntry(ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
> > +                                 opticalStabilization);
> > +
> > +       uint8_t captureIntent = ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW;
> > +       requestTemplate->addEntry(ANDROID_CONTROL_CAPTURE_INTENT,
> > +                                 captureIntent);
> > +
> > +       return requestTemplate;
> > +}
> > +
> > +std::unique_ptr<CameraMetadata>
> > CameraCapabilities::requestTemplateVideo() const
> > +{
> > +       std::unique_ptr<CameraMetadata> previewTemplate =
> > requestTemplatePreview();
> > +       if (!previewTemplate)
> > +               return nullptr;
> > +
> > +       /*
> > +        * The video template requires a fixed FPS range. Everything else
> > +        * stays the same as the preview template.
> > +        */
> > +       camera_metadata_ro_entry_t entry;
> > +
> >  staticMetadata_->getEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> > +                                 &entry);
> > +
> > +       /*
> > +        * Assume the AE_AVAILABLE_TARGET_FPS_RANGE static metadata
> > +        * has been assembled as {{min, max} {max, max}}.
> > +        */
> > +       previewTemplate->updateEntry(ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
> > +                                    entry.data.i32 + 2, 2);
> > +
> > +       return previewTemplate;
> > +}
> > diff --git a/src/android/camera_capabilities.h
> > b/src/android/camera_capabilities.h
> > new file mode 100644
> > index 000000000000..f511607bbd90
> > --- /dev/null
> > +++ b/src/android/camera_capabilities.h
> > @@ -0,0 +1,65 @@
> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > +/*
> > + * Copyright (C) 2021, Google Inc.
> > + *
> > + * camera_capabilities.h - Camera static properties manager
> > + */
> > +#ifndef __ANDROID_CAMERA_CAPABILITIES_H__
> > +#define __ANDROID_CAMERA_CAPABILITIES_H__
> > +
> > +#include <map>
> > +#include <memory>
> > +#include <vector>
> > +
> > +#include <libcamera/camera.h>
> > +#include <libcamera/class.h>
> > +#include <libcamera/formats.h>
> > +#include <libcamera/geometry.h>
> > +
> > +#include "camera_metadata.h"
> > +
> > +class CameraCapabilities
> > +{
> > +public:
> > +       CameraCapabilities() = default;
> > +
> > +       int initialize(std::shared_ptr<libcamera::Camera> camera,
> > +                      int orientation, int facing);
> > +
> > +       CameraMetadata *staticMetadata() const { return
> > staticMetadata_.get(); }
> > +       libcamera::PixelFormat toPixelFormat(int format) const;
> > +       unsigned int maxJpegBufferSize() const { return
> > maxJpegBufferSize_; }
> > +
> > +       std::unique_ptr<CameraMetadata> requestTemplatePreview() const;
> > +       std::unique_ptr<CameraMetadata> requestTemplateVideo() const;
> > +
> > +private:
> > +       LIBCAMERA_DISABLE_COPY_AND_MOVE(CameraCapabilities)
> > +
> > +       struct Camera3StreamConfiguration {
> > +               libcamera::Size resolution;
> > +               int androidFormat;
> > +       };
> > +
> > +       std::vector<libcamera::Size>
> > +       getYUVResolutions(libcamera::CameraConfiguration *cameraConfig,
> > +                         const libcamera::PixelFormat &pixelFormat,
> > +                         const std::vector<libcamera::Size> &resolutions);
> > +       std::vector<libcamera::Size>
> > +       getRawResolutions(const libcamera::PixelFormat &pixelFormat);
> > +       int initializeStreamConfigurations();
> > +
> > +       int initializeStaticMetadata();
> > +
> > +       std::shared_ptr<libcamera::Camera> camera_;
> > +
> > +       int facing_;
> > +       int orientation_;
> > +
> > +       std::vector<Camera3StreamConfiguration> streamConfigurations_;
> > +       std::map<int, libcamera::PixelFormat> formatsMap_;
> > +       std::unique_ptr<CameraMetadata> staticMetadata_;
> > +       unsigned int maxJpegBufferSize_;
> > +};
> > +
> > +#endif /* __ANDROID_CAMERA_CAPABILITIES_H__ */
> > diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
> > index 8c71fd0675d3..4bd125d7020a 100644
> > --- a/src/android/camera_device.cpp
> > +++ b/src/android/camera_device.cpp
> > @@ -10,11 +10,8 @@
> >  #include "camera_ops.h"
> >  #include "post_processor.h"
> >
> > -#include <array>
> > -#include <cmath>
> >  #include <fstream>
> >  #include <sys/mman.h>
> > -#include <tuple>
> >  #include <unistd.h>
> >  #include <vector>
> >
> > @@ -23,7 +20,6 @@
> >  #include <libcamera/formats.h>
> >  #include <libcamera/property_ids.h>
> >
> > -#include "libcamera/internal/formats.h"
> >  #include "libcamera/internal/log.h"
> >  #include "libcamera/internal/thread.h"
> >  #include "libcamera/internal/utils.h"
> > @@ -36,94 +32,6 @@ LOG_DECLARE_CATEGORY(HAL)
> >
> >  namespace {
> >
> > -/*
> > - * \var camera3Resolutions
> > - * \brief The list of image resolutions defined as mandatory to be
> > supported by
> > - * the Android Camera3 specification
> > - */
> > -const std::vector<Size> camera3Resolutions = {
> > -       { 320, 240 },
> > -       { 640, 480 },
> > -       { 1280, 720 },
> > -       { 1920, 1080 }
> > -};
> > -
> > -/*
> > - * \struct Camera3Format
> > - * \brief Data associated with an Android format identifier
> > - * \var libcameraFormats List of libcamera pixel formats compatible with
> > the
> > - * Android format
> > - * \var name The human-readable representation of the Android format code
> > - */
> > -struct Camera3Format {
> > -       std::vector<PixelFormat> libcameraFormats;
> > -       bool mandatory;
> > -       const char *name;
> > -};
> > -
> > -/*
> > - * \var camera3FormatsMap
> > - * \brief Associate Android format code with ancillary data
> > - */
> > -const std::map<int, const Camera3Format> camera3FormatsMap = {
> > -       {
> > -               HAL_PIXEL_FORMAT_BLOB, {
> > -                       { formats::MJPEG },
> > -                       true,
> > -                       "BLOB"
> > -               }
> > -       }, {
> > -               HAL_PIXEL_FORMAT_YCbCr_420_888, {
> > -                       { formats::NV12, formats::NV21 },
> > -                       true,
> > -                       "YCbCr_420_888"
> > -               }
> > -       }, {
> > -               /*
> > -                * \todo Translate IMPLEMENTATION_DEFINED inspecting the
> > gralloc
> > -                * usage flag. For now, copy the YCbCr_420 configuration.
> > -                */
> > -               HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, {
> > -                       { formats::NV12, formats::NV21 },
> > -                       true,
> > -                       "IMPLEMENTATION_DEFINED"
> > -               }
> > -       }, {
> > -               HAL_PIXEL_FORMAT_RAW10, {
> > -                       {
> > -                               formats::SBGGR10_CSI2P,
> > -                               formats::SGBRG10_CSI2P,
> > -                               formats::SGRBG10_CSI2P,
> > -                               formats::SRGGB10_CSI2P
> > -                       },
> > -                       false,
> > -                       "RAW10"
> > -               }
> > -       }, {
> > -               HAL_PIXEL_FORMAT_RAW12, {
> > -                       {
> > -                               formats::SBGGR12_CSI2P,
> > -                               formats::SGBRG12_CSI2P,
> > -                               formats::SGRBG12_CSI2P,
> > -                               formats::SRGGB12_CSI2P
> > -                       },
> > -                       false,
> > -                       "RAW12"
> > -               }
> > -       }, {
> > -               HAL_PIXEL_FORMAT_RAW16, {
> > -                       {
> > -                               formats::SBGGR16,
> > -                               formats::SGBRG16,
> > -                               formats::SGRBG16,
> > -                               formats::SRGGB16
> > -                       },
> > -                       false,
> > -                       "RAW16"
> > -               }
> > -       },
> > -};
> > -
> >  /*
> >   * \struct Camera3StreamConfig
> >   * \brief Data to store StreamConfiguration associated with
> > camera3_stream(s)
> > @@ -512,242 +420,7 @@ int CameraDevice::initialize(const CameraConfigData
> > *cameraConfigData)
> >                 orientation_ = 0;
> >         }
> >
> > -       /* Acquire the camera and initialize available stream
> > configurations. */
> > -       int ret = camera_->acquire();
> > -       if (ret) {
> > -               LOG(HAL, Error) << "Failed to temporarily acquire the
> > camera";
> > -               return ret;
> > -       }
> > -
> > -       ret = initializeStreamConfigurations();
> > -       camera_->release();
> > -       return ret;
> > -}
> > -
> > -std::vector<Size> CameraDevice::getYUVResolutions(CameraConfiguration
> > *cameraConfig,
> > -                                                 const PixelFormat
> > &pixelFormat,
> > -                                                 const std::vector<Size>
> > &resolutions)
> > -{
> > -       std::vector<Size> supportedResolutions;
> > -
> > -       StreamConfiguration &cfg = cameraConfig->at(0);
> > -       for (const Size &res : resolutions) {
> > -               cfg.pixelFormat = pixelFormat;
> > -               cfg.size = res;
> > -
> > -               CameraConfiguration::Status status =
> > cameraConfig->validate();
> > -               if (status != CameraConfiguration::Valid) {
> > -                       LOG(HAL, Debug) << cfg.toString() << " not
> > supported";
> > -                       continue;
> > -               }
> > -
> > -               LOG(HAL, Debug) << cfg.toString() << " supported";
> > -
> > -               supportedResolutions.push_back(res);
> > -       }
> > -
> > -       return supportedResolutions;
> > -}
> > -
> > -std::vector<Size> CameraDevice::getRawResolutions(const
> > libcamera::PixelFormat &pixelFormat)
> > -{
> > -       std::unique_ptr<CameraConfiguration> cameraConfig =
> > -               camera_->generateConfiguration({ StreamRole::Raw });
> > -       StreamConfiguration &cfg = cameraConfig->at(0);
> > -       const StreamFormats &formats = cfg.formats();
> > -       std::vector<Size> supportedResolutions =
> > formats.sizes(pixelFormat);
> > -
> > -       return supportedResolutions;
> > -}
> > -
> > -/*
> > - * Initialize the format conversion map to translate from Android format
> > - * identifier to libcamera pixel formats and fill in the list of supported
> > - * stream configurations to be reported to the Android camera framework
> > through
> > - * the static stream configuration metadata.
> > - */
> > -int CameraDevice::initializeStreamConfigurations()
> > -{
> > -       /*
> > -        * Get the maximum output resolutions
> > -        * \todo Get this from the camera properties once defined
> > -        */
> > -       std::unique_ptr<CameraConfiguration> cameraConfig =
> > -               camera_->generateConfiguration({ StillCapture });
> > -       if (!cameraConfig) {
> > -               LOG(HAL, Error) << "Failed to get maximum resolution";
> > -               return -EINVAL;
> > -       }
> > -       StreamConfiguration &cfg = cameraConfig->at(0);
> > -
> > -       /*
> > -        * \todo JPEG - Adjust the maximum available resolution by taking
> > the
> > -        * JPEG encoder requirements into account (alignment and aspect
> > ratio).
> > -        */
> > -       const Size maxRes = cfg.size;
> > -       LOG(HAL, Debug) << "Maximum supported resolution: " <<
> > maxRes.toString();
> > -
> > -       /*
> > -        * Build the list of supported image resolutions.
> > -        *
> > -        * The resolutions listed in camera3Resolution are mandatory to be
> > -        * supported, up to the camera maximum resolution.
> > -        *
> > -        * Augment the list by adding resolutions calculated from the
> > camera
> > -        * maximum one.
> > -        */
> > -       std::vector<Size> cameraResolutions;
> > -       std::copy_if(camera3Resolutions.begin(), camera3Resolutions.end(),
> > -                    std::back_inserter(cameraResolutions),
> > -                    [&](const Size &res) { return res < maxRes; });
> > -
> > -       /*
> > -        * The Camera3 specification suggests adding 1/2 and 1/4 of the
> > maximum
> > -        * resolution.
> > -        */
> > -       for (unsigned int divider = 2;; divider <<= 1) {
> > -               Size derivedSize{
> > -                       maxRes.width / divider,
> > -                       maxRes.height / divider,
> > -               };
> > -
> > -               if (derivedSize.width < 320 ||
> > -                   derivedSize.height < 240)
> > -                       break;
> > -
> > -               cameraResolutions.push_back(derivedSize);
> > -       }
> > -       cameraResolutions.push_back(maxRes);
> > -
> > -       /* Remove duplicated entries from the list of supported
> > resolutions. */
> > -       std::sort(cameraResolutions.begin(), cameraResolutions.end());
> > -       auto last = std::unique(cameraResolutions.begin(),
> > cameraResolutions.end());
> > -       cameraResolutions.erase(last, cameraResolutions.end());
> > -
> > -       /*
> > -        * Build the list of supported camera formats.
> > -        *
> > -        * To each Android format a list of compatible libcamera formats is
> > -        * associated. The first libcamera format that tests successful is
> > added
> > -        * to the format translation map used when configuring the streams.
> > -        * It is then tested against the list of supported camera
> > resolutions to
> > -        * build the stream configuration map reported through the camera
> > static
> > -        * metadata.
> > -        */
> > -       Size maxJpegSize;
> > -       for (const auto &format : camera3FormatsMap) {
> > -               int androidFormat = format.first;
> > -               const Camera3Format &camera3Format = format.second;
> > -               const std::vector<PixelFormat> &libcameraFormats =
> > -                       camera3Format.libcameraFormats;
> > -
> > -               LOG(HAL, Debug) << "Trying to map Android format "
> > -                               << camera3Format.name;
> > -
> > -               /*
> > -                * JPEG is always supported, either produced directly by
> > the
> > -                * camera, or encoded in the HAL.
> > -                */
> > -               if (androidFormat == HAL_PIXEL_FORMAT_BLOB) {
> > -                       formatsMap_[androidFormat] = formats::MJPEG;
> > -                       LOG(HAL, Debug) << "Mapped Android format "
> > -                                       << camera3Format.name << " to "
> > -                                       << formats::MJPEG.toString()
> > -                                       << " (fixed mapping)";
> > -                       continue;
> > -               }
> > -
> > -               /*
> > -                * Test the libcamera formats that can produce images
> > -                * compatible with the format defined by Android.
> > -                */
> > -               PixelFormat mappedFormat;
> > -               for (const PixelFormat &pixelFormat : libcameraFormats) {
> > -
> > -                       LOG(HAL, Debug) << "Testing " <<
> > pixelFormat.toString();
> > -
> > -                       /*
> > -                        * The stream configuration size can be adjusted,
> > -                        * not the pixel format.
> > -                        *
> > -                        * \todo This could be simplified once all pipeline
> > -                        * handlers will report the StreamFormats list of
> > -                        * supported formats.
> > -                        */
> > -                       cfg.pixelFormat = pixelFormat;
> > -
> > -                       CameraConfiguration::Status status =
> > cameraConfig->validate();
> > -                       if (status != CameraConfiguration::Invalid &&
> > -                           cfg.pixelFormat == pixelFormat) {
> > -                               mappedFormat = pixelFormat;
> > -                               break;
> > -                       }
> > -               }
> > -
> > -               if (!mappedFormat.isValid()) {
> > -                       /* If the format is not mandatory, skip it. */
> > -                       if (!camera3Format.mandatory)
> > -                               continue;
> > -
> > -                       LOG(HAL, Error)
> > -                               << "Failed to map mandatory Android format
> > "
> > -                               << camera3Format.name << " ("
> > -                               << utils::hex(androidFormat) << "):
> > aborting";
> > -                       return -EINVAL;
> > -               }
> > -
> > -               /*
> > -                * Record the mapping and then proceed to generate the
> > -                * stream configurations map, by testing the image
> > resolutions.
> > -                */
> > -               formatsMap_[androidFormat] = mappedFormat;
> > -               LOG(HAL, Debug) << "Mapped Android format "
> > -                               << camera3Format.name << " to "
> > -                               << mappedFormat.toString();
> > -
> > -               std::vector<Size> resolutions;
> > -               const PixelFormatInfo &info =
> > PixelFormatInfo::info(mappedFormat);
> > -               if (info.colourEncoding ==
> > PixelFormatInfo::ColourEncodingRAW)
> > -                       resolutions = getRawResolutions(mappedFormat);
> > -               else
> > -                       resolutions = getYUVResolutions(cameraConfig.get(),
> > -                                                       mappedFormat,
> > -                                                       cameraResolutions);
> > -
> > -               for (const Size &res : resolutions) {
> > -                       streamConfigurations_.push_back({ res,
> > androidFormat });
> > -
> > -                       /*
> > -                        * If the format is HAL_PIXEL_FORMAT_YCbCr_420_888
> > -                        * from which JPEG is produced, add an entry for
> > -                        * the JPEG stream.
> > -                        *
> > -                        * \todo Wire the JPEG encoder to query the
> > supported
> > -                        * sizes provided a list of formats it can encode.
> > -                        *
> > -                        * \todo Support JPEG streams produced by the
> > Camera
> > -                        * natively.
> > -                        */
> > -                       if (androidFormat ==
> > HAL_PIXEL_FORMAT_YCbCr_420_888) {
> > -                               streamConfigurations_.push_back(
> > -                                       { res, HAL_PIXEL_FORMAT_BLOB });
> > -                               maxJpegSize = std::max(maxJpegSize, res);
> > -                       }
> > -               }
> > -
> > -               /*
> > -                * \todo Calculate the maximum JPEG buffer size by asking
> > the
> > -                * encoder giving the maximum frame size required.
> > -                */
> > -               maxJpegBufferSize_ = maxJpegSize.width *
> > maxJpegSize.height * 1.5;
> > -       }
> > -
> > -       LOG(HAL, Debug) << "Collected stream configuration map: ";
> > -       for (const auto &entry : streamConfigurations_)
> > -               LOG(HAL, Debug) << "{ " << entry.resolution.toString() <<
> > " - "
> > -                               << utils::hex(entry.androidFormat) << " }";
> > -
> > -       return 0;
> > +       return capabilities_.initialize(camera_, orientation_, facing_);
> >  }
> >
> >  /*
> > @@ -817,802 +490,19 @@ void CameraDevice::stop()
> >         state_ = State::Stopped;
> >  }
> >
> > -void CameraDevice::setCallbacks(const camera3_callback_ops_t *callbacks)
> > +unsigned int CameraDevice::maxJpegBufferSize() const
> >  {
> > -       callbacks_ = callbacks;
> > +       return capabilities_.maxJpegBufferSize();
> >  }
> >
> > -/*
> > - * Return static information for the camera.
> > - */
> > -const camera_metadata_t *CameraDevice::getStaticMetadata()
> > -{
> > -       if (staticMetadata_)
> > -               return staticMetadata_->get();
> > -
> > -       staticMetadata_ = std::make_unique<CameraMetadata>(64, 1024);
> > -       if (!staticMetadata_->isValid()) {
> > -               LOG(HAL, Error) << "Failed to allocate static metadata";
> > -               staticMetadata_.reset();
> > -               return nullptr;
> > -       }
> > -
> > -       const ControlInfoMap &controlsInfo = camera_->controls();
> > -       const ControlList &properties = camera_->properties();
> > -
> > -       /* Color correction static metadata. */
> > -       {
> > -               std::vector<uint8_t> data;
> > -               data.reserve(3);
> > -               const auto &infoMap =
> > controlsInfo.find(&controls::draft::ColorCorrectionAberrationMode);
> > -               if (infoMap != controlsInfo.end()) {
> > -                       for (const auto &value : infoMap->second.values())
> > -                               data.push_back(value.get<int32_t>());
> > -               } else {
> > -
> >  data.push_back(ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF);
> > -               }
> > -
> >  staticMetadata_->addEntry(ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
> > -                                         data);
> > -       }
> > -
> > -       /* Control static metadata. */
> > -       std::vector<uint8_t> aeAvailableAntiBandingModes = {
> > -               ANDROID_CONTROL_AE_ANTIBANDING_MODE_OFF,
> > -               ANDROID_CONTROL_AE_ANTIBANDING_MODE_50HZ,
> > -               ANDROID_CONTROL_AE_ANTIBANDING_MODE_60HZ,
> > -               ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO,
> > -       };
> > -
> >  staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,
> > -                                 aeAvailableAntiBandingModes);
> > -
> > -       std::vector<uint8_t> aeAvailableModes = {
> > -               ANDROID_CONTROL_AE_MODE_ON,
> > -       };
> > -       staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_MODES,
> > -                                 aeAvailableModes);
> > -
> > -       int64_t minFrameDurationNsec = -1;
> > -       int64_t maxFrameDurationNsec = -1;
> > -       const auto frameDurationsInfo =
> > controlsInfo.find(&controls::FrameDurationLimits);
> > -       if (frameDurationsInfo != controlsInfo.end()) {
> > -               minFrameDurationNsec =
> > frameDurationsInfo->second.min().get<int64_t>() * 1000;
> > -               maxFrameDurationNsec =
> > frameDurationsInfo->second.max().get<int64_t>() * 1000;
> > -
> > -               /*
> > -                * Adjust the minimum frame duration to comply with Android
> > -                * requirements. The camera service mandates all
> > preview/record
> > -                * streams to have a minimum frame duration < 33,366
> > milliseconds
> > -                * (see MAX_PREVIEW_RECORD_DURATION_NS in the camera
> > service
> > -                * implementation).
> > -                *
> > -                * If we're close enough (+ 500 useconds) to that value,
> > round
> > -                * the minimum frame duration of the camera to an accepted
> > -                * value.
> > -                */
> > -               static constexpr int64_t MAX_PREVIEW_RECORD_DURATION_NS =
> > 1e9 / 29.97;
> > -               if (minFrameDurationNsec > MAX_PREVIEW_RECORD_DURATION_NS
> > &&
> > -                   minFrameDurationNsec < MAX_PREVIEW_RECORD_DURATION_NS
> > + 500000)
> > -                       minFrameDurationNsec =
> > MAX_PREVIEW_RECORD_DURATION_NS - 1000;
> > -
> > -               /*
> > -                * The AE routine frame rate limits are computed using the
> > frame
> > -                * duration limits, as libcamera clips the AE routine to
> > the
> > -                * frame durations.
> > -                */
> > -               int32_t maxFps = std::round(1e9 / minFrameDurationNsec);
> > -               int32_t minFps = std::round(1e9 / maxFrameDurationNsec);
> > -               minFps = std::max(1, minFps);
> > -
> > -               /*
> > -                * Force rounding errors so that we have the proper frame
> > -                * durations for when we reuse these variables later
> > -                */
> > -               minFrameDurationNsec = 1e9 / maxFps;
> > -               maxFrameDurationNsec = 1e9 / minFps;
> > -
> > -               /*
> > -                * Register to the camera service {min, max} and {max, max}
> > -                * intervals as requested by the metadata documentation.
> > -                */
> > -               int32_t availableAeFpsTarget[] = {
> > -                       minFps, maxFps, maxFps, maxFps
> > -               };
> > -
> >  staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> > -                                         availableAeFpsTarget);
> > -       }
> > -
> > -       std::vector<int32_t> aeCompensationRange = {
> > -               0, 0,
> > -       };
> > -       staticMetadata_->addEntry(ANDROID_CONTROL_AE_COMPENSATION_RANGE,
> > -                                 aeCompensationRange);
> > -
> > -       const camera_metadata_rational_t aeCompensationStep[] = {
> > -               { 0, 1 }
> > -       };
> > -       staticMetadata_->addEntry(ANDROID_CONTROL_AE_COMPENSATION_STEP,
> > -                                 aeCompensationStep);
> > -
> > -       std::vector<uint8_t> availableAfModes = {
> > -               ANDROID_CONTROL_AF_MODE_OFF,
> > -       };
> > -       staticMetadata_->addEntry(ANDROID_CONTROL_AF_AVAILABLE_MODES,
> > -                                 availableAfModes);
> > -
> > -       std::vector<uint8_t> availableEffects = {
> > -               ANDROID_CONTROL_EFFECT_MODE_OFF,
> > -       };
> > -       staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_EFFECTS,
> > -                                 availableEffects);
> > -
> > -       std::vector<uint8_t> availableSceneModes = {
> > -               ANDROID_CONTROL_SCENE_MODE_DISABLED,
> > -       };
> > -       staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_SCENE_MODES,
> > -                                 availableSceneModes);
> > -
> > -       std::vector<uint8_t> availableStabilizationModes = {
> > -               ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF,
> > -       };
> > -
> >  staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
> > -                                 availableStabilizationModes);
> > -
> > -       /*
> > -        * \todo Inspect the Camera capabilities to report the available
> > -        * AWB modes. Default to AUTO as CTS tests require it.
> > -        */
> > -       std::vector<uint8_t> availableAwbModes = {
> > -               ANDROID_CONTROL_AWB_MODE_AUTO,
> > -       };
> > -       staticMetadata_->addEntry(ANDROID_CONTROL_AWB_AVAILABLE_MODES,
> > -                                 availableAwbModes);
> > -
> > -       std::vector<int32_t> availableMaxRegions = {
> > -               0, 0, 0,
> > -       };
> > -       staticMetadata_->addEntry(ANDROID_CONTROL_MAX_REGIONS,
> > -                                 availableMaxRegions);
> > -
> > -       std::vector<uint8_t> sceneModesOverride = {
> > -               ANDROID_CONTROL_AE_MODE_ON,
> > -               ANDROID_CONTROL_AWB_MODE_AUTO,
> > -               ANDROID_CONTROL_AF_MODE_OFF,
> > -       };
> > -       staticMetadata_->addEntry(ANDROID_CONTROL_SCENE_MODE_OVERRIDES,
> > -                                 sceneModesOverride);
> > -
> > -       uint8_t aeLockAvailable = ANDROID_CONTROL_AE_LOCK_AVAILABLE_FALSE;
> > -       staticMetadata_->addEntry(ANDROID_CONTROL_AE_LOCK_AVAILABLE,
> > -                                 aeLockAvailable);
> > -
> > -       uint8_t awbLockAvailable =
> > ANDROID_CONTROL_AWB_LOCK_AVAILABLE_FALSE;
> > -       staticMetadata_->addEntry(ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
> > -                                 awbLockAvailable);
> > -
> > -       char availableControlModes = ANDROID_CONTROL_MODE_AUTO;
> > -       staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_MODES,
> > -                                 availableControlModes);
> > -
> > -       /* JPEG static metadata. */
> > -
> > -       /*
> > -        * Create the list of supported thumbnail sizes by inspecting the
> > -        * available JPEG resolutions collected in streamConfigurations_
> > and
> > -        * generate one entry for each aspect ratio.
> > -        *
> > -        * The JPEG thumbnailer can freely scale, so pick an arbitrary
> > -        * (160, 160) size as the bounding rectangle, which is then
> > cropped to
> > -        * the different supported aspect ratios.
> > -        */
> > -       constexpr Size maxJpegThumbnail(160, 160);
> > -       std::vector<Size> thumbnailSizes;
> > -       thumbnailSizes.push_back({ 0, 0 });
> > -       for (const auto &entry : streamConfigurations_) {
> > -               if (entry.androidFormat != HAL_PIXEL_FORMAT_BLOB)
> > -                       continue;
> > -
> > -               Size thumbnailSize = maxJpegThumbnail
> > -                                    .boundedToAspectRatio({
> > entry.resolution.width,
> > -
> > entry.resolution.height });
> > -               thumbnailSizes.push_back(thumbnailSize);
> > -       }
> > -
> > -       std::sort(thumbnailSizes.begin(), thumbnailSizes.end());
> > -       auto last = std::unique(thumbnailSizes.begin(),
> > thumbnailSizes.end());
> > -       thumbnailSizes.erase(last, thumbnailSizes.end());
> > -
> > -       /* Transform sizes in to a list of integers that can be consumed.
> > */
> > -       std::vector<int32_t> thumbnailEntries;
> > -       thumbnailEntries.reserve(thumbnailSizes.size() * 2);
> > -       for (const auto &size : thumbnailSizes) {
> > -               thumbnailEntries.push_back(size.width);
> > -               thumbnailEntries.push_back(size.height);
> > -       }
> > -       staticMetadata_->addEntry(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,
> > -                                 thumbnailEntries);
> > -
> > -       staticMetadata_->addEntry(ANDROID_JPEG_MAX_SIZE,
> > maxJpegBufferSize_);
> > -
> > -       /* Sensor static metadata. */
> > -       std::array<int32_t, 2> pixelArraySize;
> > -       {
> > -               const Size &size =
> > properties.get(properties::PixelArraySize);
> > -               pixelArraySize[0] = size.width;
> > -               pixelArraySize[1] = size.height;
> > -
> >  staticMetadata_->addEntry(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
> > -                                         pixelArraySize);
> > -       }
> > -
> > -       if (properties.contains(properties::UnitCellSize)) {
> > -               const Size &cellSize =
> > properties.get<Size>(properties::UnitCellSize);
> > -               std::array<float, 2> physicalSize{
> > -                       cellSize.width * pixelArraySize[0] / 1e6f,
> > -                       cellSize.height * pixelArraySize[1] / 1e6f
> > -               };
> > -
> >  staticMetadata_->addEntry(ANDROID_SENSOR_INFO_PHYSICAL_SIZE,
> > -                                         physicalSize);
> > -       }
> > -
> > -       {
> > -               const Span<const Rectangle> &rects =
> > -                       properties.get(properties::PixelArrayActiveAreas);
> > -               std::vector<int32_t> data{
> > -                       static_cast<int32_t>(rects[0].x),
> > -                       static_cast<int32_t>(rects[0].y),
> > -                       static_cast<int32_t>(rects[0].width),
> > -                       static_cast<int32_t>(rects[0].height),
> > -               };
> > -
> >  staticMetadata_->addEntry(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
> > -                                         data);
> > -       }
> > -
> > -       int32_t sensitivityRange[] = {
> > -               32, 2400,
> > -       };
> > -       staticMetadata_->addEntry(ANDROID_SENSOR_INFO_SENSITIVITY_RANGE,
> > -                                 sensitivityRange);
> > -
> > -       /* Report the color filter arrangement if the camera reports it. */
> > -       if
> > (properties.contains(properties::draft::ColorFilterArrangement)) {
> > -               uint8_t filterArr =
> > properties.get(properties::draft::ColorFilterArrangement);
> > -
> >  staticMetadata_->addEntry(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,
> > -                                         filterArr);
> > -       }
> > -
> > -       const auto &exposureInfo =
> > controlsInfo.find(&controls::ExposureTime);
> > -       if (exposureInfo != controlsInfo.end()) {
> > -               int64_t exposureTimeRange[2] = {
> > -                       exposureInfo->second.min().get<int32_t>() * 1000LL,
> > -                       exposureInfo->second.max().get<int32_t>() * 1000LL,
> > -               };
> > -
> >  staticMetadata_->addEntry(ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE,
> > -                                         exposureTimeRange, 2);
> > -       }
> > -
> > -       staticMetadata_->addEntry(ANDROID_SENSOR_ORIENTATION,
> > orientation_);
> > -
> > -       std::vector<int32_t> testPatternModes = {
> > -               ANDROID_SENSOR_TEST_PATTERN_MODE_OFF
> > -       };
> > -       const auto &testPatternsInfo =
> > -               controlsInfo.find(&controls::draft::TestPatternMode);
> > -       if (testPatternsInfo != controlsInfo.end()) {
> > -               const auto &values = testPatternsInfo->second.values();
> > -               ASSERT(!values.empty());
> > -               for (const auto &value : values) {
> > -                       switch (value.get<int32_t>()) {
> > -                       case controls::draft::TestPatternModeOff:
> > -                               /*
> > -                                * ANDROID_SENSOR_TEST_PATTERN_MODE_OFF is
> > -                                * already in testPatternModes.
> > -                                */
> > -                               break;
> > -
> > -                       case controls::draft::TestPatternModeSolidColor:
> > -                               testPatternModes.push_back(
> > -
> >  ANDROID_SENSOR_TEST_PATTERN_MODE_SOLID_COLOR);
> > -                               break;
> > -
> > -                       case controls::draft::TestPatternModeColorBars:
> > -                               testPatternModes.push_back(
> > -
> >  ANDROID_SENSOR_TEST_PATTERN_MODE_COLOR_BARS);
> > -                               break;
> > -
> > -                       case
> > controls::draft::TestPatternModeColorBarsFadeToGray:
> > -                               testPatternModes.push_back(
> > -
> >  ANDROID_SENSOR_TEST_PATTERN_MODE_COLOR_BARS_FADE_TO_GRAY);
> > -                               break;
> > -
> > -                       case controls::draft::TestPatternModePn9:
> > -                               testPatternModes.push_back(
> > -
> >  ANDROID_SENSOR_TEST_PATTERN_MODE_PN9);
> > -                               break;
> > -
> > -                       case controls::draft::TestPatternModeCustom1:
> > -                               /* We don't support this yet. */
> > -                               break;
> > -
> > -                       default:
> > -                               LOG(HAL, Error) << "Unknown test pattern
> > mode: "
> > -                                               << value.get<int32_t>();
> > -                               continue;
> > -                       }
> > -               }
> > -       }
> > -
> >  staticMetadata_->addEntry(ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES,
> > -                                 testPatternModes);
> > -
> > -       uint8_t timestampSource =
> > ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN;
> > -       staticMetadata_->addEntry(ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE,
> > -                                 timestampSource);
> > -
> > -       if (maxFrameDurationNsec > 0)
> > -
> >  staticMetadata_->addEntry(ANDROID_SENSOR_INFO_MAX_FRAME_DURATION,
> > -                                         maxFrameDurationNsec);
> > -
> > -       /* Statistics static metadata. */
> > -       uint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
> > -
> >  staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
> > -                                 faceDetectMode);
> > -
> > -       int32_t maxFaceCount = 0;
> > -       staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
> > -                                 maxFaceCount);
> > -
> > -       {
> > -               std::vector<uint8_t> data;
> > -               data.reserve(2);
> > -               const auto &infoMap =
> > controlsInfo.find(&controls::draft::LensShadingMapMode);
> > -               if (infoMap != controlsInfo.end()) {
> > -                       for (const auto &value : infoMap->second.values())
> > -                               data.push_back(value.get<int32_t>());
> > -               } else {
> > -
> >  data.push_back(ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF);
> > -               }
> > -
> >  staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES,
> > -                                         data);
> > -       }
> > -
> > -       /* Sync static metadata. */
> > -       int32_t maxLatency = ANDROID_SYNC_MAX_LATENCY_UNKNOWN;
> > -       staticMetadata_->addEntry(ANDROID_SYNC_MAX_LATENCY, maxLatency);
> > -
> > -       /* Flash static metadata. */
> > -       char flashAvailable = ANDROID_FLASH_INFO_AVAILABLE_FALSE;
> > -       staticMetadata_->addEntry(ANDROID_FLASH_INFO_AVAILABLE,
> > -                                 flashAvailable);
> > -
> > -       /* Lens static metadata. */
> > -       std::vector<float> lensApertures = {
> > -               2.53 / 100,
> > -       };
> > -       staticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_APERTURES,
> > -                                 lensApertures);
> > -
> > -       uint8_t lensFacing;
> > -       switch (facing_) {
> > -       default:
> > -       case CAMERA_FACING_FRONT:
> > -               lensFacing = ANDROID_LENS_FACING_FRONT;
> > -               break;
> > -       case CAMERA_FACING_BACK:
> > -               lensFacing = ANDROID_LENS_FACING_BACK;
> > -               break;
> > -       case CAMERA_FACING_EXTERNAL:
> > -               lensFacing = ANDROID_LENS_FACING_EXTERNAL;
> > -               break;
> > -       }
> > -       staticMetadata_->addEntry(ANDROID_LENS_FACING, lensFacing);
> > -
> > -       std::vector<float> lensFocalLengths = {
> > -               1,
> > -       };
> > -
> >  staticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS,
> > -                                 lensFocalLengths);
> > -
> > -       std::vector<uint8_t> opticalStabilizations = {
> > -               ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF,
> > -       };
> > -
> >  staticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
> > -                                 opticalStabilizations);
> > -
> > -       float hypeFocalDistance = 0;
> > -       staticMetadata_->addEntry(ANDROID_LENS_INFO_HYPERFOCAL_DISTANCE,
> > -                                 hypeFocalDistance);
> > -
> > -       float minFocusDistance = 0;
> > -       staticMetadata_->addEntry(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
> > -                                 minFocusDistance);
> > -
> > -       /* Noise reduction modes. */
> > -       {
> > -               std::vector<uint8_t> data;
> > -               data.reserve(5);
> > -               const auto &infoMap =
> > controlsInfo.find(&controls::draft::NoiseReductionMode);
> > -               if (infoMap != controlsInfo.end()) {
> > -                       for (const auto &value : infoMap->second.values())
> > -                               data.push_back(value.get<int32_t>());
> > -               } else {
> > -                       data.push_back(ANDROID_NOISE_REDUCTION_MODE_OFF);
> > -               }
> > -
> >  staticMetadata_->addEntry(ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
> > -                                         data);
> > -       }
> > -
> > -       /* Scaler static metadata. */
> > -
> > -       /*
> > -        * \todo The digital zoom factor is a property that depends on the
> > -        * desired output configuration and the sensor frame size input to
> > the
> > -        * ISP. This information is not available to the Android HAL, not
> > at
> > -        * initialization time at least.
> > -        *
> > -        * As a workaround rely on pipeline handlers initializing the
> > -        * ScalerCrop control with the camera default configuration and
> > use the
> > -        * maximum and minimum crop rectangles to calculate the digital
> > zoom
> > -        * factor.
> > -        */
> > -       float maxZoom = 1.0f;
> > -       const auto scalerCrop = controlsInfo.find(&controls::ScalerCrop);
> > -       if (scalerCrop != controlsInfo.end()) {
> > -               Rectangle min = scalerCrop->second.min().get<Rectangle>();
> > -               Rectangle max = scalerCrop->second.max().get<Rectangle>();
> > -               maxZoom = std::min(1.0f * max.width / min.width,
> > -                                  1.0f * max.height / min.height);
> > -       }
> > -
> >  staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
> > -                                 maxZoom);
> > -
> > -       std::vector<uint32_t> availableStreamConfigurations;
> > -       availableStreamConfigurations.reserve(streamConfigurations_.size()
> > * 4);
> > -       for (const auto &entry : streamConfigurations_) {
> > -
> >  availableStreamConfigurations.push_back(entry.androidFormat);
> > -
> >  availableStreamConfigurations.push_back(entry.resolution.width);
> > -
> >  availableStreamConfigurations.push_back(entry.resolution.height);
> > -               availableStreamConfigurations.push_back(
> > -
> >  ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT);
> > -       }
> > -
> >  staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
> > -                                 availableStreamConfigurations);
> > -
> > -       std::vector<int64_t> availableStallDurations = {
> > -               ANDROID_SCALER_AVAILABLE_FORMATS_BLOB, 2560, 1920,
> > 33333333,
> > -       };
> > -       staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,
> > -                                 availableStallDurations);
> > -
> > -       /* Use the minimum frame duration for all the YUV/RGB formats. */
> > -       if (minFrameDurationNsec > 0) {
> > -               std::vector<int64_t> minFrameDurations;
> > -               minFrameDurations.reserve(streamConfigurations_.size() *
> > 4);
> > -               for (const auto &entry : streamConfigurations_) {
> > -                       minFrameDurations.push_back(entry.androidFormat);
> > -
> >  minFrameDurations.push_back(entry.resolution.width);
> > -
> >  minFrameDurations.push_back(entry.resolution.height);
> > -                       minFrameDurations.push_back(minFrameDurationNsec);
> > -               }
> > -
> >  staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
> > -                                         minFrameDurations);
> > -       }
> > -
> > -       uint8_t croppingType = ANDROID_SCALER_CROPPING_TYPE_CENTER_ONLY;
> > -       staticMetadata_->addEntry(ANDROID_SCALER_CROPPING_TYPE,
> > croppingType);
> > -
> > -       /* Info static metadata. */
> > -       uint8_t supportedHWLevel =
> > ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
> > -       staticMetadata_->addEntry(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
> > -                                 supportedHWLevel);
> > -
> > -       /* Request static metadata. */
> > -       int32_t partialResultCount = 1;
> > -       staticMetadata_->addEntry(ANDROID_REQUEST_PARTIAL_RESULT_COUNT,
> > -                                 partialResultCount);
> > -
> > -       {
> > -               /* Default the value to 2 if not reported by the camera. */
> > -               uint8_t maxPipelineDepth = 2;
> > -               const auto &infoMap =
> > controlsInfo.find(&controls::draft::PipelineDepth);
> > -               if (infoMap != controlsInfo.end())
> > -                       maxPipelineDepth =
> > infoMap->second.max().get<int32_t>();
> > -
> >  staticMetadata_->addEntry(ANDROID_REQUEST_PIPELINE_MAX_DEPTH,
> > -                                         maxPipelineDepth);
> > -       }
> > -
> > -       /* LIMITED does not support reprocessing. */
> > -       uint32_t maxNumInputStreams = 0;
> > -       staticMetadata_->addEntry(ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,
> > -                                 maxNumInputStreams);
> > -
> > -       std::vector<uint8_t> availableCapabilities = {
> > -               ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE,
> > -       };
> > -
> > -       /* Report if camera supports RAW. */
> > -       bool rawStreamAvailable = false;
> > -       std::unique_ptr<CameraConfiguration> cameraConfig =
> > -               camera_->generateConfiguration({ StreamRole::Raw });
> > -       if (cameraConfig && !cameraConfig->empty()) {
> > -               const PixelFormatInfo &info =
> > -
> >  PixelFormatInfo::info(cameraConfig->at(0).pixelFormat);
> > -               /* Only advertise RAW support if RAW16 is possible. */
> > -               if (info.colourEncoding ==
> > PixelFormatInfo::ColourEncodingRAW &&
> > -                   info.bitsPerPixel == 16) {
> > -                       rawStreamAvailable = true;
> > -
> >  availableCapabilities.push_back(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW);
> > -               }
> > -       }
> > -
> > -       /* Number of { RAW, YUV, JPEG } supported output streams */
> > -       int32_t numOutStreams[] = { rawStreamAvailable, 2, 1 };
> > -       staticMetadata_->addEntry(ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,
> > -                                 numOutStreams);
> > -
> > -       staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
> > -                                 availableCapabilities);
> > -
> > -       std::vector<int32_t> availableCharacteristicsKeys = {
> > -               ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
> > -               ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,
> > -               ANDROID_CONTROL_AE_AVAILABLE_MODES,
> > -               ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> > -               ANDROID_CONTROL_AE_COMPENSATION_RANGE,
> > -               ANDROID_CONTROL_AE_COMPENSATION_STEP,
> > -               ANDROID_CONTROL_AE_LOCK_AVAILABLE,
> > -               ANDROID_CONTROL_AF_AVAILABLE_MODES,
> > -               ANDROID_CONTROL_AVAILABLE_EFFECTS,
> > -               ANDROID_CONTROL_AVAILABLE_MODES,
> > -               ANDROID_CONTROL_AVAILABLE_SCENE_MODES,
> > -               ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
> > -               ANDROID_CONTROL_AWB_AVAILABLE_MODES,
> > -               ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
> > -               ANDROID_CONTROL_MAX_REGIONS,
> > -               ANDROID_CONTROL_SCENE_MODE_OVERRIDES,
> > -               ANDROID_FLASH_INFO_AVAILABLE,
> > -               ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
> > -               ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,
> > -               ANDROID_JPEG_MAX_SIZE,
> > -               ANDROID_LENS_FACING,
> > -               ANDROID_LENS_INFO_AVAILABLE_APERTURES,
> > -               ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS,
> > -               ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
> > -               ANDROID_LENS_INFO_HYPERFOCAL_DISTANCE,
> > -               ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
> > -               ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
> > -               ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
> > -               ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,
> > -               ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,
> > -               ANDROID_REQUEST_PARTIAL_RESULT_COUNT,
> > -               ANDROID_REQUEST_PIPELINE_MAX_DEPTH,
> > -               ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
> > -               ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
> > -               ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,
> > -               ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
> > -               ANDROID_SCALER_CROPPING_TYPE,
> > -               ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES,
> > -               ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
> > -               ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,
> > -               ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE,
> > -               ANDROID_SENSOR_INFO_MAX_FRAME_DURATION,
> > -               ANDROID_SENSOR_INFO_PHYSICAL_SIZE,
> > -               ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
> > -               ANDROID_SENSOR_INFO_SENSITIVITY_RANGE,
> > -               ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE,
> > -               ANDROID_SENSOR_ORIENTATION,
> > -               ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
> > -               ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
> > -               ANDROID_SYNC_MAX_LATENCY,
> > -       };
> > -
> >  staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS,
> > -                                 availableCharacteristicsKeys);
> > -
> > -       std::vector<int32_t> availableRequestKeys = {
> > -               ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
> > -               ANDROID_CONTROL_AE_ANTIBANDING_MODE,
> > -               ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
> > -               ANDROID_CONTROL_AE_LOCK,
> > -               ANDROID_CONTROL_AE_MODE,
> > -               ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
> > -               ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
> > -               ANDROID_CONTROL_AF_MODE,
> > -               ANDROID_CONTROL_AF_TRIGGER,
> > -               ANDROID_CONTROL_AWB_LOCK,
> > -               ANDROID_CONTROL_AWB_MODE,
> > -               ANDROID_CONTROL_CAPTURE_INTENT,
> > -               ANDROID_CONTROL_EFFECT_MODE,
> > -               ANDROID_CONTROL_MODE,
> > -               ANDROID_CONTROL_SCENE_MODE,
> > -               ANDROID_CONTROL_VIDEO_STABILIZATION_MODE,
> > -               ANDROID_FLASH_MODE,
> > -               ANDROID_JPEG_ORIENTATION,
> > -               ANDROID_JPEG_QUALITY,
> > -               ANDROID_JPEG_THUMBNAIL_QUALITY,
> > -               ANDROID_JPEG_THUMBNAIL_SIZE,
> > -               ANDROID_LENS_APERTURE,
> > -               ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
> > -               ANDROID_NOISE_REDUCTION_MODE,
> > -               ANDROID_SCALER_CROP_REGION,
> > -               ANDROID_STATISTICS_FACE_DETECT_MODE
> > -       };
> > -       staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS,
> > -                                 availableRequestKeys);
> > -
> > -       std::vector<int32_t> availableResultKeys = {
> > -               ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
> > -               ANDROID_CONTROL_AE_ANTIBANDING_MODE,
> > -               ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
> > -               ANDROID_CONTROL_AE_LOCK,
> > -               ANDROID_CONTROL_AE_MODE,
> > -               ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
> > -               ANDROID_CONTROL_AE_STATE,
> > -               ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
> > -               ANDROID_CONTROL_AF_MODE,
> > -               ANDROID_CONTROL_AF_STATE,
> > -               ANDROID_CONTROL_AF_TRIGGER,
> > -               ANDROID_CONTROL_AWB_LOCK,
> > -               ANDROID_CONTROL_AWB_MODE,
> > -               ANDROID_CONTROL_AWB_STATE,
> > -               ANDROID_CONTROL_CAPTURE_INTENT,
> > -               ANDROID_CONTROL_EFFECT_MODE,
> > -               ANDROID_CONTROL_MODE,
> > -               ANDROID_CONTROL_SCENE_MODE,
> > -               ANDROID_CONTROL_VIDEO_STABILIZATION_MODE,
> > -               ANDROID_FLASH_MODE,
> > -               ANDROID_FLASH_STATE,
> > -               ANDROID_JPEG_GPS_COORDINATES,
> > -               ANDROID_JPEG_GPS_PROCESSING_METHOD,
> > -               ANDROID_JPEG_GPS_TIMESTAMP,
> > -               ANDROID_JPEG_ORIENTATION,
> > -               ANDROID_JPEG_QUALITY,
> > -               ANDROID_JPEG_SIZE,
> > -               ANDROID_JPEG_THUMBNAIL_QUALITY,
> > -               ANDROID_JPEG_THUMBNAIL_SIZE,
> > -               ANDROID_LENS_APERTURE,
> > -               ANDROID_LENS_FOCAL_LENGTH,
> > -               ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
> > -               ANDROID_LENS_STATE,
> > -               ANDROID_NOISE_REDUCTION_MODE,
> > -               ANDROID_REQUEST_PIPELINE_DEPTH,
> > -               ANDROID_SCALER_CROP_REGION,
> > -               ANDROID_SENSOR_EXPOSURE_TIME,
> > -               ANDROID_SENSOR_FRAME_DURATION,
> > -               ANDROID_SENSOR_ROLLING_SHUTTER_SKEW,
> > -               ANDROID_SENSOR_TEST_PATTERN_MODE,
> > -               ANDROID_SENSOR_TIMESTAMP,
> > -               ANDROID_STATISTICS_FACE_DETECT_MODE,
> > -               ANDROID_STATISTICS_LENS_SHADING_MAP_MODE,
> > -               ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE,
> > -               ANDROID_STATISTICS_SCENE_FLICKER,
> > -       };
> > -       staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_RESULT_KEYS,
> > -                                 availableResultKeys);
> > -
> > -       if (!staticMetadata_->isValid()) {
> > -               LOG(HAL, Error) << "Failed to construct static metadata";
> > -               staticMetadata_.reset();
> > -               return nullptr;
> > -       }
> > -
> > -       if (staticMetadata_->resized()) {
> > -               auto [entryCount, dataCount] = staticMetadata_->usage();
> > -               LOG(HAL, Info)
> > -                       << "Static metadata resized: " << entryCount
> > -                       << " entries and " << dataCount << " bytes used";
> > -       }
> > -
> > -       return staticMetadata_->get();
> > -}
> > -
> > -std::unique_ptr<CameraMetadata> CameraDevice::requestTemplatePreview()
> > +void CameraDevice::setCallbacks(const camera3_callback_ops_t *callbacks)
> >  {
> > -       /*
> > -        * \todo Keep this in sync with the actual number of entries.
> > -        * Currently: 20 entries, 35 bytes
> > -        */
> > -       auto requestTemplate = std::make_unique<CameraMetadata>(21, 36);
> > -       if (!requestTemplate->isValid()) {
> > -               return nullptr;
> > -       }
> > -
> > -       /* Get the FPS range registered in the static metadata. */
> > -       camera_metadata_ro_entry_t entry;
> > -       bool found =
> > staticMetadata_->getEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> > -                                              &entry);
> > -       if (!found) {
> > -               LOG(HAL, Error) << "Cannot create capture template without
> > FPS range";
> > -               return nullptr;
> > -       }
> > -
> > -       /*
> > -        * Assume the AE_AVAILABLE_TARGET_FPS_RANGE static metadata
> > -        * has been assembled as {{min, max} {max, max}}.
> > -        */
> > -       requestTemplate->addEntry(ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
> > -                                 entry.data.i32, 2);
> > -
> > -       uint8_t aeMode = ANDROID_CONTROL_AE_MODE_ON;
> > -       requestTemplate->addEntry(ANDROID_CONTROL_AE_MODE, aeMode);
> > -
> > -       int32_t aeExposureCompensation = 0;
> > -       requestTemplate->addEntry(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
> > -                                 aeExposureCompensation);
> > -
> > -       uint8_t aePrecaptureTrigger =
> > ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_IDLE;
> > -       requestTemplate->addEntry(ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
> > -                                 aePrecaptureTrigger);
> > -
> > -       uint8_t aeLock = ANDROID_CONTROL_AE_LOCK_OFF;
> > -       requestTemplate->addEntry(ANDROID_CONTROL_AE_LOCK, aeLock);
> > -
> > -       uint8_t aeAntibandingMode =
> > ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO;
> > -       requestTemplate->addEntry(ANDROID_CONTROL_AE_ANTIBANDING_MODE,
> > -                                 aeAntibandingMode);
> > -
> > -       uint8_t afMode = ANDROID_CONTROL_AF_MODE_OFF;
> > -       requestTemplate->addEntry(ANDROID_CONTROL_AF_MODE, afMode);
> > -
> > -       uint8_t afTrigger = ANDROID_CONTROL_AF_TRIGGER_IDLE;
> > -       requestTemplate->addEntry(ANDROID_CONTROL_AF_TRIGGER, afTrigger);
> > -
> > -       uint8_t awbMode = ANDROID_CONTROL_AWB_MODE_AUTO;
> > -       requestTemplate->addEntry(ANDROID_CONTROL_AWB_MODE, awbMode);
> > -
> > -       uint8_t awbLock = ANDROID_CONTROL_AWB_LOCK_OFF;
> > -       requestTemplate->addEntry(ANDROID_CONTROL_AWB_LOCK, awbLock);
> > -
> > -       uint8_t flashMode = ANDROID_FLASH_MODE_OFF;
> > -       requestTemplate->addEntry(ANDROID_FLASH_MODE, flashMode);
> > -
> > -       uint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
> > -       requestTemplate->addEntry(ANDROID_STATISTICS_FACE_DETECT_MODE,
> > -                                 faceDetectMode);
> > -
> > -       uint8_t noiseReduction = ANDROID_NOISE_REDUCTION_MODE_OFF;
> > -       requestTemplate->addEntry(ANDROID_NOISE_REDUCTION_MODE,
> > -                                 noiseReduction);
> > -
> > -       uint8_t aberrationMode =
> > ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF;
> > -       requestTemplate->addEntry(ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
> > -                                 aberrationMode);
> > -
> > -       uint8_t controlMode = ANDROID_CONTROL_MODE_AUTO;
> > -       requestTemplate->addEntry(ANDROID_CONTROL_MODE, controlMode);
> > -
> > -       float lensAperture = 2.53 / 100;
> > -       requestTemplate->addEntry(ANDROID_LENS_APERTURE, lensAperture);
> > -
> > -       uint8_t opticalStabilization =
> > ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF;
> > -       requestTemplate->addEntry(ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
> > -                                 opticalStabilization);
> > -
> > -       uint8_t captureIntent = ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW;
> > -       requestTemplate->addEntry(ANDROID_CONTROL_CAPTURE_INTENT,
> > -                                 captureIntent);
> > -
> > -       return requestTemplate;
> > +       callbacks_ = callbacks;
> >  }
> >
> > -std::unique_ptr<CameraMetadata> CameraDevice::requestTemplateVideo()
> > +const camera_metadata_t *CameraDevice::getStaticMetadata()
> >  {
> > -       std::unique_ptr<CameraMetadata> previewTemplate =
> > requestTemplatePreview();
> > -       if (!previewTemplate)
> > -               return nullptr;
> > -
> > -       /*
> > -        * The video template requires a fixed FPS range. Everything else
> > -        * stays the same as the preview template.
> > -        */
> > -       camera_metadata_ro_entry_t entry;
> > -
> >  staticMetadata_->getEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> > -                                 &entry);
> > -
> > -       /*
> > -        * Assume the AE_AVAILABLE_TARGET_FPS_RANGE static metadata
> > -        * has been assembled as {{min, max} {max, max}}.
> > -        */
> > -       previewTemplate->updateEntry(ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
> > -                                    entry.data.i32 + 2, 2);
> > -
> > -       return previewTemplate;
> > +       return capabilities_.staticMetadata()->get();
> >  }
> >
> >  /*
> > @@ -1630,7 +520,7 @@ const camera_metadata_t
> > *CameraDevice::constructDefaultRequestSettings(int type)
> >         switch (type) {
> >         case CAMERA3_TEMPLATE_PREVIEW:
> >                 captureIntent = ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW;
> > -               requestTemplate = requestTemplatePreview();
> > +               requestTemplate = capabilities_.requestTemplatePreview();
> >                 break;
> >         case CAMERA3_TEMPLATE_STILL_CAPTURE:
> >                 /*
> > @@ -1638,15 +528,15 @@ const camera_metadata_t
> > *CameraDevice::constructDefaultRequestSettings(int type)
> >                  * for the torch mode we currently do not support.
> >                  */
> >                 captureIntent =
> > ANDROID_CONTROL_CAPTURE_INTENT_STILL_CAPTURE;
> > -               requestTemplate = requestTemplatePreview();
> > +               requestTemplate = capabilities_.requestTemplatePreview();
> >                 break;
> >         case CAMERA3_TEMPLATE_VIDEO_RECORD:
> >                 captureIntent =
> > ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_RECORD;
> > -               requestTemplate = requestTemplateVideo();
> > +               requestTemplate = capabilities_.requestTemplateVideo();
> >                 break;
> >         case CAMERA3_TEMPLATE_VIDEO_SNAPSHOT:
> >                 captureIntent =
> > ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT;
> > -               requestTemplate = requestTemplateVideo();
> > +               requestTemplate = capabilities_.requestTemplateVideo();
> >                 break;
> >         /* \todo Implement templates generation for the remaining use
> > cases. */
> >         case CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG:
> > @@ -1668,19 +558,6 @@ const camera_metadata_t
> > *CameraDevice::constructDefaultRequestSettings(int type)
> >         return requestTemplates_[type]->get();
> >  }
> >
> > -PixelFormat CameraDevice::toPixelFormat(int format) const
> > -{
> > -       /* Translate Android format code to libcamera pixel format. */
> > -       auto it = formatsMap_.find(format);
> > -       if (it == formatsMap_.end()) {
> > -               LOG(HAL, Error) << "Requested format " <<
> > utils::hex(format)
> > -                               << " not supported";
> > -               return PixelFormat();
> > -       }
> > -
> > -       return it->second;
> > -}
> > -
> >  /*
> >   * Inspect the stream_list to produce a list of StreamConfiguration to
> >   * be use to configure the Camera.
> > @@ -1727,7 +604,7 @@ int
> > CameraDevice::configureStreams(camera3_stream_configuration_t *stream_list)
> >                 camera3_stream_t *stream = stream_list->streams[i];
> >                 Size size(stream->width, stream->height);
> >
> > -               PixelFormat format = toPixelFormat(stream->format);
> > +               PixelFormat format =
> > capabilities_.toPixelFormat(stream->format);
> >
> >                 LOG(HAL, Info) << "Stream #" << i
> >                                << ", direction: " << stream->stream_type
> > diff --git a/src/android/camera_device.h b/src/android/camera_device.h
> > index 4aadb27c562c..090fe28a551e 100644
> > --- a/src/android/camera_device.h
> > +++ b/src/android/camera_device.h
> > @@ -10,14 +10,12 @@
> >  #include <map>
> >  #include <memory>
> >  #include <mutex>
> > -#include <tuple>
> >  #include <vector>
> >
> >  #include <hardware/camera3.h>
> >
> >  #include <libcamera/buffer.h>
> >  #include <libcamera/camera.h>
> > -#include <libcamera/geometry.h>
> >  #include <libcamera/request.h>
> >  #include <libcamera/stream.h>
> >
> > @@ -26,6 +24,7 @@
> >  #include "libcamera/internal/message.h"
> >  #include "libcamera/internal/thread.h"
> >
> > +#include "camera_capabilities.h"
> >  #include "camera_metadata.h"
> >  #include "camera_stream.h"
> >  #include "camera_worker.h"
> > @@ -57,7 +56,7 @@ public:
> >         const std::string &model() const { return model_; }
> >         int facing() const { return facing_; }
> >         int orientation() const { return orientation_; }
> > -       unsigned int maxJpegBufferSize() const { return
> > maxJpegBufferSize_; }
> > +       unsigned int maxJpegBufferSize() const;
> >
> >         void setCallbacks(const camera3_callback_ops_t *callbacks);
> >         const camera_metadata_t *getStaticMetadata();
> > @@ -86,11 +85,6 @@ private:
> >                 std::unique_ptr<CaptureRequest> request_;
> >         };
> >
> > -       struct Camera3StreamConfiguration {
> > -               libcamera::Size resolution;
> > -               int androidFormat;
> > -       };
> > -
> >         enum class State {
> >                 Stopped,
> >                 Flushing,
> > @@ -99,22 +93,11 @@ private:
> >
> >         void stop();
> >
> > -       int initializeStreamConfigurations();
> > -       std::vector<libcamera::Size>
> > -       getYUVResolutions(libcamera::CameraConfiguration *cameraConfig,
> > -                         const libcamera::PixelFormat &pixelFormat,
> > -                         const std::vector<libcamera::Size> &resolutions);
> > -       std::vector<libcamera::Size>
> > -       getRawResolutions(const libcamera::PixelFormat &pixelFormat);
> > -
> >         libcamera::FrameBuffer *createFrameBuffer(const buffer_handle_t
> > camera3buffer);
> >         void abortRequest(camera3_capture_request_t *request);
> >         void notifyShutter(uint32_t frameNumber, uint64_t timestamp);
> >         void notifyError(uint32_t frameNumber, camera3_stream_t *stream,
> >                          camera3_error_msg_code code);
> > -       std::unique_ptr<CameraMetadata> requestTemplatePreview();
> > -       std::unique_ptr<CameraMetadata> requestTemplateVideo();
> > -       libcamera::PixelFormat toPixelFormat(int format) const;
> >         int processControls(Camera3RequestDescriptor *descriptor);
> >         std::unique_ptr<CameraMetadata> getResultMetadata(
> >                 const Camera3RequestDescriptor &descriptor) const;
> > @@ -129,13 +112,11 @@ private:
> >
> >         std::shared_ptr<libcamera::Camera> camera_;
> >         std::unique_ptr<libcamera::CameraConfiguration> config_;
> > +       CameraCapabilities capabilities_;
> >
> > -       std::unique_ptr<CameraMetadata> staticMetadata_;
> >         std::map<unsigned int, std::unique_ptr<CameraMetadata>>
> > requestTemplates_;
> >         const camera3_callback_ops_t *callbacks_;
> >
> > -       std::vector<Camera3StreamConfiguration> streamConfigurations_;
> > -       std::map<int, libcamera::PixelFormat> formatsMap_;
> >         std::vector<CameraStream> streams_;
> >
> >         libcamera::Mutex descriptorsMutex_; /* Protects descriptors_. */
> > @@ -147,8 +128,6 @@ private:
> >         int facing_;
> >         int orientation_;
> >
> > -       unsigned int maxJpegBufferSize_;
> > -
> >         CameraMetadata lastSettings_;
> >  };
> >
> > diff --git a/src/android/meson.build b/src/android/meson.build
> > index f27fd5316705..6270fb201338 100644
> > --- a/src/android/meson.build
> > +++ b/src/android/meson.build
> > @@ -44,6 +44,7 @@ subdir('cros')
> >
> >  android_hal_sources = files([
> >      'camera3_hal.cpp',
> > +    'camera_capabilities.cpp',
> >      'camera_device.cpp',
> >      'camera_hal_config.cpp',
> >      'camera_hal_manager.cpp',
> > --
> > 2.31.1
> >
> >
Laurent Pinchart June 22, 2021, 10:01 a.m. UTC | #3
Hi Jacopo,

Thank you for the patch.

On Mon, Jun 21, 2021 at 05:29:54PM +0200, Jacopo Mondi wrote:
> The camera_device.cpp has grown a little too much, and it has quickly
> become hard to maintain. Break out the handling of the static
> information collected at camera initialization time to a new
> CameraCapabilities class.
> 
> Break out from the camera_device.cpp file all the functions relative to:

s/relative/related/

> - Initialization of supported stream configurations
> - Initialization of static metadata
> - Initialization of request templates
> 
> Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
> Acked-by: Paul Elder <paul.elder@ideasonboard.com>
> Tested-by: Paul Elder <paul.elder@ideasonboard.com>
> ---
>  src/android/camera_capabilities.cpp | 1164 +++++++++++++++++++++++++++
>  src/android/camera_capabilities.h   |   65 ++
>  src/android/camera_device.cpp       | 1147 +-------------------------
>  src/android/camera_device.h         |   27 +-
>  src/android/meson.build             |    1 +
>  5 files changed, 1245 insertions(+), 1159 deletions(-)
>  create mode 100644 src/android/camera_capabilities.cpp
>  create mode 100644 src/android/camera_capabilities.h
> 
> diff --git a/src/android/camera_capabilities.cpp b/src/android/camera_capabilities.cpp
> new file mode 100644
> index 000000000000..311a2c839586
> --- /dev/null
> +++ b/src/android/camera_capabilities.cpp
> @@ -0,0 +1,1164 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2021, Google Inc.
> + *
> + * camera_capabilities.cpp - Camera static properties manager
> + */
> +
> +#include "camera_capabilities.h"
> +
> +#include <array>
> +#include <cmath>
> +
> +#include <hardware/camera3.h>
> +
> +#include <libcamera/control_ids.h>
> +#include <libcamera/controls.h>
> +#include <libcamera/property_ids.h>
> +
> +#include "libcamera/internal/formats.h"
> +#include "libcamera/internal/log.h"
> +
> +using namespace libcamera;
> +
> +LOG_DECLARE_CATEGORY(HAL)
> +
> +namespace {
> +
> +/*
> + * \var camera3Resolutions
> + * \brief The list of image resolutions defined as mandatory to be supported by
> + * the Android Camera3 specification
> + */
> +const std::vector<Size> camera3Resolutions = {
> +	{ 320, 240 },
> +	{ 640, 480 },
> +	{ 1280, 720 },
> +	{ 1920, 1080 }
> +};
> +
> +/*
> + * \struct Camera3Format
> + * \brief Data associated with an Android format identifier
> + * \var libcameraFormats List of libcamera pixel formats compatible with the
> + * Android format
> + * \var name The human-readable representation of the Android format code
> + */
> +struct Camera3Format {
> +	std::vector<PixelFormat> libcameraFormats;
> +	bool mandatory;
> +	const char *name;
> +};
> +
> +/*
> + * \var camera3FormatsMap
> + * \brief Associate Android format code with ancillary data
> + */
> +const std::map<int, const Camera3Format> camera3FormatsMap = {
> +	{
> +		HAL_PIXEL_FORMAT_BLOB, {
> +			{ formats::MJPEG },
> +			true,
> +			"BLOB"
> +		}
> +	}, {
> +		HAL_PIXEL_FORMAT_YCbCr_420_888, {
> +			{ formats::NV12, formats::NV21 },
> +			true,
> +			"YCbCr_420_888"
> +		}
> +	}, {
> +		/*
> +		 * \todo Translate IMPLEMENTATION_DEFINED inspecting the gralloc
> +		 * usage flag. For now, copy the YCbCr_420 configuration.
> +		 */
> +		HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, {
> +			{ formats::NV12, formats::NV21 },
> +			true,
> +			"IMPLEMENTATION_DEFINED"
> +		}
> +	}, {
> +		HAL_PIXEL_FORMAT_RAW10, {
> +			{
> +				formats::SBGGR10_CSI2P,
> +				formats::SGBRG10_CSI2P,
> +				formats::SGRBG10_CSI2P,
> +				formats::SRGGB10_CSI2P
> +			},
> +			false,
> +			"RAW10"
> +		}
> +	}, {
> +		HAL_PIXEL_FORMAT_RAW12, {
> +			{
> +				formats::SBGGR12_CSI2P,
> +				formats::SGBRG12_CSI2P,
> +				formats::SGRBG12_CSI2P,
> +				formats::SRGGB12_CSI2P
> +			},
> +			false,
> +			"RAW12"
> +		}
> +	}, {
> +		HAL_PIXEL_FORMAT_RAW16, {
> +			{
> +				formats::SBGGR16,
> +				formats::SGBRG16,
> +				formats::SGRBG16,
> +				formats::SRGGB16
> +			},
> +			false,
> +			"RAW16"
> +		}
> +	},
> +};
> +
> +} /* namespace */
> +
> +int CameraCapabilities::initialize(std::shared_ptr<libcamera::Camera> camera,
> +				   int orientation, int facing)
> +{
> +	camera_ = camera;
> +	orientation_ = orientation;
> +	facing_ = facing;
> +
> +	/* Acquire the camera and initialize available stream configurations. */
> +	int ret = camera_->acquire();
> +	if (ret) {
> +		LOG(HAL, Error) << "Failed to temporarily acquire the camera";
> +		return ret;
> +	}
> +
> +	ret = initializeStreamConfigurations();
> +	camera_->release();
> +	if (ret)
> +		return ret;
> +
> +	return initializeStaticMetadata();
> +}
> +
> +std::vector<Size> CameraCapabilities::getYUVResolutions(CameraConfiguration *cameraConfig,
> +							const PixelFormat &pixelFormat,
> +							const std::vector<Size> &resolutions)
> +{
> +	std::vector<Size> supportedResolutions;
> +
> +	StreamConfiguration &cfg = cameraConfig->at(0);
> +	for (const Size &res : resolutions) {
> +		cfg.pixelFormat = pixelFormat;
> +		cfg.size = res;
> +
> +		CameraConfiguration::Status status = cameraConfig->validate();
> +		if (status != CameraConfiguration::Valid) {
> +			LOG(HAL, Debug) << cfg.toString() << " not supported";
> +			continue;
> +		}
> +
> +		LOG(HAL, Debug) << cfg.toString() << " supported";
> +
> +		supportedResolutions.push_back(res);
> +	}
> +
> +	return supportedResolutions;
> +}
> +
> +std::vector<Size> CameraCapabilities::getRawResolutions(const libcamera::PixelFormat &pixelFormat)
> +{
> +	std::unique_ptr<CameraConfiguration> cameraConfig =
> +		camera_->generateConfiguration({ StreamRole::Raw });
> +	StreamConfiguration &cfg = cameraConfig->at(0);
> +	const StreamFormats &formats = cfg.formats();
> +	std::vector<Size> supportedResolutions = formats.sizes(pixelFormat);
> +
> +	return supportedResolutions;
> +}
> +
> +/*
> + * Initialize the format conversion map to translate from Android format
> + * identifier to libcamera pixel formats and fill in the list of supported
> + * stream configurations to be reported to the Android camera framework through
> + * the Camera static metadata.

Did you mean s/Camera/camera/ as it's not a class name ?

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

> + */
> +int CameraCapabilities::initializeStreamConfigurations()
> +{
> +	/*
> +	 * Get the maximum output resolutions
> +	 * \todo Get this from the camera properties once defined
> +	 */
> +	std::unique_ptr<CameraConfiguration> cameraConfig =
> +		camera_->generateConfiguration({ StillCapture });
> +	if (!cameraConfig) {
> +		LOG(HAL, Error) << "Failed to get maximum resolution";
> +		return -EINVAL;
> +	}
> +	StreamConfiguration &cfg = cameraConfig->at(0);
> +
> +	/*
> +	 * \todo JPEG - Adjust the maximum available resolution by taking the
> +	 * JPEG encoder requirements into account (alignment and aspect ratio).
> +	 */
> +	const Size maxRes = cfg.size;
> +	LOG(HAL, Debug) << "Maximum supported resolution: " << maxRes.toString();
> +
> +	/*
> +	 * Build the list of supported image resolutions.
> +	 *
> +	 * The resolutions listed in camera3Resolution are mandatory to be
> +	 * supported, up to the camera maximum resolution.
> +	 *
> +	 * Augment the list by adding resolutions calculated from the camera
> +	 * maximum one.
> +	 */
> +	std::vector<Size> cameraResolutions;
> +	std::copy_if(camera3Resolutions.begin(), camera3Resolutions.end(),
> +		     std::back_inserter(cameraResolutions),
> +		     [&](const Size &res) { return res < maxRes; });
> +
> +	/*
> +	 * The Camera3 specification suggests adding 1/2 and 1/4 of the maximum
> +	 * resolution.
> +	 */
> +	for (unsigned int divider = 2;; divider <<= 1) {
> +		Size derivedSize{
> +			maxRes.width / divider,
> +			maxRes.height / divider,
> +		};
> +
> +		if (derivedSize.width < 320 ||
> +		    derivedSize.height < 240)
> +			break;
> +
> +		cameraResolutions.push_back(derivedSize);
> +	}
> +	cameraResolutions.push_back(maxRes);
> +
> +	/* Remove duplicated entries from the list of supported resolutions. */
> +	std::sort(cameraResolutions.begin(), cameraResolutions.end());
> +	auto last = std::unique(cameraResolutions.begin(), cameraResolutions.end());
> +	cameraResolutions.erase(last, cameraResolutions.end());
> +
> +	/*
> +	 * Build the list of supported camera formats.
> +	 *
> +	 * To each Android format a list of compatible libcamera formats is
> +	 * associated. The first libcamera format that tests successful is added
> +	 * to the format translation map used when configuring the streams.
> +	 * It is then tested against the list of supported camera resolutions to
> +	 * build the stream configuration map reported through the camera static
> +	 * metadata.
> +	 */
> +	Size maxJpegSize;
> +	for (const auto &format : camera3FormatsMap) {
> +		int androidFormat = format.first;
> +		const Camera3Format &camera3Format = format.second;
> +		const std::vector<PixelFormat> &libcameraFormats =
> +			camera3Format.libcameraFormats;
> +
> +		LOG(HAL, Debug) << "Trying to map Android format "
> +				<< camera3Format.name;
> +
> +		/*
> +		 * JPEG is always supported, either produced directly by the
> +		 * camera, or encoded in the HAL.
> +		 */
> +		if (androidFormat == HAL_PIXEL_FORMAT_BLOB) {
> +			formatsMap_[androidFormat] = formats::MJPEG;
> +			LOG(HAL, Debug) << "Mapped Android format "
> +					<< camera3Format.name << " to "
> +					<< formats::MJPEG.toString()
> +					<< " (fixed mapping)";
> +			continue;
> +		}
> +
> +		/*
> +		 * Test the libcamera formats that can produce images
> +		 * compatible with the format defined by Android.
> +		 */
> +		PixelFormat mappedFormat;
> +		for (const PixelFormat &pixelFormat : libcameraFormats) {
> +
> +			LOG(HAL, Debug) << "Testing " << pixelFormat.toString();
> +
> +			/*
> +			 * The stream configuration size can be adjusted,
> +			 * not the pixel format.
> +			 *
> +			 * \todo This could be simplified once all pipeline
> +			 * handlers will report the StreamFormats list of
> +			 * supported formats.
> +			 */
> +			cfg.pixelFormat = pixelFormat;
> +
> +			CameraConfiguration::Status status = cameraConfig->validate();
> +			if (status != CameraConfiguration::Invalid &&
> +			    cfg.pixelFormat == pixelFormat) {
> +				mappedFormat = pixelFormat;
> +				break;
> +			}
> +		}
> +
> +		if (!mappedFormat.isValid()) {
> +			/* If the format is not mandatory, skip it. */
> +			if (!camera3Format.mandatory)
> +				continue;
> +
> +			LOG(HAL, Error)
> +				<< "Failed to map mandatory Android format "
> +				<< camera3Format.name << " ("
> +				<< utils::hex(androidFormat) << "): aborting";
> +			return -EINVAL;
> +		}
> +
> +		/*
> +		 * Record the mapping and then proceed to generate the
> +		 * stream configurations map, by testing the image resolutions.
> +		 */
> +		formatsMap_[androidFormat] = mappedFormat;
> +		LOG(HAL, Debug) << "Mapped Android format "
> +				<< camera3Format.name << " to "
> +				<< mappedFormat.toString();
> +
> +		std::vector<Size> resolutions;
> +		const PixelFormatInfo &info = PixelFormatInfo::info(mappedFormat);
> +		if (info.colourEncoding == PixelFormatInfo::ColourEncodingRAW)
> +			resolutions = getRawResolutions(mappedFormat);
> +		else
> +			resolutions = getYUVResolutions(cameraConfig.get(),
> +							mappedFormat,
> +							cameraResolutions);
> +
> +		for (const Size &res : resolutions) {
> +			streamConfigurations_.push_back({ res, androidFormat });
> +
> +			/*
> +			 * If the format is HAL_PIXEL_FORMAT_YCbCr_420_888
> +			 * from which JPEG is produced, add an entry for
> +			 * the JPEG stream.
> +			 *
> +			 * \todo Wire the JPEG encoder to query the supported
> +			 * sizes provided a list of formats it can encode.
> +			 *
> +			 * \todo Support JPEG streams produced by the Camera
> +			 * natively.
> +			 */
> +			if (androidFormat == HAL_PIXEL_FORMAT_YCbCr_420_888) {
> +				streamConfigurations_.push_back(
> +					{ res, HAL_PIXEL_FORMAT_BLOB });
> +				maxJpegSize = std::max(maxJpegSize, res);
> +			}
> +		}
> +
> +		/*
> +		 * \todo Calculate the maximum JPEG buffer size by asking the
> +		 * encoder giving the maximum frame size required.
> +		 */
> +		maxJpegBufferSize_ = maxJpegSize.width * maxJpegSize.height * 1.5;
> +	}
> +
> +	LOG(HAL, Debug) << "Collected stream configuration map: ";
> +	for (const auto &entry : streamConfigurations_)
> +		LOG(HAL, Debug) << "{ " << entry.resolution.toString() << " - "
> +				<< utils::hex(entry.androidFormat) << " }";
> +
> +	return 0;
> +}
> +
> +int CameraCapabilities::initializeStaticMetadata()
> +{
> +	staticMetadata_ = std::make_unique<CameraMetadata>(64, 1024);
> +	if (!staticMetadata_->isValid()) {
> +		LOG(HAL, Error) << "Failed to allocate static metadata";
> +		staticMetadata_.reset();
> +		return -EINVAL;
> +	}
> +
> +	const ControlInfoMap &controlsInfo = camera_->controls();
> +	const ControlList &properties = camera_->properties();
> +
> +	/* Color correction static metadata. */
> +	{
> +		std::vector<uint8_t> data;
> +		data.reserve(3);
> +		const auto &infoMap = controlsInfo.find(&controls::draft::ColorCorrectionAberrationMode);
> +		if (infoMap != controlsInfo.end()) {
> +			for (const auto &value : infoMap->second.values())
> +				data.push_back(value.get<int32_t>());
> +		} else {
> +			data.push_back(ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF);
> +		}
> +		staticMetadata_->addEntry(ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
> +					  data);
> +	}
> +
> +	/* Control static metadata. */
> +	std::vector<uint8_t> aeAvailableAntiBandingModes = {
> +		ANDROID_CONTROL_AE_ANTIBANDING_MODE_OFF,
> +		ANDROID_CONTROL_AE_ANTIBANDING_MODE_50HZ,
> +		ANDROID_CONTROL_AE_ANTIBANDING_MODE_60HZ,
> +		ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO,
> +	};
> +	staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,
> +				  aeAvailableAntiBandingModes);
> +
> +	std::vector<uint8_t> aeAvailableModes = {
> +		ANDROID_CONTROL_AE_MODE_ON,
> +	};
> +	staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_MODES,
> +				  aeAvailableModes);
> +
> +	int64_t minFrameDurationNsec = -1;
> +	int64_t maxFrameDurationNsec = -1;
> +	const auto frameDurationsInfo = controlsInfo.find(&controls::FrameDurationLimits);
> +	if (frameDurationsInfo != controlsInfo.end()) {
> +		minFrameDurationNsec = frameDurationsInfo->second.min().get<int64_t>() * 1000;
> +		maxFrameDurationNsec = frameDurationsInfo->second.max().get<int64_t>() * 1000;
> +
> +		/*
> +		 * Adjust the minimum frame duration to comply with Android
> +		 * requirements. The camera service mandates all preview/record
> +		 * streams to have a minimum frame duration < 33,366 milliseconds
> +		 * (see MAX_PREVIEW_RECORD_DURATION_NS in the camera service
> +		 * implementation).
> +		 *
> +		 * If we're close enough (+ 500 useconds) to that value, round
> +		 * the minimum frame duration of the camera to an accepted
> +		 * value.
> +		 */
> +		static constexpr int64_t MAX_PREVIEW_RECORD_DURATION_NS = 1e9 / 29.97;
> +		if (minFrameDurationNsec > MAX_PREVIEW_RECORD_DURATION_NS &&
> +		    minFrameDurationNsec < MAX_PREVIEW_RECORD_DURATION_NS + 500000)
> +			minFrameDurationNsec = MAX_PREVIEW_RECORD_DURATION_NS - 1000;
> +
> +		/*
> +		 * The AE routine frame rate limits are computed using the frame
> +		 * duration limits, as libcamera clips the AE routine to the
> +		 * frame durations.
> +		 */
> +		int32_t maxFps = std::round(1e9 / minFrameDurationNsec);
> +		int32_t minFps = std::round(1e9 / maxFrameDurationNsec);
> +		minFps = std::max(1, minFps);
> +
> +		/*
> +		 * Force rounding errors so that we have the proper frame
> +		 * durations for when we reuse these variables later
> +		 */
> +		minFrameDurationNsec = 1e9 / maxFps;
> +		maxFrameDurationNsec = 1e9 / minFps;
> +
> +		/*
> +		 * Register to the camera service {min, max} and {max, max}
> +		 * intervals as requested by the metadata documentation.
> +		 */
> +		int32_t availableAeFpsTarget[] = {
> +			minFps, maxFps, maxFps, maxFps
> +		};
> +		staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> +					  availableAeFpsTarget);
> +	}
> +
> +	std::vector<int32_t> aeCompensationRange = {
> +		0, 0,
> +	};
> +	staticMetadata_->addEntry(ANDROID_CONTROL_AE_COMPENSATION_RANGE,
> +				  aeCompensationRange);
> +
> +	const camera_metadata_rational_t aeCompensationStep[] = {
> +		{ 0, 1 }
> +	};
> +	staticMetadata_->addEntry(ANDROID_CONTROL_AE_COMPENSATION_STEP,
> +				  aeCompensationStep);
> +
> +	std::vector<uint8_t> availableAfModes = {
> +		ANDROID_CONTROL_AF_MODE_OFF,
> +	};
> +	staticMetadata_->addEntry(ANDROID_CONTROL_AF_AVAILABLE_MODES,
> +				  availableAfModes);
> +
> +	std::vector<uint8_t> availableEffects = {
> +		ANDROID_CONTROL_EFFECT_MODE_OFF,
> +	};
> +	staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_EFFECTS,
> +				  availableEffects);
> +
> +	std::vector<uint8_t> availableSceneModes = {
> +		ANDROID_CONTROL_SCENE_MODE_DISABLED,
> +	};
> +	staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_SCENE_MODES,
> +				  availableSceneModes);
> +
> +	std::vector<uint8_t> availableStabilizationModes = {
> +		ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF,
> +	};
> +	staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
> +				  availableStabilizationModes);
> +
> +	/*
> +	 * \todo Inspect the Camera capabilities to report the available
> +	 * AWB modes. Default to AUTO as CTS tests require it.
> +	 */
> +	std::vector<uint8_t> availableAwbModes = {
> +		ANDROID_CONTROL_AWB_MODE_AUTO,
> +	};
> +	staticMetadata_->addEntry(ANDROID_CONTROL_AWB_AVAILABLE_MODES,
> +				  availableAwbModes);
> +
> +	std::vector<int32_t> availableMaxRegions = {
> +		0, 0, 0,
> +	};
> +	staticMetadata_->addEntry(ANDROID_CONTROL_MAX_REGIONS,
> +				  availableMaxRegions);
> +
> +	std::vector<uint8_t> sceneModesOverride = {
> +		ANDROID_CONTROL_AE_MODE_ON,
> +		ANDROID_CONTROL_AWB_MODE_AUTO,
> +		ANDROID_CONTROL_AF_MODE_OFF,
> +	};
> +	staticMetadata_->addEntry(ANDROID_CONTROL_SCENE_MODE_OVERRIDES,
> +				  sceneModesOverride);
> +
> +	uint8_t aeLockAvailable = ANDROID_CONTROL_AE_LOCK_AVAILABLE_FALSE;
> +	staticMetadata_->addEntry(ANDROID_CONTROL_AE_LOCK_AVAILABLE,
> +				  aeLockAvailable);
> +
> +	uint8_t awbLockAvailable = ANDROID_CONTROL_AWB_LOCK_AVAILABLE_FALSE;
> +	staticMetadata_->addEntry(ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
> +				  awbLockAvailable);
> +
> +	char availableControlModes = ANDROID_CONTROL_MODE_AUTO;
> +	staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_MODES,
> +				  availableControlModes);
> +
> +	/* JPEG static metadata. */
> +
> +	/*
> +	 * Create the list of supported thumbnail sizes by inspecting the
> +	 * available JPEG resolutions collected in streamConfigurations_ and
> +	 * generate one entry for each aspect ratio.
> +	 *
> +	 * The JPEG thumbnailer can freely scale, so pick an arbitrary
> +	 * (160, 160) size as the bounding rectangle, which is then cropped to
> +	 * the different supported aspect ratios.
> +	 */
> +	constexpr Size maxJpegThumbnail(160, 160);
> +	std::vector<Size> thumbnailSizes;
> +	thumbnailSizes.push_back({ 0, 0 });
> +	for (const auto &entry : streamConfigurations_) {
> +		if (entry.androidFormat != HAL_PIXEL_FORMAT_BLOB)
> +			continue;
> +
> +		Size thumbnailSize = maxJpegThumbnail
> +				     .boundedToAspectRatio({ entry.resolution.width,
> +							     entry.resolution.height });
> +		thumbnailSizes.push_back(thumbnailSize);
> +	}
> +
> +	std::sort(thumbnailSizes.begin(), thumbnailSizes.end());
> +	auto last = std::unique(thumbnailSizes.begin(), thumbnailSizes.end());
> +	thumbnailSizes.erase(last, thumbnailSizes.end());
> +
> +	/* Transform sizes in to a list of integers that can be consumed. */
> +	std::vector<int32_t> thumbnailEntries;
> +	thumbnailEntries.reserve(thumbnailSizes.size() * 2);
> +	for (const auto &size : thumbnailSizes) {
> +		thumbnailEntries.push_back(size.width);
> +		thumbnailEntries.push_back(size.height);
> +	}
> +	staticMetadata_->addEntry(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,
> +				  thumbnailEntries);
> +
> +	staticMetadata_->addEntry(ANDROID_JPEG_MAX_SIZE, maxJpegBufferSize_);
> +
> +	/* Sensor static metadata. */
> +	std::array<int32_t, 2> pixelArraySize;
> +	{
> +		const Size &size = properties.get(properties::PixelArraySize);
> +		pixelArraySize[0] = size.width;
> +		pixelArraySize[1] = size.height;
> +		staticMetadata_->addEntry(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
> +					  pixelArraySize);
> +	}
> +
> +	if (properties.contains(properties::UnitCellSize)) {
> +		const Size &cellSize = properties.get<Size>(properties::UnitCellSize);
> +		std::array<float, 2> physicalSize{
> +			cellSize.width * pixelArraySize[0] / 1e6f,
> +			cellSize.height * pixelArraySize[1] / 1e6f
> +		};
> +		staticMetadata_->addEntry(ANDROID_SENSOR_INFO_PHYSICAL_SIZE,
> +					  physicalSize);
> +	}
> +
> +	{
> +		const Span<const Rectangle> &rects =
> +			properties.get(properties::PixelArrayActiveAreas);
> +		std::vector<int32_t> data{
> +			static_cast<int32_t>(rects[0].x),
> +			static_cast<int32_t>(rects[0].y),
> +			static_cast<int32_t>(rects[0].width),
> +			static_cast<int32_t>(rects[0].height),
> +		};
> +		staticMetadata_->addEntry(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
> +					  data);
> +	}
> +
> +	int32_t sensitivityRange[] = {
> +		32, 2400,
> +	};
> +	staticMetadata_->addEntry(ANDROID_SENSOR_INFO_SENSITIVITY_RANGE,
> +				  sensitivityRange);
> +
> +	/* Report the color filter arrangement if the camera reports it. */
> +	if (properties.contains(properties::draft::ColorFilterArrangement)) {
> +		uint8_t filterArr = properties.get(properties::draft::ColorFilterArrangement);
> +		staticMetadata_->addEntry(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,
> +					  filterArr);
> +	}
> +
> +	const auto &exposureInfo = controlsInfo.find(&controls::ExposureTime);
> +	if (exposureInfo != controlsInfo.end()) {
> +		int64_t exposureTimeRange[2] = {
> +			exposureInfo->second.min().get<int32_t>() * 1000LL,
> +			exposureInfo->second.max().get<int32_t>() * 1000LL,
> +		};
> +		staticMetadata_->addEntry(ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE,
> +					  exposureTimeRange, 2);
> +	}
> +
> +	staticMetadata_->addEntry(ANDROID_SENSOR_ORIENTATION, orientation_);
> +
> +	std::vector<int32_t> testPatternModes = {
> +		ANDROID_SENSOR_TEST_PATTERN_MODE_OFF
> +	};
> +	const auto &testPatternsInfo =
> +		controlsInfo.find(&controls::draft::TestPatternMode);
> +	if (testPatternsInfo != controlsInfo.end()) {
> +		const auto &values = testPatternsInfo->second.values();
> +		ASSERT(!values.empty());
> +		for (const auto &value : values) {
> +			switch (value.get<int32_t>()) {
> +			case controls::draft::TestPatternModeOff:
> +				/*
> +				 * ANDROID_SENSOR_TEST_PATTERN_MODE_OFF is
> +				 * already in testPatternModes.
> +				 */
> +				break;
> +
> +			case controls::draft::TestPatternModeSolidColor:
> +				testPatternModes.push_back(
> +					ANDROID_SENSOR_TEST_PATTERN_MODE_SOLID_COLOR);
> +				break;
> +
> +			case controls::draft::TestPatternModeColorBars:
> +				testPatternModes.push_back(
> +					ANDROID_SENSOR_TEST_PATTERN_MODE_COLOR_BARS);
> +				break;
> +
> +			case controls::draft::TestPatternModeColorBarsFadeToGray:
> +				testPatternModes.push_back(
> +					ANDROID_SENSOR_TEST_PATTERN_MODE_COLOR_BARS_FADE_TO_GRAY);
> +				break;
> +
> +			case controls::draft::TestPatternModePn9:
> +				testPatternModes.push_back(
> +					ANDROID_SENSOR_TEST_PATTERN_MODE_PN9);
> +				break;
> +
> +			case controls::draft::TestPatternModeCustom1:
> +				/* We don't support this yet. */
> +				break;
> +
> +			default:
> +				LOG(HAL, Error) << "Unknown test pattern mode: "
> +						<< value.get<int32_t>();
> +				continue;
> +			}
> +		}
> +	}
> +	staticMetadata_->addEntry(ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES,
> +				  testPatternModes);
> +
> +	uint8_t timestampSource = ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN;
> +	staticMetadata_->addEntry(ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE,
> +				  timestampSource);
> +
> +	if (maxFrameDurationNsec > 0)
> +		staticMetadata_->addEntry(ANDROID_SENSOR_INFO_MAX_FRAME_DURATION,
> +					  maxFrameDurationNsec);
> +
> +	/* Statistics static metadata. */
> +	uint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
> +	staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
> +				  faceDetectMode);
> +
> +	int32_t maxFaceCount = 0;
> +	staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
> +				  maxFaceCount);
> +
> +	{
> +		std::vector<uint8_t> data;
> +		data.reserve(2);
> +		const auto &infoMap = controlsInfo.find(&controls::draft::LensShadingMapMode);
> +		if (infoMap != controlsInfo.end()) {
> +			for (const auto &value : infoMap->second.values())
> +				data.push_back(value.get<int32_t>());
> +		} else {
> +			data.push_back(ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF);
> +		}
> +		staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES,
> +					  data);
> +	}
> +
> +	/* Sync static metadata. */
> +	int32_t maxLatency = ANDROID_SYNC_MAX_LATENCY_UNKNOWN;
> +	staticMetadata_->addEntry(ANDROID_SYNC_MAX_LATENCY, maxLatency);
> +
> +	/* Flash static metadata. */
> +	char flashAvailable = ANDROID_FLASH_INFO_AVAILABLE_FALSE;
> +	staticMetadata_->addEntry(ANDROID_FLASH_INFO_AVAILABLE,
> +				  flashAvailable);
> +
> +	/* Lens static metadata. */
> +	std::vector<float> lensApertures = {
> +		2.53 / 100,
> +	};
> +	staticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_APERTURES,
> +				  lensApertures);
> +
> +	uint8_t lensFacing;
> +	switch (facing_) {
> +	default:
> +	case CAMERA_FACING_FRONT:
> +		lensFacing = ANDROID_LENS_FACING_FRONT;
> +		break;
> +	case CAMERA_FACING_BACK:
> +		lensFacing = ANDROID_LENS_FACING_BACK;
> +		break;
> +	case CAMERA_FACING_EXTERNAL:
> +		lensFacing = ANDROID_LENS_FACING_EXTERNAL;
> +		break;
> +	}
> +	staticMetadata_->addEntry(ANDROID_LENS_FACING, lensFacing);
> +
> +	std::vector<float> lensFocalLengths = {
> +		1,
> +	};
> +	staticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS,
> +				  lensFocalLengths);
> +
> +	std::vector<uint8_t> opticalStabilizations = {
> +		ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF,
> +	};
> +	staticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
> +				  opticalStabilizations);
> +
> +	float hypeFocalDistance = 0;
> +	staticMetadata_->addEntry(ANDROID_LENS_INFO_HYPERFOCAL_DISTANCE,
> +				  hypeFocalDistance);
> +
> +	float minFocusDistance = 0;
> +	staticMetadata_->addEntry(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
> +				  minFocusDistance);
> +
> +	/* Noise reduction modes. */
> +	{
> +		std::vector<uint8_t> data;
> +		data.reserve(5);
> +		const auto &infoMap = controlsInfo.find(&controls::draft::NoiseReductionMode);
> +		if (infoMap != controlsInfo.end()) {
> +			for (const auto &value : infoMap->second.values())
> +				data.push_back(value.get<int32_t>());
> +		} else {
> +			data.push_back(ANDROID_NOISE_REDUCTION_MODE_OFF);
> +		}
> +		staticMetadata_->addEntry(ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
> +					  data);
> +	}
> +
> +	/* Scaler static metadata. */
> +
> +	/*
> +	 * \todo The digital zoom factor is a property that depends on the
> +	 * desired output configuration and the sensor frame size input to the
> +	 * ISP. This information is not available to the Android HAL, not at
> +	 * initialization time at least.
> +	 *
> +	 * As a workaround rely on pipeline handlers initializing the
> +	 * ScalerCrop control with the camera default configuration and use the
> +	 * maximum and minimum crop rectangles to calculate the digital zoom
> +	 * factor.
> +	 */
> +	float maxZoom = 1.0f;
> +	const auto scalerCrop = controlsInfo.find(&controls::ScalerCrop);
> +	if (scalerCrop != controlsInfo.end()) {
> +		Rectangle min = scalerCrop->second.min().get<Rectangle>();
> +		Rectangle max = scalerCrop->second.max().get<Rectangle>();
> +		maxZoom = std::min(1.0f * max.width / min.width,
> +				   1.0f * max.height / min.height);
> +	}
> +	staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
> +				  maxZoom);
> +
> +	std::vector<uint32_t> availableStreamConfigurations;
> +	availableStreamConfigurations.reserve(streamConfigurations_.size() * 4);
> +	for (const auto &entry : streamConfigurations_) {
> +		availableStreamConfigurations.push_back(entry.androidFormat);
> +		availableStreamConfigurations.push_back(entry.resolution.width);
> +		availableStreamConfigurations.push_back(entry.resolution.height);
> +		availableStreamConfigurations.push_back(
> +			ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT);
> +	}
> +	staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
> +				  availableStreamConfigurations);
> +
> +	std::vector<int64_t> availableStallDurations = {
> +		ANDROID_SCALER_AVAILABLE_FORMATS_BLOB, 2560, 1920, 33333333,
> +	};
> +	staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,
> +				  availableStallDurations);
> +
> +	/* Use the minimum frame duration for all the YUV/RGB formats. */
> +	if (minFrameDurationNsec > 0) {
> +		std::vector<int64_t> minFrameDurations;
> +		minFrameDurations.reserve(streamConfigurations_.size() * 4);
> +		for (const auto &entry : streamConfigurations_) {
> +			minFrameDurations.push_back(entry.androidFormat);
> +			minFrameDurations.push_back(entry.resolution.width);
> +			minFrameDurations.push_back(entry.resolution.height);
> +			minFrameDurations.push_back(minFrameDurationNsec);
> +		}
> +		staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
> +					  minFrameDurations);
> +	}
> +
> +	uint8_t croppingType = ANDROID_SCALER_CROPPING_TYPE_CENTER_ONLY;
> +	staticMetadata_->addEntry(ANDROID_SCALER_CROPPING_TYPE, croppingType);
> +
> +	/* Info static metadata. */
> +	uint8_t supportedHWLevel = ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
> +	staticMetadata_->addEntry(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
> +				  supportedHWLevel);
> +
> +	/* Request static metadata. */
> +	int32_t partialResultCount = 1;
> +	staticMetadata_->addEntry(ANDROID_REQUEST_PARTIAL_RESULT_COUNT,
> +				  partialResultCount);
> +
> +	{
> +		/* Default the value to 2 if not reported by the camera. */
> +		uint8_t maxPipelineDepth = 2;
> +		const auto &infoMap = controlsInfo.find(&controls::draft::PipelineDepth);
> +		if (infoMap != controlsInfo.end())
> +			maxPipelineDepth = infoMap->second.max().get<int32_t>();
> +		staticMetadata_->addEntry(ANDROID_REQUEST_PIPELINE_MAX_DEPTH,
> +					  maxPipelineDepth);
> +	}
> +
> +	/* LIMITED does not support reprocessing. */
> +	uint32_t maxNumInputStreams = 0;
> +	staticMetadata_->addEntry(ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,
> +				  maxNumInputStreams);
> +
> +	std::vector<uint8_t> availableCapabilities = {
> +		ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE,
> +	};
> +
> +	/* Report if camera supports RAW. */
> +	bool rawStreamAvailable = false;
> +	std::unique_ptr<CameraConfiguration> cameraConfig =
> +		camera_->generateConfiguration({ StreamRole::Raw });
> +	if (cameraConfig && !cameraConfig->empty()) {
> +		const PixelFormatInfo &info =
> +			PixelFormatInfo::info(cameraConfig->at(0).pixelFormat);
> +		/* Only advertise RAW support if RAW16 is possible. */
> +		if (info.colourEncoding == PixelFormatInfo::ColourEncodingRAW &&
> +		    info.bitsPerPixel == 16) {
> +			rawStreamAvailable = true;
> +			availableCapabilities.push_back(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW);
> +		}
> +	}
> +
> +	/* Number of { RAW, YUV, JPEG } supported output streams */
> +	int32_t numOutStreams[] = { rawStreamAvailable, 2, 1 };
> +	staticMetadata_->addEntry(ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,
> +				  numOutStreams);
> +
> +	staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
> +				  availableCapabilities);
> +
> +	std::vector<int32_t> availableCharacteristicsKeys = {
> +		ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
> +		ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,
> +		ANDROID_CONTROL_AE_AVAILABLE_MODES,
> +		ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> +		ANDROID_CONTROL_AE_COMPENSATION_RANGE,
> +		ANDROID_CONTROL_AE_COMPENSATION_STEP,
> +		ANDROID_CONTROL_AE_LOCK_AVAILABLE,
> +		ANDROID_CONTROL_AF_AVAILABLE_MODES,
> +		ANDROID_CONTROL_AVAILABLE_EFFECTS,
> +		ANDROID_CONTROL_AVAILABLE_MODES,
> +		ANDROID_CONTROL_AVAILABLE_SCENE_MODES,
> +		ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
> +		ANDROID_CONTROL_AWB_AVAILABLE_MODES,
> +		ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
> +		ANDROID_CONTROL_MAX_REGIONS,
> +		ANDROID_CONTROL_SCENE_MODE_OVERRIDES,
> +		ANDROID_FLASH_INFO_AVAILABLE,
> +		ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
> +		ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,
> +		ANDROID_JPEG_MAX_SIZE,
> +		ANDROID_LENS_FACING,
> +		ANDROID_LENS_INFO_AVAILABLE_APERTURES,
> +		ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS,
> +		ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
> +		ANDROID_LENS_INFO_HYPERFOCAL_DISTANCE,
> +		ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
> +		ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
> +		ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
> +		ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,
> +		ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,
> +		ANDROID_REQUEST_PARTIAL_RESULT_COUNT,
> +		ANDROID_REQUEST_PIPELINE_MAX_DEPTH,
> +		ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
> +		ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
> +		ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,
> +		ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
> +		ANDROID_SCALER_CROPPING_TYPE,
> +		ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES,
> +		ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
> +		ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,
> +		ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE,
> +		ANDROID_SENSOR_INFO_MAX_FRAME_DURATION,
> +		ANDROID_SENSOR_INFO_PHYSICAL_SIZE,
> +		ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
> +		ANDROID_SENSOR_INFO_SENSITIVITY_RANGE,
> +		ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE,
> +		ANDROID_SENSOR_ORIENTATION,
> +		ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
> +		ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
> +		ANDROID_SYNC_MAX_LATENCY,
> +	};
> +	staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS,
> +				  availableCharacteristicsKeys);
> +
> +	std::vector<int32_t> availableRequestKeys = {
> +		ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
> +		ANDROID_CONTROL_AE_ANTIBANDING_MODE,
> +		ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
> +		ANDROID_CONTROL_AE_LOCK,
> +		ANDROID_CONTROL_AE_MODE,
> +		ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
> +		ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
> +		ANDROID_CONTROL_AF_MODE,
> +		ANDROID_CONTROL_AF_TRIGGER,
> +		ANDROID_CONTROL_AWB_LOCK,
> +		ANDROID_CONTROL_AWB_MODE,
> +		ANDROID_CONTROL_CAPTURE_INTENT,
> +		ANDROID_CONTROL_EFFECT_MODE,
> +		ANDROID_CONTROL_MODE,
> +		ANDROID_CONTROL_SCENE_MODE,
> +		ANDROID_CONTROL_VIDEO_STABILIZATION_MODE,
> +		ANDROID_FLASH_MODE,
> +		ANDROID_JPEG_ORIENTATION,
> +		ANDROID_JPEG_QUALITY,
> +		ANDROID_JPEG_THUMBNAIL_QUALITY,
> +		ANDROID_JPEG_THUMBNAIL_SIZE,
> +		ANDROID_LENS_APERTURE,
> +		ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
> +		ANDROID_NOISE_REDUCTION_MODE,
> +		ANDROID_SCALER_CROP_REGION,
> +		ANDROID_STATISTICS_FACE_DETECT_MODE
> +	};
> +	staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS,
> +				  availableRequestKeys);
> +
> +	std::vector<int32_t> availableResultKeys = {
> +		ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
> +		ANDROID_CONTROL_AE_ANTIBANDING_MODE,
> +		ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
> +		ANDROID_CONTROL_AE_LOCK,
> +		ANDROID_CONTROL_AE_MODE,
> +		ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
> +		ANDROID_CONTROL_AE_STATE,
> +		ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
> +		ANDROID_CONTROL_AF_MODE,
> +		ANDROID_CONTROL_AF_STATE,
> +		ANDROID_CONTROL_AF_TRIGGER,
> +		ANDROID_CONTROL_AWB_LOCK,
> +		ANDROID_CONTROL_AWB_MODE,
> +		ANDROID_CONTROL_AWB_STATE,
> +		ANDROID_CONTROL_CAPTURE_INTENT,
> +		ANDROID_CONTROL_EFFECT_MODE,
> +		ANDROID_CONTROL_MODE,
> +		ANDROID_CONTROL_SCENE_MODE,
> +		ANDROID_CONTROL_VIDEO_STABILIZATION_MODE,
> +		ANDROID_FLASH_MODE,
> +		ANDROID_FLASH_STATE,
> +		ANDROID_JPEG_GPS_COORDINATES,
> +		ANDROID_JPEG_GPS_PROCESSING_METHOD,
> +		ANDROID_JPEG_GPS_TIMESTAMP,
> +		ANDROID_JPEG_ORIENTATION,
> +		ANDROID_JPEG_QUALITY,
> +		ANDROID_JPEG_SIZE,
> +		ANDROID_JPEG_THUMBNAIL_QUALITY,
> +		ANDROID_JPEG_THUMBNAIL_SIZE,
> +		ANDROID_LENS_APERTURE,
> +		ANDROID_LENS_FOCAL_LENGTH,
> +		ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
> +		ANDROID_LENS_STATE,
> +		ANDROID_NOISE_REDUCTION_MODE,
> +		ANDROID_REQUEST_PIPELINE_DEPTH,
> +		ANDROID_SCALER_CROP_REGION,
> +		ANDROID_SENSOR_EXPOSURE_TIME,
> +		ANDROID_SENSOR_FRAME_DURATION,
> +		ANDROID_SENSOR_ROLLING_SHUTTER_SKEW,
> +		ANDROID_SENSOR_TEST_PATTERN_MODE,
> +		ANDROID_SENSOR_TIMESTAMP,
> +		ANDROID_STATISTICS_FACE_DETECT_MODE,
> +		ANDROID_STATISTICS_LENS_SHADING_MAP_MODE,
> +		ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE,
> +		ANDROID_STATISTICS_SCENE_FLICKER,
> +	};
> +	staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_RESULT_KEYS,
> +				  availableResultKeys);
> +
> +	if (!staticMetadata_->isValid()) {
> +		LOG(HAL, Error) << "Failed to construct static metadata";
> +		staticMetadata_.reset();
> +		return -EINVAL;
> +	}
> +
> +	if (staticMetadata_->resized()) {
> +		auto [entryCount, dataCount] = staticMetadata_->usage();
> +		LOG(HAL, Info)
> +			<< "Static metadata resized: " << entryCount
> +			<< " entries and " << dataCount << " bytes used";
> +	}
> +
> +	return 0;
> +}
> +
> +/* Translate Android format code to libcamera pixel format. */
> +PixelFormat CameraCapabilities::toPixelFormat(int format) const
> +{
> +	auto it = formatsMap_.find(format);
> +	if (it == formatsMap_.end()) {
> +		LOG(HAL, Error) << "Requested format " << utils::hex(format)
> +				<< " not supported";
> +		return PixelFormat();
> +	}
> +
> +	return it->second;
> +}
> +
> +std::unique_ptr<CameraMetadata> CameraCapabilities::requestTemplatePreview() const
> +{
> +	/*
> +	 * \todo Keep this in sync with the actual number of entries.
> +	 * Currently: 20 entries, 35 bytes
> +	 */
> +	auto requestTemplate = std::make_unique<CameraMetadata>(21, 36);
> +	if (!requestTemplate->isValid()) {
> +		return nullptr;
> +	}
> +
> +	/* Get the FPS range registered in the static metadata. */
> +	camera_metadata_ro_entry_t entry;
> +	bool found = staticMetadata_->getEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> +					       &entry);
> +	if (!found) {
> +		LOG(HAL, Error) << "Cannot create capture template without FPS range";
> +		return nullptr;
> +	}
> +
> +	/*
> +	 * Assume the AE_AVAILABLE_TARGET_FPS_RANGE static metadata
> +	 * has been assembled as {{min, max} {max, max}}.
> +	 */
> +	requestTemplate->addEntry(ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
> +				  entry.data.i32, 2);
> +
> +	uint8_t aeMode = ANDROID_CONTROL_AE_MODE_ON;
> +	requestTemplate->addEntry(ANDROID_CONTROL_AE_MODE, aeMode);
> +
> +	int32_t aeExposureCompensation = 0;
> +	requestTemplate->addEntry(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
> +				  aeExposureCompensation);
> +
> +	uint8_t aePrecaptureTrigger = ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_IDLE;
> +	requestTemplate->addEntry(ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
> +				  aePrecaptureTrigger);
> +
> +	uint8_t aeLock = ANDROID_CONTROL_AE_LOCK_OFF;
> +	requestTemplate->addEntry(ANDROID_CONTROL_AE_LOCK, aeLock);
> +
> +	uint8_t aeAntibandingMode = ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO;
> +	requestTemplate->addEntry(ANDROID_CONTROL_AE_ANTIBANDING_MODE,
> +				  aeAntibandingMode);
> +
> +	uint8_t afMode = ANDROID_CONTROL_AF_MODE_OFF;
> +	requestTemplate->addEntry(ANDROID_CONTROL_AF_MODE, afMode);
> +
> +	uint8_t afTrigger = ANDROID_CONTROL_AF_TRIGGER_IDLE;
> +	requestTemplate->addEntry(ANDROID_CONTROL_AF_TRIGGER, afTrigger);
> +
> +	uint8_t awbMode = ANDROID_CONTROL_AWB_MODE_AUTO;
> +	requestTemplate->addEntry(ANDROID_CONTROL_AWB_MODE, awbMode);
> +
> +	uint8_t awbLock = ANDROID_CONTROL_AWB_LOCK_OFF;
> +	requestTemplate->addEntry(ANDROID_CONTROL_AWB_LOCK, awbLock);
> +
> +	uint8_t flashMode = ANDROID_FLASH_MODE_OFF;
> +	requestTemplate->addEntry(ANDROID_FLASH_MODE, flashMode);
> +
> +	uint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
> +	requestTemplate->addEntry(ANDROID_STATISTICS_FACE_DETECT_MODE,
> +				  faceDetectMode);
> +
> +	uint8_t noiseReduction = ANDROID_NOISE_REDUCTION_MODE_OFF;
> +	requestTemplate->addEntry(ANDROID_NOISE_REDUCTION_MODE,
> +				  noiseReduction);
> +
> +	uint8_t aberrationMode = ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF;
> +	requestTemplate->addEntry(ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
> +				  aberrationMode);
> +
> +	uint8_t controlMode = ANDROID_CONTROL_MODE_AUTO;
> +	requestTemplate->addEntry(ANDROID_CONTROL_MODE, controlMode);
> +
> +	float lensAperture = 2.53 / 100;
> +	requestTemplate->addEntry(ANDROID_LENS_APERTURE, lensAperture);
> +
> +	uint8_t opticalStabilization = ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF;
> +	requestTemplate->addEntry(ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
> +				  opticalStabilization);
> +
> +	uint8_t captureIntent = ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW;
> +	requestTemplate->addEntry(ANDROID_CONTROL_CAPTURE_INTENT,
> +				  captureIntent);
> +
> +	return requestTemplate;
> +}
> +
> +std::unique_ptr<CameraMetadata> CameraCapabilities::requestTemplateVideo() const
> +{
> +	std::unique_ptr<CameraMetadata> previewTemplate = requestTemplatePreview();
> +	if (!previewTemplate)
> +		return nullptr;
> +
> +	/*
> +	 * The video template requires a fixed FPS range. Everything else
> +	 * stays the same as the preview template.
> +	 */
> +	camera_metadata_ro_entry_t entry;
> +	staticMetadata_->getEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> +				  &entry);
> +
> +	/*
> +	 * Assume the AE_AVAILABLE_TARGET_FPS_RANGE static metadata
> +	 * has been assembled as {{min, max} {max, max}}.
> +	 */
> +	previewTemplate->updateEntry(ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
> +				     entry.data.i32 + 2, 2);
> +
> +	return previewTemplate;
> +}
> diff --git a/src/android/camera_capabilities.h b/src/android/camera_capabilities.h
> new file mode 100644
> index 000000000000..f511607bbd90
> --- /dev/null
> +++ b/src/android/camera_capabilities.h
> @@ -0,0 +1,65 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2021, Google Inc.
> + *
> + * camera_capabilities.h - Camera static properties manager
> + */
> +#ifndef __ANDROID_CAMERA_CAPABILITIES_H__
> +#define __ANDROID_CAMERA_CAPABILITIES_H__
> +
> +#include <map>
> +#include <memory>
> +#include <vector>
> +
> +#include <libcamera/camera.h>
> +#include <libcamera/class.h>
> +#include <libcamera/formats.h>
> +#include <libcamera/geometry.h>
> +
> +#include "camera_metadata.h"
> +
> +class CameraCapabilities
> +{
> +public:
> +	CameraCapabilities() = default;
> +
> +	int initialize(std::shared_ptr<libcamera::Camera> camera,
> +		       int orientation, int facing);
> +
> +	CameraMetadata *staticMetadata() const { return staticMetadata_.get(); }
> +	libcamera::PixelFormat toPixelFormat(int format) const;
> +	unsigned int maxJpegBufferSize() const { return maxJpegBufferSize_; }
> +
> +	std::unique_ptr<CameraMetadata> requestTemplatePreview() const;
> +	std::unique_ptr<CameraMetadata> requestTemplateVideo() const;
> +
> +private:
> +	LIBCAMERA_DISABLE_COPY_AND_MOVE(CameraCapabilities)
> +
> +	struct Camera3StreamConfiguration {
> +		libcamera::Size resolution;
> +		int androidFormat;
> +	};
> +
> +	std::vector<libcamera::Size>
> +	getYUVResolutions(libcamera::CameraConfiguration *cameraConfig,
> +			  const libcamera::PixelFormat &pixelFormat,
> +			  const std::vector<libcamera::Size> &resolutions);
> +	std::vector<libcamera::Size>
> +	getRawResolutions(const libcamera::PixelFormat &pixelFormat);
> +	int initializeStreamConfigurations();
> +
> +	int initializeStaticMetadata();
> +
> +	std::shared_ptr<libcamera::Camera> camera_;
> +
> +	int facing_;
> +	int orientation_;
> +
> +	std::vector<Camera3StreamConfiguration> streamConfigurations_;
> +	std::map<int, libcamera::PixelFormat> formatsMap_;
> +	std::unique_ptr<CameraMetadata> staticMetadata_;
> +	unsigned int maxJpegBufferSize_;
> +};
> +
> +#endif /* __ANDROID_CAMERA_CAPABILITIES_H__ */
> diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
> index 8c71fd0675d3..4bd125d7020a 100644
> --- a/src/android/camera_device.cpp
> +++ b/src/android/camera_device.cpp
> @@ -10,11 +10,8 @@
>  #include "camera_ops.h"
>  #include "post_processor.h"
>  
> -#include <array>
> -#include <cmath>
>  #include <fstream>
>  #include <sys/mman.h>
> -#include <tuple>
>  #include <unistd.h>
>  #include <vector>
>  
> @@ -23,7 +20,6 @@
>  #include <libcamera/formats.h>
>  #include <libcamera/property_ids.h>
>  
> -#include "libcamera/internal/formats.h"
>  #include "libcamera/internal/log.h"
>  #include "libcamera/internal/thread.h"
>  #include "libcamera/internal/utils.h"
> @@ -36,94 +32,6 @@ LOG_DECLARE_CATEGORY(HAL)
>  
>  namespace {
>  
> -/*
> - * \var camera3Resolutions
> - * \brief The list of image resolutions defined as mandatory to be supported by
> - * the Android Camera3 specification
> - */
> -const std::vector<Size> camera3Resolutions = {
> -	{ 320, 240 },
> -	{ 640, 480 },
> -	{ 1280, 720 },
> -	{ 1920, 1080 }
> -};
> -
> -/*
> - * \struct Camera3Format
> - * \brief Data associated with an Android format identifier
> - * \var libcameraFormats List of libcamera pixel formats compatible with the
> - * Android format
> - * \var name The human-readable representation of the Android format code
> - */
> -struct Camera3Format {
> -	std::vector<PixelFormat> libcameraFormats;
> -	bool mandatory;
> -	const char *name;
> -};
> -
> -/*
> - * \var camera3FormatsMap
> - * \brief Associate Android format code with ancillary data
> - */
> -const std::map<int, const Camera3Format> camera3FormatsMap = {
> -	{
> -		HAL_PIXEL_FORMAT_BLOB, {
> -			{ formats::MJPEG },
> -			true,
> -			"BLOB"
> -		}
> -	}, {
> -		HAL_PIXEL_FORMAT_YCbCr_420_888, {
> -			{ formats::NV12, formats::NV21 },
> -			true,
> -			"YCbCr_420_888"
> -		}
> -	}, {
> -		/*
> -		 * \todo Translate IMPLEMENTATION_DEFINED inspecting the gralloc
> -		 * usage flag. For now, copy the YCbCr_420 configuration.
> -		 */
> -		HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, {
> -			{ formats::NV12, formats::NV21 },
> -			true,
> -			"IMPLEMENTATION_DEFINED"
> -		}
> -	}, {
> -		HAL_PIXEL_FORMAT_RAW10, {
> -			{
> -				formats::SBGGR10_CSI2P,
> -				formats::SGBRG10_CSI2P,
> -				formats::SGRBG10_CSI2P,
> -				formats::SRGGB10_CSI2P
> -			},
> -			false,
> -			"RAW10"
> -		}
> -	}, {
> -		HAL_PIXEL_FORMAT_RAW12, {
> -			{
> -				formats::SBGGR12_CSI2P,
> -				formats::SGBRG12_CSI2P,
> -				formats::SGRBG12_CSI2P,
> -				formats::SRGGB12_CSI2P
> -			},
> -			false,
> -			"RAW12"
> -		}
> -	}, {
> -		HAL_PIXEL_FORMAT_RAW16, {
> -			{
> -				formats::SBGGR16,
> -				formats::SGBRG16,
> -				formats::SGRBG16,
> -				formats::SRGGB16
> -			},
> -			false,
> -			"RAW16"
> -		}
> -	},
> -};
> -
>  /*
>   * \struct Camera3StreamConfig
>   * \brief Data to store StreamConfiguration associated with camera3_stream(s)
> @@ -512,242 +420,7 @@ int CameraDevice::initialize(const CameraConfigData *cameraConfigData)
>  		orientation_ = 0;
>  	}
>  
> -	/* Acquire the camera and initialize available stream configurations. */
> -	int ret = camera_->acquire();
> -	if (ret) {
> -		LOG(HAL, Error) << "Failed to temporarily acquire the camera";
> -		return ret;
> -	}
> -
> -	ret = initializeStreamConfigurations();
> -	camera_->release();
> -	return ret;
> -}
> -
> -std::vector<Size> CameraDevice::getYUVResolutions(CameraConfiguration *cameraConfig,
> -						  const PixelFormat &pixelFormat,
> -						  const std::vector<Size> &resolutions)
> -{
> -	std::vector<Size> supportedResolutions;
> -
> -	StreamConfiguration &cfg = cameraConfig->at(0);
> -	for (const Size &res : resolutions) {
> -		cfg.pixelFormat = pixelFormat;
> -		cfg.size = res;
> -
> -		CameraConfiguration::Status status = cameraConfig->validate();
> -		if (status != CameraConfiguration::Valid) {
> -			LOG(HAL, Debug) << cfg.toString() << " not supported";
> -			continue;
> -		}
> -
> -		LOG(HAL, Debug) << cfg.toString() << " supported";
> -
> -		supportedResolutions.push_back(res);
> -	}
> -
> -	return supportedResolutions;
> -}
> -
> -std::vector<Size> CameraDevice::getRawResolutions(const libcamera::PixelFormat &pixelFormat)
> -{
> -	std::unique_ptr<CameraConfiguration> cameraConfig =
> -		camera_->generateConfiguration({ StreamRole::Raw });
> -	StreamConfiguration &cfg = cameraConfig->at(0);
> -	const StreamFormats &formats = cfg.formats();
> -	std::vector<Size> supportedResolutions = formats.sizes(pixelFormat);
> -
> -	return supportedResolutions;
> -}
> -
> -/*
> - * Initialize the format conversion map to translate from Android format
> - * identifier to libcamera pixel formats and fill in the list of supported
> - * stream configurations to be reported to the Android camera framework through
> - * the static stream configuration metadata.
> - */
> -int CameraDevice::initializeStreamConfigurations()
> -{
> -	/*
> -	 * Get the maximum output resolutions
> -	 * \todo Get this from the camera properties once defined
> -	 */
> -	std::unique_ptr<CameraConfiguration> cameraConfig =
> -		camera_->generateConfiguration({ StillCapture });
> -	if (!cameraConfig) {
> -		LOG(HAL, Error) << "Failed to get maximum resolution";
> -		return -EINVAL;
> -	}
> -	StreamConfiguration &cfg = cameraConfig->at(0);
> -
> -	/*
> -	 * \todo JPEG - Adjust the maximum available resolution by taking the
> -	 * JPEG encoder requirements into account (alignment and aspect ratio).
> -	 */
> -	const Size maxRes = cfg.size;
> -	LOG(HAL, Debug) << "Maximum supported resolution: " << maxRes.toString();
> -
> -	/*
> -	 * Build the list of supported image resolutions.
> -	 *
> -	 * The resolutions listed in camera3Resolution are mandatory to be
> -	 * supported, up to the camera maximum resolution.
> -	 *
> -	 * Augment the list by adding resolutions calculated from the camera
> -	 * maximum one.
> -	 */
> -	std::vector<Size> cameraResolutions;
> -	std::copy_if(camera3Resolutions.begin(), camera3Resolutions.end(),
> -		     std::back_inserter(cameraResolutions),
> -		     [&](const Size &res) { return res < maxRes; });
> -
> -	/*
> -	 * The Camera3 specification suggests adding 1/2 and 1/4 of the maximum
> -	 * resolution.
> -	 */
> -	for (unsigned int divider = 2;; divider <<= 1) {
> -		Size derivedSize{
> -			maxRes.width / divider,
> -			maxRes.height / divider,
> -		};
> -
> -		if (derivedSize.width < 320 ||
> -		    derivedSize.height < 240)
> -			break;
> -
> -		cameraResolutions.push_back(derivedSize);
> -	}
> -	cameraResolutions.push_back(maxRes);
> -
> -	/* Remove duplicated entries from the list of supported resolutions. */
> -	std::sort(cameraResolutions.begin(), cameraResolutions.end());
> -	auto last = std::unique(cameraResolutions.begin(), cameraResolutions.end());
> -	cameraResolutions.erase(last, cameraResolutions.end());
> -
> -	/*
> -	 * Build the list of supported camera formats.
> -	 *
> -	 * To each Android format a list of compatible libcamera formats is
> -	 * associated. The first libcamera format that tests successful is added
> -	 * to the format translation map used when configuring the streams.
> -	 * It is then tested against the list of supported camera resolutions to
> -	 * build the stream configuration map reported through the camera static
> -	 * metadata.
> -	 */
> -	Size maxJpegSize;
> -	for (const auto &format : camera3FormatsMap) {
> -		int androidFormat = format.first;
> -		const Camera3Format &camera3Format = format.second;
> -		const std::vector<PixelFormat> &libcameraFormats =
> -			camera3Format.libcameraFormats;
> -
> -		LOG(HAL, Debug) << "Trying to map Android format "
> -				<< camera3Format.name;
> -
> -		/*
> -		 * JPEG is always supported, either produced directly by the
> -		 * camera, or encoded in the HAL.
> -		 */
> -		if (androidFormat == HAL_PIXEL_FORMAT_BLOB) {
> -			formatsMap_[androidFormat] = formats::MJPEG;
> -			LOG(HAL, Debug) << "Mapped Android format "
> -					<< camera3Format.name << " to "
> -					<< formats::MJPEG.toString()
> -					<< " (fixed mapping)";
> -			continue;
> -		}
> -
> -		/*
> -		 * Test the libcamera formats that can produce images
> -		 * compatible with the format defined by Android.
> -		 */
> -		PixelFormat mappedFormat;
> -		for (const PixelFormat &pixelFormat : libcameraFormats) {
> -
> -			LOG(HAL, Debug) << "Testing " << pixelFormat.toString();
> -
> -			/*
> -			 * The stream configuration size can be adjusted,
> -			 * not the pixel format.
> -			 *
> -			 * \todo This could be simplified once all pipeline
> -			 * handlers will report the StreamFormats list of
> -			 * supported formats.
> -			 */
> -			cfg.pixelFormat = pixelFormat;
> -
> -			CameraConfiguration::Status status = cameraConfig->validate();
> -			if (status != CameraConfiguration::Invalid &&
> -			    cfg.pixelFormat == pixelFormat) {
> -				mappedFormat = pixelFormat;
> -				break;
> -			}
> -		}
> -
> -		if (!mappedFormat.isValid()) {
> -			/* If the format is not mandatory, skip it. */
> -			if (!camera3Format.mandatory)
> -				continue;
> -
> -			LOG(HAL, Error)
> -				<< "Failed to map mandatory Android format "
> -				<< camera3Format.name << " ("
> -				<< utils::hex(androidFormat) << "): aborting";
> -			return -EINVAL;
> -		}
> -
> -		/*
> -		 * Record the mapping and then proceed to generate the
> -		 * stream configurations map, by testing the image resolutions.
> -		 */
> -		formatsMap_[androidFormat] = mappedFormat;
> -		LOG(HAL, Debug) << "Mapped Android format "
> -				<< camera3Format.name << " to "
> -				<< mappedFormat.toString();
> -
> -		std::vector<Size> resolutions;
> -		const PixelFormatInfo &info = PixelFormatInfo::info(mappedFormat);
> -		if (info.colourEncoding == PixelFormatInfo::ColourEncodingRAW)
> -			resolutions = getRawResolutions(mappedFormat);
> -		else
> -			resolutions = getYUVResolutions(cameraConfig.get(),
> -							mappedFormat,
> -							cameraResolutions);
> -
> -		for (const Size &res : resolutions) {
> -			streamConfigurations_.push_back({ res, androidFormat });
> -
> -			/*
> -			 * If the format is HAL_PIXEL_FORMAT_YCbCr_420_888
> -			 * from which JPEG is produced, add an entry for
> -			 * the JPEG stream.
> -			 *
> -			 * \todo Wire the JPEG encoder to query the supported
> -			 * sizes provided a list of formats it can encode.
> -			 *
> -			 * \todo Support JPEG streams produced by the Camera
> -			 * natively.
> -			 */
> -			if (androidFormat == HAL_PIXEL_FORMAT_YCbCr_420_888) {
> -				streamConfigurations_.push_back(
> -					{ res, HAL_PIXEL_FORMAT_BLOB });
> -				maxJpegSize = std::max(maxJpegSize, res);
> -			}
> -		}
> -
> -		/*
> -		 * \todo Calculate the maximum JPEG buffer size by asking the
> -		 * encoder giving the maximum frame size required.
> -		 */
> -		maxJpegBufferSize_ = maxJpegSize.width * maxJpegSize.height * 1.5;
> -	}
> -
> -	LOG(HAL, Debug) << "Collected stream configuration map: ";
> -	for (const auto &entry : streamConfigurations_)
> -		LOG(HAL, Debug) << "{ " << entry.resolution.toString() << " - "
> -				<< utils::hex(entry.androidFormat) << " }";
> -
> -	return 0;
> +	return capabilities_.initialize(camera_, orientation_, facing_);
>  }
>  
>  /*
> @@ -817,802 +490,19 @@ void CameraDevice::stop()
>  	state_ = State::Stopped;
>  }
>  
> -void CameraDevice::setCallbacks(const camera3_callback_ops_t *callbacks)
> +unsigned int CameraDevice::maxJpegBufferSize() const
>  {
> -	callbacks_ = callbacks;
> +	return capabilities_.maxJpegBufferSize();
>  }
>  
> -/*
> - * Return static information for the camera.
> - */
> -const camera_metadata_t *CameraDevice::getStaticMetadata()
> -{
> -	if (staticMetadata_)
> -		return staticMetadata_->get();
> -
> -	staticMetadata_ = std::make_unique<CameraMetadata>(64, 1024);
> -	if (!staticMetadata_->isValid()) {
> -		LOG(HAL, Error) << "Failed to allocate static metadata";
> -		staticMetadata_.reset();
> -		return nullptr;
> -	}
> -
> -	const ControlInfoMap &controlsInfo = camera_->controls();
> -	const ControlList &properties = camera_->properties();
> -
> -	/* Color correction static metadata. */
> -	{
> -		std::vector<uint8_t> data;
> -		data.reserve(3);
> -		const auto &infoMap = controlsInfo.find(&controls::draft::ColorCorrectionAberrationMode);
> -		if (infoMap != controlsInfo.end()) {
> -			for (const auto &value : infoMap->second.values())
> -				data.push_back(value.get<int32_t>());
> -		} else {
> -			data.push_back(ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF);
> -		}
> -		staticMetadata_->addEntry(ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
> -					  data);
> -	}
> -
> -	/* Control static metadata. */
> -	std::vector<uint8_t> aeAvailableAntiBandingModes = {
> -		ANDROID_CONTROL_AE_ANTIBANDING_MODE_OFF,
> -		ANDROID_CONTROL_AE_ANTIBANDING_MODE_50HZ,
> -		ANDROID_CONTROL_AE_ANTIBANDING_MODE_60HZ,
> -		ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO,
> -	};
> -	staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,
> -				  aeAvailableAntiBandingModes);
> -
> -	std::vector<uint8_t> aeAvailableModes = {
> -		ANDROID_CONTROL_AE_MODE_ON,
> -	};
> -	staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_MODES,
> -				  aeAvailableModes);
> -
> -	int64_t minFrameDurationNsec = -1;
> -	int64_t maxFrameDurationNsec = -1;
> -	const auto frameDurationsInfo = controlsInfo.find(&controls::FrameDurationLimits);
> -	if (frameDurationsInfo != controlsInfo.end()) {
> -		minFrameDurationNsec = frameDurationsInfo->second.min().get<int64_t>() * 1000;
> -		maxFrameDurationNsec = frameDurationsInfo->second.max().get<int64_t>() * 1000;
> -
> -		/*
> -		 * Adjust the minimum frame duration to comply with Android
> -		 * requirements. The camera service mandates all preview/record
> -		 * streams to have a minimum frame duration < 33,366 milliseconds
> -		 * (see MAX_PREVIEW_RECORD_DURATION_NS in the camera service
> -		 * implementation).
> -		 *
> -		 * If we're close enough (+ 500 useconds) to that value, round
> -		 * the minimum frame duration of the camera to an accepted
> -		 * value.
> -		 */
> -		static constexpr int64_t MAX_PREVIEW_RECORD_DURATION_NS = 1e9 / 29.97;
> -		if (minFrameDurationNsec > MAX_PREVIEW_RECORD_DURATION_NS &&
> -		    minFrameDurationNsec < MAX_PREVIEW_RECORD_DURATION_NS + 500000)
> -			minFrameDurationNsec = MAX_PREVIEW_RECORD_DURATION_NS - 1000;
> -
> -		/*
> -		 * The AE routine frame rate limits are computed using the frame
> -		 * duration limits, as libcamera clips the AE routine to the
> -		 * frame durations.
> -		 */
> -		int32_t maxFps = std::round(1e9 / minFrameDurationNsec);
> -		int32_t minFps = std::round(1e9 / maxFrameDurationNsec);
> -		minFps = std::max(1, minFps);
> -
> -		/*
> -		 * Force rounding errors so that we have the proper frame
> -		 * durations for when we reuse these variables later
> -		 */
> -		minFrameDurationNsec = 1e9 / maxFps;
> -		maxFrameDurationNsec = 1e9 / minFps;
> -
> -		/*
> -		 * Register to the camera service {min, max} and {max, max}
> -		 * intervals as requested by the metadata documentation.
> -		 */
> -		int32_t availableAeFpsTarget[] = {
> -			minFps, maxFps, maxFps, maxFps
> -		};
> -		staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> -					  availableAeFpsTarget);
> -	}
> -
> -	std::vector<int32_t> aeCompensationRange = {
> -		0, 0,
> -	};
> -	staticMetadata_->addEntry(ANDROID_CONTROL_AE_COMPENSATION_RANGE,
> -				  aeCompensationRange);
> -
> -	const camera_metadata_rational_t aeCompensationStep[] = {
> -		{ 0, 1 }
> -	};
> -	staticMetadata_->addEntry(ANDROID_CONTROL_AE_COMPENSATION_STEP,
> -				  aeCompensationStep);
> -
> -	std::vector<uint8_t> availableAfModes = {
> -		ANDROID_CONTROL_AF_MODE_OFF,
> -	};
> -	staticMetadata_->addEntry(ANDROID_CONTROL_AF_AVAILABLE_MODES,
> -				  availableAfModes);
> -
> -	std::vector<uint8_t> availableEffects = {
> -		ANDROID_CONTROL_EFFECT_MODE_OFF,
> -	};
> -	staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_EFFECTS,
> -				  availableEffects);
> -
> -	std::vector<uint8_t> availableSceneModes = {
> -		ANDROID_CONTROL_SCENE_MODE_DISABLED,
> -	};
> -	staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_SCENE_MODES,
> -				  availableSceneModes);
> -
> -	std::vector<uint8_t> availableStabilizationModes = {
> -		ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF,
> -	};
> -	staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
> -				  availableStabilizationModes);
> -
> -	/*
> -	 * \todo Inspect the Camera capabilities to report the available
> -	 * AWB modes. Default to AUTO as CTS tests require it.
> -	 */
> -	std::vector<uint8_t> availableAwbModes = {
> -		ANDROID_CONTROL_AWB_MODE_AUTO,
> -	};
> -	staticMetadata_->addEntry(ANDROID_CONTROL_AWB_AVAILABLE_MODES,
> -				  availableAwbModes);
> -
> -	std::vector<int32_t> availableMaxRegions = {
> -		0, 0, 0,
> -	};
> -	staticMetadata_->addEntry(ANDROID_CONTROL_MAX_REGIONS,
> -				  availableMaxRegions);
> -
> -	std::vector<uint8_t> sceneModesOverride = {
> -		ANDROID_CONTROL_AE_MODE_ON,
> -		ANDROID_CONTROL_AWB_MODE_AUTO,
> -		ANDROID_CONTROL_AF_MODE_OFF,
> -	};
> -	staticMetadata_->addEntry(ANDROID_CONTROL_SCENE_MODE_OVERRIDES,
> -				  sceneModesOverride);
> -
> -	uint8_t aeLockAvailable = ANDROID_CONTROL_AE_LOCK_AVAILABLE_FALSE;
> -	staticMetadata_->addEntry(ANDROID_CONTROL_AE_LOCK_AVAILABLE,
> -				  aeLockAvailable);
> -
> -	uint8_t awbLockAvailable = ANDROID_CONTROL_AWB_LOCK_AVAILABLE_FALSE;
> -	staticMetadata_->addEntry(ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
> -				  awbLockAvailable);
> -
> -	char availableControlModes = ANDROID_CONTROL_MODE_AUTO;
> -	staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_MODES,
> -				  availableControlModes);
> -
> -	/* JPEG static metadata. */
> -
> -	/*
> -	 * Create the list of supported thumbnail sizes by inspecting the
> -	 * available JPEG resolutions collected in streamConfigurations_ and
> -	 * generate one entry for each aspect ratio.
> -	 *
> -	 * The JPEG thumbnailer can freely scale, so pick an arbitrary
> -	 * (160, 160) size as the bounding rectangle, which is then cropped to
> -	 * the different supported aspect ratios.
> -	 */
> -	constexpr Size maxJpegThumbnail(160, 160);
> -	std::vector<Size> thumbnailSizes;
> -	thumbnailSizes.push_back({ 0, 0 });
> -	for (const auto &entry : streamConfigurations_) {
> -		if (entry.androidFormat != HAL_PIXEL_FORMAT_BLOB)
> -			continue;
> -
> -		Size thumbnailSize = maxJpegThumbnail
> -				     .boundedToAspectRatio({ entry.resolution.width,
> -							     entry.resolution.height });
> -		thumbnailSizes.push_back(thumbnailSize);
> -	}
> -
> -	std::sort(thumbnailSizes.begin(), thumbnailSizes.end());
> -	auto last = std::unique(thumbnailSizes.begin(), thumbnailSizes.end());
> -	thumbnailSizes.erase(last, thumbnailSizes.end());
> -
> -	/* Transform sizes in to a list of integers that can be consumed. */
> -	std::vector<int32_t> thumbnailEntries;
> -	thumbnailEntries.reserve(thumbnailSizes.size() * 2);
> -	for (const auto &size : thumbnailSizes) {
> -		thumbnailEntries.push_back(size.width);
> -		thumbnailEntries.push_back(size.height);
> -	}
> -	staticMetadata_->addEntry(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,
> -				  thumbnailEntries);
> -
> -	staticMetadata_->addEntry(ANDROID_JPEG_MAX_SIZE, maxJpegBufferSize_);
> -
> -	/* Sensor static metadata. */
> -	std::array<int32_t, 2> pixelArraySize;
> -	{
> -		const Size &size = properties.get(properties::PixelArraySize);
> -		pixelArraySize[0] = size.width;
> -		pixelArraySize[1] = size.height;
> -		staticMetadata_->addEntry(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
> -					  pixelArraySize);
> -	}
> -
> -	if (properties.contains(properties::UnitCellSize)) {
> -		const Size &cellSize = properties.get<Size>(properties::UnitCellSize);
> -		std::array<float, 2> physicalSize{
> -			cellSize.width * pixelArraySize[0] / 1e6f,
> -			cellSize.height * pixelArraySize[1] / 1e6f
> -		};
> -		staticMetadata_->addEntry(ANDROID_SENSOR_INFO_PHYSICAL_SIZE,
> -					  physicalSize);
> -	}
> -
> -	{
> -		const Span<const Rectangle> &rects =
> -			properties.get(properties::PixelArrayActiveAreas);
> -		std::vector<int32_t> data{
> -			static_cast<int32_t>(rects[0].x),
> -			static_cast<int32_t>(rects[0].y),
> -			static_cast<int32_t>(rects[0].width),
> -			static_cast<int32_t>(rects[0].height),
> -		};
> -		staticMetadata_->addEntry(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
> -					  data);
> -	}
> -
> -	int32_t sensitivityRange[] = {
> -		32, 2400,
> -	};
> -	staticMetadata_->addEntry(ANDROID_SENSOR_INFO_SENSITIVITY_RANGE,
> -				  sensitivityRange);
> -
> -	/* Report the color filter arrangement if the camera reports it. */
> -	if (properties.contains(properties::draft::ColorFilterArrangement)) {
> -		uint8_t filterArr = properties.get(properties::draft::ColorFilterArrangement);
> -		staticMetadata_->addEntry(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,
> -					  filterArr);
> -	}
> -
> -	const auto &exposureInfo = controlsInfo.find(&controls::ExposureTime);
> -	if (exposureInfo != controlsInfo.end()) {
> -		int64_t exposureTimeRange[2] = {
> -			exposureInfo->second.min().get<int32_t>() * 1000LL,
> -			exposureInfo->second.max().get<int32_t>() * 1000LL,
> -		};
> -		staticMetadata_->addEntry(ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE,
> -					  exposureTimeRange, 2);
> -	}
> -
> -	staticMetadata_->addEntry(ANDROID_SENSOR_ORIENTATION, orientation_);
> -
> -	std::vector<int32_t> testPatternModes = {
> -		ANDROID_SENSOR_TEST_PATTERN_MODE_OFF
> -	};
> -	const auto &testPatternsInfo =
> -		controlsInfo.find(&controls::draft::TestPatternMode);
> -	if (testPatternsInfo != controlsInfo.end()) {
> -		const auto &values = testPatternsInfo->second.values();
> -		ASSERT(!values.empty());
> -		for (const auto &value : values) {
> -			switch (value.get<int32_t>()) {
> -			case controls::draft::TestPatternModeOff:
> -				/*
> -				 * ANDROID_SENSOR_TEST_PATTERN_MODE_OFF is
> -				 * already in testPatternModes.
> -				 */
> -				break;
> -
> -			case controls::draft::TestPatternModeSolidColor:
> -				testPatternModes.push_back(
> -					ANDROID_SENSOR_TEST_PATTERN_MODE_SOLID_COLOR);
> -				break;
> -
> -			case controls::draft::TestPatternModeColorBars:
> -				testPatternModes.push_back(
> -					ANDROID_SENSOR_TEST_PATTERN_MODE_COLOR_BARS);
> -				break;
> -
> -			case controls::draft::TestPatternModeColorBarsFadeToGray:
> -				testPatternModes.push_back(
> -					ANDROID_SENSOR_TEST_PATTERN_MODE_COLOR_BARS_FADE_TO_GRAY);
> -				break;
> -
> -			case controls::draft::TestPatternModePn9:
> -				testPatternModes.push_back(
> -					ANDROID_SENSOR_TEST_PATTERN_MODE_PN9);
> -				break;
> -
> -			case controls::draft::TestPatternModeCustom1:
> -				/* We don't support this yet. */
> -				break;
> -
> -			default:
> -				LOG(HAL, Error) << "Unknown test pattern mode: "
> -						<< value.get<int32_t>();
> -				continue;
> -			}
> -		}
> -	}
> -	staticMetadata_->addEntry(ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES,
> -				  testPatternModes);
> -
> -	uint8_t timestampSource = ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN;
> -	staticMetadata_->addEntry(ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE,
> -				  timestampSource);
> -
> -	if (maxFrameDurationNsec > 0)
> -		staticMetadata_->addEntry(ANDROID_SENSOR_INFO_MAX_FRAME_DURATION,
> -					  maxFrameDurationNsec);
> -
> -	/* Statistics static metadata. */
> -	uint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
> -	staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
> -				  faceDetectMode);
> -
> -	int32_t maxFaceCount = 0;
> -	staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
> -				  maxFaceCount);
> -
> -	{
> -		std::vector<uint8_t> data;
> -		data.reserve(2);
> -		const auto &infoMap = controlsInfo.find(&controls::draft::LensShadingMapMode);
> -		if (infoMap != controlsInfo.end()) {
> -			for (const auto &value : infoMap->second.values())
> -				data.push_back(value.get<int32_t>());
> -		} else {
> -			data.push_back(ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF);
> -		}
> -		staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES,
> -					  data);
> -	}
> -
> -	/* Sync static metadata. */
> -	int32_t maxLatency = ANDROID_SYNC_MAX_LATENCY_UNKNOWN;
> -	staticMetadata_->addEntry(ANDROID_SYNC_MAX_LATENCY, maxLatency);
> -
> -	/* Flash static metadata. */
> -	char flashAvailable = ANDROID_FLASH_INFO_AVAILABLE_FALSE;
> -	staticMetadata_->addEntry(ANDROID_FLASH_INFO_AVAILABLE,
> -				  flashAvailable);
> -
> -	/* Lens static metadata. */
> -	std::vector<float> lensApertures = {
> -		2.53 / 100,
> -	};
> -	staticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_APERTURES,
> -				  lensApertures);
> -
> -	uint8_t lensFacing;
> -	switch (facing_) {
> -	default:
> -	case CAMERA_FACING_FRONT:
> -		lensFacing = ANDROID_LENS_FACING_FRONT;
> -		break;
> -	case CAMERA_FACING_BACK:
> -		lensFacing = ANDROID_LENS_FACING_BACK;
> -		break;
> -	case CAMERA_FACING_EXTERNAL:
> -		lensFacing = ANDROID_LENS_FACING_EXTERNAL;
> -		break;
> -	}
> -	staticMetadata_->addEntry(ANDROID_LENS_FACING, lensFacing);
> -
> -	std::vector<float> lensFocalLengths = {
> -		1,
> -	};
> -	staticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS,
> -				  lensFocalLengths);
> -
> -	std::vector<uint8_t> opticalStabilizations = {
> -		ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF,
> -	};
> -	staticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
> -				  opticalStabilizations);
> -
> -	float hypeFocalDistance = 0;
> -	staticMetadata_->addEntry(ANDROID_LENS_INFO_HYPERFOCAL_DISTANCE,
> -				  hypeFocalDistance);
> -
> -	float minFocusDistance = 0;
> -	staticMetadata_->addEntry(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
> -				  minFocusDistance);
> -
> -	/* Noise reduction modes. */
> -	{
> -		std::vector<uint8_t> data;
> -		data.reserve(5);
> -		const auto &infoMap = controlsInfo.find(&controls::draft::NoiseReductionMode);
> -		if (infoMap != controlsInfo.end()) {
> -			for (const auto &value : infoMap->second.values())
> -				data.push_back(value.get<int32_t>());
> -		} else {
> -			data.push_back(ANDROID_NOISE_REDUCTION_MODE_OFF);
> -		}
> -		staticMetadata_->addEntry(ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
> -					  data);
> -	}
> -
> -	/* Scaler static metadata. */
> -
> -	/*
> -	 * \todo The digital zoom factor is a property that depends on the
> -	 * desired output configuration and the sensor frame size input to the
> -	 * ISP. This information is not available to the Android HAL, not at
> -	 * initialization time at least.
> -	 *
> -	 * As a workaround rely on pipeline handlers initializing the
> -	 * ScalerCrop control with the camera default configuration and use the
> -	 * maximum and minimum crop rectangles to calculate the digital zoom
> -	 * factor.
> -	 */
> -	float maxZoom = 1.0f;
> -	const auto scalerCrop = controlsInfo.find(&controls::ScalerCrop);
> -	if (scalerCrop != controlsInfo.end()) {
> -		Rectangle min = scalerCrop->second.min().get<Rectangle>();
> -		Rectangle max = scalerCrop->second.max().get<Rectangle>();
> -		maxZoom = std::min(1.0f * max.width / min.width,
> -				   1.0f * max.height / min.height);
> -	}
> -	staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
> -				  maxZoom);
> -
> -	std::vector<uint32_t> availableStreamConfigurations;
> -	availableStreamConfigurations.reserve(streamConfigurations_.size() * 4);
> -	for (const auto &entry : streamConfigurations_) {
> -		availableStreamConfigurations.push_back(entry.androidFormat);
> -		availableStreamConfigurations.push_back(entry.resolution.width);
> -		availableStreamConfigurations.push_back(entry.resolution.height);
> -		availableStreamConfigurations.push_back(
> -			ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT);
> -	}
> -	staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
> -				  availableStreamConfigurations);
> -
> -	std::vector<int64_t> availableStallDurations = {
> -		ANDROID_SCALER_AVAILABLE_FORMATS_BLOB, 2560, 1920, 33333333,
> -	};
> -	staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,
> -				  availableStallDurations);
> -
> -	/* Use the minimum frame duration for all the YUV/RGB formats. */
> -	if (minFrameDurationNsec > 0) {
> -		std::vector<int64_t> minFrameDurations;
> -		minFrameDurations.reserve(streamConfigurations_.size() * 4);
> -		for (const auto &entry : streamConfigurations_) {
> -			minFrameDurations.push_back(entry.androidFormat);
> -			minFrameDurations.push_back(entry.resolution.width);
> -			minFrameDurations.push_back(entry.resolution.height);
> -			minFrameDurations.push_back(minFrameDurationNsec);
> -		}
> -		staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
> -					  minFrameDurations);
> -	}
> -
> -	uint8_t croppingType = ANDROID_SCALER_CROPPING_TYPE_CENTER_ONLY;
> -	staticMetadata_->addEntry(ANDROID_SCALER_CROPPING_TYPE, croppingType);
> -
> -	/* Info static metadata. */
> -	uint8_t supportedHWLevel = ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
> -	staticMetadata_->addEntry(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
> -				  supportedHWLevel);
> -
> -	/* Request static metadata. */
> -	int32_t partialResultCount = 1;
> -	staticMetadata_->addEntry(ANDROID_REQUEST_PARTIAL_RESULT_COUNT,
> -				  partialResultCount);
> -
> -	{
> -		/* Default the value to 2 if not reported by the camera. */
> -		uint8_t maxPipelineDepth = 2;
> -		const auto &infoMap = controlsInfo.find(&controls::draft::PipelineDepth);
> -		if (infoMap != controlsInfo.end())
> -			maxPipelineDepth = infoMap->second.max().get<int32_t>();
> -		staticMetadata_->addEntry(ANDROID_REQUEST_PIPELINE_MAX_DEPTH,
> -					  maxPipelineDepth);
> -	}
> -
> -	/* LIMITED does not support reprocessing. */
> -	uint32_t maxNumInputStreams = 0;
> -	staticMetadata_->addEntry(ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,
> -				  maxNumInputStreams);
> -
> -	std::vector<uint8_t> availableCapabilities = {
> -		ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE,
> -	};
> -
> -	/* Report if camera supports RAW. */
> -	bool rawStreamAvailable = false;
> -	std::unique_ptr<CameraConfiguration> cameraConfig =
> -		camera_->generateConfiguration({ StreamRole::Raw });
> -	if (cameraConfig && !cameraConfig->empty()) {
> -		const PixelFormatInfo &info =
> -			PixelFormatInfo::info(cameraConfig->at(0).pixelFormat);
> -		/* Only advertise RAW support if RAW16 is possible. */
> -		if (info.colourEncoding == PixelFormatInfo::ColourEncodingRAW &&
> -		    info.bitsPerPixel == 16) {
> -			rawStreamAvailable = true;
> -			availableCapabilities.push_back(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW);
> -		}
> -	}
> -
> -	/* Number of { RAW, YUV, JPEG } supported output streams */
> -	int32_t numOutStreams[] = { rawStreamAvailable, 2, 1 };
> -	staticMetadata_->addEntry(ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,
> -				  numOutStreams);
> -
> -	staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
> -				  availableCapabilities);
> -
> -	std::vector<int32_t> availableCharacteristicsKeys = {
> -		ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
> -		ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,
> -		ANDROID_CONTROL_AE_AVAILABLE_MODES,
> -		ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> -		ANDROID_CONTROL_AE_COMPENSATION_RANGE,
> -		ANDROID_CONTROL_AE_COMPENSATION_STEP,
> -		ANDROID_CONTROL_AE_LOCK_AVAILABLE,
> -		ANDROID_CONTROL_AF_AVAILABLE_MODES,
> -		ANDROID_CONTROL_AVAILABLE_EFFECTS,
> -		ANDROID_CONTROL_AVAILABLE_MODES,
> -		ANDROID_CONTROL_AVAILABLE_SCENE_MODES,
> -		ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
> -		ANDROID_CONTROL_AWB_AVAILABLE_MODES,
> -		ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
> -		ANDROID_CONTROL_MAX_REGIONS,
> -		ANDROID_CONTROL_SCENE_MODE_OVERRIDES,
> -		ANDROID_FLASH_INFO_AVAILABLE,
> -		ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
> -		ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,
> -		ANDROID_JPEG_MAX_SIZE,
> -		ANDROID_LENS_FACING,
> -		ANDROID_LENS_INFO_AVAILABLE_APERTURES,
> -		ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS,
> -		ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
> -		ANDROID_LENS_INFO_HYPERFOCAL_DISTANCE,
> -		ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
> -		ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
> -		ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
> -		ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,
> -		ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,
> -		ANDROID_REQUEST_PARTIAL_RESULT_COUNT,
> -		ANDROID_REQUEST_PIPELINE_MAX_DEPTH,
> -		ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
> -		ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
> -		ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,
> -		ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
> -		ANDROID_SCALER_CROPPING_TYPE,
> -		ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES,
> -		ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
> -		ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,
> -		ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE,
> -		ANDROID_SENSOR_INFO_MAX_FRAME_DURATION,
> -		ANDROID_SENSOR_INFO_PHYSICAL_SIZE,
> -		ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
> -		ANDROID_SENSOR_INFO_SENSITIVITY_RANGE,
> -		ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE,
> -		ANDROID_SENSOR_ORIENTATION,
> -		ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
> -		ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
> -		ANDROID_SYNC_MAX_LATENCY,
> -	};
> -	staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS,
> -				  availableCharacteristicsKeys);
> -
> -	std::vector<int32_t> availableRequestKeys = {
> -		ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
> -		ANDROID_CONTROL_AE_ANTIBANDING_MODE,
> -		ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
> -		ANDROID_CONTROL_AE_LOCK,
> -		ANDROID_CONTROL_AE_MODE,
> -		ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
> -		ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
> -		ANDROID_CONTROL_AF_MODE,
> -		ANDROID_CONTROL_AF_TRIGGER,
> -		ANDROID_CONTROL_AWB_LOCK,
> -		ANDROID_CONTROL_AWB_MODE,
> -		ANDROID_CONTROL_CAPTURE_INTENT,
> -		ANDROID_CONTROL_EFFECT_MODE,
> -		ANDROID_CONTROL_MODE,
> -		ANDROID_CONTROL_SCENE_MODE,
> -		ANDROID_CONTROL_VIDEO_STABILIZATION_MODE,
> -		ANDROID_FLASH_MODE,
> -		ANDROID_JPEG_ORIENTATION,
> -		ANDROID_JPEG_QUALITY,
> -		ANDROID_JPEG_THUMBNAIL_QUALITY,
> -		ANDROID_JPEG_THUMBNAIL_SIZE,
> -		ANDROID_LENS_APERTURE,
> -		ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
> -		ANDROID_NOISE_REDUCTION_MODE,
> -		ANDROID_SCALER_CROP_REGION,
> -		ANDROID_STATISTICS_FACE_DETECT_MODE
> -	};
> -	staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS,
> -				  availableRequestKeys);
> -
> -	std::vector<int32_t> availableResultKeys = {
> -		ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
> -		ANDROID_CONTROL_AE_ANTIBANDING_MODE,
> -		ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
> -		ANDROID_CONTROL_AE_LOCK,
> -		ANDROID_CONTROL_AE_MODE,
> -		ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
> -		ANDROID_CONTROL_AE_STATE,
> -		ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
> -		ANDROID_CONTROL_AF_MODE,
> -		ANDROID_CONTROL_AF_STATE,
> -		ANDROID_CONTROL_AF_TRIGGER,
> -		ANDROID_CONTROL_AWB_LOCK,
> -		ANDROID_CONTROL_AWB_MODE,
> -		ANDROID_CONTROL_AWB_STATE,
> -		ANDROID_CONTROL_CAPTURE_INTENT,
> -		ANDROID_CONTROL_EFFECT_MODE,
> -		ANDROID_CONTROL_MODE,
> -		ANDROID_CONTROL_SCENE_MODE,
> -		ANDROID_CONTROL_VIDEO_STABILIZATION_MODE,
> -		ANDROID_FLASH_MODE,
> -		ANDROID_FLASH_STATE,
> -		ANDROID_JPEG_GPS_COORDINATES,
> -		ANDROID_JPEG_GPS_PROCESSING_METHOD,
> -		ANDROID_JPEG_GPS_TIMESTAMP,
> -		ANDROID_JPEG_ORIENTATION,
> -		ANDROID_JPEG_QUALITY,
> -		ANDROID_JPEG_SIZE,
> -		ANDROID_JPEG_THUMBNAIL_QUALITY,
> -		ANDROID_JPEG_THUMBNAIL_SIZE,
> -		ANDROID_LENS_APERTURE,
> -		ANDROID_LENS_FOCAL_LENGTH,
> -		ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
> -		ANDROID_LENS_STATE,
> -		ANDROID_NOISE_REDUCTION_MODE,
> -		ANDROID_REQUEST_PIPELINE_DEPTH,
> -		ANDROID_SCALER_CROP_REGION,
> -		ANDROID_SENSOR_EXPOSURE_TIME,
> -		ANDROID_SENSOR_FRAME_DURATION,
> -		ANDROID_SENSOR_ROLLING_SHUTTER_SKEW,
> -		ANDROID_SENSOR_TEST_PATTERN_MODE,
> -		ANDROID_SENSOR_TIMESTAMP,
> -		ANDROID_STATISTICS_FACE_DETECT_MODE,
> -		ANDROID_STATISTICS_LENS_SHADING_MAP_MODE,
> -		ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE,
> -		ANDROID_STATISTICS_SCENE_FLICKER,
> -	};
> -	staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_RESULT_KEYS,
> -				  availableResultKeys);
> -
> -	if (!staticMetadata_->isValid()) {
> -		LOG(HAL, Error) << "Failed to construct static metadata";
> -		staticMetadata_.reset();
> -		return nullptr;
> -	}
> -
> -	if (staticMetadata_->resized()) {
> -		auto [entryCount, dataCount] = staticMetadata_->usage();
> -		LOG(HAL, Info)
> -			<< "Static metadata resized: " << entryCount
> -			<< " entries and " << dataCount << " bytes used";
> -	}
> -
> -	return staticMetadata_->get();
> -}
> -
> -std::unique_ptr<CameraMetadata> CameraDevice::requestTemplatePreview()
> +void CameraDevice::setCallbacks(const camera3_callback_ops_t *callbacks)
>  {
> -	/*
> -	 * \todo Keep this in sync with the actual number of entries.
> -	 * Currently: 20 entries, 35 bytes
> -	 */
> -	auto requestTemplate = std::make_unique<CameraMetadata>(21, 36);
> -	if (!requestTemplate->isValid()) {
> -		return nullptr;
> -	}
> -
> -	/* Get the FPS range registered in the static metadata. */
> -	camera_metadata_ro_entry_t entry;
> -	bool found = staticMetadata_->getEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> -					       &entry);
> -	if (!found) {
> -		LOG(HAL, Error) << "Cannot create capture template without FPS range";
> -		return nullptr;
> -	}
> -
> -	/*
> -	 * Assume the AE_AVAILABLE_TARGET_FPS_RANGE static metadata
> -	 * has been assembled as {{min, max} {max, max}}.
> -	 */
> -	requestTemplate->addEntry(ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
> -				  entry.data.i32, 2);
> -
> -	uint8_t aeMode = ANDROID_CONTROL_AE_MODE_ON;
> -	requestTemplate->addEntry(ANDROID_CONTROL_AE_MODE, aeMode);
> -
> -	int32_t aeExposureCompensation = 0;
> -	requestTemplate->addEntry(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
> -				  aeExposureCompensation);
> -
> -	uint8_t aePrecaptureTrigger = ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_IDLE;
> -	requestTemplate->addEntry(ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
> -				  aePrecaptureTrigger);
> -
> -	uint8_t aeLock = ANDROID_CONTROL_AE_LOCK_OFF;
> -	requestTemplate->addEntry(ANDROID_CONTROL_AE_LOCK, aeLock);
> -
> -	uint8_t aeAntibandingMode = ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO;
> -	requestTemplate->addEntry(ANDROID_CONTROL_AE_ANTIBANDING_MODE,
> -				  aeAntibandingMode);
> -
> -	uint8_t afMode = ANDROID_CONTROL_AF_MODE_OFF;
> -	requestTemplate->addEntry(ANDROID_CONTROL_AF_MODE, afMode);
> -
> -	uint8_t afTrigger = ANDROID_CONTROL_AF_TRIGGER_IDLE;
> -	requestTemplate->addEntry(ANDROID_CONTROL_AF_TRIGGER, afTrigger);
> -
> -	uint8_t awbMode = ANDROID_CONTROL_AWB_MODE_AUTO;
> -	requestTemplate->addEntry(ANDROID_CONTROL_AWB_MODE, awbMode);
> -
> -	uint8_t awbLock = ANDROID_CONTROL_AWB_LOCK_OFF;
> -	requestTemplate->addEntry(ANDROID_CONTROL_AWB_LOCK, awbLock);
> -
> -	uint8_t flashMode = ANDROID_FLASH_MODE_OFF;
> -	requestTemplate->addEntry(ANDROID_FLASH_MODE, flashMode);
> -
> -	uint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
> -	requestTemplate->addEntry(ANDROID_STATISTICS_FACE_DETECT_MODE,
> -				  faceDetectMode);
> -
> -	uint8_t noiseReduction = ANDROID_NOISE_REDUCTION_MODE_OFF;
> -	requestTemplate->addEntry(ANDROID_NOISE_REDUCTION_MODE,
> -				  noiseReduction);
> -
> -	uint8_t aberrationMode = ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF;
> -	requestTemplate->addEntry(ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
> -				  aberrationMode);
> -
> -	uint8_t controlMode = ANDROID_CONTROL_MODE_AUTO;
> -	requestTemplate->addEntry(ANDROID_CONTROL_MODE, controlMode);
> -
> -	float lensAperture = 2.53 / 100;
> -	requestTemplate->addEntry(ANDROID_LENS_APERTURE, lensAperture);
> -
> -	uint8_t opticalStabilization = ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF;
> -	requestTemplate->addEntry(ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
> -				  opticalStabilization);
> -
> -	uint8_t captureIntent = ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW;
> -	requestTemplate->addEntry(ANDROID_CONTROL_CAPTURE_INTENT,
> -				  captureIntent);
> -
> -	return requestTemplate;
> +	callbacks_ = callbacks;
>  }
>  
> -std::unique_ptr<CameraMetadata> CameraDevice::requestTemplateVideo()
> +const camera_metadata_t *CameraDevice::getStaticMetadata()
>  {
> -	std::unique_ptr<CameraMetadata> previewTemplate = requestTemplatePreview();
> -	if (!previewTemplate)
> -		return nullptr;
> -
> -	/*
> -	 * The video template requires a fixed FPS range. Everything else
> -	 * stays the same as the preview template.
> -	 */
> -	camera_metadata_ro_entry_t entry;
> -	staticMetadata_->getEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> -				  &entry);
> -
> -	/*
> -	 * Assume the AE_AVAILABLE_TARGET_FPS_RANGE static metadata
> -	 * has been assembled as {{min, max} {max, max}}.
> -	 */
> -	previewTemplate->updateEntry(ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
> -				     entry.data.i32 + 2, 2);
> -
> -	return previewTemplate;
> +	return capabilities_.staticMetadata()->get();
>  }
>  
>  /*
> @@ -1630,7 +520,7 @@ const camera_metadata_t *CameraDevice::constructDefaultRequestSettings(int type)
>  	switch (type) {
>  	case CAMERA3_TEMPLATE_PREVIEW:
>  		captureIntent = ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW;
> -		requestTemplate = requestTemplatePreview();
> +		requestTemplate = capabilities_.requestTemplatePreview();
>  		break;
>  	case CAMERA3_TEMPLATE_STILL_CAPTURE:
>  		/*
> @@ -1638,15 +528,15 @@ const camera_metadata_t *CameraDevice::constructDefaultRequestSettings(int type)
>  		 * for the torch mode we currently do not support.
>  		 */
>  		captureIntent = ANDROID_CONTROL_CAPTURE_INTENT_STILL_CAPTURE;
> -		requestTemplate = requestTemplatePreview();
> +		requestTemplate = capabilities_.requestTemplatePreview();
>  		break;
>  	case CAMERA3_TEMPLATE_VIDEO_RECORD:
>  		captureIntent = ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_RECORD;
> -		requestTemplate = requestTemplateVideo();
> +		requestTemplate = capabilities_.requestTemplateVideo();
>  		break;
>  	case CAMERA3_TEMPLATE_VIDEO_SNAPSHOT:
>  		captureIntent = ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT;
> -		requestTemplate = requestTemplateVideo();
> +		requestTemplate = capabilities_.requestTemplateVideo();
>  		break;
>  	/* \todo Implement templates generation for the remaining use cases. */
>  	case CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG:
> @@ -1668,19 +558,6 @@ const camera_metadata_t *CameraDevice::constructDefaultRequestSettings(int type)
>  	return requestTemplates_[type]->get();
>  }
>  
> -PixelFormat CameraDevice::toPixelFormat(int format) const
> -{
> -	/* Translate Android format code to libcamera pixel format. */
> -	auto it = formatsMap_.find(format);
> -	if (it == formatsMap_.end()) {
> -		LOG(HAL, Error) << "Requested format " << utils::hex(format)
> -				<< " not supported";
> -		return PixelFormat();
> -	}
> -
> -	return it->second;
> -}
> -
>  /*
>   * Inspect the stream_list to produce a list of StreamConfiguration to
>   * be use to configure the Camera.
> @@ -1727,7 +604,7 @@ int CameraDevice::configureStreams(camera3_stream_configuration_t *stream_list)
>  		camera3_stream_t *stream = stream_list->streams[i];
>  		Size size(stream->width, stream->height);
>  
> -		PixelFormat format = toPixelFormat(stream->format);
> +		PixelFormat format = capabilities_.toPixelFormat(stream->format);
>  
>  		LOG(HAL, Info) << "Stream #" << i
>  			       << ", direction: " << stream->stream_type
> diff --git a/src/android/camera_device.h b/src/android/camera_device.h
> index 4aadb27c562c..090fe28a551e 100644
> --- a/src/android/camera_device.h
> +++ b/src/android/camera_device.h
> @@ -10,14 +10,12 @@
>  #include <map>
>  #include <memory>
>  #include <mutex>
> -#include <tuple>
>  #include <vector>
>  
>  #include <hardware/camera3.h>
>  
>  #include <libcamera/buffer.h>
>  #include <libcamera/camera.h>
> -#include <libcamera/geometry.h>
>  #include <libcamera/request.h>
>  #include <libcamera/stream.h>
>  
> @@ -26,6 +24,7 @@
>  #include "libcamera/internal/message.h"
>  #include "libcamera/internal/thread.h"
>  
> +#include "camera_capabilities.h"
>  #include "camera_metadata.h"
>  #include "camera_stream.h"
>  #include "camera_worker.h"
> @@ -57,7 +56,7 @@ public:
>  	const std::string &model() const { return model_; }
>  	int facing() const { return facing_; }
>  	int orientation() const { return orientation_; }
> -	unsigned int maxJpegBufferSize() const { return maxJpegBufferSize_; }
> +	unsigned int maxJpegBufferSize() const;
>  
>  	void setCallbacks(const camera3_callback_ops_t *callbacks);
>  	const camera_metadata_t *getStaticMetadata();
> @@ -86,11 +85,6 @@ private:
>  		std::unique_ptr<CaptureRequest> request_;
>  	};
>  
> -	struct Camera3StreamConfiguration {
> -		libcamera::Size resolution;
> -		int androidFormat;
> -	};
> -
>  	enum class State {
>  		Stopped,
>  		Flushing,
> @@ -99,22 +93,11 @@ private:
>  
>  	void stop();
>  
> -	int initializeStreamConfigurations();
> -	std::vector<libcamera::Size>
> -	getYUVResolutions(libcamera::CameraConfiguration *cameraConfig,
> -			  const libcamera::PixelFormat &pixelFormat,
> -			  const std::vector<libcamera::Size> &resolutions);
> -	std::vector<libcamera::Size>
> -	getRawResolutions(const libcamera::PixelFormat &pixelFormat);
> -
>  	libcamera::FrameBuffer *createFrameBuffer(const buffer_handle_t camera3buffer);
>  	void abortRequest(camera3_capture_request_t *request);
>  	void notifyShutter(uint32_t frameNumber, uint64_t timestamp);
>  	void notifyError(uint32_t frameNumber, camera3_stream_t *stream,
>  			 camera3_error_msg_code code);
> -	std::unique_ptr<CameraMetadata> requestTemplatePreview();
> -	std::unique_ptr<CameraMetadata> requestTemplateVideo();
> -	libcamera::PixelFormat toPixelFormat(int format) const;
>  	int processControls(Camera3RequestDescriptor *descriptor);
>  	std::unique_ptr<CameraMetadata> getResultMetadata(
>  		const Camera3RequestDescriptor &descriptor) const;
> @@ -129,13 +112,11 @@ private:
>  
>  	std::shared_ptr<libcamera::Camera> camera_;
>  	std::unique_ptr<libcamera::CameraConfiguration> config_;
> +	CameraCapabilities capabilities_;
>  
> -	std::unique_ptr<CameraMetadata> staticMetadata_;
>  	std::map<unsigned int, std::unique_ptr<CameraMetadata>> requestTemplates_;
>  	const camera3_callback_ops_t *callbacks_;
>  
> -	std::vector<Camera3StreamConfiguration> streamConfigurations_;
> -	std::map<int, libcamera::PixelFormat> formatsMap_;
>  	std::vector<CameraStream> streams_;
>  
>  	libcamera::Mutex descriptorsMutex_; /* Protects descriptors_. */
> @@ -147,8 +128,6 @@ private:
>  	int facing_;
>  	int orientation_;
>  
> -	unsigned int maxJpegBufferSize_;
> -
>  	CameraMetadata lastSettings_;
>  };
>  
> diff --git a/src/android/meson.build b/src/android/meson.build
> index f27fd5316705..6270fb201338 100644
> --- a/src/android/meson.build
> +++ b/src/android/meson.build
> @@ -44,6 +44,7 @@ subdir('cros')
>  
>  android_hal_sources = files([
>      'camera3_hal.cpp',
> +    'camera_capabilities.cpp',
>      'camera_device.cpp',
>      'camera_hal_config.cpp',
>      'camera_hal_manager.cpp',
>
Hirokazu Honda June 23, 2021, 3:25 a.m. UTC | #4
Hi Jacopo,

On Tue, Jun 22, 2021 at 4:40 PM Jacopo Mondi <jacopo@jmondi.org> wrote:
>
> Hi Hiro,
>
> On Tue, Jun 22, 2021 at 10:34:27AM +0900, Hirokazu Honda wrote:
> > Hi Jacopo, thank you for the patch.
> >
> > I failed to apply the patch on the top of the latest tree to review.
> > Could you tell me the parent commit which I can apply this patch?
>
> weird, I just applied these two patches cleanly on the latest
> master which for me is
> 969da3189439 ("libcamera: utils: Support systems that lack secure_getenv and issetugid"
>

I tried applying the series whose id is 2162. But the series doesn't
contain "[PATCH 1/2] android: Sort source files alphabetically".
In fact, the patch is not shown in patchwork. It is strange.
I managed to apply this patch after manually applying 1/2.
>
> >
> > -Hiro
> > On Tue, Jun 22, 2021 at 12:29 AM Jacopo Mondi <jacopo@jmondi.org> wrote:
> >
> > > The camera_device.cpp has grown a little too much, and it has quickly
> > > become hard to maintain. Break out the handling of the static
> > > information collected at camera initialization time to a new
> > > CameraCapabilities class.
> > >
> > > Break out from the camera_device.cpp file all the functions relative to:
> > > - Initialization of supported stream configurations
> > > - Initialization of static metadata
> > > - Initialization of request templates
> > >
> > > Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
> > > Acked-by: Paul Elder <paul.elder@ideasonboard.com>
> > > Tested-by: Paul Elder <paul.elder@ideasonboard.com>
> > > ---
> > >  src/android/camera_capabilities.cpp | 1164 +++++++++++++++++++++++++++
> > >  src/android/camera_capabilities.h   |   65 ++
> > >  src/android/camera_device.cpp       | 1147 +-------------------------
> > >  src/android/camera_device.h         |   27 +-
> > >  src/android/meson.build             |    1 +
> > >  5 files changed, 1245 insertions(+), 1159 deletions(-)
> > >  create mode 100644 src/android/camera_capabilities.cpp
> > >  create mode 100644 src/android/camera_capabilities.h
> > >
> > > diff --git a/src/android/camera_capabilities.cpp
> > > b/src/android/camera_capabilities.cpp
> > > new file mode 100644
> > > index 000000000000..311a2c839586
> > > --- /dev/null
> > > +++ b/src/android/camera_capabilities.cpp
> > > @@ -0,0 +1,1164 @@
> > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > > +/*
> > > + * Copyright (C) 2021, Google Inc.
> > > + *
> > > + * camera_capabilities.cpp - Camera static properties manager
> > > + */
> > > +
> > > +#include "camera_capabilities.h"
> > > +
> > > +#include <array>
> > > +#include <cmath>
> > > +
> > > +#include <hardware/camera3.h>
> > > +
> > > +#include <libcamera/control_ids.h>
> > > +#include <libcamera/controls.h>
> > > +#include <libcamera/property_ids.h>
> > > +
> > > +#include "libcamera/internal/formats.h"
> > > +#include "libcamera/internal/log.h"
> > > +
> > > +using namespace libcamera;
> > > +
> > > +LOG_DECLARE_CATEGORY(HAL)
> > > +
> > > +namespace {
> > > +
> > > +/*
> > > + * \var camera3Resolutions
> > > + * \brief The list of image resolutions defined as mandatory to be
> > > supported by
> > > + * the Android Camera3 specification
> > > + */
> > > +const std::vector<Size> camera3Resolutions = {
> > > +       { 320, 240 },
> > > +       { 640, 480 },
> > > +       { 1280, 720 },
> > > +       { 1920, 1080 }
> > > +};
> > > +
> > > +/*
> > > + * \struct Camera3Format
> > > + * \brief Data associated with an Android format identifier
> > > + * \var libcameraFormats List of libcamera pixel formats compatible with
> > > the
> > > + * Android format
> > > + * \var name The human-readable representation of the Android format code
> > > + */
> > > +struct Camera3Format {
> > > +       std::vector<PixelFormat> libcameraFormats;
> > > +       bool mandatory;
> > > +       const char *name;
> > > +};
> > > +
> > > +/*
> > > + * \var camera3FormatsMap
> > > + * \brief Associate Android format code with ancillary data
> > > + */
> > > +const std::map<int, const Camera3Format> camera3FormatsMap = {
> > > +       {
> > > +               HAL_PIXEL_FORMAT_BLOB, {
> > > +                       { formats::MJPEG },
> > > +                       true,
> > > +                       "BLOB"
> > > +               }
> > > +       }, {
> > > +               HAL_PIXEL_FORMAT_YCbCr_420_888, {
> > > +                       { formats::NV12, formats::NV21 },
> > > +                       true,
> > > +                       "YCbCr_420_888"
> > > +               }
> > > +       }, {
> > > +               /*
> > > +                * \todo Translate IMPLEMENTATION_DEFINED inspecting the
> > > gralloc
> > > +                * usage flag. For now, copy the YCbCr_420 configuration.
> > > +                */
> > > +               HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, {
> > > +                       { formats::NV12, formats::NV21 },
> > > +                       true,
> > > +                       "IMPLEMENTATION_DEFINED"
> > > +               }
> > > +       }, {
> > > +               HAL_PIXEL_FORMAT_RAW10, {
> > > +                       {
> > > +                               formats::SBGGR10_CSI2P,
> > > +                               formats::SGBRG10_CSI2P,
> > > +                               formats::SGRBG10_CSI2P,
> > > +                               formats::SRGGB10_CSI2P
> > > +                       },
> > > +                       false,
> > > +                       "RAW10"
> > > +               }
> > > +       }, {
> > > +               HAL_PIXEL_FORMAT_RAW12, {
> > > +                       {
> > > +                               formats::SBGGR12_CSI2P,
> > > +                               formats::SGBRG12_CSI2P,
> > > +                               formats::SGRBG12_CSI2P,
> > > +                               formats::SRGGB12_CSI2P
> > > +                       },
> > > +                       false,
> > > +                       "RAW12"
> > > +               }
> > > +       }, {
> > > +               HAL_PIXEL_FORMAT_RAW16, {
> > > +                       {
> > > +                               formats::SBGGR16,
> > > +                               formats::SGBRG16,
> > > +                               formats::SGRBG16,
> > > +                               formats::SRGGB16
> > > +                       },
> > > +                       false,
> > > +                       "RAW16"
> > > +               }
> > > +       },
> > > +};
> > > +
> > > +} /* namespace */
> > > +
> > > +int CameraCapabilities::initialize(std::shared_ptr<libcamera::Camera>
> > > camera,
> > > +                                  int orientation, int facing)
> > > +{
> > > +       camera_ = camera;
> > > +       orientation_ = orientation;
> > > +       facing_ = facing;
> > > +
> > > +       /* Acquire the camera and initialize available stream
> > > configurations. */
> > > +       int ret = camera_->acquire();
> > > +       if (ret) {
> > > +               LOG(HAL, Error) << "Failed to temporarily acquire the
> > > camera";
> > > +               return ret;
> > > +       }
> > > +
> > > +       ret = initializeStreamConfigurations();
> > > +       camera_->release();
> > > +       if (ret)
> > > +               return ret;
> > > +
> > > +       return initializeStaticMetadata();
> > > +}
> > > +
> > > +std::vector<Size>
> > > CameraCapabilities::getYUVResolutions(CameraConfiguration *cameraConfig,
> > > +                                                       const PixelFormat
> > > &pixelFormat,
> > > +                                                       const
> > > std::vector<Size> &resolutions)
> > > +{
> > > +       std::vector<Size> supportedResolutions;
> > > +
> > > +       StreamConfiguration &cfg = cameraConfig->at(0);
> > > +       for (const Size &res : resolutions) {
> > > +               cfg.pixelFormat = pixelFormat;
> > > +               cfg.size = res;
> > > +
> > > +               CameraConfiguration::Status status =
> > > cameraConfig->validate();
> > > +               if (status != CameraConfiguration::Valid) {
> > > +                       LOG(HAL, Debug) << cfg.toString() << " not
> > > supported";
> > > +                       continue;
> > > +               }
> > > +
> > > +               LOG(HAL, Debug) << cfg.toString() << " supported";
> > > +
> > > +               supportedResolutions.push_back(res);
> > > +       }
> > > +
> > > +       return supportedResolutions;
> > > +}
> > > +
> > > +std::vector<Size> CameraCapabilities::getRawResolutions(const
> > > libcamera::PixelFormat &pixelFormat)
> > > +{
> > > +       std::unique_ptr<CameraConfiguration> cameraConfig =
> > > +               camera_->generateConfiguration({ StreamRole::Raw });
> > > +       StreamConfiguration &cfg = cameraConfig->at(0);
> > > +       const StreamFormats &formats = cfg.formats();
> > > +       std::vector<Size> supportedResolutions =
> > > formats.sizes(pixelFormat);
> > > +
> > > +       return supportedResolutions;
> > > +}
> > > +
> > > +/*
> > > + * Initialize the format conversion map to translate from Android format
> > > + * identifier to libcamera pixel formats and fill in the list of supported
> > > + * stream configurations to be reported to the Android camera framework
> > > through
> > > + * the Camera static metadata.
> > > + */
> > > +int CameraCapabilities::initializeStreamConfigurations()
> > > +{
> > > +       /*
> > > +        * Get the maximum output resolutions
> > > +        * \todo Get this from the camera properties once defined
> > > +        */
> > > +       std::unique_ptr<CameraConfiguration> cameraConfig =
> > > +               camera_->generateConfiguration({ StillCapture });
> > > +       if (!cameraConfig) {
> > > +               LOG(HAL, Error) << "Failed to get maximum resolution";
> > > +               return -EINVAL;
> > > +       }
> > > +       StreamConfiguration &cfg = cameraConfig->at(0);
> > > +
> > > +       /*
> > > +        * \todo JPEG - Adjust the maximum available resolution by taking
> > > the
> > > +        * JPEG encoder requirements into account (alignment and aspect
> > > ratio).
> > > +        */
> > > +       const Size maxRes = cfg.size;
> > > +       LOG(HAL, Debug) << "Maximum supported resolution: " <<
> > > maxRes.toString();
> > > +
> > > +       /*
> > > +        * Build the list of supported image resolutions.
> > > +        *
> > > +        * The resolutions listed in camera3Resolution are mandatory to be
> > > +        * supported, up to the camera maximum resolution.
> > > +        *
> > > +        * Augment the list by adding resolutions calculated from the
> > > camera
> > > +        * maximum one.
> > > +        */
> > > +       std::vector<Size> cameraResolutions;
> > > +       std::copy_if(camera3Resolutions.begin(), camera3Resolutions.end(),
> > > +                    std::back_inserter(cameraResolutions),
> > > +                    [&](const Size &res) { return res < maxRes; });
> > > +
> > > +       /*
> > > +        * The Camera3 specification suggests adding 1/2 and 1/4 of the
> > > maximum
> > > +        * resolution.
> > > +        */
> > > +       for (unsigned int divider = 2;; divider <<= 1) {
> > > +               Size derivedSize{
> > > +                       maxRes.width / divider,
> > > +                       maxRes.height / divider,
> > > +               };
> > > +
> > > +               if (derivedSize.width < 320 ||
> > > +                   derivedSize.height < 240)
> > > +                       break;
> > > +
> > > +               cameraResolutions.push_back(derivedSize);
> > > +       }
> > > +       cameraResolutions.push_back(maxRes);
> > > +
> > > +       /* Remove duplicated entries from the list of supported
> > > resolutions. */
> > > +       std::sort(cameraResolutions.begin(), cameraResolutions.end());
> > > +       auto last = std::unique(cameraResolutions.begin(),
> > > cameraResolutions.end());
> > > +       cameraResolutions.erase(last, cameraResolutions.end());
> > > +
> > > +       /*
> > > +        * Build the list of supported camera formats.
> > > +        *
> > > +        * To each Android format a list of compatible libcamera formats is
> > > +        * associated. The first libcamera format that tests successful is
> > > added
> > > +        * to the format translation map used when configuring the streams.
> > > +        * It is then tested against the list of supported camera
> > > resolutions to
> > > +        * build the stream configuration map reported through the camera
> > > static
> > > +        * metadata.
> > > +        */
> > > +       Size maxJpegSize;
> > > +       for (const auto &format : camera3FormatsMap) {
> > > +               int androidFormat = format.first;
> > > +               const Camera3Format &camera3Format = format.second;
> > > +               const std::vector<PixelFormat> &libcameraFormats =
> > > +                       camera3Format.libcameraFormats;
> > > +
> > > +               LOG(HAL, Debug) << "Trying to map Android format "
> > > +                               << camera3Format.name;
> > > +
> > > +               /*
> > > +                * JPEG is always supported, either produced directly by
> > > the
> > > +                * camera, or encoded in the HAL.
> > > +                */
> > > +               if (androidFormat == HAL_PIXEL_FORMAT_BLOB) {
> > > +                       formatsMap_[androidFormat] = formats::MJPEG;
> > > +                       LOG(HAL, Debug) << "Mapped Android format "
> > > +                                       << camera3Format.name << " to "
> > > +                                       << formats::MJPEG.toString()
> > > +                                       << " (fixed mapping)";
> > > +                       continue;
> > > +               }
> > > +
> > > +               /*
> > > +                * Test the libcamera formats that can produce images
> > > +                * compatible with the format defined by Android.
> > > +                */
> > > +               PixelFormat mappedFormat;
> > > +               for (const PixelFormat &pixelFormat : libcameraFormats) {
> > > +
> > > +                       LOG(HAL, Debug) << "Testing " <<
> > > pixelFormat.toString();
> > > +
> > > +                       /*
> > > +                        * The stream configuration size can be adjusted,
> > > +                        * not the pixel format.
> > > +                        *
> > > +                        * \todo This could be simplified once all pipeline
> > > +                        * handlers will report the StreamFormats list of
> > > +                        * supported formats.
> > > +                        */
> > > +                       cfg.pixelFormat = pixelFormat;
> > > +
> > > +                       CameraConfiguration::Status status =
> > > cameraConfig->validate();
> > > +                       if (status != CameraConfiguration::Invalid &&
> > > +                           cfg.pixelFormat == pixelFormat) {
> > > +                               mappedFormat = pixelFormat;
> > > +                               break;
> > > +                       }
> > > +               }
> > > +
> > > +               if (!mappedFormat.isValid()) {
> > > +                       /* If the format is not mandatory, skip it. */
> > > +                       if (!camera3Format.mandatory)
> > > +                               continue;
> > > +
> > > +                       LOG(HAL, Error)
> > > +                               << "Failed to map mandatory Android format
> > > "
> > > +                               << camera3Format.name << " ("
> > > +                               << utils::hex(androidFormat) << "):
> > > aborting";
> > > +                       return -EINVAL;
> > > +               }
> > > +
> > > +               /*
> > > +                * Record the mapping and then proceed to generate the
> > > +                * stream configurations map, by testing the image
> > > resolutions.
> > > +                */
> > > +               formatsMap_[androidFormat] = mappedFormat;
> > > +               LOG(HAL, Debug) << "Mapped Android format "
> > > +                               << camera3Format.name << " to "
> > > +                               << mappedFormat.toString();
> > > +
> > > +               std::vector<Size> resolutions;
> > > +               const PixelFormatInfo &info =
> > > PixelFormatInfo::info(mappedFormat);
> > > +               if (info.colourEncoding ==
> > > PixelFormatInfo::ColourEncodingRAW)
> > > +                       resolutions = getRawResolutions(mappedFormat);
> > > +               else
> > > +                       resolutions = getYUVResolutions(cameraConfig.get(),
> > > +                                                       mappedFormat,
> > > +                                                       cameraResolutions);
> > > +
> > > +               for (const Size &res : resolutions) {
> > > +                       streamConfigurations_.push_back({ res,
> > > androidFormat });
> > > +
> > > +                       /*
> > > +                        * If the format is HAL_PIXEL_FORMAT_YCbCr_420_888
> > > +                        * from which JPEG is produced, add an entry for
> > > +                        * the JPEG stream.
> > > +                        *
> > > +                        * \todo Wire the JPEG encoder to query the
> > > supported
> > > +                        * sizes provided a list of formats it can encode.
> > > +                        *
> > > +                        * \todo Support JPEG streams produced by the
> > > Camera
> > > +                        * natively.
> > > +                        */
> > > +                       if (androidFormat ==
> > > HAL_PIXEL_FORMAT_YCbCr_420_888) {
> > > +                               streamConfigurations_.push_back(
> > > +                                       { res, HAL_PIXEL_FORMAT_BLOB });
> > > +                               maxJpegSize = std::max(maxJpegSize, res);
> > > +                       }
> > > +               }
> > > +
> > > +               /*
> > > +                * \todo Calculate the maximum JPEG buffer size by asking
> > > the
> > > +                * encoder giving the maximum frame size required.
> > > +                */
> > > +               maxJpegBufferSize_ = maxJpegSize.width *
> > > maxJpegSize.height * 1.5;
> > > +       }
> > > +
> > > +       LOG(HAL, Debug) << "Collected stream configuration map: ";
> > > +       for (const auto &entry : streamConfigurations_)
> > > +               LOG(HAL, Debug) << "{ " << entry.resolution.toString() <<
> > > " - "
> > > +                               << utils::hex(entry.androidFormat) << " }";
> > > +
> > > +       return 0;
> > > +}
> > > +
> > > +int CameraCapabilities::initializeStaticMetadata()
> > > +{
> > > +       staticMetadata_ = std::make_unique<CameraMetadata>(64, 1024);
> > > +       if (!staticMetadata_->isValid()) {
> > > +               LOG(HAL, Error) << "Failed to allocate static metadata";
> > > +               staticMetadata_.reset();
> > > +               return -EINVAL;
> > > +       }
> > > +
> > > +       const ControlInfoMap &controlsInfo = camera_->controls();
> > > +       const ControlList &properties = camera_->properties();
> > > +
> > > +       /* Color correction static metadata. */
> > > +       {
> > > +               std::vector<uint8_t> data;
> > > +               data.reserve(3);
> > > +               const auto &infoMap =
> > > controlsInfo.find(&controls::draft::ColorCorrectionAberrationMode);
> > > +               if (infoMap != controlsInfo.end()) {
> > > +                       for (const auto &value : infoMap->second.values())
> > > +                               data.push_back(value.get<int32_t>());
> > > +               } else {
> > > +
> > >  data.push_back(ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF);
> > > +               }
> > > +
> > >  staticMetadata_->addEntry(ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
> > > +                                         data);
> > > +       }
> > > +
> > > +       /* Control static metadata. */
> > > +       std::vector<uint8_t> aeAvailableAntiBandingModes = {
> > > +               ANDROID_CONTROL_AE_ANTIBANDING_MODE_OFF,
> > > +               ANDROID_CONTROL_AE_ANTIBANDING_MODE_50HZ,
> > > +               ANDROID_CONTROL_AE_ANTIBANDING_MODE_60HZ,
> > > +               ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO,
> > > +       };
> > > +
> > >  staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,
> > > +                                 aeAvailableAntiBandingModes);
> > > +
> > > +       std::vector<uint8_t> aeAvailableModes = {
> > > +               ANDROID_CONTROL_AE_MODE_ON,
> > > +       };
> > > +       staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_MODES,
> > > +                                 aeAvailableModes);
> > > +
> > > +       int64_t minFrameDurationNsec = -1;
> > > +       int64_t maxFrameDurationNsec = -1;
> > > +       const auto frameDurationsInfo =
> > > controlsInfo.find(&controls::FrameDurationLimits);
> > > +       if (frameDurationsInfo != controlsInfo.end()) {
> > > +               minFrameDurationNsec =
> > > frameDurationsInfo->second.min().get<int64_t>() * 1000;
> > > +               maxFrameDurationNsec =
> > > frameDurationsInfo->second.max().get<int64_t>() * 1000;
> > > +
> > > +               /*
> > > +                * Adjust the minimum frame duration to comply with Android
> > > +                * requirements. The camera service mandates all
> > > preview/record
> > > +                * streams to have a minimum frame duration < 33,366
> > > milliseconds
> > > +                * (see MAX_PREVIEW_RECORD_DURATION_NS in the camera
> > > service
> > > +                * implementation).
> > > +                *
> > > +                * If we're close enough (+ 500 useconds) to that value,
> > > round
> > > +                * the minimum frame duration of the camera to an accepted
> > > +                * value.
> > > +                */
> > > +               static constexpr int64_t MAX_PREVIEW_RECORD_DURATION_NS =
> > > 1e9 / 29.97;
> > > +               if (minFrameDurationNsec > MAX_PREVIEW_RECORD_DURATION_NS
> > > &&
> > > +                   minFrameDurationNsec < MAX_PREVIEW_RECORD_DURATION_NS
> > > + 500000)
> > > +                       minFrameDurationNsec =
> > > MAX_PREVIEW_RECORD_DURATION_NS - 1000;
> > > +
> > > +               /*
> > > +                * The AE routine frame rate limits are computed using the
> > > frame
> > > +                * duration limits, as libcamera clips the AE routine to
> > > the
> > > +                * frame durations.
> > > +                */
> > > +               int32_t maxFps = std::round(1e9 / minFrameDurationNsec);
> > > +               int32_t minFps = std::round(1e9 / maxFrameDurationNsec);
> > > +               minFps = std::max(1, minFps);
> > > +
> > > +               /*
> > > +                * Force rounding errors so that we have the proper frame
> > > +                * durations for when we reuse these variables later
> > > +                */
> > > +               minFrameDurationNsec = 1e9 / maxFps;
> > > +               maxFrameDurationNsec = 1e9 / minFps;
> > > +
> > > +               /*
> > > +                * Register to the camera service {min, max} and {max, max}
> > > +                * intervals as requested by the metadata documentation.
> > > +                */
> > > +               int32_t availableAeFpsTarget[] = {
> > > +                       minFps, maxFps, maxFps, maxFps
> > > +               };
> > > +
> > >  staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> > > +                                         availableAeFpsTarget);
> > > +       }
> > > +
> > > +       std::vector<int32_t> aeCompensationRange = {
> > > +               0, 0,
> > > +       };
> > > +       staticMetadata_->addEntry(ANDROID_CONTROL_AE_COMPENSATION_RANGE,
> > > +                                 aeCompensationRange);
> > > +
> > > +       const camera_metadata_rational_t aeCompensationStep[] = {
> > > +               { 0, 1 }
> > > +       };
> > > +       staticMetadata_->addEntry(ANDROID_CONTROL_AE_COMPENSATION_STEP,
> > > +                                 aeCompensationStep);
> > > +
> > > +       std::vector<uint8_t> availableAfModes = {
> > > +               ANDROID_CONTROL_AF_MODE_OFF,
> > > +       };
> > > +       staticMetadata_->addEntry(ANDROID_CONTROL_AF_AVAILABLE_MODES,
> > > +                                 availableAfModes);
> > > +
> > > +       std::vector<uint8_t> availableEffects = {
> > > +               ANDROID_CONTROL_EFFECT_MODE_OFF,
> > > +       };
> > > +       staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_EFFECTS,
> > > +                                 availableEffects);
> > > +
> > > +       std::vector<uint8_t> availableSceneModes = {
> > > +               ANDROID_CONTROL_SCENE_MODE_DISABLED,
> > > +       };
> > > +       staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_SCENE_MODES,
> > > +                                 availableSceneModes);
> > > +
> > > +       std::vector<uint8_t> availableStabilizationModes = {
> > > +               ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF,
> > > +       };
> > > +
> > >  staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
> > > +                                 availableStabilizationModes);
> > > +
> > > +       /*
> > > +        * \todo Inspect the Camera capabilities to report the available
> > > +        * AWB modes. Default to AUTO as CTS tests require it.
> > > +        */
> > > +       std::vector<uint8_t> availableAwbModes = {
> > > +               ANDROID_CONTROL_AWB_MODE_AUTO,
> > > +       };
> > > +       staticMetadata_->addEntry(ANDROID_CONTROL_AWB_AVAILABLE_MODES,
> > > +                                 availableAwbModes);
> > > +
> > > +       std::vector<int32_t> availableMaxRegions = {
> > > +               0, 0, 0,
> > > +       };
> > > +       staticMetadata_->addEntry(ANDROID_CONTROL_MAX_REGIONS,
> > > +                                 availableMaxRegions);
> > > +
> > > +       std::vector<uint8_t> sceneModesOverride = {
> > > +               ANDROID_CONTROL_AE_MODE_ON,
> > > +               ANDROID_CONTROL_AWB_MODE_AUTO,
> > > +               ANDROID_CONTROL_AF_MODE_OFF,
> > > +       };
> > > +       staticMetadata_->addEntry(ANDROID_CONTROL_SCENE_MODE_OVERRIDES,
> > > +                                 sceneModesOverride);
> > > +
> > > +       uint8_t aeLockAvailable = ANDROID_CONTROL_AE_LOCK_AVAILABLE_FALSE;
> > > +       staticMetadata_->addEntry(ANDROID_CONTROL_AE_LOCK_AVAILABLE,
> > > +                                 aeLockAvailable);
> > > +
> > > +       uint8_t awbLockAvailable =
> > > ANDROID_CONTROL_AWB_LOCK_AVAILABLE_FALSE;
> > > +       staticMetadata_->addEntry(ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
> > > +                                 awbLockAvailable);
> > > +
> > > +       char availableControlModes = ANDROID_CONTROL_MODE_AUTO;
> > > +       staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_MODES,
> > > +                                 availableControlModes);
> > > +
> > > +       /* JPEG static metadata. */
> > > +
> > > +       /*
> > > +        * Create the list of supported thumbnail sizes by inspecting the
> > > +        * available JPEG resolutions collected in streamConfigurations_
> > > and
> > > +        * generate one entry for each aspect ratio.
> > > +        *
> > > +        * The JPEG thumbnailer can freely scale, so pick an arbitrary
> > > +        * (160, 160) size as the bounding rectangle, which is then
> > > cropped to
> > > +        * the different supported aspect ratios.
> > > +        */
> > > +       constexpr Size maxJpegThumbnail(160, 160);
> > > +       std::vector<Size> thumbnailSizes;
> > > +       thumbnailSizes.push_back({ 0, 0 });
> > > +       for (const auto &entry : streamConfigurations_) {
> > > +               if (entry.androidFormat != HAL_PIXEL_FORMAT_BLOB)
> > > +                       continue;
> > > +
> > > +               Size thumbnailSize = maxJpegThumbnail
> > > +                                    .boundedToAspectRatio({
> > > entry.resolution.width,
> > > +
> > > entry.resolution.height });
> > > +               thumbnailSizes.push_back(thumbnailSize);
> > > +       }
> > > +
> > > +       std::sort(thumbnailSizes.begin(), thumbnailSizes.end());
> > > +       auto last = std::unique(thumbnailSizes.begin(),
> > > thumbnailSizes.end());
> > > +       thumbnailSizes.erase(last, thumbnailSizes.end());
> > > +
> > > +       /* Transform sizes in to a list of integers that can be consumed.
> > > */
> > > +       std::vector<int32_t> thumbnailEntries;
> > > +       thumbnailEntries.reserve(thumbnailSizes.size() * 2);
> > > +       for (const auto &size : thumbnailSizes) {
> > > +               thumbnailEntries.push_back(size.width);
> > > +               thumbnailEntries.push_back(size.height);
> > > +       }
> > > +       staticMetadata_->addEntry(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,
> > > +                                 thumbnailEntries);
> > > +
> > > +       staticMetadata_->addEntry(ANDROID_JPEG_MAX_SIZE,
> > > maxJpegBufferSize_);
> > > +
> > > +       /* Sensor static metadata. */
> > > +       std::array<int32_t, 2> pixelArraySize;
> > > +       {
> > > +               const Size &size =
> > > properties.get(properties::PixelArraySize);
> > > +               pixelArraySize[0] = size.width;
> > > +               pixelArraySize[1] = size.height;
> > > +
> > >  staticMetadata_->addEntry(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
> > > +                                         pixelArraySize);
> > > +       }
> > > +
> > > +       if (properties.contains(properties::UnitCellSize)) {
> > > +               const Size &cellSize =
> > > properties.get<Size>(properties::UnitCellSize);
> > > +               std::array<float, 2> physicalSize{
> > > +                       cellSize.width * pixelArraySize[0] / 1e6f,
> > > +                       cellSize.height * pixelArraySize[1] / 1e6f
> > > +               };
> > > +
> > >  staticMetadata_->addEntry(ANDROID_SENSOR_INFO_PHYSICAL_SIZE,
> > > +                                         physicalSize);
> > > +       }
> > > +
> > > +       {
> > > +               const Span<const Rectangle> &rects =
> > > +                       properties.get(properties::PixelArrayActiveAreas);
> > > +               std::vector<int32_t> data{
> > > +                       static_cast<int32_t>(rects[0].x),
> > > +                       static_cast<int32_t>(rects[0].y),
> > > +                       static_cast<int32_t>(rects[0].width),
> > > +                       static_cast<int32_t>(rects[0].height),
> > > +               };
> > > +
> > >  staticMetadata_->addEntry(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
> > > +                                         data);
> > > +       }
> > > +
> > > +       int32_t sensitivityRange[] = {
> > > +               32, 2400,
> > > +       };
> > > +       staticMetadata_->addEntry(ANDROID_SENSOR_INFO_SENSITIVITY_RANGE,
> > > +                                 sensitivityRange);
> > > +
> > > +       /* Report the color filter arrangement if the camera reports it. */
> > > +       if
> > > (properties.contains(properties::draft::ColorFilterArrangement)) {
> > > +               uint8_t filterArr =
> > > properties.get(properties::draft::ColorFilterArrangement);
> > > +
> > >  staticMetadata_->addEntry(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,
> > > +                                         filterArr);
> > > +       }
> > > +
> > > +       const auto &exposureInfo =
> > > controlsInfo.find(&controls::ExposureTime);
> > > +       if (exposureInfo != controlsInfo.end()) {
> > > +               int64_t exposureTimeRange[2] = {
> > > +                       exposureInfo->second.min().get<int32_t>() * 1000LL,
> > > +                       exposureInfo->second.max().get<int32_t>() * 1000LL,
> > > +               };
> > > +
> > >  staticMetadata_->addEntry(ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE,
> > > +                                         exposureTimeRange, 2);
> > > +       }
> > > +
> > > +       staticMetadata_->addEntry(ANDROID_SENSOR_ORIENTATION,
> > > orientation_);
> > > +
> > > +       std::vector<int32_t> testPatternModes = {
> > > +               ANDROID_SENSOR_TEST_PATTERN_MODE_OFF
> > > +       };
> > > +       const auto &testPatternsInfo =
> > > +               controlsInfo.find(&controls::draft::TestPatternMode);
> > > +       if (testPatternsInfo != controlsInfo.end()) {
> > > +               const auto &values = testPatternsInfo->second.values();
> > > +               ASSERT(!values.empty());
> > > +               for (const auto &value : values) {
> > > +                       switch (value.get<int32_t>()) {
> > > +                       case controls::draft::TestPatternModeOff:
> > > +                               /*
> > > +                                * ANDROID_SENSOR_TEST_PATTERN_MODE_OFF is
> > > +                                * already in testPatternModes.
> > > +                                */
> > > +                               break;
> > > +
> > > +                       case controls::draft::TestPatternModeSolidColor:
> > > +                               testPatternModes.push_back(
> > > +
> > >  ANDROID_SENSOR_TEST_PATTERN_MODE_SOLID_COLOR);
> > > +                               break;
> > > +
> > > +                       case controls::draft::TestPatternModeColorBars:
> > > +                               testPatternModes.push_back(
> > > +
> > >  ANDROID_SENSOR_TEST_PATTERN_MODE_COLOR_BARS);
> > > +                               break;
> > > +
> > > +                       case
> > > controls::draft::TestPatternModeColorBarsFadeToGray:
> > > +                               testPatternModes.push_back(
> > > +
> > >  ANDROID_SENSOR_TEST_PATTERN_MODE_COLOR_BARS_FADE_TO_GRAY);
> > > +                               break;
> > > +
> > > +                       case controls::draft::TestPatternModePn9:
> > > +                               testPatternModes.push_back(
> > > +
> > >  ANDROID_SENSOR_TEST_PATTERN_MODE_PN9);
> > > +                               break;
> > > +
> > > +                       case controls::draft::TestPatternModeCustom1:
> > > +                               /* We don't support this yet. */
> > > +                               break;
> > > +
> > > +                       default:
> > > +                               LOG(HAL, Error) << "Unknown test pattern
> > > mode: "
> > > +                                               << value.get<int32_t>();
> > > +                               continue;
> > > +                       }
> > > +               }
> > > +       }
> > > +
> > >  staticMetadata_->addEntry(ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES,
> > > +                                 testPatternModes);
> > > +
> > > +       uint8_t timestampSource =
> > > ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN;
> > > +       staticMetadata_->addEntry(ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE,
> > > +                                 timestampSource);
> > > +
> > > +       if (maxFrameDurationNsec > 0)
> > > +
> > >  staticMetadata_->addEntry(ANDROID_SENSOR_INFO_MAX_FRAME_DURATION,
> > > +                                         maxFrameDurationNsec);
> > > +
> > > +       /* Statistics static metadata. */
> > > +       uint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
> > > +
> > >  staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
> > > +                                 faceDetectMode);
> > > +
> > > +       int32_t maxFaceCount = 0;
> > > +       staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
> > > +                                 maxFaceCount);
> > > +
> > > +       {
> > > +               std::vector<uint8_t> data;
> > > +               data.reserve(2);
> > > +               const auto &infoMap =
> > > controlsInfo.find(&controls::draft::LensShadingMapMode);
> > > +               if (infoMap != controlsInfo.end()) {
> > > +                       for (const auto &value : infoMap->second.values())
> > > +                               data.push_back(value.get<int32_t>());
> > > +               } else {
> > > +
> > >  data.push_back(ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF);
> > > +               }
> > > +
> > >  staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES,
> > > +                                         data);
> > > +       }
> > > +
> > > +       /* Sync static metadata. */
> > > +       int32_t maxLatency = ANDROID_SYNC_MAX_LATENCY_UNKNOWN;
> > > +       staticMetadata_->addEntry(ANDROID_SYNC_MAX_LATENCY, maxLatency);
> > > +
> > > +       /* Flash static metadata. */
> > > +       char flashAvailable = ANDROID_FLASH_INFO_AVAILABLE_FALSE;
> > > +       staticMetadata_->addEntry(ANDROID_FLASH_INFO_AVAILABLE,
> > > +                                 flashAvailable);
> > > +
> > > +       /* Lens static metadata. */
> > > +       std::vector<float> lensApertures = {
> > > +               2.53 / 100,
> > > +       };
> > > +       staticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_APERTURES,
> > > +                                 lensApertures);
> > > +
> > > +       uint8_t lensFacing;
> > > +       switch (facing_) {
> > > +       default:
> > > +       case CAMERA_FACING_FRONT:
> > > +               lensFacing = ANDROID_LENS_FACING_FRONT;
> > > +               break;
> > > +       case CAMERA_FACING_BACK:
> > > +               lensFacing = ANDROID_LENS_FACING_BACK;
> > > +               break;
> > > +       case CAMERA_FACING_EXTERNAL:
> > > +               lensFacing = ANDROID_LENS_FACING_EXTERNAL;
> > > +               break;
> > > +       }
> > > +       staticMetadata_->addEntry(ANDROID_LENS_FACING, lensFacing);
> > > +
> > > +       std::vector<float> lensFocalLengths = {
> > > +               1,
> > > +       };
> > > +
> > >  staticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS,
> > > +                                 lensFocalLengths);
> > > +
> > > +       std::vector<uint8_t> opticalStabilizations = {
> > > +               ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF,
> > > +       };
> > > +
> > >  staticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
> > > +                                 opticalStabilizations);
> > > +
> > > +       float hypeFocalDistance = 0;
> > > +       staticMetadata_->addEntry(ANDROID_LENS_INFO_HYPERFOCAL_DISTANCE,
> > > +                                 hypeFocalDistance);
> > > +
> > > +       float minFocusDistance = 0;
> > > +       staticMetadata_->addEntry(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
> > > +                                 minFocusDistance);
> > > +
> > > +       /* Noise reduction modes. */
> > > +       {
> > > +               std::vector<uint8_t> data;
> > > +               data.reserve(5);
> > > +               const auto &infoMap =
> > > controlsInfo.find(&controls::draft::NoiseReductionMode);
> > > +               if (infoMap != controlsInfo.end()) {
> > > +                       for (const auto &value : infoMap->second.values())
> > > +                               data.push_back(value.get<int32_t>());
> > > +               } else {
> > > +                       data.push_back(ANDROID_NOISE_REDUCTION_MODE_OFF);
> > > +               }
> > > +
> > >  staticMetadata_->addEntry(ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
> > > +                                         data);
> > > +       }
> > > +
> > > +       /* Scaler static metadata. */
> > > +
> > > +       /*
> > > +        * \todo The digital zoom factor is a property that depends on the
> > > +        * desired output configuration and the sensor frame size input to
> > > the
> > > +        * ISP. This information is not available to the Android HAL, not
> > > at
> > > +        * initialization time at least.
> > > +        *
> > > +        * As a workaround rely on pipeline handlers initializing the
> > > +        * ScalerCrop control with the camera default configuration and
> > > use the
> > > +        * maximum and minimum crop rectangles to calculate the digital
> > > zoom
> > > +        * factor.
> > > +        */
> > > +       float maxZoom = 1.0f;
> > > +       const auto scalerCrop = controlsInfo.find(&controls::ScalerCrop);
> > > +       if (scalerCrop != controlsInfo.end()) {
> > > +               Rectangle min = scalerCrop->second.min().get<Rectangle>();
> > > +               Rectangle max = scalerCrop->second.max().get<Rectangle>();
> > > +               maxZoom = std::min(1.0f * max.width / min.width,
> > > +                                  1.0f * max.height / min.height);
> > > +       }
> > > +
> > >  staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
> > > +                                 maxZoom);
> > > +
> > > +       std::vector<uint32_t> availableStreamConfigurations;
> > > +       availableStreamConfigurations.reserve(streamConfigurations_.size()
> > > * 4);
> > > +       for (const auto &entry : streamConfigurations_) {
> > > +
> > >  availableStreamConfigurations.push_back(entry.androidFormat);
> > > +
> > >  availableStreamConfigurations.push_back(entry.resolution.width);
> > > +
> > >  availableStreamConfigurations.push_back(entry.resolution.height);
> > > +               availableStreamConfigurations.push_back(
> > > +
> > >  ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT);
> > > +       }
> > > +
> > >  staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
> > > +                                 availableStreamConfigurations);
> > > +
> > > +       std::vector<int64_t> availableStallDurations = {
> > > +               ANDROID_SCALER_AVAILABLE_FORMATS_BLOB, 2560, 1920,
> > > 33333333,
> > > +       };
> > > +       staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,
> > > +                                 availableStallDurations);
> > > +
> > > +       /* Use the minimum frame duration for all the YUV/RGB formats. */
> > > +       if (minFrameDurationNsec > 0) {
> > > +               std::vector<int64_t> minFrameDurations;
> > > +               minFrameDurations.reserve(streamConfigurations_.size() *
> > > 4);
> > > +               for (const auto &entry : streamConfigurations_) {
> > > +                       minFrameDurations.push_back(entry.androidFormat);
> > > +
> > >  minFrameDurations.push_back(entry.resolution.width);
> > > +
> > >  minFrameDurations.push_back(entry.resolution.height);
> > > +                       minFrameDurations.push_back(minFrameDurationNsec);
> > > +               }
> > > +
> > >  staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
> > > +                                         minFrameDurations);
> > > +       }
> > > +
> > > +       uint8_t croppingType = ANDROID_SCALER_CROPPING_TYPE_CENTER_ONLY;
> > > +       staticMetadata_->addEntry(ANDROID_SCALER_CROPPING_TYPE,
> > > croppingType);
> > > +
> > > +       /* Info static metadata. */
> > > +       uint8_t supportedHWLevel =
> > > ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
> > > +       staticMetadata_->addEntry(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
> > > +                                 supportedHWLevel);
> > > +
> > > +       /* Request static metadata. */
> > > +       int32_t partialResultCount = 1;
> > > +       staticMetadata_->addEntry(ANDROID_REQUEST_PARTIAL_RESULT_COUNT,
> > > +                                 partialResultCount);
> > > +
> > > +       {
> > > +               /* Default the value to 2 if not reported by the camera. */
> > > +               uint8_t maxPipelineDepth = 2;
> > > +               const auto &infoMap =
> > > controlsInfo.find(&controls::draft::PipelineDepth);
> > > +               if (infoMap != controlsInfo.end())
> > > +                       maxPipelineDepth =
> > > infoMap->second.max().get<int32_t>();
> > > +
> > >  staticMetadata_->addEntry(ANDROID_REQUEST_PIPELINE_MAX_DEPTH,
> > > +                                         maxPipelineDepth);
> > > +       }
> > > +
> > > +       /* LIMITED does not support reprocessing. */
> > > +       uint32_t maxNumInputStreams = 0;
> > > +       staticMetadata_->addEntry(ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,
> > > +                                 maxNumInputStreams);
> > > +
> > > +       std::vector<uint8_t> availableCapabilities = {
> > > +               ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE,
> > > +       };
> > > +
> > > +       /* Report if camera supports RAW. */
> > > +       bool rawStreamAvailable = false;
> > > +       std::unique_ptr<CameraConfiguration> cameraConfig =
> > > +               camera_->generateConfiguration({ StreamRole::Raw });
> > > +       if (cameraConfig && !cameraConfig->empty()) {
> > > +               const PixelFormatInfo &info =
> > > +
> > >  PixelFormatInfo::info(cameraConfig->at(0).pixelFormat);
> > > +               /* Only advertise RAW support if RAW16 is possible. */
> > > +               if (info.colourEncoding ==
> > > PixelFormatInfo::ColourEncodingRAW &&
> > > +                   info.bitsPerPixel == 16) {
> > > +                       rawStreamAvailable = true;
> > > +
> > >  availableCapabilities.push_back(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW);
> > > +               }
> > > +       }
> > > +
> > > +       /* Number of { RAW, YUV, JPEG } supported output streams */
> > > +       int32_t numOutStreams[] = { rawStreamAvailable, 2, 1 };
> > > +       staticMetadata_->addEntry(ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,
> > > +                                 numOutStreams);
> > > +
> > > +       staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
> > > +                                 availableCapabilities);
> > > +
> > > +       std::vector<int32_t> availableCharacteristicsKeys = {
> > > +               ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
> > > +               ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,
> > > +               ANDROID_CONTROL_AE_AVAILABLE_MODES,
> > > +               ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> > > +               ANDROID_CONTROL_AE_COMPENSATION_RANGE,
> > > +               ANDROID_CONTROL_AE_COMPENSATION_STEP,
> > > +               ANDROID_CONTROL_AE_LOCK_AVAILABLE,
> > > +               ANDROID_CONTROL_AF_AVAILABLE_MODES,
> > > +               ANDROID_CONTROL_AVAILABLE_EFFECTS,
> > > +               ANDROID_CONTROL_AVAILABLE_MODES,
> > > +               ANDROID_CONTROL_AVAILABLE_SCENE_MODES,
> > > +               ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
> > > +               ANDROID_CONTROL_AWB_AVAILABLE_MODES,
> > > +               ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
> > > +               ANDROID_CONTROL_MAX_REGIONS,
> > > +               ANDROID_CONTROL_SCENE_MODE_OVERRIDES,
> > > +               ANDROID_FLASH_INFO_AVAILABLE,
> > > +               ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
> > > +               ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,
> > > +               ANDROID_JPEG_MAX_SIZE,
> > > +               ANDROID_LENS_FACING,
> > > +               ANDROID_LENS_INFO_AVAILABLE_APERTURES,
> > > +               ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS,
> > > +               ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
> > > +               ANDROID_LENS_INFO_HYPERFOCAL_DISTANCE,
> > > +               ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
> > > +               ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
> > > +               ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
> > > +               ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,
> > > +               ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,
> > > +               ANDROID_REQUEST_PARTIAL_RESULT_COUNT,
> > > +               ANDROID_REQUEST_PIPELINE_MAX_DEPTH,
> > > +               ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
> > > +               ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
> > > +               ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,
> > > +               ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
> > > +               ANDROID_SCALER_CROPPING_TYPE,
> > > +               ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES,
> > > +               ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
> > > +               ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,
> > > +               ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE,
> > > +               ANDROID_SENSOR_INFO_MAX_FRAME_DURATION,
> > > +               ANDROID_SENSOR_INFO_PHYSICAL_SIZE,
> > > +               ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
> > > +               ANDROID_SENSOR_INFO_SENSITIVITY_RANGE,
> > > +               ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE,
> > > +               ANDROID_SENSOR_ORIENTATION,
> > > +               ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
> > > +               ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
> > > +               ANDROID_SYNC_MAX_LATENCY,
> > > +       };
> > > +
> > >  staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS,
> > > +                                 availableCharacteristicsKeys);
> > > +
> > > +       std::vector<int32_t> availableRequestKeys = {
> > > +               ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
> > > +               ANDROID_CONTROL_AE_ANTIBANDING_MODE,
> > > +               ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
> > > +               ANDROID_CONTROL_AE_LOCK,
> > > +               ANDROID_CONTROL_AE_MODE,
> > > +               ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
> > > +               ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
> > > +               ANDROID_CONTROL_AF_MODE,
> > > +               ANDROID_CONTROL_AF_TRIGGER,
> > > +               ANDROID_CONTROL_AWB_LOCK,
> > > +               ANDROID_CONTROL_AWB_MODE,
> > > +               ANDROID_CONTROL_CAPTURE_INTENT,
> > > +               ANDROID_CONTROL_EFFECT_MODE,
> > > +               ANDROID_CONTROL_MODE,
> > > +               ANDROID_CONTROL_SCENE_MODE,
> > > +               ANDROID_CONTROL_VIDEO_STABILIZATION_MODE,
> > > +               ANDROID_FLASH_MODE,
> > > +               ANDROID_JPEG_ORIENTATION,
> > > +               ANDROID_JPEG_QUALITY,
> > > +               ANDROID_JPEG_THUMBNAIL_QUALITY,
> > > +               ANDROID_JPEG_THUMBNAIL_SIZE,
> > > +               ANDROID_LENS_APERTURE,
> > > +               ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
> > > +               ANDROID_NOISE_REDUCTION_MODE,
> > > +               ANDROID_SCALER_CROP_REGION,
> > > +               ANDROID_STATISTICS_FACE_DETECT_MODE
> > > +       };
> > > +       staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS,
> > > +                                 availableRequestKeys);
> > > +
> > > +       std::vector<int32_t> availableResultKeys = {
> > > +               ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
> > > +               ANDROID_CONTROL_AE_ANTIBANDING_MODE,
> > > +               ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
> > > +               ANDROID_CONTROL_AE_LOCK,
> > > +               ANDROID_CONTROL_AE_MODE,
> > > +               ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
> > > +               ANDROID_CONTROL_AE_STATE,
> > > +               ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
> > > +               ANDROID_CONTROL_AF_MODE,
> > > +               ANDROID_CONTROL_AF_STATE,
> > > +               ANDROID_CONTROL_AF_TRIGGER,
> > > +               ANDROID_CONTROL_AWB_LOCK,
> > > +               ANDROID_CONTROL_AWB_MODE,
> > > +               ANDROID_CONTROL_AWB_STATE,
> > > +               ANDROID_CONTROL_CAPTURE_INTENT,
> > > +               ANDROID_CONTROL_EFFECT_MODE,
> > > +               ANDROID_CONTROL_MODE,
> > > +               ANDROID_CONTROL_SCENE_MODE,
> > > +               ANDROID_CONTROL_VIDEO_STABILIZATION_MODE,
> > > +               ANDROID_FLASH_MODE,
> > > +               ANDROID_FLASH_STATE,
> > > +               ANDROID_JPEG_GPS_COORDINATES,
> > > +               ANDROID_JPEG_GPS_PROCESSING_METHOD,
> > > +               ANDROID_JPEG_GPS_TIMESTAMP,
> > > +               ANDROID_JPEG_ORIENTATION,
> > > +               ANDROID_JPEG_QUALITY,
> > > +               ANDROID_JPEG_SIZE,
> > > +               ANDROID_JPEG_THUMBNAIL_QUALITY,
> > > +               ANDROID_JPEG_THUMBNAIL_SIZE,
> > > +               ANDROID_LENS_APERTURE,
> > > +               ANDROID_LENS_FOCAL_LENGTH,
> > > +               ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
> > > +               ANDROID_LENS_STATE,
> > > +               ANDROID_NOISE_REDUCTION_MODE,
> > > +               ANDROID_REQUEST_PIPELINE_DEPTH,
> > > +               ANDROID_SCALER_CROP_REGION,
> > > +               ANDROID_SENSOR_EXPOSURE_TIME,
> > > +               ANDROID_SENSOR_FRAME_DURATION,
> > > +               ANDROID_SENSOR_ROLLING_SHUTTER_SKEW,
> > > +               ANDROID_SENSOR_TEST_PATTERN_MODE,
> > > +               ANDROID_SENSOR_TIMESTAMP,
> > > +               ANDROID_STATISTICS_FACE_DETECT_MODE,
> > > +               ANDROID_STATISTICS_LENS_SHADING_MAP_MODE,
> > > +               ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE,
> > > +               ANDROID_STATISTICS_SCENE_FLICKER,
> > > +       };
> > > +       staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_RESULT_KEYS,
> > > +                                 availableResultKeys);
> > > +
> > > +       if (!staticMetadata_->isValid()) {
> > > +               LOG(HAL, Error) << "Failed to construct static metadata";
> > > +               staticMetadata_.reset();
> > > +               return -EINVAL;
> > > +       }
> > > +
> > > +       if (staticMetadata_->resized()) {
> > > +               auto [entryCount, dataCount] = staticMetadata_->usage();
> > > +               LOG(HAL, Info)
> > > +                       << "Static metadata resized: " << entryCount
> > > +                       << " entries and " << dataCount << " bytes used";
> > > +       }
> > > +
> > > +       return 0;
> > > +}
> > > +
> > > +/* Translate Android format code to libcamera pixel format. */
> > > +PixelFormat CameraCapabilities::toPixelFormat(int format) const
> > > +{
> > > +       auto it = formatsMap_.find(format);
> > > +       if (it == formatsMap_.end()) {
> > > +               LOG(HAL, Error) << "Requested format " <<
> > > utils::hex(format)
> > > +                               << " not supported";
> > > +               return PixelFormat();
> > > +       }
> > > +
> > > +       return it->second;
> > > +}
> > > +
> > > +std::unique_ptr<CameraMetadata>
> > > CameraCapabilities::requestTemplatePreview() const
> > > +{
> > > +       /*
> > > +        * \todo Keep this in sync with the actual number of entries.
> > > +        * Currently: 20 entries, 35 bytes
> > > +        */
> > > +       auto requestTemplate = std::make_unique<CameraMetadata>(21, 36);
> > > +       if (!requestTemplate->isValid()) {
> > > +               return nullptr;
> > > +       }
> > > +
> > > +       /* Get the FPS range registered in the static metadata. */
> > > +       camera_metadata_ro_entry_t entry;
> > > +       bool found =
> > > staticMetadata_->getEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> > > +                                              &entry);
> > > +       if (!found) {
> > > +               LOG(HAL, Error) << "Cannot create capture template without
> > > FPS range";
> > > +               return nullptr;
> > > +       }
> > > +
> > > +       /*
> > > +        * Assume the AE_AVAILABLE_TARGET_FPS_RANGE static metadata
> > > +        * has been assembled as {{min, max} {max, max}}.
> > > +        */
> > > +       requestTemplate->addEntry(ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
> > > +                                 entry.data.i32, 2);
> > > +
> > > +       uint8_t aeMode = ANDROID_CONTROL_AE_MODE_ON;
> > > +       requestTemplate->addEntry(ANDROID_CONTROL_AE_MODE, aeMode);
> > > +
> > > +       int32_t aeExposureCompensation = 0;
> > > +       requestTemplate->addEntry(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
> > > +                                 aeExposureCompensation);
> > > +
> > > +       uint8_t aePrecaptureTrigger =
> > > ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_IDLE;
> > > +       requestTemplate->addEntry(ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
> > > +                                 aePrecaptureTrigger);
> > > +
> > > +       uint8_t aeLock = ANDROID_CONTROL_AE_LOCK_OFF;
> > > +       requestTemplate->addEntry(ANDROID_CONTROL_AE_LOCK, aeLock);
> > > +
> > > +       uint8_t aeAntibandingMode =
> > > ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO;
> > > +       requestTemplate->addEntry(ANDROID_CONTROL_AE_ANTIBANDING_MODE,
> > > +                                 aeAntibandingMode);
> > > +
> > > +       uint8_t afMode = ANDROID_CONTROL_AF_MODE_OFF;
> > > +       requestTemplate->addEntry(ANDROID_CONTROL_AF_MODE, afMode);
> > > +
> > > +       uint8_t afTrigger = ANDROID_CONTROL_AF_TRIGGER_IDLE;
> > > +       requestTemplate->addEntry(ANDROID_CONTROL_AF_TRIGGER, afTrigger);
> > > +
> > > +       uint8_t awbMode = ANDROID_CONTROL_AWB_MODE_AUTO;
> > > +       requestTemplate->addEntry(ANDROID_CONTROL_AWB_MODE, awbMode);
> > > +
> > > +       uint8_t awbLock = ANDROID_CONTROL_AWB_LOCK_OFF;
> > > +       requestTemplate->addEntry(ANDROID_CONTROL_AWB_LOCK, awbLock);
> > > +
> > > +       uint8_t flashMode = ANDROID_FLASH_MODE_OFF;
> > > +       requestTemplate->addEntry(ANDROID_FLASH_MODE, flashMode);
> > > +
> > > +       uint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
> > > +       requestTemplate->addEntry(ANDROID_STATISTICS_FACE_DETECT_MODE,
> > > +                                 faceDetectMode);
> > > +
> > > +       uint8_t noiseReduction = ANDROID_NOISE_REDUCTION_MODE_OFF;
> > > +       requestTemplate->addEntry(ANDROID_NOISE_REDUCTION_MODE,
> > > +                                 noiseReduction);
> > > +
> > > +       uint8_t aberrationMode =
> > > ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF;
> > > +       requestTemplate->addEntry(ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
> > > +                                 aberrationMode);
> > > +
> > > +       uint8_t controlMode = ANDROID_CONTROL_MODE_AUTO;
> > > +       requestTemplate->addEntry(ANDROID_CONTROL_MODE, controlMode);
> > > +
> > > +       float lensAperture = 2.53 / 100;
> > > +       requestTemplate->addEntry(ANDROID_LENS_APERTURE, lensAperture);
> > > +
> > > +       uint8_t opticalStabilization =
> > > ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF;
> > > +       requestTemplate->addEntry(ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
> > > +                                 opticalStabilization);
> > > +
> > > +       uint8_t captureIntent = ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW;
> > > +       requestTemplate->addEntry(ANDROID_CONTROL_CAPTURE_INTENT,
> > > +                                 captureIntent);
> > > +
> > > +       return requestTemplate;
> > > +}
> > > +
> > > +std::unique_ptr<CameraMetadata>
> > > CameraCapabilities::requestTemplateVideo() const
> > > +{
> > > +       std::unique_ptr<CameraMetadata> previewTemplate =
> > > requestTemplatePreview();
> > > +       if (!previewTemplate)
> > > +               return nullptr;
> > > +
> > > +       /*
> > > +        * The video template requires a fixed FPS range. Everything else
> > > +        * stays the same as the preview template.
> > > +        */
> > > +       camera_metadata_ro_entry_t entry;
> > > +
> > >  staticMetadata_->getEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> > > +                                 &entry);
> > > +
> > > +       /*
> > > +        * Assume the AE_AVAILABLE_TARGET_FPS_RANGE static metadata
> > > +        * has been assembled as {{min, max} {max, max}}.
> > > +        */
> > > +       previewTemplate->updateEntry(ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
> > > +                                    entry.data.i32 + 2, 2);
> > > +
> > > +       return previewTemplate;
> > > +}
> > > diff --git a/src/android/camera_capabilities.h
> > > b/src/android/camera_capabilities.h
> > > new file mode 100644
> > > index 000000000000..f511607bbd90
> > > --- /dev/null
> > > +++ b/src/android/camera_capabilities.h
> > > @@ -0,0 +1,65 @@
> > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > > +/*
> > > + * Copyright (C) 2021, Google Inc.
> > > + *
> > > + * camera_capabilities.h - Camera static properties manager
> > > + */
> > > +#ifndef __ANDROID_CAMERA_CAPABILITIES_H__
> > > +#define __ANDROID_CAMERA_CAPABILITIES_H__
> > > +
> > > +#include <map>
> > > +#include <memory>
> > > +#include <vector>
> > > +
> > > +#include <libcamera/camera.h>
> > > +#include <libcamera/class.h>
> > > +#include <libcamera/formats.h>
> > > +#include <libcamera/geometry.h>
> > > +
> > > +#include "camera_metadata.h"
> > > +
> > > +class CameraCapabilities
> > > +{
> > > +public:
> > > +       CameraCapabilities() = default;
> > > +
> > > +       int initialize(std::shared_ptr<libcamera::Camera> camera,
> > > +                      int orientation, int facing);
> > > +
> > > +       CameraMetadata *staticMetadata() const { return
> > > staticMetadata_.get(); }
> > > +       libcamera::PixelFormat toPixelFormat(int format) const;
> > > +       unsigned int maxJpegBufferSize() const { return
> > > maxJpegBufferSize_; }
> > > +
> > > +       std::unique_ptr<CameraMetadata> requestTemplatePreview() const;
> > > +       std::unique_ptr<CameraMetadata> requestTemplateVideo() const;
> > > +
> > > +private:
> > > +       LIBCAMERA_DISABLE_COPY_AND_MOVE(CameraCapabilities)
> > > +
> > > +       struct Camera3StreamConfiguration {
> > > +               libcamera::Size resolution;
> > > +               int androidFormat;
> > > +       };
> > > +
> > > +       std::vector<libcamera::Size>
> > > +       getYUVResolutions(libcamera::CameraConfiguration *cameraConfig,
> > > +                         const libcamera::PixelFormat &pixelFormat,
> > > +                         const std::vector<libcamera::Size> &resolutions);
> > > +       std::vector<libcamera::Size>
> > > +       getRawResolutions(const libcamera::PixelFormat &pixelFormat);
> > > +       int initializeStreamConfigurations();
> > > +
> > > +       int initializeStaticMetadata();
> > > +
> > > +       std::shared_ptr<libcamera::Camera> camera_;
> > > +
> > > +       int facing_;
> > > +       int orientation_;
> > > +
> > > +       std::vector<Camera3StreamConfiguration> streamConfigurations_;
> > > +       std::map<int, libcamera::PixelFormat> formatsMap_;
> > > +       std::unique_ptr<CameraMetadata> staticMetadata_;
> > > +       unsigned int maxJpegBufferSize_;
> > > +};
> > > +
> > > +#endif /* __ANDROID_CAMERA_CAPABILITIES_H__ */
> > > diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
> > > index 8c71fd0675d3..4bd125d7020a 100644
> > > --- a/src/android/camera_device.cpp
> > > +++ b/src/android/camera_device.cpp
> > > @@ -10,11 +10,8 @@
> > >  #include "camera_ops.h"
> > >  #include "post_processor.h"
> > >
> > > -#include <array>
> > > -#include <cmath>
> > >  #include <fstream>
> > >  #include <sys/mman.h>
> > > -#include <tuple>
> > >  #include <unistd.h>
> > >  #include <vector>
> > >
> > > @@ -23,7 +20,6 @@
> > >  #include <libcamera/formats.h>
> > >  #include <libcamera/property_ids.h>
> > >
> > > -#include "libcamera/internal/formats.h"
> > >  #include "libcamera/internal/log.h"
> > >  #include "libcamera/internal/thread.h"
> > >  #include "libcamera/internal/utils.h"
> > > @@ -36,94 +32,6 @@ LOG_DECLARE_CATEGORY(HAL)
> > >
> > >  namespace {
> > >
> > > -/*
> > > - * \var camera3Resolutions
> > > - * \brief The list of image resolutions defined as mandatory to be
> > > supported by
> > > - * the Android Camera3 specification
> > > - */
> > > -const std::vector<Size> camera3Resolutions = {
> > > -       { 320, 240 },
> > > -       { 640, 480 },
> > > -       { 1280, 720 },
> > > -       { 1920, 1080 }
> > > -};
> > > -
> > > -/*
> > > - * \struct Camera3Format
> > > - * \brief Data associated with an Android format identifier
> > > - * \var libcameraFormats List of libcamera pixel formats compatible with
> > > the
> > > - * Android format
> > > - * \var name The human-readable representation of the Android format code
> > > - */
> > > -struct Camera3Format {
> > > -       std::vector<PixelFormat> libcameraFormats;
> > > -       bool mandatory;
> > > -       const char *name;
> > > -};
> > > -
> > > -/*
> > > - * \var camera3FormatsMap
> > > - * \brief Associate Android format code with ancillary data
> > > - */
> > > -const std::map<int, const Camera3Format> camera3FormatsMap = {
> > > -       {
> > > -               HAL_PIXEL_FORMAT_BLOB, {
> > > -                       { formats::MJPEG },
> > > -                       true,
> > > -                       "BLOB"
> > > -               }
> > > -       }, {
> > > -               HAL_PIXEL_FORMAT_YCbCr_420_888, {
> > > -                       { formats::NV12, formats::NV21 },
> > > -                       true,
> > > -                       "YCbCr_420_888"
> > > -               }
> > > -       }, {
> > > -               /*
> > > -                * \todo Translate IMPLEMENTATION_DEFINED inspecting the
> > > gralloc
> > > -                * usage flag. For now, copy the YCbCr_420 configuration.
> > > -                */
> > > -               HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, {
> > > -                       { formats::NV12, formats::NV21 },
> > > -                       true,
> > > -                       "IMPLEMENTATION_DEFINED"
> > > -               }
> > > -       }, {
> > > -               HAL_PIXEL_FORMAT_RAW10, {
> > > -                       {
> > > -                               formats::SBGGR10_CSI2P,
> > > -                               formats::SGBRG10_CSI2P,
> > > -                               formats::SGRBG10_CSI2P,
> > > -                               formats::SRGGB10_CSI2P
> > > -                       },
> > > -                       false,
> > > -                       "RAW10"
> > > -               }
> > > -       }, {
> > > -               HAL_PIXEL_FORMAT_RAW12, {
> > > -                       {
> > > -                               formats::SBGGR12_CSI2P,
> > > -                               formats::SGBRG12_CSI2P,
> > > -                               formats::SGRBG12_CSI2P,
> > > -                               formats::SRGGB12_CSI2P
> > > -                       },
> > > -                       false,
> > > -                       "RAW12"
> > > -               }
> > > -       }, {
> > > -               HAL_PIXEL_FORMAT_RAW16, {
> > > -                       {
> > > -                               formats::SBGGR16,
> > > -                               formats::SGBRG16,
> > > -                               formats::SGRBG16,
> > > -                               formats::SRGGB16
> > > -                       },
> > > -                       false,
> > > -                       "RAW16"
> > > -               }
> > > -       },
> > > -};
> > > -
> > >  /*
> > >   * \struct Camera3StreamConfig
> > >   * \brief Data to store StreamConfiguration associated with
> > > camera3_stream(s)
> > > @@ -512,242 +420,7 @@ int CameraDevice::initialize(const CameraConfigData
> > > *cameraConfigData)
> > >                 orientation_ = 0;
> > >         }
> > >
> > > -       /* Acquire the camera and initialize available stream
> > > configurations. */
> > > -       int ret = camera_->acquire();
> > > -       if (ret) {
> > > -               LOG(HAL, Error) << "Failed to temporarily acquire the
> > > camera";
> > > -               return ret;
> > > -       }
> > > -
> > > -       ret = initializeStreamConfigurations();
> > > -       camera_->release();
> > > -       return ret;
> > > -}
> > > -
> > > -std::vector<Size> CameraDevice::getYUVResolutions(CameraConfiguration
> > > *cameraConfig,
> > > -                                                 const PixelFormat
> > > &pixelFormat,
> > > -                                                 const std::vector<Size>
> > > &resolutions)
> > > -{
> > > -       std::vector<Size> supportedResolutions;
> > > -
> > > -       StreamConfiguration &cfg = cameraConfig->at(0);
> > > -       for (const Size &res : resolutions) {
> > > -               cfg.pixelFormat = pixelFormat;
> > > -               cfg.size = res;
> > > -
> > > -               CameraConfiguration::Status status =
> > > cameraConfig->validate();
> > > -               if (status != CameraConfiguration::Valid) {
> > > -                       LOG(HAL, Debug) << cfg.toString() << " not
> > > supported";
> > > -                       continue;
> > > -               }
> > > -
> > > -               LOG(HAL, Debug) << cfg.toString() << " supported";
> > > -
> > > -               supportedResolutions.push_back(res);
> > > -       }
> > > -
> > > -       return supportedResolutions;
> > > -}
> > > -
> > > -std::vector<Size> CameraDevice::getRawResolutions(const
> > > libcamera::PixelFormat &pixelFormat)
> > > -{
> > > -       std::unique_ptr<CameraConfiguration> cameraConfig =
> > > -               camera_->generateConfiguration({ StreamRole::Raw });
> > > -       StreamConfiguration &cfg = cameraConfig->at(0);
> > > -       const StreamFormats &formats = cfg.formats();
> > > -       std::vector<Size> supportedResolutions =
> > > formats.sizes(pixelFormat);
> > > -
> > > -       return supportedResolutions;
> > > -}
> > > -
> > > -/*
> > > - * Initialize the format conversion map to translate from Android format
> > > - * identifier to libcamera pixel formats and fill in the list of supported
> > > - * stream configurations to be reported to the Android camera framework
> > > through
> > > - * the static stream configuration metadata.
> > > - */
> > > -int CameraDevice::initializeStreamConfigurations()
> > > -{
> > > -       /*
> > > -        * Get the maximum output resolutions
> > > -        * \todo Get this from the camera properties once defined
> > > -        */
> > > -       std::unique_ptr<CameraConfiguration> cameraConfig =
> > > -               camera_->generateConfiguration({ StillCapture });
> > > -       if (!cameraConfig) {
> > > -               LOG(HAL, Error) << "Failed to get maximum resolution";
> > > -               return -EINVAL;
> > > -       }
> > > -       StreamConfiguration &cfg = cameraConfig->at(0);
> > > -
> > > -       /*
> > > -        * \todo JPEG - Adjust the maximum available resolution by taking
> > > the
> > > -        * JPEG encoder requirements into account (alignment and aspect
> > > ratio).
> > > -        */
> > > -       const Size maxRes = cfg.size;
> > > -       LOG(HAL, Debug) << "Maximum supported resolution: " <<
> > > maxRes.toString();
> > > -
> > > -       /*
> > > -        * Build the list of supported image resolutions.
> > > -        *
> > > -        * The resolutions listed in camera3Resolution are mandatory to be
> > > -        * supported, up to the camera maximum resolution.
> > > -        *
> > > -        * Augment the list by adding resolutions calculated from the
> > > camera
> > > -        * maximum one.
> > > -        */
> > > -       std::vector<Size> cameraResolutions;
> > > -       std::copy_if(camera3Resolutions.begin(), camera3Resolutions.end(),
> > > -                    std::back_inserter(cameraResolutions),
> > > -                    [&](const Size &res) { return res < maxRes; });
> > > -
> > > -       /*
> > > -        * The Camera3 specification suggests adding 1/2 and 1/4 of the
> > > maximum
> > > -        * resolution.
> > > -        */
> > > -       for (unsigned int divider = 2;; divider <<= 1) {
> > > -               Size derivedSize{
> > > -                       maxRes.width / divider,
> > > -                       maxRes.height / divider,
> > > -               };
> > > -
> > > -               if (derivedSize.width < 320 ||
> > > -                   derivedSize.height < 240)
> > > -                       break;
> > > -
> > > -               cameraResolutions.push_back(derivedSize);
> > > -       }
> > > -       cameraResolutions.push_back(maxRes);
> > > -
> > > -       /* Remove duplicated entries from the list of supported
> > > resolutions. */
> > > -       std::sort(cameraResolutions.begin(), cameraResolutions.end());
> > > -       auto last = std::unique(cameraResolutions.begin(),
> > > cameraResolutions.end());
> > > -       cameraResolutions.erase(last, cameraResolutions.end());
> > > -
> > > -       /*
> > > -        * Build the list of supported camera formats.
> > > -        *
> > > -        * To each Android format a list of compatible libcamera formats is
> > > -        * associated. The first libcamera format that tests successful is
> > > added
> > > -        * to the format translation map used when configuring the streams.
> > > -        * It is then tested against the list of supported camera
> > > resolutions to
> > > -        * build the stream configuration map reported through the camera
> > > static
> > > -        * metadata.
> > > -        */
> > > -       Size maxJpegSize;
> > > -       for (const auto &format : camera3FormatsMap) {
> > > -               int androidFormat = format.first;
> > > -               const Camera3Format &camera3Format = format.second;
> > > -               const std::vector<PixelFormat> &libcameraFormats =
> > > -                       camera3Format.libcameraFormats;
> > > -
> > > -               LOG(HAL, Debug) << "Trying to map Android format "
> > > -                               << camera3Format.name;
> > > -
> > > -               /*
> > > -                * JPEG is always supported, either produced directly by
> > > the
> > > -                * camera, or encoded in the HAL.
> > > -                */
> > > -               if (androidFormat == HAL_PIXEL_FORMAT_BLOB) {
> > > -                       formatsMap_[androidFormat] = formats::MJPEG;
> > > -                       LOG(HAL, Debug) << "Mapped Android format "
> > > -                                       << camera3Format.name << " to "
> > > -                                       << formats::MJPEG.toString()
> > > -                                       << " (fixed mapping)";
> > > -                       continue;
> > > -               }
> > > -
> > > -               /*
> > > -                * Test the libcamera formats that can produce images
> > > -                * compatible with the format defined by Android.
> > > -                */
> > > -               PixelFormat mappedFormat;
> > > -               for (const PixelFormat &pixelFormat : libcameraFormats) {
> > > -
> > > -                       LOG(HAL, Debug) << "Testing " <<
> > > pixelFormat.toString();
> > > -
> > > -                       /*
> > > -                        * The stream configuration size can be adjusted,
> > > -                        * not the pixel format.
> > > -                        *
> > > -                        * \todo This could be simplified once all pipeline
> > > -                        * handlers will report the StreamFormats list of
> > > -                        * supported formats.
> > > -                        */
> > > -                       cfg.pixelFormat = pixelFormat;
> > > -
> > > -                       CameraConfiguration::Status status =
> > > cameraConfig->validate();
> > > -                       if (status != CameraConfiguration::Invalid &&
> > > -                           cfg.pixelFormat == pixelFormat) {
> > > -                               mappedFormat = pixelFormat;
> > > -                               break;
> > > -                       }
> > > -               }
> > > -
> > > -               if (!mappedFormat.isValid()) {
> > > -                       /* If the format is not mandatory, skip it. */
> > > -                       if (!camera3Format.mandatory)
> > > -                               continue;
> > > -
> > > -                       LOG(HAL, Error)
> > > -                               << "Failed to map mandatory Android format
> > > "
> > > -                               << camera3Format.name << " ("
> > > -                               << utils::hex(androidFormat) << "):
> > > aborting";
> > > -                       return -EINVAL;
> > > -               }
> > > -
> > > -               /*
> > > -                * Record the mapping and then proceed to generate the
> > > -                * stream configurations map, by testing the image
> > > resolutions.
> > > -                */
> > > -               formatsMap_[androidFormat] = mappedFormat;
> > > -               LOG(HAL, Debug) << "Mapped Android format "
> > > -                               << camera3Format.name << " to "
> > > -                               << mappedFormat.toString();
> > > -
> > > -               std::vector<Size> resolutions;
> > > -               const PixelFormatInfo &info =
> > > PixelFormatInfo::info(mappedFormat);
> > > -               if (info.colourEncoding ==
> > > PixelFormatInfo::ColourEncodingRAW)
> > > -                       resolutions = getRawResolutions(mappedFormat);
> > > -               else
> > > -                       resolutions = getYUVResolutions(cameraConfig.get(),
> > > -                                                       mappedFormat,
> > > -                                                       cameraResolutions);
> > > -
> > > -               for (const Size &res : resolutions) {
> > > -                       streamConfigurations_.push_back({ res,
> > > androidFormat });
> > > -
> > > -                       /*
> > > -                        * If the format is HAL_PIXEL_FORMAT_YCbCr_420_888
> > > -                        * from which JPEG is produced, add an entry for
> > > -                        * the JPEG stream.
> > > -                        *
> > > -                        * \todo Wire the JPEG encoder to query the
> > > supported
> > > -                        * sizes provided a list of formats it can encode.
> > > -                        *
> > > -                        * \todo Support JPEG streams produced by the
> > > Camera
> > > -                        * natively.
> > > -                        */
> > > -                       if (androidFormat ==
> > > HAL_PIXEL_FORMAT_YCbCr_420_888) {
> > > -                               streamConfigurations_.push_back(
> > > -                                       { res, HAL_PIXEL_FORMAT_BLOB });
> > > -                               maxJpegSize = std::max(maxJpegSize, res);
> > > -                       }
> > > -               }
> > > -
> > > -               /*
> > > -                * \todo Calculate the maximum JPEG buffer size by asking
> > > the
> > > -                * encoder giving the maximum frame size required.
> > > -                */
> > > -               maxJpegBufferSize_ = maxJpegSize.width *
> > > maxJpegSize.height * 1.5;
> > > -       }
> > > -
> > > -       LOG(HAL, Debug) << "Collected stream configuration map: ";
> > > -       for (const auto &entry : streamConfigurations_)
> > > -               LOG(HAL, Debug) << "{ " << entry.resolution.toString() <<
> > > " - "
> > > -                               << utils::hex(entry.androidFormat) << " }";
> > > -
> > > -       return 0;
> > > +       return capabilities_.initialize(camera_, orientation_, facing_);
> > >  }
> > >
> > >  /*
> > > @@ -817,802 +490,19 @@ void CameraDevice::stop()
> > >         state_ = State::Stopped;
> > >  }
> > >
> > > -void CameraDevice::setCallbacks(const camera3_callback_ops_t *callbacks)
> > > +unsigned int CameraDevice::maxJpegBufferSize() const
> > >  {
> > > -       callbacks_ = callbacks;
> > > +       return capabilities_.maxJpegBufferSize();
> > >  }
> > >
> > > -/*
> > > - * Return static information for the camera.
> > > - */
> > > -const camera_metadata_t *CameraDevice::getStaticMetadata()
> > > -{
> > > -       if (staticMetadata_)
> > > -               return staticMetadata_->get();
> > > -
> > > -       staticMetadata_ = std::make_unique<CameraMetadata>(64, 1024);
> > > -       if (!staticMetadata_->isValid()) {
> > > -               LOG(HAL, Error) << "Failed to allocate static metadata";
> > > -               staticMetadata_.reset();
> > > -               return nullptr;
> > > -       }
> > > -
> > > -       const ControlInfoMap &controlsInfo = camera_->controls();
> > > -       const ControlList &properties = camera_->properties();
> > > -
> > > -       /* Color correction static metadata. */
> > > -       {
> > > -               std::vector<uint8_t> data;
> > > -               data.reserve(3);
> > > -               const auto &infoMap =
> > > controlsInfo.find(&controls::draft::ColorCorrectionAberrationMode);
> > > -               if (infoMap != controlsInfo.end()) {
> > > -                       for (const auto &value : infoMap->second.values())
> > > -                               data.push_back(value.get<int32_t>());
> > > -               } else {
> > > -
> > >  data.push_back(ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF);
> > > -               }
> > > -
> > >  staticMetadata_->addEntry(ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
> > > -                                         data);
> > > -       }
> > > -
> > > -       /* Control static metadata. */
> > > -       std::vector<uint8_t> aeAvailableAntiBandingModes = {
> > > -               ANDROID_CONTROL_AE_ANTIBANDING_MODE_OFF,
> > > -               ANDROID_CONTROL_AE_ANTIBANDING_MODE_50HZ,
> > > -               ANDROID_CONTROL_AE_ANTIBANDING_MODE_60HZ,
> > > -               ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO,
> > > -       };
> > > -
> > >  staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,
> > > -                                 aeAvailableAntiBandingModes);
> > > -
> > > -       std::vector<uint8_t> aeAvailableModes = {
> > > -               ANDROID_CONTROL_AE_MODE_ON,
> > > -       };
> > > -       staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_MODES,
> > > -                                 aeAvailableModes);
> > > -
> > > -       int64_t minFrameDurationNsec = -1;
> > > -       int64_t maxFrameDurationNsec = -1;
> > > -       const auto frameDurationsInfo =
> > > controlsInfo.find(&controls::FrameDurationLimits);
> > > -       if (frameDurationsInfo != controlsInfo.end()) {
> > > -               minFrameDurationNsec =
> > > frameDurationsInfo->second.min().get<int64_t>() * 1000;
> > > -               maxFrameDurationNsec =
> > > frameDurationsInfo->second.max().get<int64_t>() * 1000;
> > > -
> > > -               /*
> > > -                * Adjust the minimum frame duration to comply with Android
> > > -                * requirements. The camera service mandates all
> > > preview/record
> > > -                * streams to have a minimum frame duration < 33,366
> > > milliseconds
> > > -                * (see MAX_PREVIEW_RECORD_DURATION_NS in the camera
> > > service
> > > -                * implementation).
> > > -                *
> > > -                * If we're close enough (+ 500 useconds) to that value,
> > > round
> > > -                * the minimum frame duration of the camera to an accepted
> > > -                * value.
> > > -                */
> > > -               static constexpr int64_t MAX_PREVIEW_RECORD_DURATION_NS =
> > > 1e9 / 29.97;
> > > -               if (minFrameDurationNsec > MAX_PREVIEW_RECORD_DURATION_NS
> > > &&
> > > -                   minFrameDurationNsec < MAX_PREVIEW_RECORD_DURATION_NS
> > > + 500000)
> > > -                       minFrameDurationNsec =
> > > MAX_PREVIEW_RECORD_DURATION_NS - 1000;
> > > -
> > > -               /*
> > > -                * The AE routine frame rate limits are computed using the
> > > frame
> > > -                * duration limits, as libcamera clips the AE routine to
> > > the
> > > -                * frame durations.
> > > -                */
> > > -               int32_t maxFps = std::round(1e9 / minFrameDurationNsec);
> > > -               int32_t minFps = std::round(1e9 / maxFrameDurationNsec);
> > > -               minFps = std::max(1, minFps);
> > > -
> > > -               /*
> > > -                * Force rounding errors so that we have the proper frame
> > > -                * durations for when we reuse these variables later
> > > -                */
> > > -               minFrameDurationNsec = 1e9 / maxFps;
> > > -               maxFrameDurationNsec = 1e9 / minFps;
> > > -
> > > -               /*
> > > -                * Register to the camera service {min, max} and {max, max}
> > > -                * intervals as requested by the metadata documentation.
> > > -                */
> > > -               int32_t availableAeFpsTarget[] = {
> > > -                       minFps, maxFps, maxFps, maxFps
> > > -               };
> > > -
> > >  staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> > > -                                         availableAeFpsTarget);
> > > -       }
> > > -
> > > -       std::vector<int32_t> aeCompensationRange = {
> > > -               0, 0,
> > > -       };
> > > -       staticMetadata_->addEntry(ANDROID_CONTROL_AE_COMPENSATION_RANGE,
> > > -                                 aeCompensationRange);
> > > -
> > > -       const camera_metadata_rational_t aeCompensationStep[] = {
> > > -               { 0, 1 }
> > > -       };
> > > -       staticMetadata_->addEntry(ANDROID_CONTROL_AE_COMPENSATION_STEP,
> > > -                                 aeCompensationStep);
> > > -
> > > -       std::vector<uint8_t> availableAfModes = {
> > > -               ANDROID_CONTROL_AF_MODE_OFF,
> > > -       };
> > > -       staticMetadata_->addEntry(ANDROID_CONTROL_AF_AVAILABLE_MODES,
> > > -                                 availableAfModes);
> > > -
> > > -       std::vector<uint8_t> availableEffects = {
> > > -               ANDROID_CONTROL_EFFECT_MODE_OFF,
> > > -       };
> > > -       staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_EFFECTS,
> > > -                                 availableEffects);
> > > -
> > > -       std::vector<uint8_t> availableSceneModes = {
> > > -               ANDROID_CONTROL_SCENE_MODE_DISABLED,
> > > -       };
> > > -       staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_SCENE_MODES,
> > > -                                 availableSceneModes);
> > > -
> > > -       std::vector<uint8_t> availableStabilizationModes = {
> > > -               ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF,
> > > -       };
> > > -
> > >  staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
> > > -                                 availableStabilizationModes);
> > > -
> > > -       /*
> > > -        * \todo Inspect the Camera capabilities to report the available
> > > -        * AWB modes. Default to AUTO as CTS tests require it.
> > > -        */
> > > -       std::vector<uint8_t> availableAwbModes = {
> > > -               ANDROID_CONTROL_AWB_MODE_AUTO,
> > > -       };
> > > -       staticMetadata_->addEntry(ANDROID_CONTROL_AWB_AVAILABLE_MODES,
> > > -                                 availableAwbModes);
> > > -
> > > -       std::vector<int32_t> availableMaxRegions = {
> > > -               0, 0, 0,
> > > -       };
> > > -       staticMetadata_->addEntry(ANDROID_CONTROL_MAX_REGIONS,
> > > -                                 availableMaxRegions);
> > > -
> > > -       std::vector<uint8_t> sceneModesOverride = {
> > > -               ANDROID_CONTROL_AE_MODE_ON,
> > > -               ANDROID_CONTROL_AWB_MODE_AUTO,
> > > -               ANDROID_CONTROL_AF_MODE_OFF,
> > > -       };
> > > -       staticMetadata_->addEntry(ANDROID_CONTROL_SCENE_MODE_OVERRIDES,
> > > -                                 sceneModesOverride);
> > > -
> > > -       uint8_t aeLockAvailable = ANDROID_CONTROL_AE_LOCK_AVAILABLE_FALSE;
> > > -       staticMetadata_->addEntry(ANDROID_CONTROL_AE_LOCK_AVAILABLE,
> > > -                                 aeLockAvailable);
> > > -
> > > -       uint8_t awbLockAvailable =
> > > ANDROID_CONTROL_AWB_LOCK_AVAILABLE_FALSE;
> > > -       staticMetadata_->addEntry(ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
> > > -                                 awbLockAvailable);
> > > -
> > > -       char availableControlModes = ANDROID_CONTROL_MODE_AUTO;
> > > -       staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_MODES,
> > > -                                 availableControlModes);
> > > -
> > > -       /* JPEG static metadata. */
> > > -
> > > -       /*
> > > -        * Create the list of supported thumbnail sizes by inspecting the
> > > -        * available JPEG resolutions collected in streamConfigurations_
> > > and
> > > -        * generate one entry for each aspect ratio.
> > > -        *
> > > -        * The JPEG thumbnailer can freely scale, so pick an arbitrary
> > > -        * (160, 160) size as the bounding rectangle, which is then
> > > cropped to
> > > -        * the different supported aspect ratios.
> > > -        */
> > > -       constexpr Size maxJpegThumbnail(160, 160);
> > > -       std::vector<Size> thumbnailSizes;
> > > -       thumbnailSizes.push_back({ 0, 0 });
> > > -       for (const auto &entry : streamConfigurations_) {
> > > -               if (entry.androidFormat != HAL_PIXEL_FORMAT_BLOB)
> > > -                       continue;
> > > -
> > > -               Size thumbnailSize = maxJpegThumbnail
> > > -                                    .boundedToAspectRatio({
> > > entry.resolution.width,
> > > -
> > > entry.resolution.height });
> > > -               thumbnailSizes.push_back(thumbnailSize);
> > > -       }
> > > -
> > > -       std::sort(thumbnailSizes.begin(), thumbnailSizes.end());
> > > -       auto last = std::unique(thumbnailSizes.begin(),
> > > thumbnailSizes.end());
> > > -       thumbnailSizes.erase(last, thumbnailSizes.end());
> > > -
> > > -       /* Transform sizes in to a list of integers that can be consumed.
> > > */
> > > -       std::vector<int32_t> thumbnailEntries;
> > > -       thumbnailEntries.reserve(thumbnailSizes.size() * 2);
> > > -       for (const auto &size : thumbnailSizes) {
> > > -               thumbnailEntries.push_back(size.width);
> > > -               thumbnailEntries.push_back(size.height);
> > > -       }
> > > -       staticMetadata_->addEntry(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,
> > > -                                 thumbnailEntries);
> > > -
> > > -       staticMetadata_->addEntry(ANDROID_JPEG_MAX_SIZE,
> > > maxJpegBufferSize_);
> > > -
> > > -       /* Sensor static metadata. */
> > > -       std::array<int32_t, 2> pixelArraySize;
> > > -       {
> > > -               const Size &size =
> > > properties.get(properties::PixelArraySize);
> > > -               pixelArraySize[0] = size.width;
> > > -               pixelArraySize[1] = size.height;
> > > -
> > >  staticMetadata_->addEntry(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
> > > -                                         pixelArraySize);
> > > -       }
> > > -
> > > -       if (properties.contains(properties::UnitCellSize)) {
> > > -               const Size &cellSize =
> > > properties.get<Size>(properties::UnitCellSize);
> > > -               std::array<float, 2> physicalSize{
> > > -                       cellSize.width * pixelArraySize[0] / 1e6f,
> > > -                       cellSize.height * pixelArraySize[1] / 1e6f
> > > -               };
> > > -
> > >  staticMetadata_->addEntry(ANDROID_SENSOR_INFO_PHYSICAL_SIZE,
> > > -                                         physicalSize);
> > > -       }
> > > -
> > > -       {
> > > -               const Span<const Rectangle> &rects =
> > > -                       properties.get(properties::PixelArrayActiveAreas);
> > > -               std::vector<int32_t> data{
> > > -                       static_cast<int32_t>(rects[0].x),
> > > -                       static_cast<int32_t>(rects[0].y),
> > > -                       static_cast<int32_t>(rects[0].width),
> > > -                       static_cast<int32_t>(rects[0].height),
> > > -               };
> > > -
> > >  staticMetadata_->addEntry(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
> > > -                                         data);
> > > -       }
> > > -
> > > -       int32_t sensitivityRange[] = {
> > > -               32, 2400,
> > > -       };
> > > -       staticMetadata_->addEntry(ANDROID_SENSOR_INFO_SENSITIVITY_RANGE,
> > > -                                 sensitivityRange);
> > > -
> > > -       /* Report the color filter arrangement if the camera reports it. */
> > > -       if
> > > (properties.contains(properties::draft::ColorFilterArrangement)) {
> > > -               uint8_t filterArr =
> > > properties.get(properties::draft::ColorFilterArrangement);
> > > -
> > >  staticMetadata_->addEntry(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,
> > > -                                         filterArr);
> > > -       }
> > > -
> > > -       const auto &exposureInfo =
> > > controlsInfo.find(&controls::ExposureTime);
> > > -       if (exposureInfo != controlsInfo.end()) {
> > > -               int64_t exposureTimeRange[2] = {
> > > -                       exposureInfo->second.min().get<int32_t>() * 1000LL,
> > > -                       exposureInfo->second.max().get<int32_t>() * 1000LL,
> > > -               };
> > > -
> > >  staticMetadata_->addEntry(ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE,
> > > -                                         exposureTimeRange, 2);
> > > -       }
> > > -
> > > -       staticMetadata_->addEntry(ANDROID_SENSOR_ORIENTATION,
> > > orientation_);
> > > -
> > > -       std::vector<int32_t> testPatternModes = {
> > > -               ANDROID_SENSOR_TEST_PATTERN_MODE_OFF
> > > -       };
> > > -       const auto &testPatternsInfo =
> > > -               controlsInfo.find(&controls::draft::TestPatternMode);
> > > -       if (testPatternsInfo != controlsInfo.end()) {
> > > -               const auto &values = testPatternsInfo->second.values();
> > > -               ASSERT(!values.empty());
> > > -               for (const auto &value : values) {
> > > -                       switch (value.get<int32_t>()) {
> > > -                       case controls::draft::TestPatternModeOff:
> > > -                               /*
> > > -                                * ANDROID_SENSOR_TEST_PATTERN_MODE_OFF is
> > > -                                * already in testPatternModes.
> > > -                                */
> > > -                               break;
> > > -
> > > -                       case controls::draft::TestPatternModeSolidColor:
> > > -                               testPatternModes.push_back(
> > > -
> > >  ANDROID_SENSOR_TEST_PATTERN_MODE_SOLID_COLOR);
> > > -                               break;
> > > -
> > > -                       case controls::draft::TestPatternModeColorBars:
> > > -                               testPatternModes.push_back(
> > > -
> > >  ANDROID_SENSOR_TEST_PATTERN_MODE_COLOR_BARS);
> > > -                               break;
> > > -
> > > -                       case
> > > controls::draft::TestPatternModeColorBarsFadeToGray:
> > > -                               testPatternModes.push_back(
> > > -
> > >  ANDROID_SENSOR_TEST_PATTERN_MODE_COLOR_BARS_FADE_TO_GRAY);
> > > -                               break;
> > > -
> > > -                       case controls::draft::TestPatternModePn9:
> > > -                               testPatternModes.push_back(
> > > -
> > >  ANDROID_SENSOR_TEST_PATTERN_MODE_PN9);
> > > -                               break;
> > > -
> > > -                       case controls::draft::TestPatternModeCustom1:
> > > -                               /* We don't support this yet. */
> > > -                               break;
> > > -
> > > -                       default:
> > > -                               LOG(HAL, Error) << "Unknown test pattern
> > > mode: "
> > > -                                               << value.get<int32_t>();
> > > -                               continue;
> > > -                       }
> > > -               }
> > > -       }
> > > -
> > >  staticMetadata_->addEntry(ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES,
> > > -                                 testPatternModes);
> > > -
> > > -       uint8_t timestampSource =
> > > ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN;
> > > -       staticMetadata_->addEntry(ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE,
> > > -                                 timestampSource);
> > > -
> > > -       if (maxFrameDurationNsec > 0)
> > > -
> > >  staticMetadata_->addEntry(ANDROID_SENSOR_INFO_MAX_FRAME_DURATION,
> > > -                                         maxFrameDurationNsec);
> > > -
> > > -       /* Statistics static metadata. */
> > > -       uint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
> > > -
> > >  staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
> > > -                                 faceDetectMode);
> > > -
> > > -       int32_t maxFaceCount = 0;
> > > -       staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
> > > -                                 maxFaceCount);
> > > -
> > > -       {
> > > -               std::vector<uint8_t> data;
> > > -               data.reserve(2);
> > > -               const auto &infoMap =
> > > controlsInfo.find(&controls::draft::LensShadingMapMode);
> > > -               if (infoMap != controlsInfo.end()) {
> > > -                       for (const auto &value : infoMap->second.values())
> > > -                               data.push_back(value.get<int32_t>());
> > > -               } else {
> > > -
> > >  data.push_back(ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF);
> > > -               }
> > > -
> > >  staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES,
> > > -                                         data);
> > > -       }
> > > -
> > > -       /* Sync static metadata. */
> > > -       int32_t maxLatency = ANDROID_SYNC_MAX_LATENCY_UNKNOWN;
> > > -       staticMetadata_->addEntry(ANDROID_SYNC_MAX_LATENCY, maxLatency);
> > > -
> > > -       /* Flash static metadata. */
> > > -       char flashAvailable = ANDROID_FLASH_INFO_AVAILABLE_FALSE;
> > > -       staticMetadata_->addEntry(ANDROID_FLASH_INFO_AVAILABLE,
> > > -                                 flashAvailable);
> > > -
> > > -       /* Lens static metadata. */
> > > -       std::vector<float> lensApertures = {
> > > -               2.53 / 100,
> > > -       };
> > > -       staticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_APERTURES,
> > > -                                 lensApertures);
> > > -
> > > -       uint8_t lensFacing;
> > > -       switch (facing_) {
> > > -       default:
> > > -       case CAMERA_FACING_FRONT:
> > > -               lensFacing = ANDROID_LENS_FACING_FRONT;
> > > -               break;
> > > -       case CAMERA_FACING_BACK:
> > > -               lensFacing = ANDROID_LENS_FACING_BACK;
> > > -               break;
> > > -       case CAMERA_FACING_EXTERNAL:
> > > -               lensFacing = ANDROID_LENS_FACING_EXTERNAL;
> > > -               break;
> > > -       }
> > > -       staticMetadata_->addEntry(ANDROID_LENS_FACING, lensFacing);
> > > -
> > > -       std::vector<float> lensFocalLengths = {
> > > -               1,
> > > -       };
> > > -
> > >  staticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS,
> > > -                                 lensFocalLengths);
> > > -
> > > -       std::vector<uint8_t> opticalStabilizations = {
> > > -               ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF,
> > > -       };
> > > -
> > >  staticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
> > > -                                 opticalStabilizations);
> > > -
> > > -       float hypeFocalDistance = 0;
> > > -       staticMetadata_->addEntry(ANDROID_LENS_INFO_HYPERFOCAL_DISTANCE,
> > > -                                 hypeFocalDistance);
> > > -
> > > -       float minFocusDistance = 0;
> > > -       staticMetadata_->addEntry(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
> > > -                                 minFocusDistance);
> > > -
> > > -       /* Noise reduction modes. */
> > > -       {
> > > -               std::vector<uint8_t> data;
> > > -               data.reserve(5);
> > > -               const auto &infoMap =
> > > controlsInfo.find(&controls::draft::NoiseReductionMode);
> > > -               if (infoMap != controlsInfo.end()) {
> > > -                       for (const auto &value : infoMap->second.values())
> > > -                               data.push_back(value.get<int32_t>());
> > > -               } else {
> > > -                       data.push_back(ANDROID_NOISE_REDUCTION_MODE_OFF);
> > > -               }
> > > -
> > >  staticMetadata_->addEntry(ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
> > > -                                         data);
> > > -       }
> > > -
> > > -       /* Scaler static metadata. */
> > > -
> > > -       /*
> > > -        * \todo The digital zoom factor is a property that depends on the
> > > -        * desired output configuration and the sensor frame size input to
> > > the
> > > -        * ISP. This information is not available to the Android HAL, not
> > > at
> > > -        * initialization time at least.
> > > -        *
> > > -        * As a workaround rely on pipeline handlers initializing the
> > > -        * ScalerCrop control with the camera default configuration and
> > > use the
> > > -        * maximum and minimum crop rectangles to calculate the digital
> > > zoom
> > > -        * factor.
> > > -        */
> > > -       float maxZoom = 1.0f;
> > > -       const auto scalerCrop = controlsInfo.find(&controls::ScalerCrop);
> > > -       if (scalerCrop != controlsInfo.end()) {
> > > -               Rectangle min = scalerCrop->second.min().get<Rectangle>();
> > > -               Rectangle max = scalerCrop->second.max().get<Rectangle>();
> > > -               maxZoom = std::min(1.0f * max.width / min.width,
> > > -                                  1.0f * max.height / min.height);
> > > -       }
> > > -
> > >  staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
> > > -                                 maxZoom);
> > > -
> > > -       std::vector<uint32_t> availableStreamConfigurations;
> > > -       availableStreamConfigurations.reserve(streamConfigurations_.size()
> > > * 4);
> > > -       for (const auto &entry : streamConfigurations_) {
> > > -
> > >  availableStreamConfigurations.push_back(entry.androidFormat);
> > > -
> > >  availableStreamConfigurations.push_back(entry.resolution.width);
> > > -
> > >  availableStreamConfigurations.push_back(entry.resolution.height);
> > > -               availableStreamConfigurations.push_back(
> > > -
> > >  ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT);
> > > -       }
> > > -
> > >  staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
> > > -                                 availableStreamConfigurations);
> > > -
> > > -       std::vector<int64_t> availableStallDurations = {
> > > -               ANDROID_SCALER_AVAILABLE_FORMATS_BLOB, 2560, 1920,
> > > 33333333,
> > > -       };
> > > -       staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,
> > > -                                 availableStallDurations);
> > > -
> > > -       /* Use the minimum frame duration for all the YUV/RGB formats. */
> > > -       if (minFrameDurationNsec > 0) {
> > > -               std::vector<int64_t> minFrameDurations;
> > > -               minFrameDurations.reserve(streamConfigurations_.size() *
> > > 4);
> > > -               for (const auto &entry : streamConfigurations_) {
> > > -                       minFrameDurations.push_back(entry.androidFormat);
> > > -
> > >  minFrameDurations.push_back(entry.resolution.width);
> > > -
> > >  minFrameDurations.push_back(entry.resolution.height);
> > > -                       minFrameDurations.push_back(minFrameDurationNsec);
> > > -               }
> > > -
> > >  staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
> > > -                                         minFrameDurations);
> > > -       }
> > > -
> > > -       uint8_t croppingType = ANDROID_SCALER_CROPPING_TYPE_CENTER_ONLY;
> > > -       staticMetadata_->addEntry(ANDROID_SCALER_CROPPING_TYPE,
> > > croppingType);
> > > -
> > > -       /* Info static metadata. */
> > > -       uint8_t supportedHWLevel =
> > > ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
> > > -       staticMetadata_->addEntry(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
> > > -                                 supportedHWLevel);
> > > -
> > > -       /* Request static metadata. */
> > > -       int32_t partialResultCount = 1;
> > > -       staticMetadata_->addEntry(ANDROID_REQUEST_PARTIAL_RESULT_COUNT,
> > > -                                 partialResultCount);
> > > -
> > > -       {
> > > -               /* Default the value to 2 if not reported by the camera. */
> > > -               uint8_t maxPipelineDepth = 2;
> > > -               const auto &infoMap =
> > > controlsInfo.find(&controls::draft::PipelineDepth);
> > > -               if (infoMap != controlsInfo.end())
> > > -                       maxPipelineDepth =
> > > infoMap->second.max().get<int32_t>();
> > > -
> > >  staticMetadata_->addEntry(ANDROID_REQUEST_PIPELINE_MAX_DEPTH,
> > > -                                         maxPipelineDepth);
> > > -       }
> > > -
> > > -       /* LIMITED does not support reprocessing. */
> > > -       uint32_t maxNumInputStreams = 0;
> > > -       staticMetadata_->addEntry(ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,
> > > -                                 maxNumInputStreams);
> > > -
> > > -       std::vector<uint8_t> availableCapabilities = {
> > > -               ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE,
> > > -       };
> > > -
> > > -       /* Report if camera supports RAW. */
> > > -       bool rawStreamAvailable = false;
> > > -       std::unique_ptr<CameraConfiguration> cameraConfig =
> > > -               camera_->generateConfiguration({ StreamRole::Raw });
> > > -       if (cameraConfig && !cameraConfig->empty()) {
> > > -               const PixelFormatInfo &info =
> > > -
> > >  PixelFormatInfo::info(cameraConfig->at(0).pixelFormat);
> > > -               /* Only advertise RAW support if RAW16 is possible. */
> > > -               if (info.colourEncoding ==
> > > PixelFormatInfo::ColourEncodingRAW &&
> > > -                   info.bitsPerPixel == 16) {
> > > -                       rawStreamAvailable = true;
> > > -
> > >  availableCapabilities.push_back(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW);
> > > -               }
> > > -       }
> > > -
> > > -       /* Number of { RAW, YUV, JPEG } supported output streams */
> > > -       int32_t numOutStreams[] = { rawStreamAvailable, 2, 1 };
> > > -       staticMetadata_->addEntry(ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,
> > > -                                 numOutStreams);
> > > -
> > > -       staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
> > > -                                 availableCapabilities);
> > > -
> > > -       std::vector<int32_t> availableCharacteristicsKeys = {
> > > -               ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
> > > -               ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,
> > > -               ANDROID_CONTROL_AE_AVAILABLE_MODES,
> > > -               ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> > > -               ANDROID_CONTROL_AE_COMPENSATION_RANGE,
> > > -               ANDROID_CONTROL_AE_COMPENSATION_STEP,
> > > -               ANDROID_CONTROL_AE_LOCK_AVAILABLE,
> > > -               ANDROID_CONTROL_AF_AVAILABLE_MODES,
> > > -               ANDROID_CONTROL_AVAILABLE_EFFECTS,
> > > -               ANDROID_CONTROL_AVAILABLE_MODES,
> > > -               ANDROID_CONTROL_AVAILABLE_SCENE_MODES,
> > > -               ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
> > > -               ANDROID_CONTROL_AWB_AVAILABLE_MODES,
> > > -               ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
> > > -               ANDROID_CONTROL_MAX_REGIONS,
> > > -               ANDROID_CONTROL_SCENE_MODE_OVERRIDES,
> > > -               ANDROID_FLASH_INFO_AVAILABLE,
> > > -               ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
> > > -               ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,
> > > -               ANDROID_JPEG_MAX_SIZE,
> > > -               ANDROID_LENS_FACING,
> > > -               ANDROID_LENS_INFO_AVAILABLE_APERTURES,
> > > -               ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS,
> > > -               ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
> > > -               ANDROID_LENS_INFO_HYPERFOCAL_DISTANCE,
> > > -               ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
> > > -               ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
> > > -               ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
> > > -               ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,
> > > -               ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,
> > > -               ANDROID_REQUEST_PARTIAL_RESULT_COUNT,
> > > -               ANDROID_REQUEST_PIPELINE_MAX_DEPTH,
> > > -               ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
> > > -               ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
> > > -               ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,
> > > -               ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
> > > -               ANDROID_SCALER_CROPPING_TYPE,
> > > -               ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES,
> > > -               ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
> > > -               ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,
> > > -               ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE,
> > > -               ANDROID_SENSOR_INFO_MAX_FRAME_DURATION,
> > > -               ANDROID_SENSOR_INFO_PHYSICAL_SIZE,
> > > -               ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
> > > -               ANDROID_SENSOR_INFO_SENSITIVITY_RANGE,
> > > -               ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE,
> > > -               ANDROID_SENSOR_ORIENTATION,
> > > -               ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
> > > -               ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
> > > -               ANDROID_SYNC_MAX_LATENCY,
> > > -       };
> > > -
> > >  staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS,
> > > -                                 availableCharacteristicsKeys);
> > > -
> > > -       std::vector<int32_t> availableRequestKeys = {
> > > -               ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
> > > -               ANDROID_CONTROL_AE_ANTIBANDING_MODE,
> > > -               ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
> > > -               ANDROID_CONTROL_AE_LOCK,
> > > -               ANDROID_CONTROL_AE_MODE,
> > > -               ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
> > > -               ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
> > > -               ANDROID_CONTROL_AF_MODE,
> > > -               ANDROID_CONTROL_AF_TRIGGER,
> > > -               ANDROID_CONTROL_AWB_LOCK,
> > > -               ANDROID_CONTROL_AWB_MODE,
> > > -               ANDROID_CONTROL_CAPTURE_INTENT,
> > > -               ANDROID_CONTROL_EFFECT_MODE,
> > > -               ANDROID_CONTROL_MODE,
> > > -               ANDROID_CONTROL_SCENE_MODE,
> > > -               ANDROID_CONTROL_VIDEO_STABILIZATION_MODE,
> > > -               ANDROID_FLASH_MODE,
> > > -               ANDROID_JPEG_ORIENTATION,
> > > -               ANDROID_JPEG_QUALITY,
> > > -               ANDROID_JPEG_THUMBNAIL_QUALITY,
> > > -               ANDROID_JPEG_THUMBNAIL_SIZE,
> > > -               ANDROID_LENS_APERTURE,
> > > -               ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
> > > -               ANDROID_NOISE_REDUCTION_MODE,
> > > -               ANDROID_SCALER_CROP_REGION,
> > > -               ANDROID_STATISTICS_FACE_DETECT_MODE
> > > -       };
> > > -       staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS,
> > > -                                 availableRequestKeys);
> > > -
> > > -       std::vector<int32_t> availableResultKeys = {
> > > -               ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
> > > -               ANDROID_CONTROL_AE_ANTIBANDING_MODE,
> > > -               ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
> > > -               ANDROID_CONTROL_AE_LOCK,
> > > -               ANDROID_CONTROL_AE_MODE,
> > > -               ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
> > > -               ANDROID_CONTROL_AE_STATE,
> > > -               ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
> > > -               ANDROID_CONTROL_AF_MODE,
> > > -               ANDROID_CONTROL_AF_STATE,
> > > -               ANDROID_CONTROL_AF_TRIGGER,
> > > -               ANDROID_CONTROL_AWB_LOCK,
> > > -               ANDROID_CONTROL_AWB_MODE,
> > > -               ANDROID_CONTROL_AWB_STATE,
> > > -               ANDROID_CONTROL_CAPTURE_INTENT,
> > > -               ANDROID_CONTROL_EFFECT_MODE,
> > > -               ANDROID_CONTROL_MODE,
> > > -               ANDROID_CONTROL_SCENE_MODE,
> > > -               ANDROID_CONTROL_VIDEO_STABILIZATION_MODE,
> > > -               ANDROID_FLASH_MODE,
> > > -               ANDROID_FLASH_STATE,
> > > -               ANDROID_JPEG_GPS_COORDINATES,
> > > -               ANDROID_JPEG_GPS_PROCESSING_METHOD,
> > > -               ANDROID_JPEG_GPS_TIMESTAMP,
> > > -               ANDROID_JPEG_ORIENTATION,
> > > -               ANDROID_JPEG_QUALITY,
> > > -               ANDROID_JPEG_SIZE,
> > > -               ANDROID_JPEG_THUMBNAIL_QUALITY,
> > > -               ANDROID_JPEG_THUMBNAIL_SIZE,
> > > -               ANDROID_LENS_APERTURE,
> > > -               ANDROID_LENS_FOCAL_LENGTH,
> > > -               ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
> > > -               ANDROID_LENS_STATE,
> > > -               ANDROID_NOISE_REDUCTION_MODE,
> > > -               ANDROID_REQUEST_PIPELINE_DEPTH,
> > > -               ANDROID_SCALER_CROP_REGION,
> > > -               ANDROID_SENSOR_EXPOSURE_TIME,
> > > -               ANDROID_SENSOR_FRAME_DURATION,
> > > -               ANDROID_SENSOR_ROLLING_SHUTTER_SKEW,
> > > -               ANDROID_SENSOR_TEST_PATTERN_MODE,
> > > -               ANDROID_SENSOR_TIMESTAMP,
> > > -               ANDROID_STATISTICS_FACE_DETECT_MODE,
> > > -               ANDROID_STATISTICS_LENS_SHADING_MAP_MODE,
> > > -               ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE,
> > > -               ANDROID_STATISTICS_SCENE_FLICKER,
> > > -       };
> > > -       staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_RESULT_KEYS,
> > > -                                 availableResultKeys);
> > > -
> > > -       if (!staticMetadata_->isValid()) {
> > > -               LOG(HAL, Error) << "Failed to construct static metadata";
> > > -               staticMetadata_.reset();
> > > -               return nullptr;
> > > -       }
> > > -
> > > -       if (staticMetadata_->resized()) {
> > > -               auto [entryCount, dataCount] = staticMetadata_->usage();
> > > -               LOG(HAL, Info)
> > > -                       << "Static metadata resized: " << entryCount
> > > -                       << " entries and " << dataCount << " bytes used";
> > > -       }
> > > -
> > > -       return staticMetadata_->get();
> > > -}
> > > -
> > > -std::unique_ptr<CameraMetadata> CameraDevice::requestTemplatePreview()
> > > +void CameraDevice::setCallbacks(const camera3_callback_ops_t *callbacks)
> > >  {
> > > -       /*
> > > -        * \todo Keep this in sync with the actual number of entries.
> > > -        * Currently: 20 entries, 35 bytes
> > > -        */
> > > -       auto requestTemplate = std::make_unique<CameraMetadata>(21, 36);
> > > -       if (!requestTemplate->isValid()) {
> > > -               return nullptr;
> > > -       }
> > > -
> > > -       /* Get the FPS range registered in the static metadata. */
> > > -       camera_metadata_ro_entry_t entry;
> > > -       bool found =
> > > staticMetadata_->getEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> > > -                                              &entry);
> > > -       if (!found) {
> > > -               LOG(HAL, Error) << "Cannot create capture template without
> > > FPS range";
> > > -               return nullptr;
> > > -       }
> > > -
> > > -       /*
> > > -        * Assume the AE_AVAILABLE_TARGET_FPS_RANGE static metadata
> > > -        * has been assembled as {{min, max} {max, max}}.
> > > -        */
> > > -       requestTemplate->addEntry(ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
> > > -                                 entry.data.i32, 2);
> > > -
> > > -       uint8_t aeMode = ANDROID_CONTROL_AE_MODE_ON;
> > > -       requestTemplate->addEntry(ANDROID_CONTROL_AE_MODE, aeMode);
> > > -
> > > -       int32_t aeExposureCompensation = 0;
> > > -       requestTemplate->addEntry(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
> > > -                                 aeExposureCompensation);
> > > -
> > > -       uint8_t aePrecaptureTrigger =
> > > ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_IDLE;
> > > -       requestTemplate->addEntry(ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
> > > -                                 aePrecaptureTrigger);
> > > -
> > > -       uint8_t aeLock = ANDROID_CONTROL_AE_LOCK_OFF;
> > > -       requestTemplate->addEntry(ANDROID_CONTROL_AE_LOCK, aeLock);
> > > -
> > > -       uint8_t aeAntibandingMode =
> > > ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO;
> > > -       requestTemplate->addEntry(ANDROID_CONTROL_AE_ANTIBANDING_MODE,
> > > -                                 aeAntibandingMode);
> > > -
> > > -       uint8_t afMode = ANDROID_CONTROL_AF_MODE_OFF;
> > > -       requestTemplate->addEntry(ANDROID_CONTROL_AF_MODE, afMode);
> > > -
> > > -       uint8_t afTrigger = ANDROID_CONTROL_AF_TRIGGER_IDLE;
> > > -       requestTemplate->addEntry(ANDROID_CONTROL_AF_TRIGGER, afTrigger);
> > > -
> > > -       uint8_t awbMode = ANDROID_CONTROL_AWB_MODE_AUTO;
> > > -       requestTemplate->addEntry(ANDROID_CONTROL_AWB_MODE, awbMode);
> > > -
> > > -       uint8_t awbLock = ANDROID_CONTROL_AWB_LOCK_OFF;
> > > -       requestTemplate->addEntry(ANDROID_CONTROL_AWB_LOCK, awbLock);
> > > -
> > > -       uint8_t flashMode = ANDROID_FLASH_MODE_OFF;
> > > -       requestTemplate->addEntry(ANDROID_FLASH_MODE, flashMode);
> > > -
> > > -       uint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
> > > -       requestTemplate->addEntry(ANDROID_STATISTICS_FACE_DETECT_MODE,
> > > -                                 faceDetectMode);
> > > -
> > > -       uint8_t noiseReduction = ANDROID_NOISE_REDUCTION_MODE_OFF;
> > > -       requestTemplate->addEntry(ANDROID_NOISE_REDUCTION_MODE,
> > > -                                 noiseReduction);
> > > -
> > > -       uint8_t aberrationMode =
> > > ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF;
> > > -       requestTemplate->addEntry(ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
> > > -                                 aberrationMode);
> > > -
> > > -       uint8_t controlMode = ANDROID_CONTROL_MODE_AUTO;
> > > -       requestTemplate->addEntry(ANDROID_CONTROL_MODE, controlMode);
> > > -
> > > -       float lensAperture = 2.53 / 100;
> > > -       requestTemplate->addEntry(ANDROID_LENS_APERTURE, lensAperture);
> > > -
> > > -       uint8_t opticalStabilization =
> > > ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF;
> > > -       requestTemplate->addEntry(ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
> > > -                                 opticalStabilization);
> > > -
> > > -       uint8_t captureIntent = ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW;
> > > -       requestTemplate->addEntry(ANDROID_CONTROL_CAPTURE_INTENT,
> > > -                                 captureIntent);
> > > -
> > > -       return requestTemplate;
> > > +       callbacks_ = callbacks;
> > >  }
> > >
> > > -std::unique_ptr<CameraMetadata> CameraDevice::requestTemplateVideo()
> > > +const camera_metadata_t *CameraDevice::getStaticMetadata()
> > >  {
> > > -       std::unique_ptr<CameraMetadata> previewTemplate =
> > > requestTemplatePreview();
> > > -       if (!previewTemplate)
> > > -               return nullptr;
> > > -
> > > -       /*
> > > -        * The video template requires a fixed FPS range. Everything else
> > > -        * stays the same as the preview template.
> > > -        */
> > > -       camera_metadata_ro_entry_t entry;
> > > -
> > >  staticMetadata_->getEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> > > -                                 &entry);
> > > -
> > > -       /*
> > > -        * Assume the AE_AVAILABLE_TARGET_FPS_RANGE static metadata
> > > -        * has been assembled as {{min, max} {max, max}}.
> > > -        */
> > > -       previewTemplate->updateEntry(ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
> > > -                                    entry.data.i32 + 2, 2);
> > > -
> > > -       return previewTemplate;
> > > +       return capabilities_.staticMetadata()->get();
> > >  }
> > >
> > >  /*
> > > @@ -1630,7 +520,7 @@ const camera_metadata_t
> > > *CameraDevice::constructDefaultRequestSettings(int type)
> > >         switch (type) {
> > >         case CAMERA3_TEMPLATE_PREVIEW:
> > >                 captureIntent = ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW;
> > > -               requestTemplate = requestTemplatePreview();
> > > +               requestTemplate = capabilities_.requestTemplatePreview();
> > >                 break;
> > >         case CAMERA3_TEMPLATE_STILL_CAPTURE:
> > >                 /*
> > > @@ -1638,15 +528,15 @@ const camera_metadata_t
> > > *CameraDevice::constructDefaultRequestSettings(int type)
> > >                  * for the torch mode we currently do not support.
> > >                  */
> > >                 captureIntent =
> > > ANDROID_CONTROL_CAPTURE_INTENT_STILL_CAPTURE;
> > > -               requestTemplate = requestTemplatePreview();
> > > +               requestTemplate = capabilities_.requestTemplatePreview();
> > >                 break;
> > >         case CAMERA3_TEMPLATE_VIDEO_RECORD:
> > >                 captureIntent =
> > > ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_RECORD;
> > > -               requestTemplate = requestTemplateVideo();
> > > +               requestTemplate = capabilities_.requestTemplateVideo();
> > >                 break;
> > >         case CAMERA3_TEMPLATE_VIDEO_SNAPSHOT:
> > >                 captureIntent =
> > > ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT;
> > > -               requestTemplate = requestTemplateVideo();
> > > +               requestTemplate = capabilities_.requestTemplateVideo();
> > >                 break;
> > >         /* \todo Implement templates generation for the remaining use
> > > cases. */
> > >         case CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG:
> > > @@ -1668,19 +558,6 @@ const camera_metadata_t
> > > *CameraDevice::constructDefaultRequestSettings(int type)
> > >         return requestTemplates_[type]->get();
> > >  }
> > >
> > > -PixelFormat CameraDevice::toPixelFormat(int format) const
> > > -{
> > > -       /* Translate Android format code to libcamera pixel format. */
> > > -       auto it = formatsMap_.find(format);
> > > -       if (it == formatsMap_.end()) {
> > > -               LOG(HAL, Error) << "Requested format " <<
> > > utils::hex(format)
> > > -                               << " not supported";
> > > -               return PixelFormat();
> > > -       }
> > > -
> > > -       return it->second;
> > > -}
> > > -
> > >  /*
> > >   * Inspect the stream_list to produce a list of StreamConfiguration to
> > >   * be use to configure the Camera.
> > > @@ -1727,7 +604,7 @@ int
> > > CameraDevice::configureStreams(camera3_stream_configuration_t *stream_list)
> > >                 camera3_stream_t *stream = stream_list->streams[i];
> > >                 Size size(stream->width, stream->height);
> > >
> > > -               PixelFormat format = toPixelFormat(stream->format);
> > > +               PixelFormat format =
> > > capabilities_.toPixelFormat(stream->format);
> > >
> > >                 LOG(HAL, Info) << "Stream #" << i
> > >                                << ", direction: " << stream->stream_type
> > > diff --git a/src/android/camera_device.h b/src/android/camera_device.h
> > > index 4aadb27c562c..090fe28a551e 100644
> > > --- a/src/android/camera_device.h
> > > +++ b/src/android/camera_device.h
> > > @@ -10,14 +10,12 @@
> > >  #include <map>
> > >  #include <memory>
> > >  #include <mutex>
> > > -#include <tuple>
> > >  #include <vector>
> > >
> > >  #include <hardware/camera3.h>
> > >
> > >  #include <libcamera/buffer.h>
> > >  #include <libcamera/camera.h>
> > > -#include <libcamera/geometry.h>
> > >  #include <libcamera/request.h>
> > >  #include <libcamera/stream.h>
> > >
> > > @@ -26,6 +24,7 @@
> > >  #include "libcamera/internal/message.h"
> > >  #include "libcamera/internal/thread.h"
> > >
> > > +#include "camera_capabilities.h"
> > >  #include "camera_metadata.h"
> > >  #include "camera_stream.h"
> > >  #include "camera_worker.h"
> > > @@ -57,7 +56,7 @@ public:
> > >         const std::string &model() const { return model_; }
> > >         int facing() const { return facing_; }
> > >         int orientation() const { return orientation_; }
> > > -       unsigned int maxJpegBufferSize() const { return
> > > maxJpegBufferSize_; }
> > > +       unsigned int maxJpegBufferSize() const;
> > >
> > >         void setCallbacks(const camera3_callback_ops_t *callbacks);
> > >         const camera_metadata_t *getStaticMetadata();
> > > @@ -86,11 +85,6 @@ private:
> > >                 std::unique_ptr<CaptureRequest> request_;
> > >         };
> > >
> > > -       struct Camera3StreamConfiguration {
> > > -               libcamera::Size resolution;
> > > -               int androidFormat;
> > > -       };
> > > -
> > >         enum class State {
> > >                 Stopped,
> > >                 Flushing,
> > > @@ -99,22 +93,11 @@ private:
> > >
> > >         void stop();
> > >
> > > -       int initializeStreamConfigurations();
> > > -       std::vector<libcamera::Size>
> > > -       getYUVResolutions(libcamera::CameraConfiguration *cameraConfig,
> > > -                         const libcamera::PixelFormat &pixelFormat,
> > > -                         const std::vector<libcamera::Size> &resolutions);
> > > -       std::vector<libcamera::Size>
> > > -       getRawResolutions(const libcamera::PixelFormat &pixelFormat);
> > > -
> > >         libcamera::FrameBuffer *createFrameBuffer(const buffer_handle_t
> > > camera3buffer);
> > >         void abortRequest(camera3_capture_request_t *request);
> > >         void notifyShutter(uint32_t frameNumber, uint64_t timestamp);
> > >         void notifyError(uint32_t frameNumber, camera3_stream_t *stream,
> > >                          camera3_error_msg_code code);
> > > -       std::unique_ptr<CameraMetadata> requestTemplatePreview();
> > > -       std::unique_ptr<CameraMetadata> requestTemplateVideo();
> > > -       libcamera::PixelFormat toPixelFormat(int format) const;
> > >         int processControls(Camera3RequestDescriptor *descriptor);
> > >         std::unique_ptr<CameraMetadata> getResultMetadata(
> > >                 const Camera3RequestDescriptor &descriptor) const;
> > > @@ -129,13 +112,11 @@ private:
> > >
> > >         std::shared_ptr<libcamera::Camera> camera_;
> > >         std::unique_ptr<libcamera::CameraConfiguration> config_;
> > > +       CameraCapabilities capabilities_;
> > >
> > > -       std::unique_ptr<CameraMetadata> staticMetadata_;
> > >         std::map<unsigned int, std::unique_ptr<CameraMetadata>>
> > > requestTemplates_;
> > >         const camera3_callback_ops_t *callbacks_;
> > >
> > > -       std::vector<Camera3StreamConfiguration> streamConfigurations_;
> > > -       std::map<int, libcamera::PixelFormat> formatsMap_;
> > >         std::vector<CameraStream> streams_;
> > >
> > >         libcamera::Mutex descriptorsMutex_; /* Protects descriptors_. */
> > > @@ -147,8 +128,6 @@ private:
> > >         int facing_;
> > >         int orientation_;
> > >
> > > -       unsigned int maxJpegBufferSize_;
> > > -
> > >         CameraMetadata lastSettings_;
> > >  };
> > >
> > > diff --git a/src/android/meson.build b/src/android/meson.build
> > > index f27fd5316705..6270fb201338 100644
> > > --- a/src/android/meson.build
> > > +++ b/src/android/meson.build
> > > @@ -44,6 +44,7 @@ subdir('cros')
> > >
> > >  android_hal_sources = files([
> > >      'camera3_hal.cpp',
> > > +    'camera_capabilities.cpp',
> > >      'camera_device.cpp',
> > >      'camera_hal_config.cpp',
> > >      'camera_hal_manager.cpp',

Since the code is copied as-is,
Reviewed-by: Hirokazu Honda <hiroh@chromium.org>

Although stylecheck script complains some part of code, do you think fixing it?
-Hiro
> > > --
> > > 2.31.1
> > >
> > >
Jacopo Mondi June 23, 2021, 7:03 a.m. UTC | #5
Hi Hiro,

On Wed, Jun 23, 2021 at 12:25:01PM +0900, Hirokazu Honda wrote:
> Hi Jacopo,
>

[snip]

> > > >  android_hal_sources = files([
> > > >      'camera3_hal.cpp',
> > > > +    'camera_capabilities.cpp',
> > > >      'camera_device.cpp',
> > > >      'camera_hal_config.cpp',
> > > >      'camera_hal_manager.cpp',
>
> Since the code is copied as-is,
> Reviewed-by: Hirokazu Honda <hiroh@chromium.org>

Thanks

>
> Although stylecheck script complains some part of code, do you think fixing it?

To be honest, none of the suggested changes make much sense. I'll copy
them here for reference.

--- src/android/camera_capabilities.cpp
+++ src/android/camera_capabilities.cpp
@@ -55,62 +55,19 @@
  * \brief Associate Android format code with ancillary data
  */
 const std::map<int, const Camera3Format> camera3FormatsMap = {
-	{
-		HAL_PIXEL_FORMAT_BLOB, {
-			{ formats::MJPEG },
-			true,
-			"BLOB"
-		}
-	}, {
-		HAL_PIXEL_FORMAT_YCbCr_420_888, {
-			{ formats::NV12, formats::NV21 },
-			true,
-			"YCbCr_420_888"
-		}
-	}, {
-		/*
+	{ HAL_PIXEL_FORMAT_BLOB, { { formats::MJPEG }, true, "BLOB" } },
+	{ HAL_PIXEL_FORMAT_YCbCr_420_888, { { formats::NV12, formats::NV21 }, true, "YCbCr_420_888" } },
+	{ /*
 		 * \todo Translate IMPLEMENTATION_DEFINED inspecting the gralloc
 		 * usage flag. For now, copy the YCbCr_420 configuration.
 		 */
-		HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, {
-			{ formats::NV12, formats::NV21 },
-			true,
-			"IMPLEMENTATION_DEFINED"
-		}
-	}, {
-		HAL_PIXEL_FORMAT_RAW10, {
-			{
-				formats::SBGGR10_CSI2P,
-				formats::SGBRG10_CSI2P,
-				formats::SGRBG10_CSI2P,
-				formats::SRGGB10_CSI2P
-			},
-			false,
-			"RAW10"
-		}
-	}, {
-		HAL_PIXEL_FORMAT_RAW12, {
-			{
-				formats::SBGGR12_CSI2P,
-				formats::SGBRG12_CSI2P,
-				formats::SGRBG12_CSI2P,
-				formats::SRGGB12_CSI2P
-			},
-			false,
-			"RAW12"
-		}
-	}, {
-		HAL_PIXEL_FORMAT_RAW16, {
-			{
-				formats::SBGGR16,
-				formats::SGBRG16,
-				formats::SGRBG16,
-				formats::SRGGB16
-			},
-			false,
-			"RAW16"
-		}
-	},
+	  HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
+	  { { formats::NV12, formats::NV21 },
+	    true,
+	    "IMPLEMENTATION_DEFINED" } },
+	{ HAL_PIXEL_FORMAT_RAW10, { { formats::SBGGR10_CSI2P, formats::SGBRG10_CSI2P, formats::SGRBG10_CSI2P, formats::SRGGB10_CSI2P }, false, "RAW10" } },
+	{ HAL_PIXEL_FORMAT_RAW12, { { formats::SBGGR12_CSI2P, formats::SGBRG12_CSI2P, formats::SGRBG12_CSI2P, formats::SRGGB12_CSI2P }, false, "RAW12" } },
+	{ HAL_PIXEL_FORMAT_RAW16, { { formats::SBGGR16, formats::SGBRG16, formats::SGRBG16, formats::SRGGB16 }, false, "RAW16" } },
 };

 } /* namespace */
@@ -276,7 +233,6 @@
 		 */
 		PixelFormat mappedFormat;
 		for (const PixelFormat &pixelFormat : libcameraFormats) {
-
 			LOG(HAL, Debug) << "Testing " << pixelFormat.toString();

 			/*
@@ -457,7 +413,8 @@
 	}

 	std::vector<int32_t> aeCompensationRange = {
-		0, 0,
+		0,
+		0,
 	};
 	staticMetadata_->addEntry(ANDROID_CONTROL_AE_COMPENSATION_RANGE,
 				  aeCompensationRange);
@@ -503,7 +460,9 @@
 				  availableAwbModes);

 	std::vector<int32_t> availableMaxRegions = {
-		0, 0, 0,
+		0,
+		0,
+		0,
 	};
 	staticMetadata_->addEntry(ANDROID_CONTROL_MAX_REGIONS,
 				  availableMaxRegions);
@@ -547,8 +506,8 @@
 			continue;

 		Size thumbnailSize = maxJpegThumbnail
-				     .boundedToAspectRatio({ entry.resolution.width,
-							     entry.resolution.height });
+					     .boundedToAspectRatio({ entry.resolution.width,
+								     entry.resolution.height });
 		thumbnailSizes.push_back(thumbnailSize);
 	}

@@ -602,7 +561,8 @@
 	}

 	int32_t sensitivityRange[] = {
-		32, 2400,
+		32,
+		2400,
 	};
 	staticMetadata_->addEntry(ANDROID_SENSOR_INFO_SENSITIVITY_RANGE,
 				  sensitivityRange);
@@ -811,7 +771,10 @@
 				  availableStreamConfigurations);

 	std::vector<int64_t> availableStallDurations = {
-		ANDROID_SCALER_AVAILABLE_FORMATS_BLOB, 2560, 1920, 33333333,
+		ANDROID_SCALER_AVAILABLE_FORMATS_BLOB,
+		2560,
+		1920,
+		33333333,
 	};
 	staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,
 				  availableStallDurations);
---
7 potential issues detected, please review

I think I'll ignore them all.
Thanks
   j

> -Hiro
> > > > --
> > > > 2.31.1
> > > >
> > > >
Hirokazu Honda June 23, 2021, 7:08 a.m. UTC | #6
Hi Jacopo,

On Wed, Jun 23, 2021 at 4:02 PM Jacopo Mondi <jacopo@jmondi.org> wrote:
>
> Hi Hiro,
>
> On Wed, Jun 23, 2021 at 12:25:01PM +0900, Hirokazu Honda wrote:
> > Hi Jacopo,
> >
>
> [snip]
>
> > > > >  android_hal_sources = files([
> > > > >      'camera3_hal.cpp',
> > > > > +    'camera_capabilities.cpp',
> > > > >      'camera_device.cpp',
> > > > >      'camera_hal_config.cpp',
> > > > >      'camera_hal_manager.cpp',
> >
> > Since the code is copied as-is,
> > Reviewed-by: Hirokazu Honda <hiroh@chromium.org>
>
> Thanks
>
> >
> > Although stylecheck script complains some part of code, do you think fixing it?
>
> To be honest, none of the suggested changes make much sense. I'll copy
> them here for reference.
>
> --- src/android/camera_capabilities.cpp
> +++ src/android/camera_capabilities.cpp
> @@ -55,62 +55,19 @@
>   * \brief Associate Android format code with ancillary data
>   */
>  const std::map<int, const Camera3Format> camera3FormatsMap = {
> -       {
> -               HAL_PIXEL_FORMAT_BLOB, {
> -                       { formats::MJPEG },
> -                       true,
> -                       "BLOB"
> -               }
> -       }, {
> -               HAL_PIXEL_FORMAT_YCbCr_420_888, {
> -                       { formats::NV12, formats::NV21 },
> -                       true,
> -                       "YCbCr_420_888"
> -               }
> -       }, {
> -               /*
> +       { HAL_PIXEL_FORMAT_BLOB, { { formats::MJPEG }, true, "BLOB" } },
> +       { HAL_PIXEL_FORMAT_YCbCr_420_888, { { formats::NV12, formats::NV21 }, true, "YCbCr_420_888" } },
> +       { /*
>                  * \todo Translate IMPLEMENTATION_DEFINED inspecting the gralloc
>                  * usage flag. For now, copy the YCbCr_420 configuration.
>                  */
> -               HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, {
> -                       { formats::NV12, formats::NV21 },
> -                       true,
> -                       "IMPLEMENTATION_DEFINED"
> -               }
> -       }, {
> -               HAL_PIXEL_FORMAT_RAW10, {
> -                       {
> -                               formats::SBGGR10_CSI2P,
> -                               formats::SGBRG10_CSI2P,
> -                               formats::SGRBG10_CSI2P,
> -                               formats::SRGGB10_CSI2P
> -                       },
> -                       false,
> -                       "RAW10"
> -               }
> -       }, {
> -               HAL_PIXEL_FORMAT_RAW12, {
> -                       {
> -                               formats::SBGGR12_CSI2P,
> -                               formats::SGBRG12_CSI2P,
> -                               formats::SGRBG12_CSI2P,
> -                               formats::SRGGB12_CSI2P
> -                       },
> -                       false,
> -                       "RAW12"
> -               }
> -       }, {
> -               HAL_PIXEL_FORMAT_RAW16, {
> -                       {
> -                               formats::SBGGR16,
> -                               formats::SGBRG16,
> -                               formats::SGRBG16,
> -                               formats::SRGGB16
> -                       },
> -                       false,
> -                       "RAW16"
> -               }
> -       },
> +         HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
> +         { { formats::NV12, formats::NV21 },
> +           true,
> +           "IMPLEMENTATION_DEFINED" } },
> +       { HAL_PIXEL_FORMAT_RAW10, { { formats::SBGGR10_CSI2P, formats::SGBRG10_CSI2P, formats::SGRBG10_CSI2P, formats::SRGGB10_CSI2P }, false, "RAW10" } },
> +       { HAL_PIXEL_FORMAT_RAW12, { { formats::SBGGR12_CSI2P, formats::SGBRG12_CSI2P, formats::SGRBG12_CSI2P, formats::SRGGB12_CSI2P }, false, "RAW12" } },
> +       { HAL_PIXEL_FORMAT_RAW16, { { formats::SBGGR16, formats::SGBRG16, formats::SGRBG16, formats::SRGGB16 }, false, "RAW16" } },
>  };
>
>  } /* namespace */
> @@ -276,7 +233,6 @@
>                  */
>                 PixelFormat mappedFormat;
>                 for (const PixelFormat &pixelFormat : libcameraFormats) {
> -
>                         LOG(HAL, Debug) << "Testing " << pixelFormat.toString();
>
>                         /*
> @@ -457,7 +413,8 @@
>         }
>
>         std::vector<int32_t> aeCompensationRange = {
> -               0, 0,
> +               0,
> +               0,
>         };
>         staticMetadata_->addEntry(ANDROID_CONTROL_AE_COMPENSATION_RANGE,
>                                   aeCompensationRange);
> @@ -503,7 +460,9 @@
>                                   availableAwbModes);
>
>         std::vector<int32_t> availableMaxRegions = {
> -               0, 0, 0,
> +               0,
> +               0,
> +               0,
>         };
>         staticMetadata_->addEntry(ANDROID_CONTROL_MAX_REGIONS,
>                                   availableMaxRegions);
> @@ -547,8 +506,8 @@
>                         continue;
>
>                 Size thumbnailSize = maxJpegThumbnail
> -                                    .boundedToAspectRatio({ entry.resolution.width,
> -                                                            entry.resolution.height });
> +                                            .boundedToAspectRatio({ entry.resolution.width,
> +                                                                    entry.resolution.height });
>                 thumbnailSizes.push_back(thumbnailSize);
>         }
>
> @@ -602,7 +561,8 @@
>         }
>
>         int32_t sensitivityRange[] = {
> -               32, 2400,
> +               32,
> +               2400,
>         };
>         staticMetadata_->addEntry(ANDROID_SENSOR_INFO_SENSITIVITY_RANGE,
>                                   sensitivityRange);
> @@ -811,7 +771,10 @@
>                                   availableStreamConfigurations);
>
>         std::vector<int64_t> availableStallDurations = {
> -               ANDROID_SCALER_AVAILABLE_FORMATS_BLOB, 2560, 1920, 33333333,
> +               ANDROID_SCALER_AVAILABLE_FORMATS_BLOB,
> +               2560,
> +               1920,
> +               33333333,
>         };
>         staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,
>                                   availableStallDurations);
> ---
> 7 potential issues detected, please review
>
> I think I'll ignore them all.
> Thanks

Ack.
>    j
>
> > -Hiro
> > > > > --
> > > > > 2.31.1
> > > > >
> > > > >
Kieran Bingham June 23, 2021, 9:51 a.m. UTC | #7
Hi Hiro,

On 23/06/2021 04:25, Hirokazu Honda wrote:
> Hi Jacopo,
> 
> On Tue, Jun 22, 2021 at 4:40 PM Jacopo Mondi <jacopo@jmondi.org> wrote:
>>
>> Hi Hiro,
>>
>> On Tue, Jun 22, 2021 at 10:34:27AM +0900, Hirokazu Honda wrote:
>>> Hi Jacopo, thank you for the patch.
>>>
>>> I failed to apply the patch on the top of the latest tree to review.
>>> Could you tell me the parent commit which I can apply this patch?
>>
>> weird, I just applied these two patches cleanly on the latest
>> master which for me is
>> 969da3189439 ("libcamera: utils: Support systems that lack secure_getenv and issetugid"
>>
> 
> I tried applying the series whose id is 2162. But the series doesn't
> contain "[PATCH 1/2] android: Sort source files alphabetically".
> In fact, the patch is not shown in patchwork. It is strange.
> I managed to apply this patch after manually applying 1/2.

I'm sorry about this, indeed - I think we have noticed that at times
patchwork is either rejecting or not receiving some patches. I have been
trying to identify why, but haven't got to the bottom of it yet.
--
Kieran
Hirokazu Honda June 23, 2021, 9:57 a.m. UTC | #8
Hi Kieran,

On Wed, Jun 23, 2021 at 6:51 PM Kieran Bingham
<kieran.bingham@ideasonboard.com> wrote:
>
> Hi Hiro,
>
> On 23/06/2021 04:25, Hirokazu Honda wrote:
> > Hi Jacopo,
> >
> > On Tue, Jun 22, 2021 at 4:40 PM Jacopo Mondi <jacopo@jmondi.org> wrote:
> >>
> >> Hi Hiro,
> >>
> >> On Tue, Jun 22, 2021 at 10:34:27AM +0900, Hirokazu Honda wrote:
> >>> Hi Jacopo, thank you for the patch.
> >>>
> >>> I failed to apply the patch on the top of the latest tree to review.
> >>> Could you tell me the parent commit which I can apply this patch?
> >>
> >> weird, I just applied these two patches cleanly on the latest
> >> master which for me is
> >> 969da3189439 ("libcamera: utils: Support systems that lack secure_getenv and issetugid"
> >>
> >
> > I tried applying the series whose id is 2162. But the series doesn't
> > contain "[PATCH 1/2] android: Sort source files alphabetically".
> > In fact, the patch is not shown in patchwork. It is strange.
> > I managed to apply this patch after manually applying 1/2.
>
> I'm sorry about this, indeed - I think we have noticed that at times
> patchwork is either rejecting or not receiving some patches. I have been
> trying to identify why, but haven't got to the bottom of it yet.

Oh, that's good to know.
Thanks for letting me know.

-Hiro
> --
> Kieran

Patch
diff mbox series

diff --git a/src/android/camera_capabilities.cpp b/src/android/camera_capabilities.cpp
new file mode 100644
index 000000000000..311a2c839586
--- /dev/null
+++ b/src/android/camera_capabilities.cpp
@@ -0,0 +1,1164 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2021, Google Inc.
+ *
+ * camera_capabilities.cpp - Camera static properties manager
+ */
+
+#include "camera_capabilities.h"
+
+#include <array>
+#include <cmath>
+
+#include <hardware/camera3.h>
+
+#include <libcamera/control_ids.h>
+#include <libcamera/controls.h>
+#include <libcamera/property_ids.h>
+
+#include "libcamera/internal/formats.h"
+#include "libcamera/internal/log.h"
+
+using namespace libcamera;
+
+LOG_DECLARE_CATEGORY(HAL)
+
+namespace {
+
+/*
+ * \var camera3Resolutions
+ * \brief The list of image resolutions defined as mandatory to be supported by
+ * the Android Camera3 specification
+ */
+const std::vector<Size> camera3Resolutions = {
+	{ 320, 240 },
+	{ 640, 480 },
+	{ 1280, 720 },
+	{ 1920, 1080 }
+};
+
+/*
+ * \struct Camera3Format
+ * \brief Data associated with an Android format identifier
+ * \var libcameraFormats List of libcamera pixel formats compatible with the
+ * Android format
+ * \var name The human-readable representation of the Android format code
+ */
+struct Camera3Format {
+	std::vector<PixelFormat> libcameraFormats;
+	bool mandatory;
+	const char *name;
+};
+
+/*
+ * \var camera3FormatsMap
+ * \brief Associate Android format code with ancillary data
+ */
+const std::map<int, const Camera3Format> camera3FormatsMap = {
+	{
+		HAL_PIXEL_FORMAT_BLOB, {
+			{ formats::MJPEG },
+			true,
+			"BLOB"
+		}
+	}, {
+		HAL_PIXEL_FORMAT_YCbCr_420_888, {
+			{ formats::NV12, formats::NV21 },
+			true,
+			"YCbCr_420_888"
+		}
+	}, {
+		/*
+		 * \todo Translate IMPLEMENTATION_DEFINED inspecting the gralloc
+		 * usage flag. For now, copy the YCbCr_420 configuration.
+		 */
+		HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, {
+			{ formats::NV12, formats::NV21 },
+			true,
+			"IMPLEMENTATION_DEFINED"
+		}
+	}, {
+		HAL_PIXEL_FORMAT_RAW10, {
+			{
+				formats::SBGGR10_CSI2P,
+				formats::SGBRG10_CSI2P,
+				formats::SGRBG10_CSI2P,
+				formats::SRGGB10_CSI2P
+			},
+			false,
+			"RAW10"
+		}
+	}, {
+		HAL_PIXEL_FORMAT_RAW12, {
+			{
+				formats::SBGGR12_CSI2P,
+				formats::SGBRG12_CSI2P,
+				formats::SGRBG12_CSI2P,
+				formats::SRGGB12_CSI2P
+			},
+			false,
+			"RAW12"
+		}
+	}, {
+		HAL_PIXEL_FORMAT_RAW16, {
+			{
+				formats::SBGGR16,
+				formats::SGBRG16,
+				formats::SGRBG16,
+				formats::SRGGB16
+			},
+			false,
+			"RAW16"
+		}
+	},
+};
+
+} /* namespace */
+
+int CameraCapabilities::initialize(std::shared_ptr<libcamera::Camera> camera,
+				   int orientation, int facing)
+{
+	camera_ = camera;
+	orientation_ = orientation;
+	facing_ = facing;
+
+	/* Acquire the camera and initialize available stream configurations. */
+	int ret = camera_->acquire();
+	if (ret) {
+		LOG(HAL, Error) << "Failed to temporarily acquire the camera";
+		return ret;
+	}
+
+	ret = initializeStreamConfigurations();
+	camera_->release();
+	if (ret)
+		return ret;
+
+	return initializeStaticMetadata();
+}
+
+std::vector<Size> CameraCapabilities::getYUVResolutions(CameraConfiguration *cameraConfig,
+							const PixelFormat &pixelFormat,
+							const std::vector<Size> &resolutions)
+{
+	std::vector<Size> supportedResolutions;
+
+	StreamConfiguration &cfg = cameraConfig->at(0);
+	for (const Size &res : resolutions) {
+		cfg.pixelFormat = pixelFormat;
+		cfg.size = res;
+
+		CameraConfiguration::Status status = cameraConfig->validate();
+		if (status != CameraConfiguration::Valid) {
+			LOG(HAL, Debug) << cfg.toString() << " not supported";
+			continue;
+		}
+
+		LOG(HAL, Debug) << cfg.toString() << " supported";
+
+		supportedResolutions.push_back(res);
+	}
+
+	return supportedResolutions;
+}
+
+std::vector<Size> CameraCapabilities::getRawResolutions(const libcamera::PixelFormat &pixelFormat)
+{
+	std::unique_ptr<CameraConfiguration> cameraConfig =
+		camera_->generateConfiguration({ StreamRole::Raw });
+	StreamConfiguration &cfg = cameraConfig->at(0);
+	const StreamFormats &formats = cfg.formats();
+	std::vector<Size> supportedResolutions = formats.sizes(pixelFormat);
+
+	return supportedResolutions;
+}
+
+/*
+ * Initialize the format conversion map to translate from Android format
+ * identifier to libcamera pixel formats and fill in the list of supported
+ * stream configurations to be reported to the Android camera framework through
+ * the Camera static metadata.
+ */
+int CameraCapabilities::initializeStreamConfigurations()
+{
+	/*
+	 * Get the maximum output resolutions
+	 * \todo Get this from the camera properties once defined
+	 */
+	std::unique_ptr<CameraConfiguration> cameraConfig =
+		camera_->generateConfiguration({ StillCapture });
+	if (!cameraConfig) {
+		LOG(HAL, Error) << "Failed to get maximum resolution";
+		return -EINVAL;
+	}
+	StreamConfiguration &cfg = cameraConfig->at(0);
+
+	/*
+	 * \todo JPEG - Adjust the maximum available resolution by taking the
+	 * JPEG encoder requirements into account (alignment and aspect ratio).
+	 */
+	const Size maxRes = cfg.size;
+	LOG(HAL, Debug) << "Maximum supported resolution: " << maxRes.toString();
+
+	/*
+	 * Build the list of supported image resolutions.
+	 *
+	 * The resolutions listed in camera3Resolution are mandatory to be
+	 * supported, up to the camera maximum resolution.
+	 *
+	 * Augment the list by adding resolutions calculated from the camera
+	 * maximum one.
+	 */
+	std::vector<Size> cameraResolutions;
+	std::copy_if(camera3Resolutions.begin(), camera3Resolutions.end(),
+		     std::back_inserter(cameraResolutions),
+		     [&](const Size &res) { return res < maxRes; });
+
+	/*
+	 * The Camera3 specification suggests adding 1/2 and 1/4 of the maximum
+	 * resolution.
+	 */
+	for (unsigned int divider = 2;; divider <<= 1) {
+		Size derivedSize{
+			maxRes.width / divider,
+			maxRes.height / divider,
+		};
+
+		if (derivedSize.width < 320 ||
+		    derivedSize.height < 240)
+			break;
+
+		cameraResolutions.push_back(derivedSize);
+	}
+	cameraResolutions.push_back(maxRes);
+
+	/* Remove duplicated entries from the list of supported resolutions. */
+	std::sort(cameraResolutions.begin(), cameraResolutions.end());
+	auto last = std::unique(cameraResolutions.begin(), cameraResolutions.end());
+	cameraResolutions.erase(last, cameraResolutions.end());
+
+	/*
+	 * Build the list of supported camera formats.
+	 *
+	 * To each Android format a list of compatible libcamera formats is
+	 * associated. The first libcamera format that tests successful is added
+	 * to the format translation map used when configuring the streams.
+	 * It is then tested against the list of supported camera resolutions to
+	 * build the stream configuration map reported through the camera static
+	 * metadata.
+	 */
+	Size maxJpegSize;
+	for (const auto &format : camera3FormatsMap) {
+		int androidFormat = format.first;
+		const Camera3Format &camera3Format = format.second;
+		const std::vector<PixelFormat> &libcameraFormats =
+			camera3Format.libcameraFormats;
+
+		LOG(HAL, Debug) << "Trying to map Android format "
+				<< camera3Format.name;
+
+		/*
+		 * JPEG is always supported, either produced directly by the
+		 * camera, or encoded in the HAL.
+		 */
+		if (androidFormat == HAL_PIXEL_FORMAT_BLOB) {
+			formatsMap_[androidFormat] = formats::MJPEG;
+			LOG(HAL, Debug) << "Mapped Android format "
+					<< camera3Format.name << " to "
+					<< formats::MJPEG.toString()
+					<< " (fixed mapping)";
+			continue;
+		}
+
+		/*
+		 * Test the libcamera formats that can produce images
+		 * compatible with the format defined by Android.
+		 */
+		PixelFormat mappedFormat;
+		for (const PixelFormat &pixelFormat : libcameraFormats) {
+
+			LOG(HAL, Debug) << "Testing " << pixelFormat.toString();
+
+			/*
+			 * The stream configuration size can be adjusted,
+			 * not the pixel format.
+			 *
+			 * \todo This could be simplified once all pipeline
+			 * handlers will report the StreamFormats list of
+			 * supported formats.
+			 */
+			cfg.pixelFormat = pixelFormat;
+
+			CameraConfiguration::Status status = cameraConfig->validate();
+			if (status != CameraConfiguration::Invalid &&
+			    cfg.pixelFormat == pixelFormat) {
+				mappedFormat = pixelFormat;
+				break;
+			}
+		}
+
+		if (!mappedFormat.isValid()) {
+			/* If the format is not mandatory, skip it. */
+			if (!camera3Format.mandatory)
+				continue;
+
+			LOG(HAL, Error)
+				<< "Failed to map mandatory Android format "
+				<< camera3Format.name << " ("
+				<< utils::hex(androidFormat) << "): aborting";
+			return -EINVAL;
+		}
+
+		/*
+		 * Record the mapping and then proceed to generate the
+		 * stream configurations map, by testing the image resolutions.
+		 */
+		formatsMap_[androidFormat] = mappedFormat;
+		LOG(HAL, Debug) << "Mapped Android format "
+				<< camera3Format.name << " to "
+				<< mappedFormat.toString();
+
+		std::vector<Size> resolutions;
+		const PixelFormatInfo &info = PixelFormatInfo::info(mappedFormat);
+		if (info.colourEncoding == PixelFormatInfo::ColourEncodingRAW)
+			resolutions = getRawResolutions(mappedFormat);
+		else
+			resolutions = getYUVResolutions(cameraConfig.get(),
+							mappedFormat,
+							cameraResolutions);
+
+		for (const Size &res : resolutions) {
+			streamConfigurations_.push_back({ res, androidFormat });
+
+			/*
+			 * If the format is HAL_PIXEL_FORMAT_YCbCr_420_888
+			 * from which JPEG is produced, add an entry for
+			 * the JPEG stream.
+			 *
+			 * \todo Wire the JPEG encoder to query the supported
+			 * sizes provided a list of formats it can encode.
+			 *
+			 * \todo Support JPEG streams produced by the Camera
+			 * natively.
+			 */
+			if (androidFormat == HAL_PIXEL_FORMAT_YCbCr_420_888) {
+				streamConfigurations_.push_back(
+					{ res, HAL_PIXEL_FORMAT_BLOB });
+				maxJpegSize = std::max(maxJpegSize, res);
+			}
+		}
+
+		/*
+		 * \todo Calculate the maximum JPEG buffer size by asking the
+		 * encoder giving the maximum frame size required.
+		 */
+		maxJpegBufferSize_ = maxJpegSize.width * maxJpegSize.height * 1.5;
+	}
+
+	LOG(HAL, Debug) << "Collected stream configuration map: ";
+	for (const auto &entry : streamConfigurations_)
+		LOG(HAL, Debug) << "{ " << entry.resolution.toString() << " - "
+				<< utils::hex(entry.androidFormat) << " }";
+
+	return 0;
+}
+
+int CameraCapabilities::initializeStaticMetadata()
+{
+	staticMetadata_ = std::make_unique<CameraMetadata>(64, 1024);
+	if (!staticMetadata_->isValid()) {
+		LOG(HAL, Error) << "Failed to allocate static metadata";
+		staticMetadata_.reset();
+		return -EINVAL;
+	}
+
+	const ControlInfoMap &controlsInfo = camera_->controls();
+	const ControlList &properties = camera_->properties();
+
+	/* Color correction static metadata. */
+	{
+		std::vector<uint8_t> data;
+		data.reserve(3);
+		const auto &infoMap = controlsInfo.find(&controls::draft::ColorCorrectionAberrationMode);
+		if (infoMap != controlsInfo.end()) {
+			for (const auto &value : infoMap->second.values())
+				data.push_back(value.get<int32_t>());
+		} else {
+			data.push_back(ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF);
+		}
+		staticMetadata_->addEntry(ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
+					  data);
+	}
+
+	/* Control static metadata. */
+	std::vector<uint8_t> aeAvailableAntiBandingModes = {
+		ANDROID_CONTROL_AE_ANTIBANDING_MODE_OFF,
+		ANDROID_CONTROL_AE_ANTIBANDING_MODE_50HZ,
+		ANDROID_CONTROL_AE_ANTIBANDING_MODE_60HZ,
+		ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO,
+	};
+	staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,
+				  aeAvailableAntiBandingModes);
+
+	std::vector<uint8_t> aeAvailableModes = {
+		ANDROID_CONTROL_AE_MODE_ON,
+	};
+	staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_MODES,
+				  aeAvailableModes);
+
+	int64_t minFrameDurationNsec = -1;
+	int64_t maxFrameDurationNsec = -1;
+	const auto frameDurationsInfo = controlsInfo.find(&controls::FrameDurationLimits);
+	if (frameDurationsInfo != controlsInfo.end()) {
+		minFrameDurationNsec = frameDurationsInfo->second.min().get<int64_t>() * 1000;
+		maxFrameDurationNsec = frameDurationsInfo->second.max().get<int64_t>() * 1000;
+
+		/*
+		 * Adjust the minimum frame duration to comply with Android
+		 * requirements. The camera service mandates all preview/record
+		 * streams to have a minimum frame duration < 33,366 milliseconds
+		 * (see MAX_PREVIEW_RECORD_DURATION_NS in the camera service
+		 * implementation).
+		 *
+		 * If we're close enough (+ 500 useconds) to that value, round
+		 * the minimum frame duration of the camera to an accepted
+		 * value.
+		 */
+		static constexpr int64_t MAX_PREVIEW_RECORD_DURATION_NS = 1e9 / 29.97;
+		if (minFrameDurationNsec > MAX_PREVIEW_RECORD_DURATION_NS &&
+		    minFrameDurationNsec < MAX_PREVIEW_RECORD_DURATION_NS + 500000)
+			minFrameDurationNsec = MAX_PREVIEW_RECORD_DURATION_NS - 1000;
+
+		/*
+		 * The AE routine frame rate limits are computed using the frame
+		 * duration limits, as libcamera clips the AE routine to the
+		 * frame durations.
+		 */
+		int32_t maxFps = std::round(1e9 / minFrameDurationNsec);
+		int32_t minFps = std::round(1e9 / maxFrameDurationNsec);
+		minFps = std::max(1, minFps);
+
+		/*
+		 * Force rounding errors so that we have the proper frame
+		 * durations for when we reuse these variables later
+		 */
+		minFrameDurationNsec = 1e9 / maxFps;
+		maxFrameDurationNsec = 1e9 / minFps;
+
+		/*
+		 * Register to the camera service {min, max} and {max, max}
+		 * intervals as requested by the metadata documentation.
+		 */
+		int32_t availableAeFpsTarget[] = {
+			minFps, maxFps, maxFps, maxFps
+		};
+		staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
+					  availableAeFpsTarget);
+	}
+
+	std::vector<int32_t> aeCompensationRange = {
+		0, 0,
+	};
+	staticMetadata_->addEntry(ANDROID_CONTROL_AE_COMPENSATION_RANGE,
+				  aeCompensationRange);
+
+	const camera_metadata_rational_t aeCompensationStep[] = {
+		{ 0, 1 }
+	};
+	staticMetadata_->addEntry(ANDROID_CONTROL_AE_COMPENSATION_STEP,
+				  aeCompensationStep);
+
+	std::vector<uint8_t> availableAfModes = {
+		ANDROID_CONTROL_AF_MODE_OFF,
+	};
+	staticMetadata_->addEntry(ANDROID_CONTROL_AF_AVAILABLE_MODES,
+				  availableAfModes);
+
+	std::vector<uint8_t> availableEffects = {
+		ANDROID_CONTROL_EFFECT_MODE_OFF,
+	};
+	staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_EFFECTS,
+				  availableEffects);
+
+	std::vector<uint8_t> availableSceneModes = {
+		ANDROID_CONTROL_SCENE_MODE_DISABLED,
+	};
+	staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_SCENE_MODES,
+				  availableSceneModes);
+
+	std::vector<uint8_t> availableStabilizationModes = {
+		ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF,
+	};
+	staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
+				  availableStabilizationModes);
+
+	/*
+	 * \todo Inspect the Camera capabilities to report the available
+	 * AWB modes. Default to AUTO as CTS tests require it.
+	 */
+	std::vector<uint8_t> availableAwbModes = {
+		ANDROID_CONTROL_AWB_MODE_AUTO,
+	};
+	staticMetadata_->addEntry(ANDROID_CONTROL_AWB_AVAILABLE_MODES,
+				  availableAwbModes);
+
+	std::vector<int32_t> availableMaxRegions = {
+		0, 0, 0,
+	};
+	staticMetadata_->addEntry(ANDROID_CONTROL_MAX_REGIONS,
+				  availableMaxRegions);
+
+	std::vector<uint8_t> sceneModesOverride = {
+		ANDROID_CONTROL_AE_MODE_ON,
+		ANDROID_CONTROL_AWB_MODE_AUTO,
+		ANDROID_CONTROL_AF_MODE_OFF,
+	};
+	staticMetadata_->addEntry(ANDROID_CONTROL_SCENE_MODE_OVERRIDES,
+				  sceneModesOverride);
+
+	uint8_t aeLockAvailable = ANDROID_CONTROL_AE_LOCK_AVAILABLE_FALSE;
+	staticMetadata_->addEntry(ANDROID_CONTROL_AE_LOCK_AVAILABLE,
+				  aeLockAvailable);
+
+	uint8_t awbLockAvailable = ANDROID_CONTROL_AWB_LOCK_AVAILABLE_FALSE;
+	staticMetadata_->addEntry(ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
+				  awbLockAvailable);
+
+	char availableControlModes = ANDROID_CONTROL_MODE_AUTO;
+	staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_MODES,
+				  availableControlModes);
+
+	/* JPEG static metadata. */
+
+	/*
+	 * Create the list of supported thumbnail sizes by inspecting the
+	 * available JPEG resolutions collected in streamConfigurations_ and
+	 * generate one entry for each aspect ratio.
+	 *
+	 * The JPEG thumbnailer can freely scale, so pick an arbitrary
+	 * (160, 160) size as the bounding rectangle, which is then cropped to
+	 * the different supported aspect ratios.
+	 */
+	constexpr Size maxJpegThumbnail(160, 160);
+	std::vector<Size> thumbnailSizes;
+	thumbnailSizes.push_back({ 0, 0 });
+	for (const auto &entry : streamConfigurations_) {
+		if (entry.androidFormat != HAL_PIXEL_FORMAT_BLOB)
+			continue;
+
+		Size thumbnailSize = maxJpegThumbnail
+				     .boundedToAspectRatio({ entry.resolution.width,
+							     entry.resolution.height });
+		thumbnailSizes.push_back(thumbnailSize);
+	}
+
+	std::sort(thumbnailSizes.begin(), thumbnailSizes.end());
+	auto last = std::unique(thumbnailSizes.begin(), thumbnailSizes.end());
+	thumbnailSizes.erase(last, thumbnailSizes.end());
+
+	/* Transform sizes in to a list of integers that can be consumed. */
+	std::vector<int32_t> thumbnailEntries;
+	thumbnailEntries.reserve(thumbnailSizes.size() * 2);
+	for (const auto &size : thumbnailSizes) {
+		thumbnailEntries.push_back(size.width);
+		thumbnailEntries.push_back(size.height);
+	}
+	staticMetadata_->addEntry(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,
+				  thumbnailEntries);
+
+	staticMetadata_->addEntry(ANDROID_JPEG_MAX_SIZE, maxJpegBufferSize_);
+
+	/* Sensor static metadata. */
+	std::array<int32_t, 2> pixelArraySize;
+	{
+		const Size &size = properties.get(properties::PixelArraySize);
+		pixelArraySize[0] = size.width;
+		pixelArraySize[1] = size.height;
+		staticMetadata_->addEntry(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
+					  pixelArraySize);
+	}
+
+	if (properties.contains(properties::UnitCellSize)) {
+		const Size &cellSize = properties.get<Size>(properties::UnitCellSize);
+		std::array<float, 2> physicalSize{
+			cellSize.width * pixelArraySize[0] / 1e6f,
+			cellSize.height * pixelArraySize[1] / 1e6f
+		};
+		staticMetadata_->addEntry(ANDROID_SENSOR_INFO_PHYSICAL_SIZE,
+					  physicalSize);
+	}
+
+	{
+		const Span<const Rectangle> &rects =
+			properties.get(properties::PixelArrayActiveAreas);
+		std::vector<int32_t> data{
+			static_cast<int32_t>(rects[0].x),
+			static_cast<int32_t>(rects[0].y),
+			static_cast<int32_t>(rects[0].width),
+			static_cast<int32_t>(rects[0].height),
+		};
+		staticMetadata_->addEntry(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
+					  data);
+	}
+
+	int32_t sensitivityRange[] = {
+		32, 2400,
+	};
+	staticMetadata_->addEntry(ANDROID_SENSOR_INFO_SENSITIVITY_RANGE,
+				  sensitivityRange);
+
+	/* Report the color filter arrangement if the camera reports it. */
+	if (properties.contains(properties::draft::ColorFilterArrangement)) {
+		uint8_t filterArr = properties.get(properties::draft::ColorFilterArrangement);
+		staticMetadata_->addEntry(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,
+					  filterArr);
+	}
+
+	const auto &exposureInfo = controlsInfo.find(&controls::ExposureTime);
+	if (exposureInfo != controlsInfo.end()) {
+		int64_t exposureTimeRange[2] = {
+			exposureInfo->second.min().get<int32_t>() * 1000LL,
+			exposureInfo->second.max().get<int32_t>() * 1000LL,
+		};
+		staticMetadata_->addEntry(ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE,
+					  exposureTimeRange, 2);
+	}
+
+	staticMetadata_->addEntry(ANDROID_SENSOR_ORIENTATION, orientation_);
+
+	std::vector<int32_t> testPatternModes = {
+		ANDROID_SENSOR_TEST_PATTERN_MODE_OFF
+	};
+	const auto &testPatternsInfo =
+		controlsInfo.find(&controls::draft::TestPatternMode);
+	if (testPatternsInfo != controlsInfo.end()) {
+		const auto &values = testPatternsInfo->second.values();
+		ASSERT(!values.empty());
+		for (const auto &value : values) {
+			switch (value.get<int32_t>()) {
+			case controls::draft::TestPatternModeOff:
+				/*
+				 * ANDROID_SENSOR_TEST_PATTERN_MODE_OFF is
+				 * already in testPatternModes.
+				 */
+				break;
+
+			case controls::draft::TestPatternModeSolidColor:
+				testPatternModes.push_back(
+					ANDROID_SENSOR_TEST_PATTERN_MODE_SOLID_COLOR);
+				break;
+
+			case controls::draft::TestPatternModeColorBars:
+				testPatternModes.push_back(
+					ANDROID_SENSOR_TEST_PATTERN_MODE_COLOR_BARS);
+				break;
+
+			case controls::draft::TestPatternModeColorBarsFadeToGray:
+				testPatternModes.push_back(
+					ANDROID_SENSOR_TEST_PATTERN_MODE_COLOR_BARS_FADE_TO_GRAY);
+				break;
+
+			case controls::draft::TestPatternModePn9:
+				testPatternModes.push_back(
+					ANDROID_SENSOR_TEST_PATTERN_MODE_PN9);
+				break;
+
+			case controls::draft::TestPatternModeCustom1:
+				/* We don't support this yet. */
+				break;
+
+			default:
+				LOG(HAL, Error) << "Unknown test pattern mode: "
+						<< value.get<int32_t>();
+				continue;
+			}
+		}
+	}
+	staticMetadata_->addEntry(ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES,
+				  testPatternModes);
+
+	uint8_t timestampSource = ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN;
+	staticMetadata_->addEntry(ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE,
+				  timestampSource);
+
+	if (maxFrameDurationNsec > 0)
+		staticMetadata_->addEntry(ANDROID_SENSOR_INFO_MAX_FRAME_DURATION,
+					  maxFrameDurationNsec);
+
+	/* Statistics static metadata. */
+	uint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
+	staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
+				  faceDetectMode);
+
+	int32_t maxFaceCount = 0;
+	staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
+				  maxFaceCount);
+
+	{
+		std::vector<uint8_t> data;
+		data.reserve(2);
+		const auto &infoMap = controlsInfo.find(&controls::draft::LensShadingMapMode);
+		if (infoMap != controlsInfo.end()) {
+			for (const auto &value : infoMap->second.values())
+				data.push_back(value.get<int32_t>());
+		} else {
+			data.push_back(ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF);
+		}
+		staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES,
+					  data);
+	}
+
+	/* Sync static metadata. */
+	int32_t maxLatency = ANDROID_SYNC_MAX_LATENCY_UNKNOWN;
+	staticMetadata_->addEntry(ANDROID_SYNC_MAX_LATENCY, maxLatency);
+
+	/* Flash static metadata. */
+	char flashAvailable = ANDROID_FLASH_INFO_AVAILABLE_FALSE;
+	staticMetadata_->addEntry(ANDROID_FLASH_INFO_AVAILABLE,
+				  flashAvailable);
+
+	/* Lens static metadata. */
+	std::vector<float> lensApertures = {
+		2.53 / 100,
+	};
+	staticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_APERTURES,
+				  lensApertures);
+
+	uint8_t lensFacing;
+	switch (facing_) {
+	default:
+	case CAMERA_FACING_FRONT:
+		lensFacing = ANDROID_LENS_FACING_FRONT;
+		break;
+	case CAMERA_FACING_BACK:
+		lensFacing = ANDROID_LENS_FACING_BACK;
+		break;
+	case CAMERA_FACING_EXTERNAL:
+		lensFacing = ANDROID_LENS_FACING_EXTERNAL;
+		break;
+	}
+	staticMetadata_->addEntry(ANDROID_LENS_FACING, lensFacing);
+
+	std::vector<float> lensFocalLengths = {
+		1,
+	};
+	staticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS,
+				  lensFocalLengths);
+
+	std::vector<uint8_t> opticalStabilizations = {
+		ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF,
+	};
+	staticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
+				  opticalStabilizations);
+
+	float hypeFocalDistance = 0;
+	staticMetadata_->addEntry(ANDROID_LENS_INFO_HYPERFOCAL_DISTANCE,
+				  hypeFocalDistance);
+
+	float minFocusDistance = 0;
+	staticMetadata_->addEntry(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
+				  minFocusDistance);
+
+	/* Noise reduction modes. */
+	{
+		std::vector<uint8_t> data;
+		data.reserve(5);
+		const auto &infoMap = controlsInfo.find(&controls::draft::NoiseReductionMode);
+		if (infoMap != controlsInfo.end()) {
+			for (const auto &value : infoMap->second.values())
+				data.push_back(value.get<int32_t>());
+		} else {
+			data.push_back(ANDROID_NOISE_REDUCTION_MODE_OFF);
+		}
+		staticMetadata_->addEntry(ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
+					  data);
+	}
+
+	/* Scaler static metadata. */
+
+	/*
+	 * \todo The digital zoom factor is a property that depends on the
+	 * desired output configuration and the sensor frame size input to the
+	 * ISP. This information is not available to the Android HAL, not at
+	 * initialization time at least.
+	 *
+	 * As a workaround rely on pipeline handlers initializing the
+	 * ScalerCrop control with the camera default configuration and use the
+	 * maximum and minimum crop rectangles to calculate the digital zoom
+	 * factor.
+	 */
+	float maxZoom = 1.0f;
+	const auto scalerCrop = controlsInfo.find(&controls::ScalerCrop);
+	if (scalerCrop != controlsInfo.end()) {
+		Rectangle min = scalerCrop->second.min().get<Rectangle>();
+		Rectangle max = scalerCrop->second.max().get<Rectangle>();
+		maxZoom = std::min(1.0f * max.width / min.width,
+				   1.0f * max.height / min.height);
+	}
+	staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
+				  maxZoom);
+
+	std::vector<uint32_t> availableStreamConfigurations;
+	availableStreamConfigurations.reserve(streamConfigurations_.size() * 4);
+	for (const auto &entry : streamConfigurations_) {
+		availableStreamConfigurations.push_back(entry.androidFormat);
+		availableStreamConfigurations.push_back(entry.resolution.width);
+		availableStreamConfigurations.push_back(entry.resolution.height);
+		availableStreamConfigurations.push_back(
+			ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT);
+	}
+	staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
+				  availableStreamConfigurations);
+
+	std::vector<int64_t> availableStallDurations = {
+		ANDROID_SCALER_AVAILABLE_FORMATS_BLOB, 2560, 1920, 33333333,
+	};
+	staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,
+				  availableStallDurations);
+
+	/* Use the minimum frame duration for all the YUV/RGB formats. */
+	if (minFrameDurationNsec > 0) {
+		std::vector<int64_t> minFrameDurations;
+		minFrameDurations.reserve(streamConfigurations_.size() * 4);
+		for (const auto &entry : streamConfigurations_) {
+			minFrameDurations.push_back(entry.androidFormat);
+			minFrameDurations.push_back(entry.resolution.width);
+			minFrameDurations.push_back(entry.resolution.height);
+			minFrameDurations.push_back(minFrameDurationNsec);
+		}
+		staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
+					  minFrameDurations);
+	}
+
+	uint8_t croppingType = ANDROID_SCALER_CROPPING_TYPE_CENTER_ONLY;
+	staticMetadata_->addEntry(ANDROID_SCALER_CROPPING_TYPE, croppingType);
+
+	/* Info static metadata. */
+	uint8_t supportedHWLevel = ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
+	staticMetadata_->addEntry(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
+				  supportedHWLevel);
+
+	/* Request static metadata. */
+	int32_t partialResultCount = 1;
+	staticMetadata_->addEntry(ANDROID_REQUEST_PARTIAL_RESULT_COUNT,
+				  partialResultCount);
+
+	{
+		/* Default the value to 2 if not reported by the camera. */
+		uint8_t maxPipelineDepth = 2;
+		const auto &infoMap = controlsInfo.find(&controls::draft::PipelineDepth);
+		if (infoMap != controlsInfo.end())
+			maxPipelineDepth = infoMap->second.max().get<int32_t>();
+		staticMetadata_->addEntry(ANDROID_REQUEST_PIPELINE_MAX_DEPTH,
+					  maxPipelineDepth);
+	}
+
+	/* LIMITED does not support reprocessing. */
+	uint32_t maxNumInputStreams = 0;
+	staticMetadata_->addEntry(ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,
+				  maxNumInputStreams);
+
+	std::vector<uint8_t> availableCapabilities = {
+		ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE,
+	};
+
+	/* Report if camera supports RAW. */
+	bool rawStreamAvailable = false;
+	std::unique_ptr<CameraConfiguration> cameraConfig =
+		camera_->generateConfiguration({ StreamRole::Raw });
+	if (cameraConfig && !cameraConfig->empty()) {
+		const PixelFormatInfo &info =
+			PixelFormatInfo::info(cameraConfig->at(0).pixelFormat);
+		/* Only advertise RAW support if RAW16 is possible. */
+		if (info.colourEncoding == PixelFormatInfo::ColourEncodingRAW &&
+		    info.bitsPerPixel == 16) {
+			rawStreamAvailable = true;
+			availableCapabilities.push_back(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW);
+		}
+	}
+
+	/* Number of { RAW, YUV, JPEG } supported output streams */
+	int32_t numOutStreams[] = { rawStreamAvailable, 2, 1 };
+	staticMetadata_->addEntry(ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,
+				  numOutStreams);
+
+	staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
+				  availableCapabilities);
+
+	std::vector<int32_t> availableCharacteristicsKeys = {
+		ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
+		ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,
+		ANDROID_CONTROL_AE_AVAILABLE_MODES,
+		ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
+		ANDROID_CONTROL_AE_COMPENSATION_RANGE,
+		ANDROID_CONTROL_AE_COMPENSATION_STEP,
+		ANDROID_CONTROL_AE_LOCK_AVAILABLE,
+		ANDROID_CONTROL_AF_AVAILABLE_MODES,
+		ANDROID_CONTROL_AVAILABLE_EFFECTS,
+		ANDROID_CONTROL_AVAILABLE_MODES,
+		ANDROID_CONTROL_AVAILABLE_SCENE_MODES,
+		ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
+		ANDROID_CONTROL_AWB_AVAILABLE_MODES,
+		ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
+		ANDROID_CONTROL_MAX_REGIONS,
+		ANDROID_CONTROL_SCENE_MODE_OVERRIDES,
+		ANDROID_FLASH_INFO_AVAILABLE,
+		ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
+		ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,
+		ANDROID_JPEG_MAX_SIZE,
+		ANDROID_LENS_FACING,
+		ANDROID_LENS_INFO_AVAILABLE_APERTURES,
+		ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS,
+		ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
+		ANDROID_LENS_INFO_HYPERFOCAL_DISTANCE,
+		ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
+		ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
+		ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
+		ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,
+		ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,
+		ANDROID_REQUEST_PARTIAL_RESULT_COUNT,
+		ANDROID_REQUEST_PIPELINE_MAX_DEPTH,
+		ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
+		ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
+		ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,
+		ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
+		ANDROID_SCALER_CROPPING_TYPE,
+		ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES,
+		ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
+		ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,
+		ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE,
+		ANDROID_SENSOR_INFO_MAX_FRAME_DURATION,
+		ANDROID_SENSOR_INFO_PHYSICAL_SIZE,
+		ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
+		ANDROID_SENSOR_INFO_SENSITIVITY_RANGE,
+		ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE,
+		ANDROID_SENSOR_ORIENTATION,
+		ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
+		ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
+		ANDROID_SYNC_MAX_LATENCY,
+	};
+	staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS,
+				  availableCharacteristicsKeys);
+
+	std::vector<int32_t> availableRequestKeys = {
+		ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
+		ANDROID_CONTROL_AE_ANTIBANDING_MODE,
+		ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
+		ANDROID_CONTROL_AE_LOCK,
+		ANDROID_CONTROL_AE_MODE,
+		ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
+		ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
+		ANDROID_CONTROL_AF_MODE,
+		ANDROID_CONTROL_AF_TRIGGER,
+		ANDROID_CONTROL_AWB_LOCK,
+		ANDROID_CONTROL_AWB_MODE,
+		ANDROID_CONTROL_CAPTURE_INTENT,
+		ANDROID_CONTROL_EFFECT_MODE,
+		ANDROID_CONTROL_MODE,
+		ANDROID_CONTROL_SCENE_MODE,
+		ANDROID_CONTROL_VIDEO_STABILIZATION_MODE,
+		ANDROID_FLASH_MODE,
+		ANDROID_JPEG_ORIENTATION,
+		ANDROID_JPEG_QUALITY,
+		ANDROID_JPEG_THUMBNAIL_QUALITY,
+		ANDROID_JPEG_THUMBNAIL_SIZE,
+		ANDROID_LENS_APERTURE,
+		ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
+		ANDROID_NOISE_REDUCTION_MODE,
+		ANDROID_SCALER_CROP_REGION,
+		ANDROID_STATISTICS_FACE_DETECT_MODE
+	};
+	staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS,
+				  availableRequestKeys);
+
+	std::vector<int32_t> availableResultKeys = {
+		ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
+		ANDROID_CONTROL_AE_ANTIBANDING_MODE,
+		ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
+		ANDROID_CONTROL_AE_LOCK,
+		ANDROID_CONTROL_AE_MODE,
+		ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
+		ANDROID_CONTROL_AE_STATE,
+		ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
+		ANDROID_CONTROL_AF_MODE,
+		ANDROID_CONTROL_AF_STATE,
+		ANDROID_CONTROL_AF_TRIGGER,
+		ANDROID_CONTROL_AWB_LOCK,
+		ANDROID_CONTROL_AWB_MODE,
+		ANDROID_CONTROL_AWB_STATE,
+		ANDROID_CONTROL_CAPTURE_INTENT,
+		ANDROID_CONTROL_EFFECT_MODE,
+		ANDROID_CONTROL_MODE,
+		ANDROID_CONTROL_SCENE_MODE,
+		ANDROID_CONTROL_VIDEO_STABILIZATION_MODE,
+		ANDROID_FLASH_MODE,
+		ANDROID_FLASH_STATE,
+		ANDROID_JPEG_GPS_COORDINATES,
+		ANDROID_JPEG_GPS_PROCESSING_METHOD,
+		ANDROID_JPEG_GPS_TIMESTAMP,
+		ANDROID_JPEG_ORIENTATION,
+		ANDROID_JPEG_QUALITY,
+		ANDROID_JPEG_SIZE,
+		ANDROID_JPEG_THUMBNAIL_QUALITY,
+		ANDROID_JPEG_THUMBNAIL_SIZE,
+		ANDROID_LENS_APERTURE,
+		ANDROID_LENS_FOCAL_LENGTH,
+		ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
+		ANDROID_LENS_STATE,
+		ANDROID_NOISE_REDUCTION_MODE,
+		ANDROID_REQUEST_PIPELINE_DEPTH,
+		ANDROID_SCALER_CROP_REGION,
+		ANDROID_SENSOR_EXPOSURE_TIME,
+		ANDROID_SENSOR_FRAME_DURATION,
+		ANDROID_SENSOR_ROLLING_SHUTTER_SKEW,
+		ANDROID_SENSOR_TEST_PATTERN_MODE,
+		ANDROID_SENSOR_TIMESTAMP,
+		ANDROID_STATISTICS_FACE_DETECT_MODE,
+		ANDROID_STATISTICS_LENS_SHADING_MAP_MODE,
+		ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE,
+		ANDROID_STATISTICS_SCENE_FLICKER,
+	};
+	staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_RESULT_KEYS,
+				  availableResultKeys);
+
+	if (!staticMetadata_->isValid()) {
+		LOG(HAL, Error) << "Failed to construct static metadata";
+		staticMetadata_.reset();
+		return -EINVAL;
+	}
+
+	if (staticMetadata_->resized()) {
+		auto [entryCount, dataCount] = staticMetadata_->usage();
+		LOG(HAL, Info)
+			<< "Static metadata resized: " << entryCount
+			<< " entries and " << dataCount << " bytes used";
+	}
+
+	return 0;
+}
+
+/* Translate Android format code to libcamera pixel format. */
+PixelFormat CameraCapabilities::toPixelFormat(int format) const
+{
+	auto it = formatsMap_.find(format);
+	if (it == formatsMap_.end()) {
+		LOG(HAL, Error) << "Requested format " << utils::hex(format)
+				<< " not supported";
+		return PixelFormat();
+	}
+
+	return it->second;
+}
+
+std::unique_ptr<CameraMetadata> CameraCapabilities::requestTemplatePreview() const
+{
+	/*
+	 * \todo Keep this in sync with the actual number of entries.
+	 * Currently: 20 entries, 35 bytes
+	 */
+	auto requestTemplate = std::make_unique<CameraMetadata>(21, 36);
+	if (!requestTemplate->isValid()) {
+		return nullptr;
+	}
+
+	/* Get the FPS range registered in the static metadata. */
+	camera_metadata_ro_entry_t entry;
+	bool found = staticMetadata_->getEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
+					       &entry);
+	if (!found) {
+		LOG(HAL, Error) << "Cannot create capture template without FPS range";
+		return nullptr;
+	}
+
+	/*
+	 * Assume the AE_AVAILABLE_TARGET_FPS_RANGE static metadata
+	 * has been assembled as {{min, max} {max, max}}.
+	 */
+	requestTemplate->addEntry(ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
+				  entry.data.i32, 2);
+
+	uint8_t aeMode = ANDROID_CONTROL_AE_MODE_ON;
+	requestTemplate->addEntry(ANDROID_CONTROL_AE_MODE, aeMode);
+
+	int32_t aeExposureCompensation = 0;
+	requestTemplate->addEntry(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
+				  aeExposureCompensation);
+
+	uint8_t aePrecaptureTrigger = ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_IDLE;
+	requestTemplate->addEntry(ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
+				  aePrecaptureTrigger);
+
+	uint8_t aeLock = ANDROID_CONTROL_AE_LOCK_OFF;
+	requestTemplate->addEntry(ANDROID_CONTROL_AE_LOCK, aeLock);
+
+	uint8_t aeAntibandingMode = ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO;
+	requestTemplate->addEntry(ANDROID_CONTROL_AE_ANTIBANDING_MODE,
+				  aeAntibandingMode);
+
+	uint8_t afMode = ANDROID_CONTROL_AF_MODE_OFF;
+	requestTemplate->addEntry(ANDROID_CONTROL_AF_MODE, afMode);
+
+	uint8_t afTrigger = ANDROID_CONTROL_AF_TRIGGER_IDLE;
+	requestTemplate->addEntry(ANDROID_CONTROL_AF_TRIGGER, afTrigger);
+
+	uint8_t awbMode = ANDROID_CONTROL_AWB_MODE_AUTO;
+	requestTemplate->addEntry(ANDROID_CONTROL_AWB_MODE, awbMode);
+
+	uint8_t awbLock = ANDROID_CONTROL_AWB_LOCK_OFF;
+	requestTemplate->addEntry(ANDROID_CONTROL_AWB_LOCK, awbLock);
+
+	uint8_t flashMode = ANDROID_FLASH_MODE_OFF;
+	requestTemplate->addEntry(ANDROID_FLASH_MODE, flashMode);
+
+	uint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
+	requestTemplate->addEntry(ANDROID_STATISTICS_FACE_DETECT_MODE,
+				  faceDetectMode);
+
+	uint8_t noiseReduction = ANDROID_NOISE_REDUCTION_MODE_OFF;
+	requestTemplate->addEntry(ANDROID_NOISE_REDUCTION_MODE,
+				  noiseReduction);
+
+	uint8_t aberrationMode = ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF;
+	requestTemplate->addEntry(ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
+				  aberrationMode);
+
+	uint8_t controlMode = ANDROID_CONTROL_MODE_AUTO;
+	requestTemplate->addEntry(ANDROID_CONTROL_MODE, controlMode);
+
+	float lensAperture = 2.53 / 100;
+	requestTemplate->addEntry(ANDROID_LENS_APERTURE, lensAperture);
+
+	uint8_t opticalStabilization = ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF;
+	requestTemplate->addEntry(ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
+				  opticalStabilization);
+
+	uint8_t captureIntent = ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW;
+	requestTemplate->addEntry(ANDROID_CONTROL_CAPTURE_INTENT,
+				  captureIntent);
+
+	return requestTemplate;
+}
+
+std::unique_ptr<CameraMetadata> CameraCapabilities::requestTemplateVideo() const
+{
+	std::unique_ptr<CameraMetadata> previewTemplate = requestTemplatePreview();
+	if (!previewTemplate)
+		return nullptr;
+
+	/*
+	 * The video template requires a fixed FPS range. Everything else
+	 * stays the same as the preview template.
+	 */
+	camera_metadata_ro_entry_t entry;
+	staticMetadata_->getEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
+				  &entry);
+
+	/*
+	 * Assume the AE_AVAILABLE_TARGET_FPS_RANGE static metadata
+	 * has been assembled as {{min, max} {max, max}}.
+	 */
+	previewTemplate->updateEntry(ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
+				     entry.data.i32 + 2, 2);
+
+	return previewTemplate;
+}
diff --git a/src/android/camera_capabilities.h b/src/android/camera_capabilities.h
new file mode 100644
index 000000000000..f511607bbd90
--- /dev/null
+++ b/src/android/camera_capabilities.h
@@ -0,0 +1,65 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2021, Google Inc.
+ *
+ * camera_capabilities.h - Camera static properties manager
+ */
+#ifndef __ANDROID_CAMERA_CAPABILITIES_H__
+#define __ANDROID_CAMERA_CAPABILITIES_H__
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include <libcamera/camera.h>
+#include <libcamera/class.h>
+#include <libcamera/formats.h>
+#include <libcamera/geometry.h>
+
+#include "camera_metadata.h"
+
+class CameraCapabilities
+{
+public:
+	CameraCapabilities() = default;
+
+	int initialize(std::shared_ptr<libcamera::Camera> camera,
+		       int orientation, int facing);
+
+	CameraMetadata *staticMetadata() const { return staticMetadata_.get(); }
+	libcamera::PixelFormat toPixelFormat(int format) const;
+	unsigned int maxJpegBufferSize() const { return maxJpegBufferSize_; }
+
+	std::unique_ptr<CameraMetadata> requestTemplatePreview() const;
+	std::unique_ptr<CameraMetadata> requestTemplateVideo() const;
+
+private:
+	LIBCAMERA_DISABLE_COPY_AND_MOVE(CameraCapabilities)
+
+	struct Camera3StreamConfiguration {
+		libcamera::Size resolution;
+		int androidFormat;
+	};
+
+	std::vector<libcamera::Size>
+	getYUVResolutions(libcamera::CameraConfiguration *cameraConfig,
+			  const libcamera::PixelFormat &pixelFormat,
+			  const std::vector<libcamera::Size> &resolutions);
+	std::vector<libcamera::Size>
+	getRawResolutions(const libcamera::PixelFormat &pixelFormat);
+	int initializeStreamConfigurations();
+
+	int initializeStaticMetadata();
+
+	std::shared_ptr<libcamera::Camera> camera_;
+
+	int facing_;
+	int orientation_;
+
+	std::vector<Camera3StreamConfiguration> streamConfigurations_;
+	std::map<int, libcamera::PixelFormat> formatsMap_;
+	std::unique_ptr<CameraMetadata> staticMetadata_;
+	unsigned int maxJpegBufferSize_;
+};
+
+#endif /* __ANDROID_CAMERA_CAPABILITIES_H__ */
diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
index 8c71fd0675d3..4bd125d7020a 100644
--- a/src/android/camera_device.cpp
+++ b/src/android/camera_device.cpp
@@ -10,11 +10,8 @@ 
 #include "camera_ops.h"
 #include "post_processor.h"
 
-#include <array>
-#include <cmath>
 #include <fstream>
 #include <sys/mman.h>
-#include <tuple>
 #include <unistd.h>
 #include <vector>
 
@@ -23,7 +20,6 @@ 
 #include <libcamera/formats.h>
 #include <libcamera/property_ids.h>
 
-#include "libcamera/internal/formats.h"
 #include "libcamera/internal/log.h"
 #include "libcamera/internal/thread.h"
 #include "libcamera/internal/utils.h"
@@ -36,94 +32,6 @@  LOG_DECLARE_CATEGORY(HAL)
 
 namespace {
 
-/*
- * \var camera3Resolutions
- * \brief The list of image resolutions defined as mandatory to be supported by
- * the Android Camera3 specification
- */
-const std::vector<Size> camera3Resolutions = {
-	{ 320, 240 },
-	{ 640, 480 },
-	{ 1280, 720 },
-	{ 1920, 1080 }
-};
-
-/*
- * \struct Camera3Format
- * \brief Data associated with an Android format identifier
- * \var libcameraFormats List of libcamera pixel formats compatible with the
- * Android format
- * \var name The human-readable representation of the Android format code
- */
-struct Camera3Format {
-	std::vector<PixelFormat> libcameraFormats;
-	bool mandatory;
-	const char *name;
-};
-
-/*
- * \var camera3FormatsMap
- * \brief Associate Android format code with ancillary data
- */
-const std::map<int, const Camera3Format> camera3FormatsMap = {
-	{
-		HAL_PIXEL_FORMAT_BLOB, {
-			{ formats::MJPEG },
-			true,
-			"BLOB"
-		}
-	}, {
-		HAL_PIXEL_FORMAT_YCbCr_420_888, {
-			{ formats::NV12, formats::NV21 },
-			true,
-			"YCbCr_420_888"
-		}
-	}, {
-		/*
-		 * \todo Translate IMPLEMENTATION_DEFINED inspecting the gralloc
-		 * usage flag. For now, copy the YCbCr_420 configuration.
-		 */
-		HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, {
-			{ formats::NV12, formats::NV21 },
-			true,
-			"IMPLEMENTATION_DEFINED"
-		}
-	}, {
-		HAL_PIXEL_FORMAT_RAW10, {
-			{
-				formats::SBGGR10_CSI2P,
-				formats::SGBRG10_CSI2P,
-				formats::SGRBG10_CSI2P,
-				formats::SRGGB10_CSI2P
-			},
-			false,
-			"RAW10"
-		}
-	}, {
-		HAL_PIXEL_FORMAT_RAW12, {
-			{
-				formats::SBGGR12_CSI2P,
-				formats::SGBRG12_CSI2P,
-				formats::SGRBG12_CSI2P,
-				formats::SRGGB12_CSI2P
-			},
-			false,
-			"RAW12"
-		}
-	}, {
-		HAL_PIXEL_FORMAT_RAW16, {
-			{
-				formats::SBGGR16,
-				formats::SGBRG16,
-				formats::SGRBG16,
-				formats::SRGGB16
-			},
-			false,
-			"RAW16"
-		}
-	},
-};
-
 /*
  * \struct Camera3StreamConfig
  * \brief Data to store StreamConfiguration associated with camera3_stream(s)
@@ -512,242 +420,7 @@  int CameraDevice::initialize(const CameraConfigData *cameraConfigData)
 		orientation_ = 0;
 	}
 
-	/* Acquire the camera and initialize available stream configurations. */
-	int ret = camera_->acquire();
-	if (ret) {
-		LOG(HAL, Error) << "Failed to temporarily acquire the camera";
-		return ret;
-	}
-
-	ret = initializeStreamConfigurations();
-	camera_->release();
-	return ret;
-}
-
-std::vector<Size> CameraDevice::getYUVResolutions(CameraConfiguration *cameraConfig,
-						  const PixelFormat &pixelFormat,
-						  const std::vector<Size> &resolutions)
-{
-	std::vector<Size> supportedResolutions;
-
-	StreamConfiguration &cfg = cameraConfig->at(0);
-	for (const Size &res : resolutions) {
-		cfg.pixelFormat = pixelFormat;
-		cfg.size = res;
-
-		CameraConfiguration::Status status = cameraConfig->validate();
-		if (status != CameraConfiguration::Valid) {
-			LOG(HAL, Debug) << cfg.toString() << " not supported";
-			continue;
-		}
-
-		LOG(HAL, Debug) << cfg.toString() << " supported";
-
-		supportedResolutions.push_back(res);
-	}
-
-	return supportedResolutions;
-}
-
-std::vector<Size> CameraDevice::getRawResolutions(const libcamera::PixelFormat &pixelFormat)
-{
-	std::unique_ptr<CameraConfiguration> cameraConfig =
-		camera_->generateConfiguration({ StreamRole::Raw });
-	StreamConfiguration &cfg = cameraConfig->at(0);
-	const StreamFormats &formats = cfg.formats();
-	std::vector<Size> supportedResolutions = formats.sizes(pixelFormat);
-
-	return supportedResolutions;
-}
-
-/*
- * Initialize the format conversion map to translate from Android format
- * identifier to libcamera pixel formats and fill in the list of supported
- * stream configurations to be reported to the Android camera framework through
- * the static stream configuration metadata.
- */
-int CameraDevice::initializeStreamConfigurations()
-{
-	/*
-	 * Get the maximum output resolutions
-	 * \todo Get this from the camera properties once defined
-	 */
-	std::unique_ptr<CameraConfiguration> cameraConfig =
-		camera_->generateConfiguration({ StillCapture });
-	if (!cameraConfig) {
-		LOG(HAL, Error) << "Failed to get maximum resolution";
-		return -EINVAL;
-	}
-	StreamConfiguration &cfg = cameraConfig->at(0);
-
-	/*
-	 * \todo JPEG - Adjust the maximum available resolution by taking the
-	 * JPEG encoder requirements into account (alignment and aspect ratio).
-	 */
-	const Size maxRes = cfg.size;
-	LOG(HAL, Debug) << "Maximum supported resolution: " << maxRes.toString();
-
-	/*
-	 * Build the list of supported image resolutions.
-	 *
-	 * The resolutions listed in camera3Resolution are mandatory to be
-	 * supported, up to the camera maximum resolution.
-	 *
-	 * Augment the list by adding resolutions calculated from the camera
-	 * maximum one.
-	 */
-	std::vector<Size> cameraResolutions;
-	std::copy_if(camera3Resolutions.begin(), camera3Resolutions.end(),
-		     std::back_inserter(cameraResolutions),
-		     [&](const Size &res) { return res < maxRes; });
-
-	/*
-	 * The Camera3 specification suggests adding 1/2 and 1/4 of the maximum
-	 * resolution.
-	 */
-	for (unsigned int divider = 2;; divider <<= 1) {
-		Size derivedSize{
-			maxRes.width / divider,
-			maxRes.height / divider,
-		};
-
-		if (derivedSize.width < 320 ||
-		    derivedSize.height < 240)
-			break;
-
-		cameraResolutions.push_back(derivedSize);
-	}
-	cameraResolutions.push_back(maxRes);
-
-	/* Remove duplicated entries from the list of supported resolutions. */
-	std::sort(cameraResolutions.begin(), cameraResolutions.end());
-	auto last = std::unique(cameraResolutions.begin(), cameraResolutions.end());
-	cameraResolutions.erase(last, cameraResolutions.end());
-
-	/*
-	 * Build the list of supported camera formats.
-	 *
-	 * To each Android format a list of compatible libcamera formats is
-	 * associated. The first libcamera format that tests successful is added
-	 * to the format translation map used when configuring the streams.
-	 * It is then tested against the list of supported camera resolutions to
-	 * build the stream configuration map reported through the camera static
-	 * metadata.
-	 */
-	Size maxJpegSize;
-	for (const auto &format : camera3FormatsMap) {
-		int androidFormat = format.first;
-		const Camera3Format &camera3Format = format.second;
-		const std::vector<PixelFormat> &libcameraFormats =
-			camera3Format.libcameraFormats;
-
-		LOG(HAL, Debug) << "Trying to map Android format "
-				<< camera3Format.name;
-
-		/*
-		 * JPEG is always supported, either produced directly by the
-		 * camera, or encoded in the HAL.
-		 */
-		if (androidFormat == HAL_PIXEL_FORMAT_BLOB) {
-			formatsMap_[androidFormat] = formats::MJPEG;
-			LOG(HAL, Debug) << "Mapped Android format "
-					<< camera3Format.name << " to "
-					<< formats::MJPEG.toString()
-					<< " (fixed mapping)";
-			continue;
-		}
-
-		/*
-		 * Test the libcamera formats that can produce images
-		 * compatible with the format defined by Android.
-		 */
-		PixelFormat mappedFormat;
-		for (const PixelFormat &pixelFormat : libcameraFormats) {
-
-			LOG(HAL, Debug) << "Testing " << pixelFormat.toString();
-
-			/*
-			 * The stream configuration size can be adjusted,
-			 * not the pixel format.
-			 *
-			 * \todo This could be simplified once all pipeline
-			 * handlers will report the StreamFormats list of
-			 * supported formats.
-			 */
-			cfg.pixelFormat = pixelFormat;
-
-			CameraConfiguration::Status status = cameraConfig->validate();
-			if (status != CameraConfiguration::Invalid &&
-			    cfg.pixelFormat == pixelFormat) {
-				mappedFormat = pixelFormat;
-				break;
-			}
-		}
-
-		if (!mappedFormat.isValid()) {
-			/* If the format is not mandatory, skip it. */
-			if (!camera3Format.mandatory)
-				continue;
-
-			LOG(HAL, Error)
-				<< "Failed to map mandatory Android format "
-				<< camera3Format.name << " ("
-				<< utils::hex(androidFormat) << "): aborting";
-			return -EINVAL;
-		}
-
-		/*
-		 * Record the mapping and then proceed to generate the
-		 * stream configurations map, by testing the image resolutions.
-		 */
-		formatsMap_[androidFormat] = mappedFormat;
-		LOG(HAL, Debug) << "Mapped Android format "
-				<< camera3Format.name << " to "
-				<< mappedFormat.toString();
-
-		std::vector<Size> resolutions;
-		const PixelFormatInfo &info = PixelFormatInfo::info(mappedFormat);
-		if (info.colourEncoding == PixelFormatInfo::ColourEncodingRAW)
-			resolutions = getRawResolutions(mappedFormat);
-		else
-			resolutions = getYUVResolutions(cameraConfig.get(),
-							mappedFormat,
-							cameraResolutions);
-
-		for (const Size &res : resolutions) {
-			streamConfigurations_.push_back({ res, androidFormat });
-
-			/*
-			 * If the format is HAL_PIXEL_FORMAT_YCbCr_420_888
-			 * from which JPEG is produced, add an entry for
-			 * the JPEG stream.
-			 *
-			 * \todo Wire the JPEG encoder to query the supported
-			 * sizes provided a list of formats it can encode.
-			 *
-			 * \todo Support JPEG streams produced by the Camera
-			 * natively.
-			 */
-			if (androidFormat == HAL_PIXEL_FORMAT_YCbCr_420_888) {
-				streamConfigurations_.push_back(
-					{ res, HAL_PIXEL_FORMAT_BLOB });
-				maxJpegSize = std::max(maxJpegSize, res);
-			}
-		}
-
-		/*
-		 * \todo Calculate the maximum JPEG buffer size by asking the
-		 * encoder giving the maximum frame size required.
-		 */
-		maxJpegBufferSize_ = maxJpegSize.width * maxJpegSize.height * 1.5;
-	}
-
-	LOG(HAL, Debug) << "Collected stream configuration map: ";
-	for (const auto &entry : streamConfigurations_)
-		LOG(HAL, Debug) << "{ " << entry.resolution.toString() << " - "
-				<< utils::hex(entry.androidFormat) << " }";
-
-	return 0;
+	return capabilities_.initialize(camera_, orientation_, facing_);
 }
 
 /*
@@ -817,802 +490,19 @@  void CameraDevice::stop()
 	state_ = State::Stopped;
 }
 
-void CameraDevice::setCallbacks(const camera3_callback_ops_t *callbacks)
+unsigned int CameraDevice::maxJpegBufferSize() const
 {
-	callbacks_ = callbacks;
+	return capabilities_.maxJpegBufferSize();
 }
 
-/*
- * Return static information for the camera.
- */
-const camera_metadata_t *CameraDevice::getStaticMetadata()
-{
-	if (staticMetadata_)
-		return staticMetadata_->get();
-
-	staticMetadata_ = std::make_unique<CameraMetadata>(64, 1024);
-	if (!staticMetadata_->isValid()) {
-		LOG(HAL, Error) << "Failed to allocate static metadata";
-		staticMetadata_.reset();
-		return nullptr;
-	}
-
-	const ControlInfoMap &controlsInfo = camera_->controls();
-	const ControlList &properties = camera_->properties();
-
-	/* Color correction static metadata. */
-	{
-		std::vector<uint8_t> data;
-		data.reserve(3);
-		const auto &infoMap = controlsInfo.find(&controls::draft::ColorCorrectionAberrationMode);
-		if (infoMap != controlsInfo.end()) {
-			for (const auto &value : infoMap->second.values())
-				data.push_back(value.get<int32_t>());
-		} else {
-			data.push_back(ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF);
-		}
-		staticMetadata_->addEntry(ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
-					  data);
-	}
-
-	/* Control static metadata. */
-	std::vector<uint8_t> aeAvailableAntiBandingModes = {
-		ANDROID_CONTROL_AE_ANTIBANDING_MODE_OFF,
-		ANDROID_CONTROL_AE_ANTIBANDING_MODE_50HZ,
-		ANDROID_CONTROL_AE_ANTIBANDING_MODE_60HZ,
-		ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO,
-	};
-	staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,
-				  aeAvailableAntiBandingModes);
-
-	std::vector<uint8_t> aeAvailableModes = {
-		ANDROID_CONTROL_AE_MODE_ON,
-	};
-	staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_MODES,
-				  aeAvailableModes);
-
-	int64_t minFrameDurationNsec = -1;
-	int64_t maxFrameDurationNsec = -1;
-	const auto frameDurationsInfo = controlsInfo.find(&controls::FrameDurationLimits);
-	if (frameDurationsInfo != controlsInfo.end()) {
-		minFrameDurationNsec = frameDurationsInfo->second.min().get<int64_t>() * 1000;
-		maxFrameDurationNsec = frameDurationsInfo->second.max().get<int64_t>() * 1000;
-
-		/*
-		 * Adjust the minimum frame duration to comply with Android
-		 * requirements. The camera service mandates all preview/record
-		 * streams to have a minimum frame duration < 33,366 milliseconds
-		 * (see MAX_PREVIEW_RECORD_DURATION_NS in the camera service
-		 * implementation).
-		 *
-		 * If we're close enough (+ 500 useconds) to that value, round
-		 * the minimum frame duration of the camera to an accepted
-		 * value.
-		 */
-		static constexpr int64_t MAX_PREVIEW_RECORD_DURATION_NS = 1e9 / 29.97;
-		if (minFrameDurationNsec > MAX_PREVIEW_RECORD_DURATION_NS &&
-		    minFrameDurationNsec < MAX_PREVIEW_RECORD_DURATION_NS + 500000)
-			minFrameDurationNsec = MAX_PREVIEW_RECORD_DURATION_NS - 1000;
-
-		/*
-		 * The AE routine frame rate limits are computed using the frame
-		 * duration limits, as libcamera clips the AE routine to the
-		 * frame durations.
-		 */
-		int32_t maxFps = std::round(1e9 / minFrameDurationNsec);
-		int32_t minFps = std::round(1e9 / maxFrameDurationNsec);
-		minFps = std::max(1, minFps);
-
-		/*
-		 * Force rounding errors so that we have the proper frame
-		 * durations for when we reuse these variables later
-		 */
-		minFrameDurationNsec = 1e9 / maxFps;
-		maxFrameDurationNsec = 1e9 / minFps;
-
-		/*
-		 * Register to the camera service {min, max} and {max, max}
-		 * intervals as requested by the metadata documentation.
-		 */
-		int32_t availableAeFpsTarget[] = {
-			minFps, maxFps, maxFps, maxFps
-		};
-		staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
-					  availableAeFpsTarget);
-	}
-
-	std::vector<int32_t> aeCompensationRange = {
-		0, 0,
-	};
-	staticMetadata_->addEntry(ANDROID_CONTROL_AE_COMPENSATION_RANGE,
-				  aeCompensationRange);
-
-	const camera_metadata_rational_t aeCompensationStep[] = {
-		{ 0, 1 }
-	};
-	staticMetadata_->addEntry(ANDROID_CONTROL_AE_COMPENSATION_STEP,
-				  aeCompensationStep);
-
-	std::vector<uint8_t> availableAfModes = {
-		ANDROID_CONTROL_AF_MODE_OFF,
-	};
-	staticMetadata_->addEntry(ANDROID_CONTROL_AF_AVAILABLE_MODES,
-				  availableAfModes);
-
-	std::vector<uint8_t> availableEffects = {
-		ANDROID_CONTROL_EFFECT_MODE_OFF,
-	};
-	staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_EFFECTS,
-				  availableEffects);
-
-	std::vector<uint8_t> availableSceneModes = {
-		ANDROID_CONTROL_SCENE_MODE_DISABLED,
-	};
-	staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_SCENE_MODES,
-				  availableSceneModes);
-
-	std::vector<uint8_t> availableStabilizationModes = {
-		ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF,
-	};
-	staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
-				  availableStabilizationModes);
-
-	/*
-	 * \todo Inspect the Camera capabilities to report the available
-	 * AWB modes. Default to AUTO as CTS tests require it.
-	 */
-	std::vector<uint8_t> availableAwbModes = {
-		ANDROID_CONTROL_AWB_MODE_AUTO,
-	};
-	staticMetadata_->addEntry(ANDROID_CONTROL_AWB_AVAILABLE_MODES,
-				  availableAwbModes);
-
-	std::vector<int32_t> availableMaxRegions = {
-		0, 0, 0,
-	};
-	staticMetadata_->addEntry(ANDROID_CONTROL_MAX_REGIONS,
-				  availableMaxRegions);
-
-	std::vector<uint8_t> sceneModesOverride = {
-		ANDROID_CONTROL_AE_MODE_ON,
-		ANDROID_CONTROL_AWB_MODE_AUTO,
-		ANDROID_CONTROL_AF_MODE_OFF,
-	};
-	staticMetadata_->addEntry(ANDROID_CONTROL_SCENE_MODE_OVERRIDES,
-				  sceneModesOverride);
-
-	uint8_t aeLockAvailable = ANDROID_CONTROL_AE_LOCK_AVAILABLE_FALSE;
-	staticMetadata_->addEntry(ANDROID_CONTROL_AE_LOCK_AVAILABLE,
-				  aeLockAvailable);
-
-	uint8_t awbLockAvailable = ANDROID_CONTROL_AWB_LOCK_AVAILABLE_FALSE;
-	staticMetadata_->addEntry(ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
-				  awbLockAvailable);
-
-	char availableControlModes = ANDROID_CONTROL_MODE_AUTO;
-	staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_MODES,
-				  availableControlModes);
-
-	/* JPEG static metadata. */
-
-	/*
-	 * Create the list of supported thumbnail sizes by inspecting the
-	 * available JPEG resolutions collected in streamConfigurations_ and
-	 * generate one entry for each aspect ratio.
-	 *
-	 * The JPEG thumbnailer can freely scale, so pick an arbitrary
-	 * (160, 160) size as the bounding rectangle, which is then cropped to
-	 * the different supported aspect ratios.
-	 */
-	constexpr Size maxJpegThumbnail(160, 160);
-	std::vector<Size> thumbnailSizes;
-	thumbnailSizes.push_back({ 0, 0 });
-	for (const auto &entry : streamConfigurations_) {
-		if (entry.androidFormat != HAL_PIXEL_FORMAT_BLOB)
-			continue;
-
-		Size thumbnailSize = maxJpegThumbnail
-				     .boundedToAspectRatio({ entry.resolution.width,
-							     entry.resolution.height });
-		thumbnailSizes.push_back(thumbnailSize);
-	}
-
-	std::sort(thumbnailSizes.begin(), thumbnailSizes.end());
-	auto last = std::unique(thumbnailSizes.begin(), thumbnailSizes.end());
-	thumbnailSizes.erase(last, thumbnailSizes.end());
-
-	/* Transform sizes in to a list of integers that can be consumed. */
-	std::vector<int32_t> thumbnailEntries;
-	thumbnailEntries.reserve(thumbnailSizes.size() * 2);
-	for (const auto &size : thumbnailSizes) {
-		thumbnailEntries.push_back(size.width);
-		thumbnailEntries.push_back(size.height);
-	}
-	staticMetadata_->addEntry(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,
-				  thumbnailEntries);
-
-	staticMetadata_->addEntry(ANDROID_JPEG_MAX_SIZE, maxJpegBufferSize_);
-
-	/* Sensor static metadata. */
-	std::array<int32_t, 2> pixelArraySize;
-	{
-		const Size &size = properties.get(properties::PixelArraySize);
-		pixelArraySize[0] = size.width;
-		pixelArraySize[1] = size.height;
-		staticMetadata_->addEntry(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
-					  pixelArraySize);
-	}
-
-	if (properties.contains(properties::UnitCellSize)) {
-		const Size &cellSize = properties.get<Size>(properties::UnitCellSize);
-		std::array<float, 2> physicalSize{
-			cellSize.width * pixelArraySize[0] / 1e6f,
-			cellSize.height * pixelArraySize[1] / 1e6f
-		};
-		staticMetadata_->addEntry(ANDROID_SENSOR_INFO_PHYSICAL_SIZE,
-					  physicalSize);
-	}
-
-	{
-		const Span<const Rectangle> &rects =
-			properties.get(properties::PixelArrayActiveAreas);
-		std::vector<int32_t> data{
-			static_cast<int32_t>(rects[0].x),
-			static_cast<int32_t>(rects[0].y),
-			static_cast<int32_t>(rects[0].width),
-			static_cast<int32_t>(rects[0].height),
-		};
-		staticMetadata_->addEntry(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
-					  data);
-	}
-
-	int32_t sensitivityRange[] = {
-		32, 2400,
-	};
-	staticMetadata_->addEntry(ANDROID_SENSOR_INFO_SENSITIVITY_RANGE,
-				  sensitivityRange);
-
-	/* Report the color filter arrangement if the camera reports it. */
-	if (properties.contains(properties::draft::ColorFilterArrangement)) {
-		uint8_t filterArr = properties.get(properties::draft::ColorFilterArrangement);
-		staticMetadata_->addEntry(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,
-					  filterArr);
-	}
-
-	const auto &exposureInfo = controlsInfo.find(&controls::ExposureTime);
-	if (exposureInfo != controlsInfo.end()) {
-		int64_t exposureTimeRange[2] = {
-			exposureInfo->second.min().get<int32_t>() * 1000LL,
-			exposureInfo->second.max().get<int32_t>() * 1000LL,
-		};
-		staticMetadata_->addEntry(ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE,
-					  exposureTimeRange, 2);
-	}
-
-	staticMetadata_->addEntry(ANDROID_SENSOR_ORIENTATION, orientation_);
-
-	std::vector<int32_t> testPatternModes = {
-		ANDROID_SENSOR_TEST_PATTERN_MODE_OFF
-	};
-	const auto &testPatternsInfo =
-		controlsInfo.find(&controls::draft::TestPatternMode);
-	if (testPatternsInfo != controlsInfo.end()) {
-		const auto &values = testPatternsInfo->second.values();
-		ASSERT(!values.empty());
-		for (const auto &value : values) {
-			switch (value.get<int32_t>()) {
-			case controls::draft::TestPatternModeOff:
-				/*
-				 * ANDROID_SENSOR_TEST_PATTERN_MODE_OFF is
-				 * already in testPatternModes.
-				 */
-				break;
-
-			case controls::draft::TestPatternModeSolidColor:
-				testPatternModes.push_back(
-					ANDROID_SENSOR_TEST_PATTERN_MODE_SOLID_COLOR);
-				break;
-
-			case controls::draft::TestPatternModeColorBars:
-				testPatternModes.push_back(
-					ANDROID_SENSOR_TEST_PATTERN_MODE_COLOR_BARS);
-				break;
-
-			case controls::draft::TestPatternModeColorBarsFadeToGray:
-				testPatternModes.push_back(
-					ANDROID_SENSOR_TEST_PATTERN_MODE_COLOR_BARS_FADE_TO_GRAY);
-				break;
-
-			case controls::draft::TestPatternModePn9:
-				testPatternModes.push_back(
-					ANDROID_SENSOR_TEST_PATTERN_MODE_PN9);
-				break;
-
-			case controls::draft::TestPatternModeCustom1:
-				/* We don't support this yet. */
-				break;
-
-			default:
-				LOG(HAL, Error) << "Unknown test pattern mode: "
-						<< value.get<int32_t>();
-				continue;
-			}
-		}
-	}
-	staticMetadata_->addEntry(ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES,
-				  testPatternModes);
-
-	uint8_t timestampSource = ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN;
-	staticMetadata_->addEntry(ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE,
-				  timestampSource);
-
-	if (maxFrameDurationNsec > 0)
-		staticMetadata_->addEntry(ANDROID_SENSOR_INFO_MAX_FRAME_DURATION,
-					  maxFrameDurationNsec);
-
-	/* Statistics static metadata. */
-	uint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
-	staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
-				  faceDetectMode);
-
-	int32_t maxFaceCount = 0;
-	staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
-				  maxFaceCount);
-
-	{
-		std::vector<uint8_t> data;
-		data.reserve(2);
-		const auto &infoMap = controlsInfo.find(&controls::draft::LensShadingMapMode);
-		if (infoMap != controlsInfo.end()) {
-			for (const auto &value : infoMap->second.values())
-				data.push_back(value.get<int32_t>());
-		} else {
-			data.push_back(ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF);
-		}
-		staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES,
-					  data);
-	}
-
-	/* Sync static metadata. */
-	int32_t maxLatency = ANDROID_SYNC_MAX_LATENCY_UNKNOWN;
-	staticMetadata_->addEntry(ANDROID_SYNC_MAX_LATENCY, maxLatency);
-
-	/* Flash static metadata. */
-	char flashAvailable = ANDROID_FLASH_INFO_AVAILABLE_FALSE;
-	staticMetadata_->addEntry(ANDROID_FLASH_INFO_AVAILABLE,
-				  flashAvailable);
-
-	/* Lens static metadata. */
-	std::vector<float> lensApertures = {
-		2.53 / 100,
-	};
-	staticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_APERTURES,
-				  lensApertures);
-
-	uint8_t lensFacing;
-	switch (facing_) {
-	default:
-	case CAMERA_FACING_FRONT:
-		lensFacing = ANDROID_LENS_FACING_FRONT;
-		break;
-	case CAMERA_FACING_BACK:
-		lensFacing = ANDROID_LENS_FACING_BACK;
-		break;
-	case CAMERA_FACING_EXTERNAL:
-		lensFacing = ANDROID_LENS_FACING_EXTERNAL;
-		break;
-	}
-	staticMetadata_->addEntry(ANDROID_LENS_FACING, lensFacing);
-
-	std::vector<float> lensFocalLengths = {
-		1,
-	};
-	staticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS,
-				  lensFocalLengths);
-
-	std::vector<uint8_t> opticalStabilizations = {
-		ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF,
-	};
-	staticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
-				  opticalStabilizations);
-
-	float hypeFocalDistance = 0;
-	staticMetadata_->addEntry(ANDROID_LENS_INFO_HYPERFOCAL_DISTANCE,
-				  hypeFocalDistance);
-
-	float minFocusDistance = 0;
-	staticMetadata_->addEntry(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
-				  minFocusDistance);
-
-	/* Noise reduction modes. */
-	{
-		std::vector<uint8_t> data;
-		data.reserve(5);
-		const auto &infoMap = controlsInfo.find(&controls::draft::NoiseReductionMode);
-		if (infoMap != controlsInfo.end()) {
-			for (const auto &value : infoMap->second.values())
-				data.push_back(value.get<int32_t>());
-		} else {
-			data.push_back(ANDROID_NOISE_REDUCTION_MODE_OFF);
-		}
-		staticMetadata_->addEntry(ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
-					  data);
-	}
-
-	/* Scaler static metadata. */
-
-	/*
-	 * \todo The digital zoom factor is a property that depends on the
-	 * desired output configuration and the sensor frame size input to the
-	 * ISP. This information is not available to the Android HAL, not at
-	 * initialization time at least.
-	 *
-	 * As a workaround rely on pipeline handlers initializing the
-	 * ScalerCrop control with the camera default configuration and use the
-	 * maximum and minimum crop rectangles to calculate the digital zoom
-	 * factor.
-	 */
-	float maxZoom = 1.0f;
-	const auto scalerCrop = controlsInfo.find(&controls::ScalerCrop);
-	if (scalerCrop != controlsInfo.end()) {
-		Rectangle min = scalerCrop->second.min().get<Rectangle>();
-		Rectangle max = scalerCrop->second.max().get<Rectangle>();
-		maxZoom = std::min(1.0f * max.width / min.width,
-				   1.0f * max.height / min.height);
-	}
-	staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
-				  maxZoom);
-
-	std::vector<uint32_t> availableStreamConfigurations;
-	availableStreamConfigurations.reserve(streamConfigurations_.size() * 4);
-	for (const auto &entry : streamConfigurations_) {
-		availableStreamConfigurations.push_back(entry.androidFormat);
-		availableStreamConfigurations.push_back(entry.resolution.width);
-		availableStreamConfigurations.push_back(entry.resolution.height);
-		availableStreamConfigurations.push_back(
-			ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT);
-	}
-	staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
-				  availableStreamConfigurations);
-
-	std::vector<int64_t> availableStallDurations = {
-		ANDROID_SCALER_AVAILABLE_FORMATS_BLOB, 2560, 1920, 33333333,
-	};
-	staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,
-				  availableStallDurations);
-
-	/* Use the minimum frame duration for all the YUV/RGB formats. */
-	if (minFrameDurationNsec > 0) {
-		std::vector<int64_t> minFrameDurations;
-		minFrameDurations.reserve(streamConfigurations_.size() * 4);
-		for (const auto &entry : streamConfigurations_) {
-			minFrameDurations.push_back(entry.androidFormat);
-			minFrameDurations.push_back(entry.resolution.width);
-			minFrameDurations.push_back(entry.resolution.height);
-			minFrameDurations.push_back(minFrameDurationNsec);
-		}
-		staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
-					  minFrameDurations);
-	}
-
-	uint8_t croppingType = ANDROID_SCALER_CROPPING_TYPE_CENTER_ONLY;
-	staticMetadata_->addEntry(ANDROID_SCALER_CROPPING_TYPE, croppingType);
-
-	/* Info static metadata. */
-	uint8_t supportedHWLevel = ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
-	staticMetadata_->addEntry(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
-				  supportedHWLevel);
-
-	/* Request static metadata. */
-	int32_t partialResultCount = 1;
-	staticMetadata_->addEntry(ANDROID_REQUEST_PARTIAL_RESULT_COUNT,
-				  partialResultCount);
-
-	{
-		/* Default the value to 2 if not reported by the camera. */
-		uint8_t maxPipelineDepth = 2;
-		const auto &infoMap = controlsInfo.find(&controls::draft::PipelineDepth);
-		if (infoMap != controlsInfo.end())
-			maxPipelineDepth = infoMap->second.max().get<int32_t>();
-		staticMetadata_->addEntry(ANDROID_REQUEST_PIPELINE_MAX_DEPTH,
-					  maxPipelineDepth);
-	}
-
-	/* LIMITED does not support reprocessing. */
-	uint32_t maxNumInputStreams = 0;
-	staticMetadata_->addEntry(ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,
-				  maxNumInputStreams);
-
-	std::vector<uint8_t> availableCapabilities = {
-		ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE,
-	};
-
-	/* Report if camera supports RAW. */
-	bool rawStreamAvailable = false;
-	std::unique_ptr<CameraConfiguration> cameraConfig =
-		camera_->generateConfiguration({ StreamRole::Raw });
-	if (cameraConfig && !cameraConfig->empty()) {
-		const PixelFormatInfo &info =
-			PixelFormatInfo::info(cameraConfig->at(0).pixelFormat);
-		/* Only advertise RAW support if RAW16 is possible. */
-		if (info.colourEncoding == PixelFormatInfo::ColourEncodingRAW &&
-		    info.bitsPerPixel == 16) {
-			rawStreamAvailable = true;
-			availableCapabilities.push_back(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW);
-		}
-	}
-
-	/* Number of { RAW, YUV, JPEG } supported output streams */
-	int32_t numOutStreams[] = { rawStreamAvailable, 2, 1 };
-	staticMetadata_->addEntry(ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,
-				  numOutStreams);
-
-	staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
-				  availableCapabilities);
-
-	std::vector<int32_t> availableCharacteristicsKeys = {
-		ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
-		ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,
-		ANDROID_CONTROL_AE_AVAILABLE_MODES,
-		ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
-		ANDROID_CONTROL_AE_COMPENSATION_RANGE,
-		ANDROID_CONTROL_AE_COMPENSATION_STEP,
-		ANDROID_CONTROL_AE_LOCK_AVAILABLE,
-		ANDROID_CONTROL_AF_AVAILABLE_MODES,
-		ANDROID_CONTROL_AVAILABLE_EFFECTS,
-		ANDROID_CONTROL_AVAILABLE_MODES,
-		ANDROID_CONTROL_AVAILABLE_SCENE_MODES,
-		ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
-		ANDROID_CONTROL_AWB_AVAILABLE_MODES,
-		ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
-		ANDROID_CONTROL_MAX_REGIONS,
-		ANDROID_CONTROL_SCENE_MODE_OVERRIDES,
-		ANDROID_FLASH_INFO_AVAILABLE,
-		ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
-		ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,
-		ANDROID_JPEG_MAX_SIZE,
-		ANDROID_LENS_FACING,
-		ANDROID_LENS_INFO_AVAILABLE_APERTURES,
-		ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS,
-		ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
-		ANDROID_LENS_INFO_HYPERFOCAL_DISTANCE,
-		ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
-		ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
-		ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
-		ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,
-		ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,
-		ANDROID_REQUEST_PARTIAL_RESULT_COUNT,
-		ANDROID_REQUEST_PIPELINE_MAX_DEPTH,
-		ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
-		ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
-		ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,
-		ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
-		ANDROID_SCALER_CROPPING_TYPE,
-		ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES,
-		ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
-		ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,
-		ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE,
-		ANDROID_SENSOR_INFO_MAX_FRAME_DURATION,
-		ANDROID_SENSOR_INFO_PHYSICAL_SIZE,
-		ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
-		ANDROID_SENSOR_INFO_SENSITIVITY_RANGE,
-		ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE,
-		ANDROID_SENSOR_ORIENTATION,
-		ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
-		ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
-		ANDROID_SYNC_MAX_LATENCY,
-	};
-	staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS,
-				  availableCharacteristicsKeys);
-
-	std::vector<int32_t> availableRequestKeys = {
-		ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
-		ANDROID_CONTROL_AE_ANTIBANDING_MODE,
-		ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
-		ANDROID_CONTROL_AE_LOCK,
-		ANDROID_CONTROL_AE_MODE,
-		ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
-		ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
-		ANDROID_CONTROL_AF_MODE,
-		ANDROID_CONTROL_AF_TRIGGER,
-		ANDROID_CONTROL_AWB_LOCK,
-		ANDROID_CONTROL_AWB_MODE,
-		ANDROID_CONTROL_CAPTURE_INTENT,
-		ANDROID_CONTROL_EFFECT_MODE,
-		ANDROID_CONTROL_MODE,
-		ANDROID_CONTROL_SCENE_MODE,
-		ANDROID_CONTROL_VIDEO_STABILIZATION_MODE,
-		ANDROID_FLASH_MODE,
-		ANDROID_JPEG_ORIENTATION,
-		ANDROID_JPEG_QUALITY,
-		ANDROID_JPEG_THUMBNAIL_QUALITY,
-		ANDROID_JPEG_THUMBNAIL_SIZE,
-		ANDROID_LENS_APERTURE,
-		ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
-		ANDROID_NOISE_REDUCTION_MODE,
-		ANDROID_SCALER_CROP_REGION,
-		ANDROID_STATISTICS_FACE_DETECT_MODE
-	};
-	staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS,
-				  availableRequestKeys);
-
-	std::vector<int32_t> availableResultKeys = {
-		ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
-		ANDROID_CONTROL_AE_ANTIBANDING_MODE,
-		ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
-		ANDROID_CONTROL_AE_LOCK,
-		ANDROID_CONTROL_AE_MODE,
-		ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
-		ANDROID_CONTROL_AE_STATE,
-		ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
-		ANDROID_CONTROL_AF_MODE,
-		ANDROID_CONTROL_AF_STATE,
-		ANDROID_CONTROL_AF_TRIGGER,
-		ANDROID_CONTROL_AWB_LOCK,
-		ANDROID_CONTROL_AWB_MODE,
-		ANDROID_CONTROL_AWB_STATE,
-		ANDROID_CONTROL_CAPTURE_INTENT,
-		ANDROID_CONTROL_EFFECT_MODE,
-		ANDROID_CONTROL_MODE,
-		ANDROID_CONTROL_SCENE_MODE,
-		ANDROID_CONTROL_VIDEO_STABILIZATION_MODE,
-		ANDROID_FLASH_MODE,
-		ANDROID_FLASH_STATE,
-		ANDROID_JPEG_GPS_COORDINATES,
-		ANDROID_JPEG_GPS_PROCESSING_METHOD,
-		ANDROID_JPEG_GPS_TIMESTAMP,
-		ANDROID_JPEG_ORIENTATION,
-		ANDROID_JPEG_QUALITY,
-		ANDROID_JPEG_SIZE,
-		ANDROID_JPEG_THUMBNAIL_QUALITY,
-		ANDROID_JPEG_THUMBNAIL_SIZE,
-		ANDROID_LENS_APERTURE,
-		ANDROID_LENS_FOCAL_LENGTH,
-		ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
-		ANDROID_LENS_STATE,
-		ANDROID_NOISE_REDUCTION_MODE,
-		ANDROID_REQUEST_PIPELINE_DEPTH,
-		ANDROID_SCALER_CROP_REGION,
-		ANDROID_SENSOR_EXPOSURE_TIME,
-		ANDROID_SENSOR_FRAME_DURATION,
-		ANDROID_SENSOR_ROLLING_SHUTTER_SKEW,
-		ANDROID_SENSOR_TEST_PATTERN_MODE,
-		ANDROID_SENSOR_TIMESTAMP,
-		ANDROID_STATISTICS_FACE_DETECT_MODE,
-		ANDROID_STATISTICS_LENS_SHADING_MAP_MODE,
-		ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE,
-		ANDROID_STATISTICS_SCENE_FLICKER,
-	};
-	staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_RESULT_KEYS,
-				  availableResultKeys);
-
-	if (!staticMetadata_->isValid()) {
-		LOG(HAL, Error) << "Failed to construct static metadata";
-		staticMetadata_.reset();
-		return nullptr;
-	}
-
-	if (staticMetadata_->resized()) {
-		auto [entryCount, dataCount] = staticMetadata_->usage();
-		LOG(HAL, Info)
-			<< "Static metadata resized: " << entryCount
-			<< " entries and " << dataCount << " bytes used";
-	}
-
-	return staticMetadata_->get();
-}
-
-std::unique_ptr<CameraMetadata> CameraDevice::requestTemplatePreview()
+void CameraDevice::setCallbacks(const camera3_callback_ops_t *callbacks)
 {
-	/*
-	 * \todo Keep this in sync with the actual number of entries.
-	 * Currently: 20 entries, 35 bytes
-	 */
-	auto requestTemplate = std::make_unique<CameraMetadata>(21, 36);
-	if (!requestTemplate->isValid()) {
-		return nullptr;
-	}
-
-	/* Get the FPS range registered in the static metadata. */
-	camera_metadata_ro_entry_t entry;
-	bool found = staticMetadata_->getEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
-					       &entry);
-	if (!found) {
-		LOG(HAL, Error) << "Cannot create capture template without FPS range";
-		return nullptr;
-	}
-
-	/*
-	 * Assume the AE_AVAILABLE_TARGET_FPS_RANGE static metadata
-	 * has been assembled as {{min, max} {max, max}}.
-	 */
-	requestTemplate->addEntry(ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
-				  entry.data.i32, 2);
-
-	uint8_t aeMode = ANDROID_CONTROL_AE_MODE_ON;
-	requestTemplate->addEntry(ANDROID_CONTROL_AE_MODE, aeMode);
-
-	int32_t aeExposureCompensation = 0;
-	requestTemplate->addEntry(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
-				  aeExposureCompensation);
-
-	uint8_t aePrecaptureTrigger = ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_IDLE;
-	requestTemplate->addEntry(ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
-				  aePrecaptureTrigger);
-
-	uint8_t aeLock = ANDROID_CONTROL_AE_LOCK_OFF;
-	requestTemplate->addEntry(ANDROID_CONTROL_AE_LOCK, aeLock);
-
-	uint8_t aeAntibandingMode = ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO;
-	requestTemplate->addEntry(ANDROID_CONTROL_AE_ANTIBANDING_MODE,
-				  aeAntibandingMode);
-
-	uint8_t afMode = ANDROID_CONTROL_AF_MODE_OFF;
-	requestTemplate->addEntry(ANDROID_CONTROL_AF_MODE, afMode);
-
-	uint8_t afTrigger = ANDROID_CONTROL_AF_TRIGGER_IDLE;
-	requestTemplate->addEntry(ANDROID_CONTROL_AF_TRIGGER, afTrigger);
-
-	uint8_t awbMode = ANDROID_CONTROL_AWB_MODE_AUTO;
-	requestTemplate->addEntry(ANDROID_CONTROL_AWB_MODE, awbMode);
-
-	uint8_t awbLock = ANDROID_CONTROL_AWB_LOCK_OFF;
-	requestTemplate->addEntry(ANDROID_CONTROL_AWB_LOCK, awbLock);
-
-	uint8_t flashMode = ANDROID_FLASH_MODE_OFF;
-	requestTemplate->addEntry(ANDROID_FLASH_MODE, flashMode);
-
-	uint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
-	requestTemplate->addEntry(ANDROID_STATISTICS_FACE_DETECT_MODE,
-				  faceDetectMode);
-
-	uint8_t noiseReduction = ANDROID_NOISE_REDUCTION_MODE_OFF;
-	requestTemplate->addEntry(ANDROID_NOISE_REDUCTION_MODE,
-				  noiseReduction);
-
-	uint8_t aberrationMode = ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF;
-	requestTemplate->addEntry(ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
-				  aberrationMode);
-
-	uint8_t controlMode = ANDROID_CONTROL_MODE_AUTO;
-	requestTemplate->addEntry(ANDROID_CONTROL_MODE, controlMode);
-
-	float lensAperture = 2.53 / 100;
-	requestTemplate->addEntry(ANDROID_LENS_APERTURE, lensAperture);
-
-	uint8_t opticalStabilization = ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF;
-	requestTemplate->addEntry(ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
-				  opticalStabilization);
-
-	uint8_t captureIntent = ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW;
-	requestTemplate->addEntry(ANDROID_CONTROL_CAPTURE_INTENT,
-				  captureIntent);
-
-	return requestTemplate;
+	callbacks_ = callbacks;
 }
 
-std::unique_ptr<CameraMetadata> CameraDevice::requestTemplateVideo()
+const camera_metadata_t *CameraDevice::getStaticMetadata()
 {
-	std::unique_ptr<CameraMetadata> previewTemplate = requestTemplatePreview();
-	if (!previewTemplate)
-		return nullptr;
-
-	/*
-	 * The video template requires a fixed FPS range. Everything else
-	 * stays the same as the preview template.
-	 */
-	camera_metadata_ro_entry_t entry;
-	staticMetadata_->getEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
-				  &entry);
-
-	/*
-	 * Assume the AE_AVAILABLE_TARGET_FPS_RANGE static metadata
-	 * has been assembled as {{min, max} {max, max}}.
-	 */
-	previewTemplate->updateEntry(ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
-				     entry.data.i32 + 2, 2);
-
-	return previewTemplate;
+	return capabilities_.staticMetadata()->get();
 }
 
 /*
@@ -1630,7 +520,7 @@  const camera_metadata_t *CameraDevice::constructDefaultRequestSettings(int type)
 	switch (type) {
 	case CAMERA3_TEMPLATE_PREVIEW:
 		captureIntent = ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW;
-		requestTemplate = requestTemplatePreview();
+		requestTemplate = capabilities_.requestTemplatePreview();
 		break;
 	case CAMERA3_TEMPLATE_STILL_CAPTURE:
 		/*
@@ -1638,15 +528,15 @@  const camera_metadata_t *CameraDevice::constructDefaultRequestSettings(int type)
 		 * for the torch mode we currently do not support.
 		 */
 		captureIntent = ANDROID_CONTROL_CAPTURE_INTENT_STILL_CAPTURE;
-		requestTemplate = requestTemplatePreview();
+		requestTemplate = capabilities_.requestTemplatePreview();
 		break;
 	case CAMERA3_TEMPLATE_VIDEO_RECORD:
 		captureIntent = ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_RECORD;
-		requestTemplate = requestTemplateVideo();
+		requestTemplate = capabilities_.requestTemplateVideo();
 		break;
 	case CAMERA3_TEMPLATE_VIDEO_SNAPSHOT:
 		captureIntent = ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT;
-		requestTemplate = requestTemplateVideo();
+		requestTemplate = capabilities_.requestTemplateVideo();
 		break;
 	/* \todo Implement templates generation for the remaining use cases. */
 	case CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG:
@@ -1668,19 +558,6 @@  const camera_metadata_t *CameraDevice::constructDefaultRequestSettings(int type)
 	return requestTemplates_[type]->get();
 }
 
-PixelFormat CameraDevice::toPixelFormat(int format) const
-{
-	/* Translate Android format code to libcamera pixel format. */
-	auto it = formatsMap_.find(format);
-	if (it == formatsMap_.end()) {
-		LOG(HAL, Error) << "Requested format " << utils::hex(format)
-				<< " not supported";
-		return PixelFormat();
-	}
-
-	return it->second;
-}
-
 /*
  * Inspect the stream_list to produce a list of StreamConfiguration to
  * be use to configure the Camera.
@@ -1727,7 +604,7 @@  int CameraDevice::configureStreams(camera3_stream_configuration_t *stream_list)
 		camera3_stream_t *stream = stream_list->streams[i];
 		Size size(stream->width, stream->height);
 
-		PixelFormat format = toPixelFormat(stream->format);
+		PixelFormat format = capabilities_.toPixelFormat(stream->format);
 
 		LOG(HAL, Info) << "Stream #" << i
 			       << ", direction: " << stream->stream_type
diff --git a/src/android/camera_device.h b/src/android/camera_device.h
index 4aadb27c562c..090fe28a551e 100644
--- a/src/android/camera_device.h
+++ b/src/android/camera_device.h
@@ -10,14 +10,12 @@ 
 #include <map>
 #include <memory>
 #include <mutex>
-#include <tuple>
 #include <vector>
 
 #include <hardware/camera3.h>
 
 #include <libcamera/buffer.h>
 #include <libcamera/camera.h>
-#include <libcamera/geometry.h>
 #include <libcamera/request.h>
 #include <libcamera/stream.h>
 
@@ -26,6 +24,7 @@ 
 #include "libcamera/internal/message.h"
 #include "libcamera/internal/thread.h"
 
+#include "camera_capabilities.h"
 #include "camera_metadata.h"
 #include "camera_stream.h"
 #include "camera_worker.h"
@@ -57,7 +56,7 @@  public:
 	const std::string &model() const { return model_; }
 	int facing() const { return facing_; }
 	int orientation() const { return orientation_; }
-	unsigned int maxJpegBufferSize() const { return maxJpegBufferSize_; }
+	unsigned int maxJpegBufferSize() const;
 
 	void setCallbacks(const camera3_callback_ops_t *callbacks);
 	const camera_metadata_t *getStaticMetadata();
@@ -86,11 +85,6 @@  private:
 		std::unique_ptr<CaptureRequest> request_;
 	};
 
-	struct Camera3StreamConfiguration {
-		libcamera::Size resolution;
-		int androidFormat;
-	};
-
 	enum class State {
 		Stopped,
 		Flushing,
@@ -99,22 +93,11 @@  private:
 
 	void stop();
 
-	int initializeStreamConfigurations();
-	std::vector<libcamera::Size>
-	getYUVResolutions(libcamera::CameraConfiguration *cameraConfig,
-			  const libcamera::PixelFormat &pixelFormat,
-			  const std::vector<libcamera::Size> &resolutions);
-	std::vector<libcamera::Size>
-	getRawResolutions(const libcamera::PixelFormat &pixelFormat);
-
 	libcamera::FrameBuffer *createFrameBuffer(const buffer_handle_t camera3buffer);
 	void abortRequest(camera3_capture_request_t *request);
 	void notifyShutter(uint32_t frameNumber, uint64_t timestamp);
 	void notifyError(uint32_t frameNumber, camera3_stream_t *stream,
 			 camera3_error_msg_code code);
-	std::unique_ptr<CameraMetadata> requestTemplatePreview();
-	std::unique_ptr<CameraMetadata> requestTemplateVideo();
-	libcamera::PixelFormat toPixelFormat(int format) const;
 	int processControls(Camera3RequestDescriptor *descriptor);
 	std::unique_ptr<CameraMetadata> getResultMetadata(
 		const Camera3RequestDescriptor &descriptor) const;
@@ -129,13 +112,11 @@  private:
 
 	std::shared_ptr<libcamera::Camera> camera_;
 	std::unique_ptr<libcamera::CameraConfiguration> config_;
+	CameraCapabilities capabilities_;
 
-	std::unique_ptr<CameraMetadata> staticMetadata_;
 	std::map<unsigned int, std::unique_ptr<CameraMetadata>> requestTemplates_;
 	const camera3_callback_ops_t *callbacks_;
 
-	std::vector<Camera3StreamConfiguration> streamConfigurations_;
-	std::map<int, libcamera::PixelFormat> formatsMap_;
 	std::vector<CameraStream> streams_;
 
 	libcamera::Mutex descriptorsMutex_; /* Protects descriptors_. */
@@ -147,8 +128,6 @@  private:
 	int facing_;
 	int orientation_;
 
-	unsigned int maxJpegBufferSize_;
-
 	CameraMetadata lastSettings_;
 };
 
diff --git a/src/android/meson.build b/src/android/meson.build
index f27fd5316705..6270fb201338 100644
--- a/src/android/meson.build
+++ b/src/android/meson.build
@@ -44,6 +44,7 @@  subdir('cros')
 
 android_hal_sources = files([
     'camera3_hal.cpp',
+    'camera_capabilities.cpp',
     'camera_device.cpp',
     'camera_hal_config.cpp',
     'camera_hal_manager.cpp',