Merge changes from topic "equalizer"
am: eba0da9019
Change-Id: I82025275ee812b1e06038f277bdb6a5fa28634ec
diff --git a/include/hardware/gralloc.h b/include/hardware/gralloc.h
index 1b06ebf..5dafea0 100644
--- a/include/hardware/gralloc.h
+++ b/include/hardware/gralloc.h
@@ -18,7 +18,6 @@
#ifndef ANDROID_GRALLOC_INTERFACE_H
#define ANDROID_GRALLOC_INTERFACE_H
-#include <system/window.h>
#include <system/graphics.h>
#include <hardware/hardware.h>
diff --git a/include/hardware/gralloc1.h b/include/hardware/gralloc1.h
index 4845010..0a6843f 100644
--- a/include/hardware/gralloc1.h
+++ b/include/hardware/gralloc1.h
@@ -18,7 +18,7 @@
#define ANDROID_HARDWARE_GRALLOC1_H
#include <hardware/hardware.h>
-#include <system/window.h>
+#include <cutils/native_handle.h>
__BEGIN_DECLS
diff --git a/include/hardware/tv_input.h b/include/hardware/tv_input.h
index ed3fafb..b421d43 100644
--- a/include/hardware/tv_input.h
+++ b/include/hardware/tv_input.h
@@ -23,7 +23,7 @@
#include <hardware/hardware.h>
#include <system/audio.h>
-#include <system/window.h>
+#include <cutils/native_handle.h>
__BEGIN_DECLS
diff --git a/modules/camera/3_4/Android.mk b/modules/camera/3_4/Android.mk
new file mode 100644
index 0000000..d4201ba
--- /dev/null
+++ b/modules/camera/3_4/Android.mk
@@ -0,0 +1,111 @@
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+# Prevent the HAL from building on devices not specifically
+# requesting to use it.
+ifeq ($(USE_CAMERA_V4L2_HAL), true)
+
+v4l2_shared_libs := \
+ libbase \
+ libcamera_client \
+ libcamera_metadata \
+ libcutils \
+ libhardware \
+ liblog \
+ libsync \
+ libutils \
+
+v4l2_static_libs :=
+
+v4l2_cflags := -fno-short-enums -Wall -Wextra -fvisibility=hidden
+
+v4l2_c_includes := $(call include-path-for, camera)
+
+v4l2_src_files := \
+ camera.cpp \
+ capture_request.cpp \
+ format_metadata_factory.cpp \
+ metadata/boottime_state_delegate.cpp \
+ metadata/enum_converter.cpp \
+ metadata/metadata.cpp \
+ metadata/metadata_reader.cpp \
+ request_tracker.cpp \
+ static_properties.cpp \
+ stream_format.cpp \
+ v4l2_camera.cpp \
+ v4l2_camera_hal.cpp \
+ v4l2_gralloc.cpp \
+ v4l2_metadata_factory.cpp \
+ v4l2_wrapper.cpp \
+
+v4l2_test_files := \
+ format_metadata_factory_test.cpp \
+ metadata/control_test.cpp \
+ metadata/default_option_delegate_test.cpp \
+ metadata/enum_converter_test.cpp \
+ metadata/ignored_control_delegate_test.cpp \
+ metadata/map_converter_test.cpp \
+ metadata/menu_control_options_test.cpp \
+ metadata/metadata_reader_test.cpp \
+ metadata/metadata_test.cpp \
+ metadata/no_effect_control_delegate_test.cpp \
+ metadata/partial_metadata_factory_test.cpp \
+ metadata/property_test.cpp \
+ metadata/ranged_converter_test.cpp \
+ metadata/slider_control_options_test.cpp \
+ metadata/state_test.cpp \
+ metadata/tagged_control_delegate_test.cpp \
+ metadata/tagged_control_options_test.cpp \
+ metadata/v4l2_control_delegate_test.cpp \
+ request_tracker_test.cpp \
+ static_properties_test.cpp \
+
+# V4L2 Camera HAL.
+# ==============================================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := camera.v4l2
+LOCAL_MODULE_RELATIVE_PATH := hw
+LOCAL_CFLAGS += $(v4l2_cflags)
+LOCAL_SHARED_LIBRARIES := $(v4l2_shared_libs)
+LOCAL_STATIC_LIBRARIES := \
+ libgtest_prod \
+ $(v4l2_static_libs) \
+
+LOCAL_C_INCLUDES += $(v4l2_c_includes)
+LOCAL_SRC_FILES := $(v4l2_src_files)
+include $(BUILD_SHARED_LIBRARY)
+
+# Unit tests for V4L2 Camera HAL.
+# ==============================================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := camera.v4l2_test
+LOCAL_CFLAGS += $(v4l2_cflags)
+LOCAL_SHARED_LIBRARIES := $(v4l2_shared_libs)
+LOCAL_STATIC_LIBRARIES := \
+ libBionicGtestMain \
+ libgmock \
+ $(v4l2_static_libs) \
+
+LOCAL_C_INCLUDES += $(v4l2_c_includes)
+LOCAL_SRC_FILES := \
+ $(v4l2_src_files) \
+ $(v4l2_test_files) \
+
+include $(BUILD_NATIVE_TEST)
+
+endif # USE_CAMERA_V4L2_HAL
diff --git a/modules/camera/3_4/README.md b/modules/camera/3_4/README.md
new file mode 100644
index 0000000..73d0c13
--- /dev/null
+++ b/modules/camera/3_4/README.md
@@ -0,0 +1,151 @@
+# V4L2 Camera HALv3
+
+The camera.v4l2 library implements a Camera HALv3 using the
+Video For Linux 2 (V4L2) interface. This allows it to theoretically
+work with a wide variety of devices, though the limitations of V4L2
+introduce some [caveats](#V4L2-Deficiencies), causing this HAL to
+not be fully spec-compliant.
+
+## Building a Device with the HAL
+
+To ensure the HAL is built for a device, include the following in your
+`<device>.mk`:
+
+```
+USE_CAMERA_V4L2_HAL := true
+PRODUCT_PACKAGES += camera.v4l2
+PRODUCT_PROPERTY_OVERRIDES += ro.hardware.camera=v4l2
+```
+
+The first line ensures the V4L2 HAL module is visible to the build system.
+This prevents checkbuilds on devices that don't have the necessary support
+from failing. The product packages tells the build system to include the V4L2
+HALv3 library in the system image. The final line tells the hardware manager
+to load the V4L2 HAL instead of a default Camera HAL.
+
+## Requirements for Using the HAL
+
+Devices and cameras wishing to use this HAL must meet
+the following requirements:
+
+* The camera must support BGR32, YUV420, and JPEG formats.
+* The gralloc and other graphics modules used by the device must use
+`HAL_PIXEL_FORMAT_RGBA_8888` as the `HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED`
+
+## Understanding the HAL Code
+
+There are three large pieces to the V4L2 Camera HAL: the general HALv3
+Camera & HAL code, the specific implementation using V4L2,
+and the Metadata system.
+
+For context, you may also wish to read some of the documentation in
+libhardware/include/camera3.h about how the framework interacts with the HAL.
+
+### Camera & HAL Interface
+
+The camera and HAL interfaces are implemented by the Camera and
+V4L2CameraHAL classes.
+
+The V4L2CameraHAL class deals primarily with initialization of the system.
+On creation, it searches /dev/video* nodes for ones with the necessary
+capabilities. These are then all presented to the framework as available
+for use. Further operations are passed to the individual Cameras as appropriate.
+
+The Camera class implements the general logic for handling the camera -
+opening and closing, configuring streams, preparing and tracking requests, etc.
+While it handles the logistics surrounding the camera, actual image
+capture and settings logic are implemented by calling down into the
+[V4L2 Camera](#V4L2-Camera). The Camera (using helper classes) enforces
+restrictions given in the [Metadata](#Metadata) initialized by the V4L2Camera,
+such as limits on the number of in-flight requests per stream.
+Notably, this means you should be able to replace the V4L2 implementation
+with something else, and as long as you fill in the metadata correctly the
+Camera class should "just work".
+
+### V4L2 Specific Implementation
+
+The V4L2Camera class is the implementation of all the capture functionality.
+It includes some methods for the Camera class to verify the setup, but the
+bulk of the class is the request queue. The Camera class submits CaptureRequests
+as they come in and are verified. The V4L2Camera runs these through a three
+stage asynchronous pipeline:
+
+* Acceptance: the V4L2Camera accepts the request, and puts it into waiting to be
+picked up by the enqueuer.
+* Enqueuing: the V4L2Camera reads the request settings, applies them to the
+device, takes a snapshot of the settings, and hands the buffer over to the
+V4L2 driver.
+* Dequeueing: A completed frame is reclaimed from the driver, and sent
+back to the Camera class for final processing (validation, filling in the
+result object, and sending the data back to the framework).
+
+Much of this work is aided by the V4L2Wrapper helper class,
+which provides simpler inputs and outputs around the V4L2 ioctls
+based on their known use by the HAL; filling in common values automatically
+and extracting the information useful to the HAL from the results.
+This wrapper is also used to expose V4L2 controls to their corresponding
+Metadata components.
+
+### Metadata
+
+The Metadata subsystem attempts to organize and simplify handling of
+camera metadata (system/media/camera/docs/docs.html). At the top level
+is the Metadata class and the PartialMetadataInterface. The Metadata
+class provides high level interaction with the individual components -
+filling the static metadata, validating, getting, and setting settings,
+etc. The Metadata class passes all of these things on to the component
+PartialMetadataInterfaces, each of which filter for their specific
+metadata components and perform the requested task.
+
+Some generalized metadata classes are provided to simplify common logic
+for this filtering and application. At a high level, there are three
+types:
+
+* Properties: a static value.
+* Controls: dynamically adjustable values, and optionally an
+associated static property indicating what allowable values are.
+* States: a dynamic read-only value.
+
+The Metadata system uses further interfaces and subclasses to distinguish
+the variety of different functionalities necessary for different metadata
+tags.
+
+#### Metadata Factory
+
+This V4L2 Camera HAL implementation utilizes a metadata factory method.
+This method initializes all the 100+ required metadata components for
+basic HAL spec compliance. Most do nothing/report fixed values,
+but a few are hooked up to the V4L2 driver.
+
+This HAL was initially designed for use with the Raspberry Pi camera module
+v2.1, so the fixed defaults are usually assigned based on that camera.
+
+## V4L2 Deficiencies
+
+* One stream at a time is supported. Notably, this means you must re-configure
+the stream between preview and capture if they're not the same format.
+This makes this HAL not backwards compatible with the Android Camera (v1) API
+as many of its methods attempt to do just that; Camera2 must be used instead.
+* A variety of metadata properties can't be filled in from V4L2,
+such as physical properties of the camera. Thus this HAL will never be capable
+of providing perfectly accurate information for all cameras it can theoretically
+support.
+* Android requires HALs support YUV420, JPEG, and a format of the graphics
+stack's choice ("implementation defined"). Very few cameras actually support
+all of these formats (so far the Raspberry Pi cameras are the only known ones),
+so some form of format conversion built in to the HAL would be a useful feature
+to expand the reach/usefulness of this HAL.
+* V4L2 doesn't make promises about how fast settings will apply, and there's no
+good way to determine what settings were in effect for a given frame. Thus,
+the settings passed into requests and out with results are applied/read as
+a best effort and may be incorrect.
+* Many features V4L2 is capable of are not hooked up to the HAL, so the HAL
+is underfeatured compared to the ideal/what is possible.
+
+## Other Known Issues
+
+* A variety of features are unimplemented: High speed capture,
+flash torch mode, hotplugging/unplugging.
+* The HAL uses BGR for RGBA. Again, the HAL was designed for the Raspberry Pi
+camera, which doesn't support RGB, but RGB is a common default format for
+graphics stacks.
diff --git a/modules/camera/3_4/camera.cpp b/modules/camera/3_4/camera.cpp
new file mode 100644
index 0000000..7f42eef
--- /dev/null
+++ b/modules/camera/3_4/camera.cpp
@@ -0,0 +1,596 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Modified from hardware/libhardware/modules/camera/Camera.cpp
+
+#include <cstdlib>
+#include <memory>
+#include <vector>
+#include <stdio.h>
+#include <hardware/camera3.h>
+#include <sync/sync.h>
+#include <system/camera_metadata.h>
+#include <system/graphics.h>
+#include <utils/Mutex.h>
+
+#include "metadata/metadata_common.h"
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Camera"
+#include <cutils/log.h>
+
+#define ATRACE_TAG (ATRACE_TAG_CAMERA | ATRACE_TAG_HAL)
+#include <utils/Trace.h>
+
+#include "camera.h"
+
+#define CAMERA_SYNC_TIMEOUT 5000 // in msecs
+
+namespace default_camera_hal {
+
+extern "C" {
+// Shim passed to the framework to close an opened device.
+static int close_device(hw_device_t* dev)
+{
+ camera3_device_t* cam_dev = reinterpret_cast<camera3_device_t*>(dev);
+ Camera* cam = static_cast<Camera*>(cam_dev->priv);
+ return cam->close();
+}
+} // extern "C"
+
+Camera::Camera(int id)
+ : mId(id),
+ mSettingsSet(false),
+ mBusy(false),
+ mCallbackOps(NULL),
+ mInFlightTracker(new RequestTracker)
+{
+ memset(&mTemplates, 0, sizeof(mTemplates));
+ memset(&mDevice, 0, sizeof(mDevice));
+ mDevice.common.tag = HARDWARE_DEVICE_TAG;
+ mDevice.common.version = CAMERA_DEVICE_API_VERSION_3_4;
+ mDevice.common.close = close_device;
+ mDevice.ops = const_cast<camera3_device_ops_t*>(&sOps);
+ mDevice.priv = this;
+}
+
+Camera::~Camera()
+{
+}
+
+int Camera::openDevice(const hw_module_t *module, hw_device_t **device)
+{
+ ALOGI("%s:%d: Opening camera device", __func__, mId);
+ ATRACE_CALL();
+ android::Mutex::Autolock al(mDeviceLock);
+
+ if (mBusy) {
+ ALOGE("%s:%d: Error! Camera device already opened", __func__, mId);
+ return -EBUSY;
+ }
+
+ int connectResult = connect();
+ if (connectResult != 0) {
+ return connectResult;
+ }
+ mBusy = true;
+ mDevice.common.module = const_cast<hw_module_t*>(module);
+ *device = &mDevice.common;
+ return 0;
+}
+
+int Camera::getInfo(struct camera_info *info)
+{
+ info->device_version = mDevice.common.version;
+ initDeviceInfo(info);
+ if (!mStaticInfo) {
+ int res = loadStaticInfo();
+ if (res) {
+ return res;
+ }
+ }
+ info->static_camera_characteristics = mStaticInfo->raw_metadata();
+ info->facing = mStaticInfo->facing();
+ info->orientation = mStaticInfo->orientation();
+
+ return 0;
+}
+
+int Camera::loadStaticInfo() {
+ // Using a lock here ensures |mStaticInfo| will only ever be set once,
+ // even in concurrent situations.
+ android::Mutex::Autolock al(mStaticInfoLock);
+
+ if (mStaticInfo) {
+ return 0;
+ }
+
+ std::unique_ptr<android::CameraMetadata> static_metadata =
+ std::make_unique<android::CameraMetadata>();
+ int res = initStaticInfo(static_metadata.get());
+ if (res) {
+ ALOGE("%s:%d: Failed to get static info from device.",
+ __func__, mId);
+ return res;
+ }
+
+ mStaticInfo.reset(StaticProperties::NewStaticProperties(
+ std::move(static_metadata)));
+ if (!mStaticInfo) {
+ ALOGE("%s:%d: Failed to initialize static properties from device metadata.",
+ __func__, mId);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+int Camera::close()
+{
+ ALOGI("%s:%d: Closing camera device", __func__, mId);
+ ATRACE_CALL();
+ android::Mutex::Autolock al(mDeviceLock);
+
+ if (!mBusy) {
+ ALOGE("%s:%d: Error! Camera device not open", __func__, mId);
+ return -EINVAL;
+ }
+
+ flush();
+ disconnect();
+ mBusy = false;
+ return 0;
+}
+
+int Camera::initialize(const camera3_callback_ops_t *callback_ops)
+{
+ int res;
+
+ ALOGV("%s:%d: callback_ops=%p", __func__, mId, callback_ops);
+ mCallbackOps = callback_ops;
+ // per-device specific initialization
+ res = initDevice();
+ if (res != 0) {
+ ALOGE("%s:%d: Failed to initialize device!", __func__, mId);
+ return res;
+ }
+ return 0;
+}
+
+int Camera::configureStreams(camera3_stream_configuration_t *stream_config)
+{
+ android::Mutex::Autolock al(mDeviceLock);
+
+ ALOGV("%s:%d: stream_config=%p", __func__, mId, stream_config);
+ ATRACE_CALL();
+
+ // Check that there are no in-flight requests.
+ if (!mInFlightTracker->Empty()) {
+ ALOGE("%s:%d: Can't configure streams while frames are in flight.",
+ __func__, mId);
+ return -EINVAL;
+ }
+
+ // Verify the set of streams in aggregate, and perform configuration if valid.
+ int res = validateStreamConfiguration(stream_config);
+ if (res) {
+ ALOGE("%s:%d: Failed to validate stream set", __func__, mId);
+ } else {
+ // Set up all streams. Since they've been validated,
+ // this should only result in fatal (-ENODEV) errors.
+ // This occurs after validation to ensure that if there
+ // is a non-fatal error, the stream configuration doesn't change states.
+ res = setupStreams(stream_config);
+ if (res) {
+ ALOGE("%s:%d: Failed to setup stream set", __func__, mId);
+ }
+ }
+
+ // Set trackers based on result.
+ if (!res) {
+ // Success, set up the in-flight trackers for the new streams.
+ mInFlightTracker->SetStreamConfiguration(*stream_config);
+ // Must provide new settings for the new configuration.
+ mSettingsSet = false;
+ } else if (res != -EINVAL) {
+ // Fatal error, the old configuration is invalid.
+ mInFlightTracker->ClearStreamConfiguration();
+ }
+ // On a non-fatal error the old configuration, if any, remains valid.
+ return res;
+}
+
+int Camera::validateStreamConfiguration(
+ const camera3_stream_configuration_t* stream_config)
+{
+ // Check that the configuration is well-formed.
+ if (stream_config == nullptr) {
+ ALOGE("%s:%d: NULL stream configuration array", __func__, mId);
+ return -EINVAL;
+ } else if (stream_config->num_streams == 0) {
+ ALOGE("%s:%d: Empty stream configuration array", __func__, mId);
+ return -EINVAL;
+ } else if (stream_config->streams == nullptr) {
+ ALOGE("%s:%d: NULL stream configuration streams", __func__, mId);
+ return -EINVAL;
+ }
+
+ // Check that the configuration is supported.
+ // Make sure static info has been initialized before trying to use it.
+ if (!mStaticInfo) {
+ int res = loadStaticInfo();
+ if (res) {
+ return res;
+ }
+ }
+ if (!mStaticInfo->StreamConfigurationSupported(stream_config)) {
+ ALOGE("%s:%d: Stream configuration does not match static "
+ "metadata restrictions.", __func__, mId);
+ return -EINVAL;
+ }
+
+ // Dataspace support is poorly documented - unclear if the expectation
+ // is that a device supports ALL dataspaces that could match a given
+ // format. For now, defer to child class implementation.
+ // Rotation support isn't described by metadata, so must defer to device.
+ if (!validateDataspacesAndRotations(stream_config)) {
+ ALOGE("%s:%d: Device can not handle configuration "
+ "dataspaces or rotations.", __func__, mId);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+bool Camera::isValidTemplateType(int type)
+{
+ return type > 0 && type < CAMERA3_TEMPLATE_COUNT;
+}
+
+const camera_metadata_t* Camera::constructDefaultRequestSettings(int type)
+{
+ ALOGV("%s:%d: type=%d", __func__, mId, type);
+
+ if (!isValidTemplateType(type)) {
+ ALOGE("%s:%d: Invalid template request type: %d", __func__, mId, type);
+ return NULL;
+ }
+
+ if (!mTemplates[type]) {
+ // Check if the device has the necessary features
+ // for the requested template. If not, don't bother.
+ if (!mStaticInfo->TemplateSupported(type)) {
+ ALOGW("%s:%d: Camera does not support template type %d",
+ __func__, mId, type);
+ return NULL;
+ }
+
+ // Initialize this template if it hasn't been initialized yet.
+ std::unique_ptr<android::CameraMetadata> new_template =
+ std::make_unique<android::CameraMetadata>();
+ int res = initTemplate(type, new_template.get());
+ if (res || !new_template) {
+ ALOGE("%s:%d: Failed to generate template of type: %d",
+ __func__, mId, type);
+ return NULL;
+ }
+ mTemplates[type] = std::move(new_template);
+ }
+
+ // The "locking" here only causes non-const methods to fail,
+ // which is not a problem since the CameraMetadata being locked
+ // is already const. Destructing automatically "unlocks".
+ return mTemplates[type]->getAndLock();
+}
+
+int Camera::processCaptureRequest(camera3_capture_request_t *temp_request)
+{
+ int res;
+ // TODO(b/32917568): A capture request submitted or ongoing during a flush
+ // should be returned with an error; for now they are mutually exclusive.
+ android::Mutex::Autolock al(mFlushLock);
+
+ ATRACE_CALL();
+
+ if (temp_request == NULL) {
+ ALOGE("%s:%d: NULL request recieved", __func__, mId);
+ return -EINVAL;
+ }
+
+ // Make a persistent copy of request, since otherwise it won't live
+ // past the end of this method.
+ std::shared_ptr<CaptureRequest> request = std::make_shared<CaptureRequest>(temp_request);
+
+ ALOGV("%s:%d: frame: %d", __func__, mId, request->frame_number);
+
+ if (!mInFlightTracker->CanAddRequest(*request)) {
+ // Streams are full or frame number is not unique.
+ ALOGE("%s:%d: Can not add request.", __func__, mId);
+ return -EINVAL;
+ }
+
+ // Null/Empty indicates use last settings
+ if (request->settings.isEmpty() && !mSettingsSet) {
+ ALOGE("%s:%d: NULL settings without previous set Frame:%d",
+ __func__, mId, request->frame_number);
+ return -EINVAL;
+ }
+
+ if (request->input_buffer != NULL) {
+ ALOGV("%s:%d: Reprocessing input buffer %p", __func__, mId,
+ request->input_buffer.get());
+ } else {
+ ALOGV("%s:%d: Capturing new frame.", __func__, mId);
+ }
+
+ if (!isValidRequestSettings(request->settings)) {
+ ALOGE("%s:%d: Invalid request settings.", __func__, mId);
+ return -EINVAL;
+ }
+
+ // Pre-process output buffers.
+ if (request->output_buffers.size() <= 0) {
+ ALOGE("%s:%d: Invalid number of output buffers: %d", __func__, mId,
+ request->output_buffers.size());
+ return -EINVAL;
+ }
+ for (auto& output_buffer : request->output_buffers) {
+ res = preprocessCaptureBuffer(&output_buffer);
+ if (res)
+ return -ENODEV;
+ }
+
+ // Add the request to tracking.
+ if (!mInFlightTracker->Add(request)) {
+ ALOGE("%s:%d: Failed to track request for frame %d.",
+ __func__, mId, request->frame_number);
+ return -ENODEV;
+ }
+
+ // Valid settings have been provided (mSettingsSet is a misnomer;
+ // all that matters is that a previous request with valid settings
+ // has been passed to the device, not that they've been set).
+ mSettingsSet = true;
+
+ // Send the request off to the device for completion.
+ enqueueRequest(request);
+
+ // Request is now in flight. The device will call completeRequest
+ // asynchronously when it is done filling buffers and metadata.
+ return 0;
+}
+
+void Camera::completeRequest(std::shared_ptr<CaptureRequest> request, int err)
+{
+ if (!mInFlightTracker->Remove(request)) {
+ ALOGE("%s:%d: Completed request %p is not being tracked. "
+ "It may have been cleared out during a flush.",
+ __func__, mId, request.get());
+ return;
+ }
+
+ // Since |request| has been removed from the tracking, this method
+ // MUST call sendResult (can still return a result in an error state, e.g.
+ // through completeRequestWithError) so the frame doesn't get lost.
+
+ if (err) {
+ ALOGE("%s:%d: Error completing request for frame %d.",
+ __func__, mId, request->frame_number);
+ completeRequestWithError(request);
+ return;
+ }
+
+ // Notify the framework with the shutter time (extracted from the result).
+ int64_t timestamp = 0;
+ // TODO(b/31360070): The general metadata methods should be part of the
+ // default_camera_hal namespace, not the v4l2_camera_hal namespace.
+ int res = v4l2_camera_hal::SingleTagValue(
+ request->settings, ANDROID_SENSOR_TIMESTAMP, ×tamp);
+ if (res) {
+ ALOGE("%s:%d: Request for frame %d is missing required metadata.",
+ __func__, mId, request->frame_number);
+ // TODO(b/31653322): Send RESULT error.
+ // For now sending REQUEST error instead.
+ completeRequestWithError(request);
+ return;
+ }
+ notifyShutter(request->frame_number, timestamp);
+
+ // TODO(b/31653322): Check all returned buffers for errors
+ // (if any, send BUFFER error).
+
+ sendResult(request);
+}
+
+int Camera::flush()
+{
+ ALOGV("%s:%d: Flushing.", __func__, mId);
+ // TODO(b/32917568): Synchronization. Behave "appropriately"
+ // (i.e. according to camera3.h) if process_capture_request()
+ // is called concurrently with this (in either order).
+ // Since the callback to completeRequest also may happen on a separate
+ // thread, this function should behave nicely concurrently with that too.
+ android::Mutex::Autolock al(mFlushLock);
+
+ std::set<std::shared_ptr<CaptureRequest>> requests;
+ mInFlightTracker->Clear(&requests);
+ for (auto& request : requests) {
+ // TODO(b/31653322): See camera3.h. Should return different error
+ // depending on status of the request.
+ completeRequestWithError(request);
+ }
+
+ ALOGV("%s:%d: Flushed %u requests.", __func__, mId, requests.size());
+
+ // Call down into the device flushing.
+ return flushBuffers();
+}
+
+int Camera::preprocessCaptureBuffer(camera3_stream_buffer_t *buffer)
+{
+ int res;
+ // TODO(b/29334616): This probably should be non-blocking; part
+ // of the asynchronous request processing.
+ if (buffer->acquire_fence != -1) {
+ res = sync_wait(buffer->acquire_fence, CAMERA_SYNC_TIMEOUT);
+ if (res == -ETIME) {
+ ALOGE("%s:%d: Timeout waiting on buffer acquire fence",
+ __func__, mId);
+ return res;
+ } else if (res) {
+ ALOGE("%s:%d: Error waiting on buffer acquire fence: %s(%d)",
+ __func__, mId, strerror(-res), res);
+ return res;
+ }
+ }
+
+ // Acquire fence has been waited upon.
+ buffer->acquire_fence = -1;
+ // No release fence waiting unless the device sets it.
+ buffer->release_fence = -1;
+
+ buffer->status = CAMERA3_BUFFER_STATUS_OK;
+ return 0;
+}
+
+void Camera::notifyShutter(uint32_t frame_number, uint64_t timestamp)
+{
+ camera3_notify_msg_t message;
+ memset(&message, 0, sizeof(message));
+ message.type = CAMERA3_MSG_SHUTTER;
+ message.message.shutter.frame_number = frame_number;
+ message.message.shutter.timestamp = timestamp;
+ mCallbackOps->notify(mCallbackOps, &message);
+}
+
+void Camera::completeRequestWithError(std::shared_ptr<CaptureRequest> request)
+{
+ // Send an error notification.
+ camera3_notify_msg_t message;
+ memset(&message, 0, sizeof(message));
+ message.type = CAMERA3_MSG_ERROR;
+ message.message.error.frame_number = request->frame_number;
+ message.message.error.error_stream = nullptr;
+ message.message.error.error_code = CAMERA3_MSG_ERROR_REQUEST;
+ mCallbackOps->notify(mCallbackOps, &message);
+
+ // TODO(b/31856611): Ensure all the buffers indicate their error status.
+
+ // Send the errored out result.
+ sendResult(request);
+}
+
+void Camera::sendResult(std::shared_ptr<CaptureRequest> request) {
+ // Fill in the result struct
+ // (it only needs to live until the end of the framework callback).
+ camera3_capture_result_t result {
+ request->frame_number,
+ request->settings.getAndLock(),
+ request->output_buffers.size(),
+ request->output_buffers.data(),
+ request->input_buffer.get(),
+ 1 // Total result; only 1 part.
+ };
+ // Make the framework callback.
+ mCallbackOps->process_capture_result(mCallbackOps, &result);
+}
+
+void Camera::dump(int fd)
+{
+ ALOGV("%s:%d: Dumping to fd %d", __func__, mId, fd);
+ ATRACE_CALL();
+ android::Mutex::Autolock al(mDeviceLock);
+
+ dprintf(fd, "Camera ID: %d (Busy: %d)\n", mId, mBusy);
+
+ // TODO: dump all settings
+}
+
+const char* Camera::templateToString(int type)
+{
+ switch (type) {
+ case CAMERA3_TEMPLATE_PREVIEW:
+ return "CAMERA3_TEMPLATE_PREVIEW";
+ case CAMERA3_TEMPLATE_STILL_CAPTURE:
+ return "CAMERA3_TEMPLATE_STILL_CAPTURE";
+ case CAMERA3_TEMPLATE_VIDEO_RECORD:
+ return "CAMERA3_TEMPLATE_VIDEO_RECORD";
+ case CAMERA3_TEMPLATE_VIDEO_SNAPSHOT:
+ return "CAMERA3_TEMPLATE_VIDEO_SNAPSHOT";
+ case CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG:
+ return "CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG";
+ }
+ // TODO: support vendor templates
+ return "Invalid template type!";
+}
+
+extern "C" {
+// Get handle to camera from device priv data
+static Camera *camdev_to_camera(const camera3_device_t *dev)
+{
+ return reinterpret_cast<Camera*>(dev->priv);
+}
+
+static int initialize(const camera3_device_t *dev,
+ const camera3_callback_ops_t *callback_ops)
+{
+ return camdev_to_camera(dev)->initialize(callback_ops);
+}
+
+static int configure_streams(const camera3_device_t *dev,
+ camera3_stream_configuration_t *stream_list)
+{
+ return camdev_to_camera(dev)->configureStreams(stream_list);
+}
+
+static const camera_metadata_t *construct_default_request_settings(
+ const camera3_device_t *dev, int type)
+{
+ return camdev_to_camera(dev)->constructDefaultRequestSettings(type);
+}
+
+static int process_capture_request(const camera3_device_t *dev,
+ camera3_capture_request_t *request)
+{
+ return camdev_to_camera(dev)->processCaptureRequest(request);
+}
+
+static void dump(const camera3_device_t *dev, int fd)
+{
+ camdev_to_camera(dev)->dump(fd);
+}
+
+static int flush(const camera3_device_t *dev)
+{
+ return camdev_to_camera(dev)->flush();
+}
+
+} // extern "C"
+
+const camera3_device_ops_t Camera::sOps = {
+ .initialize = default_camera_hal::initialize,
+ .configure_streams = default_camera_hal::configure_streams,
+ .register_stream_buffers = nullptr,
+ .construct_default_request_settings
+ = default_camera_hal::construct_default_request_settings,
+ .process_capture_request = default_camera_hal::process_capture_request,
+ .get_metadata_vendor_tag_ops = nullptr,
+ .dump = default_camera_hal::dump,
+ .flush = default_camera_hal::flush,
+ .reserved = {0},
+};
+
+} // namespace default_camera_hal
diff --git a/modules/camera/3_4/camera.h b/modules/camera/3_4/camera.h
new file mode 100644
index 0000000..687c733
--- /dev/null
+++ b/modules/camera/3_4/camera.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Modified from hardware/libhardware/modules/camera/Camera.h
+
+#ifndef DEFAULT_CAMERA_HAL_CAMERA_H_
+#define DEFAULT_CAMERA_HAL_CAMERA_H_
+
+#include <camera/CameraMetadata.h>
+#include <hardware/hardware.h>
+#include <hardware/camera3.h>
+#include <utils/Mutex.h>
+
+#include "capture_request.h"
+#include "metadata/metadata.h"
+#include "request_tracker.h"
+#include "static_properties.h"
+
+namespace default_camera_hal {
+// Camera represents a physical camera on a device.
+// This is constructed when the HAL module is loaded, one per physical camera.
+// TODO(b/29185945): Support hotplugging.
+// It is opened by the framework, and must be closed before it can be opened
+// again.
+// This is an abstract class, containing all logic and data shared between all
+// camera devices (front, back, etc) and common to the ISP.
+class Camera {
+ public:
+ // id is used to distinguish cameras. 0 <= id < NUM_CAMERAS.
+ // module is a handle to the HAL module, used when the device is opened.
+ Camera(int id);
+ virtual ~Camera();
+
+ // Common Camera Device Operations (see <hardware/camera_common.h>)
+ int openDevice(const hw_module_t *module, hw_device_t **device);
+ int getInfo(struct camera_info *info);
+ int close();
+
+ // Camera v3 Device Operations (see <hardware/camera3.h>)
+ int initialize(const camera3_callback_ops_t *callback_ops);
+ int configureStreams(camera3_stream_configuration_t *stream_list);
+ const camera_metadata_t *constructDefaultRequestSettings(int type);
+ int processCaptureRequest(camera3_capture_request_t *temp_request);
+ void dump(int fd);
+ int flush();
+
+ protected:
+ // Connect to the device: open dev nodes, etc.
+ virtual int connect() = 0;
+ // Disconnect from the device: close dev nodes, etc.
+ virtual void disconnect() = 0;
+ // Initialize static camera characteristics for individual device
+ virtual int initStaticInfo(android::CameraMetadata* out) = 0;
+ // Initialize a template of the given type
+ virtual int initTemplate(int type, android::CameraMetadata* out) = 0;
+ // Initialize device info: resource cost and conflicting devices
+ // (/conflicting devices length)
+ virtual void initDeviceInfo(struct camera_info *info) = 0;
+ // Separate initialization method for individual devices when opened
+ virtual int initDevice() = 0;
+ // Verify stream configuration dataspaces and rotation values
+ virtual bool validateDataspacesAndRotations(
+ const camera3_stream_configuration_t* stream_config) = 0;
+ // Set up the streams, including seting usage & max_buffers
+ virtual int setupStreams(
+ camera3_stream_configuration_t* stream_config) = 0;
+ // Verify settings are valid for a capture or reprocessing
+ virtual bool isValidRequestSettings(
+ const android::CameraMetadata& settings) = 0;
+ // Enqueue a request to receive data from the camera
+ virtual int enqueueRequest(
+ std::shared_ptr<CaptureRequest> request) = 0;
+ // Flush in flight buffers.
+ virtual int flushBuffers() = 0;
+
+
+ // Callback for when the device has filled in the requested data.
+ // Fills in the result struct, validates the data, sends appropriate
+ // notifications, and returns the result to the framework.
+ void completeRequest(
+ std::shared_ptr<CaptureRequest> request, int err);
+ // Prettyprint template names
+ const char* templateToString(int type);
+
+ private:
+ // Camera device handle returned to framework for use
+ camera3_device_t mDevice;
+ // Get static info from the device and store it in mStaticInfo.
+ int loadStaticInfo();
+ // Confirm that a stream configuration is valid.
+ int validateStreamConfiguration(
+ const camera3_stream_configuration_t* stream_config);
+ // Verify settings are valid for reprocessing an input buffer
+ bool isValidReprocessSettings(const camera_metadata_t *settings);
+ // Pre-process an output buffer
+ int preprocessCaptureBuffer(camera3_stream_buffer_t *buffer);
+ // Send a shutter notify message with start of exposure time
+ void notifyShutter(uint32_t frame_number, uint64_t timestamp);
+ // Send an error message and return the errored out result.
+ void completeRequestWithError(std::shared_ptr<CaptureRequest> request);
+ // Send a capture result for a request.
+ void sendResult(std::shared_ptr<CaptureRequest> request);
+ // Is type a valid template type (and valid index into mTemplates)
+ bool isValidTemplateType(int type);
+
+ // Identifier used by framework to distinguish cameras
+ const int mId;
+ // CameraMetadata containing static characteristics
+ std::unique_ptr<StaticProperties> mStaticInfo;
+ // Flag indicating if settings have been set since
+ // the last configure_streams() call.
+ bool mSettingsSet;
+ // Busy flag indicates camera is in use
+ bool mBusy;
+ // Camera device operations handle shared by all devices
+ const static camera3_device_ops_t sOps;
+ // Methods used to call back into the framework
+ const camera3_callback_ops_t *mCallbackOps;
+ // Lock protecting the Camera object for modifications
+ android::Mutex mDeviceLock;
+ // Lock protecting only static camera characteristics, which may
+ // be accessed without the camera device open
+ android::Mutex mStaticInfoLock;
+ android::Mutex mFlushLock;
+ // Standard camera settings templates
+ std::unique_ptr<const android::CameraMetadata> mTemplates[CAMERA3_TEMPLATE_COUNT];
+ // Track in flight requests.
+ std::unique_ptr<RequestTracker> mInFlightTracker;
+};
+} // namespace default_camera_hal
+
+#endif // DEFAULT_CAMERA_HAL_CAMERA_H_
diff --git a/modules/camera/3_4/capture_request.cpp b/modules/camera/3_4/capture_request.cpp
new file mode 100644
index 0000000..00b20cd
--- /dev/null
+++ b/modules/camera/3_4/capture_request.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "capture_request.h"
+
+#include <set>
+
+namespace default_camera_hal {
+
+CaptureRequest::CaptureRequest() : CaptureRequest(nullptr) {}
+
+CaptureRequest::CaptureRequest(const camera3_capture_request_t* request) {
+ if (!request) {
+ return;
+ }
+
+ frame_number = request->frame_number;
+
+ // CameraMetadata makes copies of camera_metadata_t through the
+ // assignment operator (the constructor taking a camera_metadata_t*
+ // takes ownership instead).
+ settings = request->settings;
+
+ // camera3_stream_buffer_t can be default copy constructed,
+ // as its pointer values are handles, not ownerships.
+
+ // Copy the input buffer.
+ if (request->input_buffer) {
+ input_buffer =
+ std::make_unique<camera3_stream_buffer_t>(*request->input_buffer);
+ }
+
+ // Safely copy all the output buffers.
+ uint32_t num_output_buffers = request->num_output_buffers;
+ if (num_output_buffers < 0 || !request->output_buffers) {
+ num_output_buffers = 0;
+ }
+ output_buffers.insert(output_buffers.end(),
+ request->output_buffers,
+ request->output_buffers + num_output_buffers);
+}
+
+} // namespace default_camera_hal
diff --git a/modules/camera/3_4/capture_request.h b/modules/camera/3_4/capture_request.h
new file mode 100644
index 0000000..e2f95fa
--- /dev/null
+++ b/modules/camera/3_4/capture_request.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DEFAULT_CAMERA_HAL_CAPTURE_REQUEST_H_
+#define DEFAULT_CAMERA_HAL_CAPTURE_REQUEST_H_
+
+#include <memory>
+#include <vector>
+
+#include <camera/CameraMetadata.h>
+#include <hardware/camera3.h>
+
+namespace default_camera_hal {
+
+// A simple wrapper for camera3_capture_request_t,
+// with a constructor that makes a deep copy from the original struct.
+struct CaptureRequest {
+ uint32_t frame_number;
+ android::CameraMetadata settings;
+ std::unique_ptr<camera3_stream_buffer_t> input_buffer;
+ std::vector<camera3_stream_buffer_t> output_buffers;
+
+ CaptureRequest();
+ // Create a deep copy of |request|.
+ CaptureRequest(const camera3_capture_request_t* request);
+};
+
+} // namespace default_camera_hal
+
+#endif // DEFAULT_CAMERA_HAL_CAPTURE_REQUEST_H_
diff --git a/modules/camera/3_4/common.h b/modules/camera/3_4/common.h
new file mode 100644
index 0000000..ca5151d
--- /dev/null
+++ b/modules/camera/3_4/common.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef V4L2_CAMERA_HAL_COMMON_H_
+#define V4L2_CAMERA_HAL_COMMON_H_
+
+// #define LOG_NDEBUG 0
+#include <cutils/log.h>
+
+#define LOG_TAG "V4L2CameraHAL"
+
+// Helpers of logging (showing function name and line number).
+#define HAL_LOGE(fmt, args...) do { \
+ ALOGE("%s:%d: " fmt, __func__, __LINE__, ##args); \
+ } while(0)
+
+#define HAL_LOGE_IF(cond, fmt, args...) do { \
+ ALOGE_IF(cond, "%s:%d: " fmt, __func__, __LINE__, ##args); \
+ } while(0)
+
+#define HAL_LOGW(fmt, args...) do { \
+ ALOGW("%s:%d: " fmt, __func__, __LINE__, ##args); \
+ } while(0)
+
+#define HAL_LOGW_IF(cond, fmt, args...) do { \
+ ALOGW_IF(cond, "%s:%d: " fmt, __func__, __LINE__, ##args); \
+ } while(0)
+
+#define HAL_LOGI(fmt, args...) do { \
+ ALOGI("%s:%d: " fmt, __func__, __LINE__, ##args); \
+ } while(0)
+
+#define HAL_LOGI_IF(cond, fmt, args...) do { \
+ ALOGI_IF(cond, "%s:%d: " fmt, __func__, __LINE__, ##args); \
+ } while(0)
+
+#define HAL_LOGD(fmt, args...) do { \
+ ALOGD("%s:%d: " fmt, __func__, __LINE__, ##args); \
+ } while(0)
+
+#define HAL_LOGV(fmt, args...) do { \
+ ALOGV("%s:%d: " fmt, __func__, __LINE__, ##args); \
+ } while(0)
+
+// Log enter/exit of methods.
+#define HAL_LOG_ENTER() HAL_LOGV("enter")
+#define HAL_LOG_EXIT() HAL_LOGV("exit")
+
+// Fix confliction in case it's defined elsewhere.
+#ifndef DISALLOW_COPY_AND_ASSIGN
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&); \
+ void operator=(const TypeName&);
+#endif
+
+#endif // V4L2_CAMERA_HAL_COMMON_H_
diff --git a/modules/camera/3_4/format_metadata_factory.cpp b/modules/camera/3_4/format_metadata_factory.cpp
new file mode 100644
index 0000000..a08f9a8
--- /dev/null
+++ b/modules/camera/3_4/format_metadata_factory.cpp
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "format_metadata_factory.h"
+
+#include "metadata/array_vector.h"
+#include "metadata/partial_metadata_factory.h"
+#include "metadata/property.h"
+
+namespace v4l2_camera_hal {
+
+static int GetHalFormats(const std::shared_ptr<V4L2Wrapper>& device,
+ std::set<int32_t>* result_formats) {
+ if (!result_formats) {
+ HAL_LOGE("Null result formats pointer passed");
+ return -EINVAL;
+ }
+
+ std::set<uint32_t> v4l2_formats;
+ int res = device->GetFormats(&v4l2_formats);
+ if (res) {
+ HAL_LOGE("Failed to get device formats.");
+ return res;
+ }
+ for (auto v4l2_format : v4l2_formats) {
+ int32_t hal_format = StreamFormat::V4L2ToHalPixelFormat(v4l2_format);
+ if (hal_format < 0) {
+ // Unrecognized/unused format. Skip it.
+ continue;
+ }
+ result_formats->insert(hal_format);
+ }
+
+ // In addition to well-defined formats, there may be an
+ // "Implementation Defined" format chosen by the HAL (in this
+ // case what that means is managed by the StreamFormat class).
+
+ // Get the V4L2 format for IMPLEMENTATION_DEFINED.
+ int v4l2_format = StreamFormat::HalToV4L2PixelFormat(
+ HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED);
+ // If it's available, add IMPLEMENTATION_DEFINED to the result set.
+ if (v4l2_format && v4l2_formats.count(v4l2_format) > 0) {
+ result_formats->insert(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED);
+ }
+
+ return 0;
+}
+
+int AddFormatComponents(
+ std::shared_ptr<V4L2Wrapper> device,
+ std::insert_iterator<PartialMetadataSet> insertion_point) {
+ HAL_LOG_ENTER();
+
+ // Get all supported formats.
+ std::set<int32_t> hal_formats;
+ int res = GetHalFormats(device, &hal_formats);
+ if (res) {
+ return res;
+ }
+
+ // Requirements check: need to support YCbCr_420_888, JPEG,
+ // and "Implementation Defined".
+ if (hal_formats.find(HAL_PIXEL_FORMAT_YCbCr_420_888) == hal_formats.end()) {
+ HAL_LOGE("YCbCr_420_888 not supported by device.");
+ return -ENODEV;
+ } else if (hal_formats.find(HAL_PIXEL_FORMAT_BLOB) == hal_formats.end()) {
+ HAL_LOGE("JPEG not supported by device.");
+ return -ENODEV;
+ } else if (hal_formats.find(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) ==
+ hal_formats.end()) {
+ HAL_LOGE("HAL implementation defined format not supported by device.");
+ return -ENODEV;
+ }
+
+ // Find sizes and frame/stall durations for all formats.
+ // We also want to find the smallest max frame duration amongst all formats,
+ // And the largest min frame duration amongst YUV (i.e. largest max frame rate
+ // supported by all YUV sizes).
+ // Stream configs are {format, width, height, direction} (input or output).
+ ArrayVector<int32_t, 4> stream_configs;
+ // Frame durations are {format, width, height, duration} (duration in ns).
+ ArrayVector<int64_t, 4> min_frame_durations;
+ // Stall durations are {format, width, height, duration} (duration in ns).
+ ArrayVector<int64_t, 4> stall_durations;
+ int64_t min_max_frame_duration = std::numeric_limits<int64_t>::max();
+ int64_t max_min_frame_duration_yuv = std::numeric_limits<int64_t>::min();
+ for (auto hal_format : hal_formats) {
+ // Get the corresponding V4L2 format.
+ uint32_t v4l2_format = StreamFormat::HalToV4L2PixelFormat(hal_format);
+ if (v4l2_format == 0) {
+ // Unrecognized/unused format. Should never happen since hal_formats
+ // came from translating a bunch of V4L2 formats above.
+ HAL_LOGE("Couldn't find V4L2 format for HAL format %d", hal_format);
+ return -ENODEV;
+ }
+
+ // Get the available sizes for this format.
+ std::set<std::array<int32_t, 2>> frame_sizes;
+ res = device->GetFormatFrameSizes(v4l2_format, &frame_sizes);
+ if (res) {
+ HAL_LOGE("Failed to get all frame sizes for format %d", v4l2_format);
+ return res;
+ }
+
+ for (const auto& frame_size : frame_sizes) {
+ // Note the format and size combination in stream configs.
+ stream_configs.push_back(
+ {{hal_format,
+ frame_size[0],
+ frame_size[1],
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT}});
+
+ // Find the duration range for this format and size.
+ std::array<int64_t, 2> duration_range;
+ res = device->GetFormatFrameDurationRange(
+ v4l2_format, frame_size, &duration_range);
+ if (res) {
+ HAL_LOGE(
+ "Failed to get frame duration range for format %d, "
+ "size %u x %u",
+ v4l2_format,
+ frame_size[0],
+ frame_size[1]);
+ return res;
+ }
+ int64_t size_min_frame_duration = duration_range[0];
+ int64_t size_max_frame_duration = duration_range[1];
+ min_frame_durations.push_back({{hal_format,
+ frame_size[0],
+ frame_size[1],
+ size_min_frame_duration}});
+
+ // Note the stall duration for this format and size.
+ // Usually 0 for non-jpeg, non-zero for JPEG.
+ // Randomly choosing absurd 1 sec for JPEG. Unsure what this breaks.
+ int64_t stall_duration = 0;
+ if (hal_format == HAL_PIXEL_FORMAT_BLOB) {
+ stall_duration = 1000000000;
+ }
+ stall_durations.push_back(
+ {{hal_format, frame_size[0], frame_size[1], stall_duration}});
+
+ // Update our search for general min & max frame durations.
+ // In theory max frame duration (min frame rate) should be consistent
+ // between all formats, but we check and only advertise the smallest
+ // available max duration just in case.
+ if (size_max_frame_duration < min_max_frame_duration) {
+ min_max_frame_duration = size_max_frame_duration;
+ }
+ // We only care about the largest min frame duration
+ // (smallest max frame rate) for YUV sizes.
+ if (hal_format == HAL_PIXEL_FORMAT_YCbCr_420_888 &&
+ size_min_frame_duration > max_min_frame_duration_yuv) {
+ max_min_frame_duration_yuv = size_min_frame_duration;
+ }
+ }
+ }
+
+ // Convert from frame durations measured in ns.
+ // Min fps supported by all formats.
+ int32_t min_fps = 1000000000 / min_max_frame_duration;
+ if (min_fps > 15) {
+ HAL_LOGE("Minimum FPS %d is larger than HAL max allowable value of 15",
+ min_fps);
+ return -ENODEV;
+ }
+ // Max fps supported by all YUV formats.
+ int32_t max_yuv_fps = 1000000000 / max_min_frame_duration_yuv;
+ // ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES should be at minimum
+ // {mi, ma}, {ma, ma} where mi and ma are min and max frame rates for
+ // YUV_420_888. Min should be at most 15.
+ std::vector<std::array<int32_t, 2>> fps_ranges;
+ fps_ranges.push_back({{min_fps, max_yuv_fps}});
+
+ std::array<int32_t, 2> video_fps_range;
+ int32_t video_fps = 30;
+ if (video_fps >= max_yuv_fps) {
+ video_fps_range = {{max_yuv_fps, max_yuv_fps}};
+ } else {
+ video_fps_range = {{video_fps, video_fps}};
+ }
+ fps_ranges.push_back(video_fps_range);
+
+ // Construct the metadata components.
+ insertion_point = std::make_unique<Property<ArrayVector<int32_t, 4>>>(
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
+ std::move(stream_configs));
+ insertion_point = std::make_unique<Property<ArrayVector<int64_t, 4>>>(
+ ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
+ std::move(min_frame_durations));
+ insertion_point = std::make_unique<Property<ArrayVector<int64_t, 4>>>(
+ ANDROID_SCALER_AVAILABLE_STALL_DURATIONS, std::move(stall_durations));
+ insertion_point = std::make_unique<Property<int64_t>>(
+ ANDROID_SENSOR_INFO_MAX_FRAME_DURATION, min_max_frame_duration);
+ // TODO(b/31019725): This should probably not be a NoEffect control.
+ insertion_point = NoEffectMenuControl<std::array<int32_t, 2>>(
+ ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
+ ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
+ fps_ranges,
+ {{CAMERA3_TEMPLATE_VIDEO_RECORD, video_fps_range},
+ {OTHER_TEMPLATES, fps_ranges[0]}});
+
+ return 0;
+}
+
+} // namespace v4l2_camera_hal
diff --git a/modules/camera/3_4/format_metadata_factory.h b/modules/camera/3_4/format_metadata_factory.h
new file mode 100644
index 0000000..4cf5952
--- /dev/null
+++ b/modules/camera/3_4/format_metadata_factory.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef V4L2_CAMERA_HAL_FORMAT_METADATA_FACTORY_H_
+#define V4L2_CAMERA_HAL_FORMAT_METADATA_FACTORY_H_
+
+#include <iterator>
+#include <memory>
+#include <set>
+
+#include "common.h"
+#include "metadata/metadata_common.h"
+#include "v4l2_wrapper.h"
+
+namespace v4l2_camera_hal {
+
+// A factory method to construct all the format-related
+// partial metadata for a V4L2 device.
+int AddFormatComponents(
+ std::shared_ptr<V4L2Wrapper> device,
+ std::insert_iterator<PartialMetadataSet> insertion_point);
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_FORMAT_METADATA_FACTORY_H_
diff --git a/modules/camera/3_4/format_metadata_factory_test.cpp b/modules/camera/3_4/format_metadata_factory_test.cpp
new file mode 100644
index 0000000..d37b09f
--- /dev/null
+++ b/modules/camera/3_4/format_metadata_factory_test.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <camera/CameraMetadata.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "format_metadata_factory.h"
+#include "metadata/test_common.h"
+#include "v4l2_wrapper_mock.h"
+
+using testing::AtLeast;
+using testing::Expectation;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::Test;
+using testing::_;
+
+namespace v4l2_camera_hal {
+
+class FormatMetadataFactoryTest : public Test {
+ protected:
+ virtual void SetUp() { mock_device_.reset(new V4L2WrapperMock()); }
+
+ virtual void ExpectMetadataTagCount(const android::CameraMetadata& metadata,
+ uint32_t tag,
+ size_t count) {
+ camera_metadata_ro_entry_t entry = metadata.find(tag);
+ EXPECT_EQ(entry.count, count);
+ }
+
+ std::shared_ptr<V4L2WrapperMock> mock_device_;
+};
+
+TEST_F(FormatMetadataFactoryTest, GetFormatMetadata) {
+ std::set<uint32_t> formats{V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_YUV420};
+ std::map<uint32_t, std::set<std::array<int32_t, 2>>> sizes{
+ {V4L2_PIX_FMT_JPEG, {{{10, 20}}, {{30, 60}}, {{120, 240}}}},
+ {V4L2_PIX_FMT_YUV420, {{{1, 2}}, {{3, 6}}, {{12, 24}}}}};
+ // These need to be on the correct order of magnitude,
+ // as there is a check for min fps > 15.
+ std::map<uint32_t, std::map<std::array<int32_t, 2>, std::array<int64_t, 2>>>
+ durations{{V4L2_PIX_FMT_JPEG,
+ {{{{10, 20}}, {{100000000, 200000000}}},
+ {{{30, 60}}, {{1000000000, 2000000000}}},
+ {{{120, 240}}, {{700000000, 1200000000}}}}},
+ {V4L2_PIX_FMT_YUV420,
+ {{{{1, 2}}, {{10000000000, 20000000000}}},
+ {{{3, 6}}, {{11000000000, 21000000000}}},
+ {{{12, 24}}, {{10500000000, 19000000000}}}}}};
+
+ // Device must support IMPLEMENTATION_DEFINED (as well as JPEG & YUV).
+ // Just duplicate the values from another format.
+ uint32_t imp_defined_format = StreamFormat::HalToV4L2PixelFormat(
+ HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED);
+ formats.insert(imp_defined_format);
+ sizes[imp_defined_format] = sizes[V4L2_PIX_FMT_YUV420];
+ durations[imp_defined_format] = durations[V4L2_PIX_FMT_YUV420];
+
+ EXPECT_CALL(*mock_device_, GetFormats(_))
+ .WillOnce(DoAll(SetArgPointee<0>(formats), Return(0)));
+
+ for (auto format : formats) {
+ std::set<std::array<int32_t, 2>> format_sizes = sizes[format];
+ EXPECT_CALL(*mock_device_, GetFormatFrameSizes(format, _))
+ .Times(AtLeast(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(format_sizes), Return(0)));
+ for (auto size : format_sizes) {
+ EXPECT_CALL(*mock_device_, GetFormatFrameDurationRange(format, size, _))
+ .Times(AtLeast(1))
+ .WillRepeatedly(
+ DoAll(SetArgPointee<2>(durations[format][size]), Return(0)));
+ }
+ }
+
+ PartialMetadataSet components;
+ ASSERT_EQ(AddFormatComponents(mock_device_,
+ std::inserter(components, components.end())),
+ 0);
+
+ for (auto& component : components) {
+ android::CameraMetadata metadata;
+ component->PopulateStaticFields(&metadata);
+ ASSERT_EQ(metadata.entryCount(), 1);
+ int32_t tag = component->StaticTags()[0];
+ switch (tag) {
+ case ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS: // Fall through.
+ case ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS: // Fall through.
+ case ANDROID_SCALER_AVAILABLE_STALL_DURATIONS: // Fall through.
+ // 3 sizes per format, 4 elements per config.
+ // # formats + 1 for IMPLEMENTATION_DEFINED.
+ ExpectMetadataTagCount(metadata, tag, (formats.size() + 1) * 3 * 4);
+ break;
+ case ANDROID_SENSOR_INFO_MAX_FRAME_DURATION:
+ // The lowest max duration from above.
+ ExpectMetadataEq(metadata, tag, 200000000);
+ break;
+ case ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES:
+ // 2 ranges ({min, max} and {max, max}), each with a min and max.
+ ExpectMetadataTagCount(metadata, tag, 4);
+ break;
+ default:
+ FAIL() << "Unexpected component created.";
+ break;
+ }
+ }
+}
+
+TEST_F(FormatMetadataFactoryTest, GetFormatMetadataMissingJpeg) {
+ uint32_t imp_defined_format = StreamFormat::HalToV4L2PixelFormat(
+ HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED);
+ std::set<uint32_t> formats{V4L2_PIX_FMT_YUV420, imp_defined_format};
+ EXPECT_CALL(*mock_device_, GetFormats(_))
+ .WillOnce(DoAll(SetArgPointee<0>(formats), Return(0)));
+ PartialMetadataSet components;
+ ASSERT_EQ(AddFormatComponents(mock_device_,
+ std::inserter(components, components.end())),
+ -ENODEV);
+}
+
+TEST_F(FormatMetadataFactoryTest, GetFormatMetadataMissingYuv) {
+ uint32_t imp_defined_format = StreamFormat::HalToV4L2PixelFormat(
+ HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED);
+ std::set<uint32_t> formats{V4L2_PIX_FMT_JPEG, imp_defined_format};
+ EXPECT_CALL(*mock_device_, GetFormats(_))
+ .WillOnce(DoAll(SetArgPointee<0>(formats), Return(0)));
+ PartialMetadataSet components;
+ ASSERT_EQ(AddFormatComponents(mock_device_,
+ std::inserter(components, components.end())),
+ -ENODEV);
+}
+
+TEST_F(FormatMetadataFactoryTest,
+ GetFormatMetadataMissingImplementationDefined) {
+ std::set<uint32_t> formats{V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_YUV420};
+ EXPECT_CALL(*mock_device_, GetFormats(_))
+ .WillOnce(DoAll(SetArgPointee<0>(formats), Return(0)));
+ PartialMetadataSet components;
+ ASSERT_EQ(AddFormatComponents(mock_device_,
+ std::inserter(components, components.end())),
+ -ENODEV);
+}
+
+} // namespace v4l2_camera_hal
diff --git a/modules/camera/3_4/function_thread.h b/modules/camera/3_4/function_thread.h
new file mode 100644
index 0000000..7a7b1e3
--- /dev/null
+++ b/modules/camera/3_4/function_thread.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef V4L2_CAMERA_HAL_FUNCTION_THREAD_H_
+#define V4L2_CAMERA_HAL_FUNCTION_THREAD_H_
+
+#include <functional>
+
+#include <utils/Thread.h>
+
+#include "common.h"
+
+namespace v4l2_camera_hal {
+
+class FunctionThread : public android::Thread {
+ public:
+ FunctionThread(std::function<bool()> function) : function_(function){};
+
+ private:
+ bool threadLoop() override {
+ bool result = function_();
+ return result;
+ };
+
+ std::function<bool()> function_;
+};
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_FUNCTION_THREAD_H_
diff --git a/modules/camera/3_4/metadata/array_vector.h b/modules/camera/3_4/metadata/array_vector.h
new file mode 100644
index 0000000..0481ed4
--- /dev/null
+++ b/modules/camera/3_4/metadata/array_vector.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef V4L2_CAMERA_HAL_ARRAY_VECTOR_H_
+#define V4L2_CAMERA_HAL_ARRAY_VECTOR_H_
+
+#include <array>
+#include <vector>
+
+namespace v4l2_camera_hal {
+// ArrayVector behaves like a std::vector of fixed length C arrays,
+// with push_back accepting std::arrays to standardize length.
+// Specific methods to get number of arrays/number of elements
+// are provided and an ambiguous "size" is not, to avoid accidental
+// incorrect use.
+template <class T, size_t N>
+class ArrayVector {
+ public:
+ const T* data() const { return mItems.data(); }
+ // The number of arrays.
+ size_t num_arrays() const { return mItems.size() / N; }
+ // The number of elements amongst all arrays.
+ size_t total_num_elements() const { return mItems.size(); }
+
+ // Access the ith array.
+ const T* operator[](int i) const { return mItems.data() + (i * N); }
+ T* operator[](int i) { return mItems.data() + (i * N); }
+
+ void push_back(const std::array<T, N>& values) {
+ mItems.insert(mItems.end(), values.begin(), values.end());
+ }
+
+ private:
+ std::vector<T> mItems;
+};
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_ARRAY_VECTOR_H_
diff --git a/modules/camera/3_4/metadata/boottime_state_delegate.cpp b/modules/camera/3_4/metadata/boottime_state_delegate.cpp
new file mode 100644
index 0000000..5024cb2
--- /dev/null
+++ b/modules/camera/3_4/metadata/boottime_state_delegate.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <string.h>
+
+#include "boottime_state_delegate.h"
+
+namespace v4l2_camera_hal {
+
+int BoottimeStateDelegate::GetValue(int64_t* value) {
+ struct timespec ts;
+
+ int res = clock_gettime(CLOCK_BOOTTIME, &ts);
+ if (res) {
+ HAL_LOGE("Failed to get BOOTTIME for state delegate: %d (%s)",
+ errno,
+ strerror(errno));
+ return -errno;
+ }
+ *value = ts.tv_sec * 1000000000ULL + ts.tv_nsec;
+
+ return 0;
+}
+
+} // namespace v4l2_camera_hal
diff --git a/modules/camera/3_4/metadata/boottime_state_delegate.h b/modules/camera/3_4/metadata/boottime_state_delegate.h
new file mode 100644
index 0000000..0a5c4b9
--- /dev/null
+++ b/modules/camera/3_4/metadata/boottime_state_delegate.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef V4L2_CAMERA_HAL_METADATA_BOOTTIME_STATE_DELEGATE_H_
+#define V4L2_CAMERA_HAL_METADATA_BOOTTIME_STATE_DELEGATE_H_
+
+#include "../common.h"
+#include "state_delegate_interface.h"
+
+namespace v4l2_camera_hal {
+
+// A StateDelegate is simply a dynamic value that can be queried.
+// The value may change between queries.
+class BoottimeStateDelegate : public StateDelegateInterface<int64_t> {
+ public:
+ BoottimeStateDelegate(){};
+ ~BoottimeStateDelegate(){};
+
+ int GetValue(int64_t* value) override;
+};
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_METADATA_BOOTTIME_STATE_DELEGATE_H_
diff --git a/modules/camera/3_4/metadata/control.h b/modules/camera/3_4/metadata/control.h
new file mode 100644
index 0000000..ad3f87b
--- /dev/null
+++ b/modules/camera/3_4/metadata/control.h
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef V4L2_CAMERA_HAL_METADATA_CONTROL_H_
+#define V4L2_CAMERA_HAL_METADATA_CONTROL_H_
+
+#include <vector>
+
+#include <system/camera_metadata.h>
+
+#include "../common.h"
+#include "metadata_common.h"
+#include "partial_metadata_interface.h"
+#include "tagged_control_delegate.h"
+#include "tagged_control_options.h"
+
+namespace v4l2_camera_hal {
+
+// A Control is a PartialMetadata with values that can be gotten/set.
+template <typename T>
+class Control : public PartialMetadataInterface {
+ public:
+ // Options are optional (i.e. nullable), delegate is not.
+ Control(std::unique_ptr<TaggedControlDelegate<T>> delegate,
+ std::unique_ptr<TaggedControlOptions<T>> options = nullptr);
+
+ virtual std::vector<int32_t> StaticTags() const override;
+ virtual std::vector<int32_t> ControlTags() const override;
+ virtual std::vector<int32_t> DynamicTags() const override;
+
+ virtual int PopulateStaticFields(
+ android::CameraMetadata* metadata) const override;
+ virtual int PopulateDynamicFields(
+ android::CameraMetadata* metadata) const override;
+ virtual int PopulateTemplateRequest(
+ int template_type, android::CameraMetadata* metadata) const override;
+ virtual bool SupportsRequestValues(
+ const android::CameraMetadata& metadata) const override;
+ virtual int SetRequestValues(
+ const android::CameraMetadata& metadata) override;
+
+ private:
+ std::unique_ptr<TaggedControlDelegate<T>> delegate_;
+ std::unique_ptr<TaggedControlOptions<T>> options_;
+
+ DISALLOW_COPY_AND_ASSIGN(Control);
+};
+
+// -----------------------------------------------------------------------------
+
+template <typename T>
+Control<T>::Control(std::unique_ptr<TaggedControlDelegate<T>> delegate,
+ std::unique_ptr<TaggedControlOptions<T>> options)
+ : delegate_(std::move(delegate)), options_(std::move(options)) {}
+
+template <typename T>
+std::vector<int32_t> Control<T>::StaticTags() const {
+ std::vector<int32_t> result;
+ if (options_ && options_->tag() != DO_NOT_REPORT_OPTIONS) {
+ result.push_back(options_->tag());
+ }
+ return result;
+}
+
+template <typename T>
+std::vector<int32_t> Control<T>::ControlTags() const {
+ return {delegate_->tag()};
+}
+
+template <typename T>
+std::vector<int32_t> Control<T>::DynamicTags() const {
+ return {delegate_->tag()};
+}
+
+template <typename T>
+int Control<T>::PopulateStaticFields(android::CameraMetadata* metadata) const {
+ if (!options_) {
+ HAL_LOGV("No options for control %d, nothing to populate.",
+ delegate_->tag());
+ return 0;
+ } else if (options_->tag() == DO_NOT_REPORT_OPTIONS) {
+ HAL_LOGV(
+ "Options for control %d are not reported, "
+ "probably are set values defined and already known by the API.",
+ delegate_->tag());
+ return 0;
+ }
+
+ return UpdateMetadata(
+ metadata, options_->tag(), options_->MetadataRepresentation());
+}
+
+template <typename T>
+int Control<T>::PopulateDynamicFields(android::CameraMetadata* metadata) const {
+ // Populate the current setting.
+ T value;
+ int res = delegate_->GetValue(&value);
+ if (res) {
+ return res;
+ }
+ return UpdateMetadata(metadata, delegate_->tag(), value);
+}
+
+template <typename T>
+int Control<T>::PopulateTemplateRequest(
+ int template_type, android::CameraMetadata* metadata) const {
+ // Populate with a default.
+ T value;
+ int res;
+ if (options_) {
+ res = options_->DefaultValueForTemplate(template_type, &value);
+ } else {
+ // If there's no options (and thus no default option),
+ // fall back to whatever the current value is.
+ res = delegate_->GetValue(&value);
+ }
+ if (res) {
+ return res;
+ }
+
+ return UpdateMetadata(metadata, delegate_->tag(), value);
+}
+
+template <typename T>
+bool Control<T>::SupportsRequestValues(
+ const android::CameraMetadata& metadata) const {
+ if (metadata.isEmpty()) {
+ // Implicitly supported.
+ return true;
+ }
+
+ // Get the requested setting for this control.
+ T requested;
+ int res = SingleTagValue(metadata, delegate_->tag(), &requested);
+ if (res == -ENOENT) {
+ // Nothing requested of this control, that's fine.
+ return true;
+ } else if (res) {
+ HAL_LOGE("Failure while searching for request value for tag %d",
+ delegate_->tag());
+ return false;
+ }
+
+ // Check that the requested setting is in the supported options.
+ if (!options_) {
+ HAL_LOGV("No options for control %d; request implicitly supported.",
+ delegate_->tag());
+ return true;
+ }
+ return options_->IsSupported(requested);
+}
+
+template <typename T>
+int Control<T>::SetRequestValues(const android::CameraMetadata& metadata) {
+ if (metadata.isEmpty()) {
+ // No changes necessary.
+ return 0;
+ }
+
+ // Get the requested value.
+ T requested;
+ int res = SingleTagValue(metadata, delegate_->tag(), &requested);
+ if (res == -ENOENT) {
+ // Nothing requested of this control, nothing to do.
+ return 0;
+ } else if (res) {
+ HAL_LOGE("Failure while searching for request value for tag %d",
+ delegate_->tag());
+ return res;
+ }
+
+ // Check that the value is supported.
+ if (options_ && !options_->IsSupported(requested)) {
+ HAL_LOGE("Unsupported value requested for control %d.", delegate_->tag());
+ return -EINVAL;
+ }
+
+ return delegate_->SetValue(requested);
+}
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_METADATA_CONTROL_H_
diff --git a/modules/camera/3_4/metadata/control_delegate_interface.h b/modules/camera/3_4/metadata/control_delegate_interface.h
new file mode 100644
index 0000000..8896e72
--- /dev/null
+++ b/modules/camera/3_4/metadata/control_delegate_interface.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef V4L2_CAMERA_HAL_METADATA_CONTROL_DELEGATE_INTERFACE_H_
+#define V4L2_CAMERA_HAL_METADATA_CONTROL_DELEGATE_INTERFACE_H_
+
+#include "state_delegate_interface.h"
+
+namespace v4l2_camera_hal {
+
+// A ControlDelegate extends StateDelegate with a setter method.
+template <typename T>
+class ControlDelegateInterface : public StateDelegateInterface<T> {
+ public:
+ virtual ~ControlDelegateInterface(){};
+
+ // ControlDelegates are allowed to be unreliable, so SetValue is best-effort;
+ // GetValue immediately after may not match (SetValue may, for example,
+ // automatically replace invalid values with valid ones,
+ // or have a delay before setting the requested value).
+ // Returns 0 on success, error code on failure.
+ virtual int SetValue(const T& value) = 0;
+ // Children must also override GetValue from StateDelegateInterface.
+};
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_METADATA_CONTROL_DELEGATE_INTERFACE_H_
diff --git a/modules/camera/3_4/metadata/control_delegate_interface_mock.h b/modules/camera/3_4/metadata/control_delegate_interface_mock.h
new file mode 100644
index 0000000..7ed05ed
--- /dev/null
+++ b/modules/camera/3_4/metadata/control_delegate_interface_mock.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Mock for control delegate interfaces.
+
+#ifndef V4L2_CAMERA_HAL_METADATA_CONTROL_DELEGATE_INTERFACE_MOCK_H_
+#define V4L2_CAMERA_HAL_METADATA_CONTROL_DELEGATE_INTERFACE_MOCK_H_
+
+#include <gmock/gmock.h>
+
+#include "control_delegate_interface.h"
+
+namespace v4l2_camera_hal {
+
+template <typename T>
+class ControlDelegateInterfaceMock : public ControlDelegateInterface<T> {
+ public:
+ ControlDelegateInterfaceMock(){};
+ MOCK_METHOD1_T(GetValue, int(T*));
+ MOCK_METHOD1_T(SetValue, int(const T&));
+};
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_METADATA_CONTROL_DELEGATE_INTERFACE_MOCK_H_
diff --git a/modules/camera/3_4/metadata/control_options_interface.h b/modules/camera/3_4/metadata/control_options_interface.h
new file mode 100644
index 0000000..438cefa
--- /dev/null
+++ b/modules/camera/3_4/metadata/control_options_interface.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef V4L2_CAMERA_HAL_METADATA_CONTROL_OPTIONS_INTERFACE_H_
+#define V4L2_CAMERA_HAL_METADATA_CONTROL_OPTIONS_INTERFACE_H_
+
+#include <vector>
+
+namespace v4l2_camera_hal {
+
+// A ControlOptions defines acceptable values for a control.
+template <typename T>
+class ControlOptionsInterface {
+ public:
+ virtual ~ControlOptionsInterface(){};
+
+ // Get a metadata-acceptable representation of the options.
+ // For enums this will be a list of values, for ranges this
+ // will be min and max, etc.
+ virtual std::vector<T> MetadataRepresentation() = 0;
+ // Get whether or not a given value is acceptable.
+ virtual bool IsSupported(const T& option);
+ // Get a default option for a given template type, from the available options.
+ // Because a default must be available, any ControlOptions should have at
+ // least one supported value.
+ virtual int DefaultValueForTemplate(int template_type, T* default_value);
+};
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_METADATA_CONTROL_OPTIONS_INTERFACE_H_
diff --git a/modules/camera/3_4/metadata/control_options_interface_mock.h b/modules/camera/3_4/metadata/control_options_interface_mock.h
new file mode 100644
index 0000000..ab8f6ee
--- /dev/null
+++ b/modules/camera/3_4/metadata/control_options_interface_mock.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Mock for control options interfaces.
+
+#ifndef V4L2_CAMERA_HAL_METADATA_CONTROL_OPTIONS_INTERFACE_MOCK_H_
+#define V4L2_CAMERA_HAL_METADATA_CONTROL_OPTIONS_INTERFACE_MOCK_H_
+
+#include <gmock/gmock.h>
+
+#include "control_options_interface.h"
+
+namespace v4l2_camera_hal {
+
+template <typename T>
+class ControlOptionsInterfaceMock : public ControlOptionsInterface<T> {
+ public:
+ ControlOptionsInterfaceMock(){};
+ MOCK_METHOD0_T(MetadataRepresentation, std::vector<T>());
+ MOCK_METHOD1_T(IsSupported, bool(const T&));
+ MOCK_METHOD2_T(DefaultValueForTemplate, int(int, T*));
+};
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_METADATA_CONTROL_OPTIONS_INTERFACE_MOCK_H_
diff --git a/modules/camera/3_4/metadata/control_test.cpp b/modules/camera/3_4/metadata/control_test.cpp
new file mode 100644
index 0000000..f76376c
--- /dev/null
+++ b/modules/camera/3_4/metadata/control_test.cpp
@@ -0,0 +1,457 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "control.h"
+
+#include <camera/CameraMetadata.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "control_delegate_interface_mock.h"
+#include "control_options_interface_mock.h"
+#include "metadata_common.h"
+#include "test_common.h"
+
+using testing::AtMost;
+using testing::Expectation;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::Test;
+using testing::_;
+
+namespace v4l2_camera_hal {
+
+class ControlTest : public Test {
+ protected:
+ virtual void SetUp() {
+ mock_delegate_.reset(new ControlDelegateInterfaceMock<uint8_t>());
+ mock_options_.reset(new ControlOptionsInterfaceMock<uint8_t>());
+ // Nullify control so an error will be thrown if a test doesn't call
+ // PrepareControl.
+ control_.reset();
+ }
+
+ virtual void PrepareControl() {
+ // Use this method after all the EXPECT_CALLs to pass ownership of the mocks
+ // to the device.
+ std::unique_ptr<TaggedControlDelegate<uint8_t>> delegate =
+ std::make_unique<TaggedControlDelegate<uint8_t>>(
+ delegate_tag_, std::move(mock_delegate_));
+ std::unique_ptr<TaggedControlOptions<uint8_t>> options =
+ std::make_unique<TaggedControlOptions<uint8_t>>(
+ report_options_ ? options_tag_ : DO_NOT_REPORT_OPTIONS,
+ std::move(mock_options_));
+ if (use_options_) {
+ control_.reset(
+ new Control<uint8_t>(std::move(delegate), std::move(options)));
+ } else {
+ control_.reset(new Control<uint8_t>(std::move(delegate)));
+ }
+ }
+
+ virtual void ExpectTags() {
+ if (use_options_ && report_options_) {
+ ASSERT_EQ(control_->StaticTags().size(), 1);
+ EXPECT_EQ(control_->StaticTags()[0], options_tag_);
+ } else {
+ EXPECT_TRUE(control_->StaticTags().empty());
+ }
+ // Controls use the same delgate, and thus tag, for getting and setting.
+ ASSERT_EQ(control_->ControlTags().size(), 1);
+ EXPECT_EQ(control_->ControlTags()[0], delegate_tag_);
+ ASSERT_EQ(control_->DynamicTags().size(), 1);
+ EXPECT_EQ(control_->DynamicTags()[0], delegate_tag_);
+ }
+
+ virtual void ExpectOptions(const std::vector<uint8_t>& options) {
+ // Options should be available.
+ android::CameraMetadata metadata;
+ ASSERT_EQ(control_->PopulateStaticFields(&metadata), 0);
+ if (use_options_ && report_options_) {
+ EXPECT_EQ(metadata.entryCount(), 1);
+ ExpectMetadataEq(metadata, options_tag_, options);
+ } else {
+ EXPECT_EQ(metadata.entryCount(), 0);
+ // Shouldn't be expecting any options.
+ EXPECT_TRUE(options.empty());
+ }
+ }
+
+ virtual void ExpectValue(uint8_t value) {
+ android::CameraMetadata metadata;
+ ASSERT_EQ(control_->PopulateDynamicFields(&metadata), 0);
+ EXPECT_EQ(metadata.entryCount(), 1);
+ ExpectMetadataEq(metadata, delegate_tag_, value);
+ }
+
+ std::unique_ptr<Control<uint8_t>> control_;
+ std::unique_ptr<ControlDelegateInterfaceMock<uint8_t>> mock_delegate_;
+ std::unique_ptr<ControlOptionsInterfaceMock<uint8_t>> mock_options_;
+ bool use_options_ = true;
+ bool report_options_ = true;
+
+ // Need tags that match the data type (uint8_t) being passed.
+ const int32_t delegate_tag_ = ANDROID_COLOR_CORRECTION_ABERRATION_MODE;
+ const int32_t options_tag_ =
+ ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES;
+};
+
+TEST_F(ControlTest, Tags) {
+ PrepareControl();
+ ExpectTags();
+}
+
+TEST_F(ControlTest, TagsNoOptions) {
+ use_options_ = false;
+ PrepareControl();
+ ExpectTags();
+}
+
+TEST_F(ControlTest, TagsUnreportedOptions) {
+ report_options_ = false;
+ PrepareControl();
+ ExpectTags();
+}
+
+TEST_F(ControlTest, PopulateStatic) {
+ std::vector<uint8_t> expected{1, 10, 20};
+ EXPECT_CALL(*mock_options_, MetadataRepresentation())
+ .WillOnce(Return(expected));
+ PrepareControl();
+ ExpectOptions(expected);
+}
+
+TEST_F(ControlTest, PopulateStaticNoOptions) {
+ use_options_ = false;
+ PrepareControl();
+ ExpectOptions({});
+}
+
+TEST_F(ControlTest, PopulateStaticUnreportedOptions) {
+ report_options_ = false;
+ PrepareControl();
+ ExpectOptions({});
+}
+
+TEST_F(ControlTest, PopulateDynamic) {
+ uint8_t test_option = 99;
+ EXPECT_CALL(*mock_delegate_, GetValue(_))
+ .WillOnce(DoAll(SetArgPointee<0>(test_option), Return(0)));
+ PrepareControl();
+ ExpectValue(test_option);
+}
+
+TEST_F(ControlTest, PopulateDynamicNoOptions) {
+ // Lack of options shouldn't change anything for PopulateDynamic.
+ use_options_ = false;
+ uint8_t test_option = 99;
+ EXPECT_CALL(*mock_delegate_, GetValue(_))
+ .WillOnce(DoAll(SetArgPointee<0>(test_option), Return(0)));
+ PrepareControl();
+ ExpectValue(test_option);
+}
+
+TEST_F(ControlTest, PopulateDynamicUnreportedOptions) {
+ // Lack of reported options shouldn't change anything for PopulateDynamic.
+ report_options_ = false;
+ uint8_t test_option = 99;
+ EXPECT_CALL(*mock_delegate_, GetValue(_))
+ .WillOnce(DoAll(SetArgPointee<0>(test_option), Return(0)));
+ PrepareControl();
+ ExpectValue(test_option);
+}
+
+TEST_F(ControlTest, PopulateDynamicFail) {
+ int err = -99;
+ EXPECT_CALL(*mock_delegate_, GetValue(_)).WillOnce(Return(err));
+ PrepareControl();
+
+ android::CameraMetadata metadata;
+ EXPECT_EQ(control_->PopulateDynamicFields(&metadata), err);
+
+ // Should not have added an entry.
+ EXPECT_TRUE(metadata.isEmpty());
+}
+
+TEST_F(ControlTest, PopulateTemplate) {
+ int template_type = 3;
+ uint8_t default_value = 123;
+ EXPECT_CALL(*mock_options_, DefaultValueForTemplate(template_type, _))
+ .WillOnce(DoAll(SetArgPointee<1>(default_value), Return(0)));
+ PrepareControl();
+
+ android::CameraMetadata metadata;
+ EXPECT_EQ(control_->PopulateTemplateRequest(template_type, &metadata), 0);
+ ExpectMetadataEq(metadata, delegate_tag_, default_value);
+}
+
+TEST_F(ControlTest, PopulateTemplateFail) {
+ int template_type = 3;
+ int err = 10;
+ EXPECT_CALL(*mock_options_, DefaultValueForTemplate(template_type, _))
+ .WillOnce(Return(err));
+ PrepareControl();
+
+ android::CameraMetadata metadata;
+ EXPECT_EQ(control_->PopulateTemplateRequest(template_type, &metadata), err);
+}
+
+TEST_F(ControlTest, PopulateTemplateOptionless) {
+ use_options_ = false;
+ int template_type = 3;
+ uint8_t value = 12;
+ // Should use delegate instead of options if no options.
+ EXPECT_CALL(*mock_delegate_, GetValue(_))
+ .WillOnce(DoAll(SetArgPointee<0>(value), Return(0)));
+ PrepareControl();
+
+ android::CameraMetadata metadata;
+ EXPECT_EQ(control_->PopulateTemplateRequest(template_type, &metadata), 0);
+ ExpectMetadataEq(metadata, delegate_tag_, value);
+}
+
+TEST_F(ControlTest, PopulateTemplateOptionlessFail) {
+ use_options_ = false;
+ int template_type = 3;
+ int err = 10;
+ // Should use delegate instead of options if no options.
+ EXPECT_CALL(*mock_delegate_, GetValue(_)).WillOnce(Return(err));
+ PrepareControl();
+
+ android::CameraMetadata metadata;
+ EXPECT_EQ(control_->PopulateTemplateRequest(template_type, &metadata), err);
+}
+
+TEST_F(ControlTest, PopulateTemplateUnreportedOptions) {
+ report_options_ = false;
+ int template_type = 3;
+ uint8_t default_value = 123;
+ // Unreported options should behave just like reported ones for templating.
+ EXPECT_CALL(*mock_options_, DefaultValueForTemplate(template_type, _))
+ .WillOnce(DoAll(SetArgPointee<1>(default_value), Return(0)));
+ PrepareControl();
+
+ android::CameraMetadata metadata;
+ EXPECT_EQ(control_->PopulateTemplateRequest(template_type, &metadata), 0);
+ ExpectMetadataEq(metadata, delegate_tag_, default_value);
+}
+
+TEST_F(ControlTest, PopulateTemplateUnreportedOptionsFail) {
+ report_options_ = false;
+ int template_type = 3;
+ int err = 10;
+ // Unreported options should behave just like reported ones for templating.
+ EXPECT_CALL(*mock_options_, DefaultValueForTemplate(template_type, _))
+ .WillOnce(Return(err));
+ PrepareControl();
+
+ android::CameraMetadata metadata;
+ EXPECT_EQ(control_->PopulateTemplateRequest(template_type, &metadata), err);
+}
+
+TEST_F(ControlTest, SupportsRequest) {
+ android::CameraMetadata metadata;
+ uint8_t test_option = 123;
+ ASSERT_EQ(UpdateMetadata(&metadata, delegate_tag_, test_option), 0);
+
+ EXPECT_CALL(*mock_options_, IsSupported(test_option)).WillOnce(Return(true));
+ PrepareControl();
+
+ EXPECT_EQ(control_->SupportsRequestValues(metadata), true);
+}
+
+TEST_F(ControlTest, SupportsRequestNoOptions) {
+ use_options_ = false;
+ android::CameraMetadata metadata;
+ uint8_t test_option = 123;
+ ASSERT_EQ(UpdateMetadata(&metadata, delegate_tag_, test_option), 0);
+ PrepareControl();
+
+ EXPECT_EQ(control_->SupportsRequestValues(metadata), true);
+}
+
+TEST_F(ControlTest, SupportsRequestUnreportedOptions) {
+ report_options_ = false;
+ android::CameraMetadata metadata;
+ uint8_t test_option = 123;
+ ASSERT_EQ(UpdateMetadata(&metadata, delegate_tag_, test_option), 0);
+
+ EXPECT_CALL(*mock_options_, IsSupported(test_option)).WillOnce(Return(true));
+ PrepareControl();
+
+ EXPECT_EQ(control_->SupportsRequestValues(metadata), true);
+}
+
+TEST_F(ControlTest, SupportsRequestFail) {
+ android::CameraMetadata metadata;
+ uint8_t test_option = 123;
+ ASSERT_EQ(UpdateMetadata(&metadata, delegate_tag_, test_option), 0);
+
+ EXPECT_CALL(*mock_options_, IsSupported(test_option)).WillOnce(Return(false));
+ PrepareControl();
+
+ EXPECT_EQ(control_->SupportsRequestValues(metadata), false);
+}
+
+TEST_F(ControlTest, SupportsRequestUnreportedOptionsFail) {
+ report_options_ = false;
+ android::CameraMetadata metadata;
+ uint8_t test_option = 123;
+ ASSERT_EQ(UpdateMetadata(&metadata, delegate_tag_, test_option), 0);
+
+ // Unreported options should still be checked against.
+ EXPECT_CALL(*mock_options_, IsSupported(test_option)).WillOnce(Return(false));
+ PrepareControl();
+
+ EXPECT_EQ(control_->SupportsRequestValues(metadata), false);
+}
+
+TEST_F(ControlTest, SupportsRequestInvalidNumber) {
+ // Start with a request for multiple values.
+ android::CameraMetadata metadata;
+ std::vector<uint8_t> test_data = {1, 2, 3};
+ ASSERT_EQ(UpdateMetadata(&metadata, delegate_tag_, test_data), 0);
+ PrepareControl();
+ EXPECT_EQ(control_->SupportsRequestValues(metadata), false);
+}
+
+TEST_F(ControlTest, SupportsRequestInvalidNumberNoOptions) {
+ use_options_ = false;
+ // Start with a request for multiple values.
+ android::CameraMetadata metadata;
+ std::vector<uint8_t> test_data = {1, 2, 3};
+ ASSERT_EQ(UpdateMetadata(&metadata, delegate_tag_, test_data), 0);
+ PrepareControl();
+ // Not having any explicit options does not exempt a control
+ // from requiring the right number of values.
+ EXPECT_EQ(control_->SupportsRequestValues(metadata), false);
+}
+
+TEST_F(ControlTest, SupportsRequestEmpty) {
+ android::CameraMetadata metadata;
+ PrepareControl();
+ EXPECT_EQ(control_->SupportsRequestValues(metadata), true);
+}
+
+TEST_F(ControlTest, SetRequest) {
+ android::CameraMetadata metadata;
+ uint8_t test_option = 123;
+ ASSERT_EQ(UpdateMetadata(&metadata, delegate_tag_, test_option), 0);
+
+ Expectation validation_check =
+ EXPECT_CALL(*mock_options_, IsSupported(test_option))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*mock_delegate_, SetValue(test_option))
+ .After(validation_check)
+ .WillOnce(Return(0));
+ PrepareControl();
+
+ // Make the request.
+ ASSERT_EQ(control_->SetRequestValues(metadata), 0);
+}
+
+TEST_F(ControlTest, SetRequestNoOptions) {
+ use_options_ = false;
+ android::CameraMetadata metadata;
+ uint8_t test_option = 123;
+ ASSERT_EQ(UpdateMetadata(&metadata, delegate_tag_, test_option), 0);
+
+ // No options, no validation check.
+ EXPECT_CALL(*mock_delegate_, SetValue(test_option)).WillOnce(Return(0));
+ PrepareControl();
+
+ // Make the request.
+ ASSERT_EQ(control_->SetRequestValues(metadata), 0);
+}
+
+TEST_F(ControlTest, SetRequestUnreportedOptions) {
+ report_options_ = false;
+ android::CameraMetadata metadata;
+ uint8_t test_option = 123;
+ ASSERT_EQ(UpdateMetadata(&metadata, delegate_tag_, test_option), 0);
+
+ // Unreported options still get a validation check.
+ Expectation validation_check =
+ EXPECT_CALL(*mock_options_, IsSupported(test_option))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*mock_delegate_, SetValue(test_option))
+ .After(validation_check)
+ .WillOnce(Return(0));
+ PrepareControl();
+
+ // Make the request.
+ ASSERT_EQ(control_->SetRequestValues(metadata), 0);
+}
+
+TEST_F(ControlTest, SetRequestSettingFail) {
+ android::CameraMetadata metadata;
+ uint8_t test_option = 123;
+ ASSERT_EQ(UpdateMetadata(&metadata, delegate_tag_, test_option), 0);
+
+ int err = 99;
+ Expectation validation_check =
+ EXPECT_CALL(*mock_options_, IsSupported(test_option))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*mock_delegate_, SetValue(test_option))
+ .After(validation_check)
+ .WillOnce(Return(err));
+ PrepareControl();
+
+ EXPECT_EQ(control_->SetRequestValues(metadata), err);
+}
+
+TEST_F(ControlTest, SetRequestValidationFail) {
+ android::CameraMetadata metadata;
+ uint8_t test_option = 123;
+ ASSERT_EQ(UpdateMetadata(&metadata, delegate_tag_, test_option), 0);
+
+ EXPECT_CALL(*mock_options_, IsSupported(test_option)).WillOnce(Return(false));
+ PrepareControl();
+
+ EXPECT_EQ(control_->SetRequestValues(metadata), -EINVAL);
+}
+
+TEST_F(ControlTest, SetRequestInvalidNumber) {
+ // Start with a request for multiple values.
+ android::CameraMetadata metadata;
+ std::vector<uint8_t> test_data = {1, 2, 3};
+ ASSERT_EQ(UpdateMetadata(&metadata, delegate_tag_, test_data), 0);
+
+ PrepareControl();
+ EXPECT_EQ(control_->SetRequestValues(metadata), -EINVAL);
+}
+
+TEST_F(ControlTest, SetRequestInvalidNumberNoOptions) {
+ use_options_ = false;
+ // Start with a request for multiple values.
+ android::CameraMetadata metadata;
+ std::vector<uint8_t> test_data = {1, 2, 3};
+ ASSERT_EQ(UpdateMetadata(&metadata, delegate_tag_, test_data), 0);
+
+ PrepareControl();
+ // Not having explicit options does not change that an incorrect
+ // number of values is invalid.
+ EXPECT_EQ(control_->SetRequestValues(metadata), -EINVAL);
+}
+
+TEST_F(ControlTest, SetRequestEmpty) {
+ // Should do nothing.
+ android::CameraMetadata metadata;
+ PrepareControl();
+ EXPECT_EQ(control_->SetRequestValues(metadata), 0);
+}
+
+} // namespace v4l2_camera_hal
diff --git a/modules/camera/3_4/metadata/converter_interface.h b/modules/camera/3_4/metadata/converter_interface.h
new file mode 100644
index 0000000..ca6a0f2
--- /dev/null
+++ b/modules/camera/3_4/metadata/converter_interface.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef V4L2_CAMERA_HAL_METADATA_CONVERTER_INTERFACE_H_
+#define V4L2_CAMERA_HAL_METADATA_CONVERTER_INTERFACE_H_
+
+#include "../common.h"
+
+namespace v4l2_camera_hal {
+
+// A ConverterInterface converts metadata values to V4L2 values vice-versa.
+template <typename TMetadata, typename TV4L2>
+class ConverterInterface {
+ public:
+ virtual ~ConverterInterface(){};
+
+ // Convert.
+ virtual int MetadataToV4L2(TMetadata value, TV4L2* conversion) = 0;
+ virtual int V4L2ToMetadata(TV4L2 value, TMetadata* conversion) = 0;
+};
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_METADATA_CONVERTER_INTERFACE_H_
diff --git a/modules/camera/3_4/metadata/converter_interface_mock.h b/modules/camera/3_4/metadata/converter_interface_mock.h
new file mode 100644
index 0000000..3f7e6f7
--- /dev/null
+++ b/modules/camera/3_4/metadata/converter_interface_mock.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Mock for converter interfaces.
+
+#ifndef V4L2_CAMERA_HAL_METADATA_CONVERTER_INTERFACE_MOCK_H_
+#define V4L2_CAMERA_HAL_METADATA_CONVERTER_INTERFACE_MOCK_H_
+
+#include <gmock/gmock.h>
+
+#include "converter_interface.h"
+
+namespace v4l2_camera_hal {
+
+template <typename TMetadata, typename TV4L2>
+class ConverterInterfaceMock : public ConverterInterface<TMetadata, TV4L2> {
+ public:
+ ConverterInterfaceMock(){};
+ MOCK_METHOD2_T(MetadataToV4L2, int(TMetadata, TV4L2*));
+ MOCK_METHOD2_T(V4L2ToMetadata, int(TV4L2, TMetadata*));
+};
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_METADATA_CONVERTER_INTERFACE_MOCK_H_
diff --git a/modules/camera/3_4/metadata/default_option_delegate.h b/modules/camera/3_4/metadata/default_option_delegate.h
new file mode 100644
index 0000000..f290318
--- /dev/null
+++ b/modules/camera/3_4/metadata/default_option_delegate.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef V4L2_CAMERA_HAL_METADATA_DEFAULT_OPTION_DELEGATE_H_
+#define V4L2_CAMERA_HAL_METADATA_DEFAULT_OPTION_DELEGATE_H_
+
+#include <errno.h>
+
+#include <map>
+
+#include <hardware/camera3.h>
+
+namespace v4l2_camera_hal {
+
+// A constant that can be used to identify an overall default.
+static constexpr int OTHER_TEMPLATES = CAMERA3_TEMPLATE_COUNT;
+
+// DefaultingOptionDelegate provides an interface to get default options from.
+template <typename T>
+class DefaultOptionDelegate {
+ public:
+ // |defaults| maps template types to default values
+ DefaultOptionDelegate(std::map<int, T> defaults)
+ : defaults_(std::move(defaults)){};
+ virtual ~DefaultOptionDelegate(){};
+
+ // Get a default value for a template type. Returns false if no default
+ // provided.
+ virtual bool DefaultValueForTemplate(int template_type, T* default_value) {
+ if (defaults_.count(template_type) > 0) {
+ // Best option is template-specific.
+ *default_value = defaults_[template_type];
+ return true;
+ } else if (defaults_.count(OTHER_TEMPLATES)) {
+ // Fall back to a general default.
+ *default_value = defaults_[OTHER_TEMPLATES];
+ return true;
+ }
+
+ return false;
+ };
+
+ private:
+ std::map<int, T> defaults_;
+};
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_METADATA_DEFAULT_OPTION_DELEGATE_H_
diff --git a/modules/camera/3_4/metadata/default_option_delegate_mock.h b/modules/camera/3_4/metadata/default_option_delegate_mock.h
new file mode 100644
index 0000000..84ec740
--- /dev/null
+++ b/modules/camera/3_4/metadata/default_option_delegate_mock.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Mock for default option delegates.
+
+#ifndef V4L2_CAMERA_HAL_METADATA_DEFAULT_OPTION_DELEGATE_MOCK_H_
+#define V4L2_CAMERA_HAL_METADATA_DEFAULT_OPTION_DELEGATE_MOCK_H_
+
+#include <gmock/gmock.h>
+
+#include "default_option_delegate.h"
+
+namespace v4l2_camera_hal {
+
+template <typename T>
+class DefaultOptionDelegateMock : public DefaultOptionDelegate<T> {
+ public:
+ DefaultOptionDelegateMock() : DefaultOptionDelegate<T>({}){};
+ MOCK_METHOD2_T(DefaultValueForTemplate, bool(int, T*));
+};
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_METADATA_DEFAULT_OPTION_DELEGATE_MOCK_H_
diff --git a/modules/camera/3_4/metadata/default_option_delegate_test.cpp b/modules/camera/3_4/metadata/default_option_delegate_test.cpp
new file mode 100644
index 0000000..7b61dd4
--- /dev/null
+++ b/modules/camera/3_4/metadata/default_option_delegate_test.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "default_option_delegate.h"
+
+#include <memory>
+
+#include <gtest/gtest.h>
+#include <hardware/camera3.h>
+
+using testing::Test;
+
+namespace v4l2_camera_hal {
+
+class DefaultOptionDelegateTest : public Test {
+ protected:
+ virtual void SetUp() {
+ dut_.reset(new DefaultOptionDelegate<int>(defaults_));
+ }
+
+ std::unique_ptr<DefaultOptionDelegate<int>> dut_;
+ std::map<int, int> defaults_{{CAMERA3_TEMPLATE_STILL_CAPTURE, 10},
+ {OTHER_TEMPLATES, 20},
+ {CAMERA3_TEMPLATE_VIDEO_SNAPSHOT, 30}};
+};
+
+TEST_F(DefaultOptionDelegateTest, SpecificDefault) {
+ int actual = 0;
+ EXPECT_TRUE(
+ dut_->DefaultValueForTemplate(CAMERA3_TEMPLATE_STILL_CAPTURE, &actual));
+ EXPECT_EQ(actual, defaults_[CAMERA3_TEMPLATE_STILL_CAPTURE]);
+}
+
+TEST_F(DefaultOptionDelegateTest, GeneralDefault) {
+ int actual = 0;
+ // No ZSL default; should fall back to the OTHER_TEMPLATES default.
+ EXPECT_TRUE(dut_->DefaultValueForTemplate(CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG,
+ &actual));
+ EXPECT_EQ(actual, defaults_[OTHER_TEMPLATES]);
+}
+
+TEST_F(DefaultOptionDelegateTest, NoDefaults) {
+ dut_.reset(new DefaultOptionDelegate<int>({}));
+ int actual = 0;
+ EXPECT_FALSE(dut_->DefaultValueForTemplate(CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG,
+ &actual));
+}
+
+} // namespace v4l2_camera_hal
diff --git a/modules/camera/3_4/metadata/enum_converter.cpp b/modules/camera/3_4/metadata/enum_converter.cpp
new file mode 100644
index 0000000..d5e0a87
--- /dev/null
+++ b/modules/camera/3_4/metadata/enum_converter.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "enum_converter.h"
+
+#include <errno.h>
+
+#include "../common.h"
+
+namespace v4l2_camera_hal {
+
+EnumConverter::EnumConverter(
+ const std::multimap<int32_t, uint8_t>& v4l2_to_metadata)
+ : v4l2_to_metadata_(v4l2_to_metadata) {
+ HAL_LOG_ENTER();
+}
+
+int EnumConverter::MetadataToV4L2(uint8_t value, int32_t* conversion) {
+ // Unfortunately no bi-directional map lookup in C++.
+ // Breaking on second, not first found so that a warning
+ // can be given if there are multiple values.
+ size_t count = 0;
+ for (auto kv : v4l2_to_metadata_) {
+ if (kv.second == value) {
+ ++count;
+ if (count == 1) {
+ // First match.
+ *conversion = kv.first;
+ } else {
+ // second match.
+ break;
+ }
+ }
+ }
+
+ if (count == 0) {
+ HAL_LOGV("Couldn't find V4L2 conversion of metadata value %d.", value);
+ return -EINVAL;
+ } else if (count > 1) {
+ HAL_LOGV(
+ "Multiple V4L2 conversions found for metadata value %d, using first.",
+ value);
+ }
+ return 0;
+}
+
+int EnumConverter::V4L2ToMetadata(int32_t value, uint8_t* conversion) {
+ auto element_range = v4l2_to_metadata_.equal_range(value);
+ if (element_range.first == element_range.second) {
+ HAL_LOGV("Couldn't find metadata conversion of V4L2 value %d.", value);
+ return -EINVAL;
+ }
+
+ auto element = element_range.first;
+ *conversion = element->second;
+
+ if (++element != element_range.second) {
+ HAL_LOGV(
+ "Multiple metadata conversions found for V4L2 value %d, using first.",
+ value);
+ }
+ return 0;
+}
+
+} // namespace v4l2_camera_hal
diff --git a/modules/camera/3_4/metadata/enum_converter.h b/modules/camera/3_4/metadata/enum_converter.h
new file mode 100644
index 0000000..df5cabb
--- /dev/null
+++ b/modules/camera/3_4/metadata/enum_converter.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef V4L2_CAMERA_HAL_METADATA_ENUM_CONVERTER_H_
+#define V4L2_CAMERA_HAL_METADATA_ENUM_CONVERTER_H_
+
+#include <map>
+
+#include "../common.h"
+#include "converter_interface.h"
+
+namespace v4l2_camera_hal {
+
+// An EnumConverter converts between enum values.
+class EnumConverter : public ConverterInterface<uint8_t, int32_t> {
+ public:
+ EnumConverter(const std::multimap<int32_t, uint8_t>& v4l2_to_metadata);
+
+ virtual int MetadataToV4L2(uint8_t value, int32_t* conversion) override;
+ virtual int V4L2ToMetadata(int32_t value, uint8_t* conversion) override;
+
+ private:
+ const std::multimap<int32_t, uint8_t> v4l2_to_metadata_;
+
+ DISALLOW_COPY_AND_ASSIGN(EnumConverter);
+};
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_METADATA_ENUM_CONVERTER_H_
diff --git a/modules/camera/3_4/metadata/enum_converter_test.cpp b/modules/camera/3_4/metadata/enum_converter_test.cpp
new file mode 100644
index 0000000..9ba7ffc
--- /dev/null
+++ b/modules/camera/3_4/metadata/enum_converter_test.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "enum_converter.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using testing::Test;
+
+namespace v4l2_camera_hal {
+
+class EnumConverterTest : public Test {
+ protected:
+ virtual void SetUp() {
+ converter_.reset(
+ new EnumConverter({{one_to_one_v4l2_, one_to_one_metadata_},
+ {one_to_many_v4l2_, many_to_one_metadata_1_},
+ {one_to_many_v4l2_, many_to_one_metadata_2_},
+ {many_to_one_v4l2_1_, one_to_many_metadata_},
+ {many_to_one_v4l2_2_, one_to_many_metadata_},
+ {unused_v4l2_, unused_metadata_}}));
+ }
+
+ std::unique_ptr<EnumConverter> converter_;
+
+ const int32_t one_to_one_v4l2_ = 12;
+ const int32_t one_to_many_v4l2_ = 34;
+ const int32_t many_to_one_v4l2_1_ = 56;
+ const int32_t many_to_one_v4l2_2_ = 78;
+ const int32_t unused_v4l2_ = 910;
+ const uint8_t one_to_one_metadata_ = 109;
+ const uint8_t one_to_many_metadata_ = 87;
+ const uint8_t many_to_one_metadata_1_ = 65;
+ const uint8_t many_to_one_metadata_2_ = 43;
+ const uint8_t unused_metadata_ = 21;
+};
+
+// Convert single.
+TEST_F(EnumConverterTest, OneToOneConversion) {
+ uint8_t metadata_val = 1;
+ ASSERT_EQ(converter_->V4L2ToMetadata(one_to_one_v4l2_, &metadata_val), 0);
+ EXPECT_EQ(metadata_val, one_to_one_metadata_);
+
+ int32_t v4l2_val = 1;
+ ASSERT_EQ(converter_->MetadataToV4L2(one_to_one_metadata_, &v4l2_val), 0);
+ EXPECT_EQ(v4l2_val, one_to_one_v4l2_);
+}
+
+TEST_F(EnumConverterTest, OneToManyConversion) {
+ // Should be one of the acceptable values.
+ uint8_t metadata_val = 1;
+ ASSERT_EQ(converter_->V4L2ToMetadata(one_to_many_v4l2_, &metadata_val), 0);
+ EXPECT_TRUE(metadata_val == many_to_one_metadata_1_ ||
+ metadata_val == many_to_one_metadata_2_);
+
+ int32_t v4l2_val = 1;
+ ASSERT_EQ(converter_->MetadataToV4L2(one_to_many_metadata_, &v4l2_val), 0);
+ EXPECT_TRUE(v4l2_val == many_to_one_v4l2_1_ ||
+ v4l2_val == many_to_one_v4l2_2_);
+}
+
+TEST_F(EnumConverterTest, ManyToOneConversion) {
+ uint8_t metadata_val = 1;
+ ASSERT_EQ(converter_->V4L2ToMetadata(many_to_one_v4l2_1_, &metadata_val), 0);
+ EXPECT_EQ(metadata_val, one_to_many_metadata_);
+ metadata_val = 1; // Reset.
+ ASSERT_EQ(converter_->V4L2ToMetadata(many_to_one_v4l2_2_, &metadata_val), 0);
+ EXPECT_EQ(metadata_val, one_to_many_metadata_);
+
+ int32_t v4l2_val = 1;
+ ASSERT_EQ(converter_->MetadataToV4L2(many_to_one_metadata_1_, &v4l2_val), 0);
+ EXPECT_EQ(v4l2_val, one_to_many_v4l2_);
+ v4l2_val = 1; // Reset.
+ ASSERT_EQ(converter_->MetadataToV4L2(many_to_one_metadata_2_, &v4l2_val), 0);
+ EXPECT_EQ(v4l2_val, one_to_many_v4l2_);
+}
+
+TEST_F(EnumConverterTest, InvalidConversion) {
+ uint8_t metadata_val = 1;
+ EXPECT_EQ(converter_->V4L2ToMetadata(1, &metadata_val), -EINVAL);
+
+ int32_t v4l2_val = 1;
+ EXPECT_EQ(converter_->MetadataToV4L2(1, &v4l2_val), -EINVAL);
+}
+
+} // namespace v4l2_camera_hal
diff --git a/modules/camera/3_4/metadata/ignored_control_delegate.h b/modules/camera/3_4/metadata/ignored_control_delegate.h
new file mode 100644
index 0000000..f1d5da1
--- /dev/null
+++ b/modules/camera/3_4/metadata/ignored_control_delegate.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef V4L2_CAMERA_HAL_METADATA_IGNORED_CONTROL_DELEGATE_H_
+#define V4L2_CAMERA_HAL_METADATA_IGNORED_CONTROL_DELEGATE_H_
+
+#include "control_delegate_interface.h"
+
+namespace v4l2_camera_hal {
+
+// An IgnoredControlDelegate, as the name implies,
+// has a fixed value and ignores all requests to set it.
+template <typename T>
+class IgnoredControlDelegate : public ControlDelegateInterface<T> {
+ public:
+ IgnoredControlDelegate(T value) : value_(value){};
+
+ int GetValue(T* value) override {
+ *value = value_;
+ return 0;
+ };
+ int SetValue(const T& value) override { return 0; };
+
+ private:
+ const T value_;
+};
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_METADATA_IGNORED_CONTROL_DELEGATE_H_
diff --git a/modules/camera/3_4/metadata/ignored_control_delegate_test.cpp b/modules/camera/3_4/metadata/ignored_control_delegate_test.cpp
new file mode 100644
index 0000000..80c30df
--- /dev/null
+++ b/modules/camera/3_4/metadata/ignored_control_delegate_test.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ignored_control_delegate.h"
+
+#include <gtest/gtest.h>
+
+using testing::Test;
+
+namespace v4l2_camera_hal {
+
+TEST(IgnoredControlDelegateTest, DefaultGet) {
+ int32_t value = 12;
+ IgnoredControlDelegate<int32_t> control(value);
+ int32_t actual = 0;
+ ASSERT_EQ(control.GetValue(&actual), 0);
+ EXPECT_EQ(actual, value);
+}
+
+TEST(IgnoredControlDelegateTest, GetAndSet) {
+ int32_t value = 12;
+ IgnoredControlDelegate<int32_t> control(value);
+ int32_t new_value = 13;
+ ASSERT_EQ(control.SetValue(new_value), 0);
+ int32_t actual = 0;
+ ASSERT_EQ(control.GetValue(&actual), 0);
+ // Should still be the default.
+ EXPECT_EQ(actual, value);
+}
+
+} // namespace v4l2_camera_hal
diff --git a/modules/camera/3_4/metadata/map_converter.h b/modules/camera/3_4/metadata/map_converter.h
new file mode 100644
index 0000000..b1734b5
--- /dev/null
+++ b/modules/camera/3_4/metadata/map_converter.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef V4L2_CAMERA_HAL_METADATA_MAP_CONVERTER_H_
+#define V4L2_CAMERA_HAL_METADATA_MAP_CONVERTER_H_
+
+#include <errno.h>
+
+#include <map>
+#include <memory>
+
+#include "../common.h"
+#include "converter_interface.h"
+
+namespace v4l2_camera_hal {
+
+// A MapConverter fits values converted by a wrapped converter
+// to a map entry corresponding to the key with the nearest value.
+template <typename TMetadata, typename TV4L2, typename TMapKey>
+class MapConverter : public ConverterInterface<TMetadata, TV4L2> {
+ public:
+ MapConverter(
+ std::shared_ptr<ConverterInterface<TMetadata, TMapKey>> wrapped_converter,
+ std::map<TMapKey, TV4L2> conversion_map);
+
+ virtual int MetadataToV4L2(TMetadata value, TV4L2* conversion) override;
+ virtual int V4L2ToMetadata(TV4L2 value, TMetadata* conversion) override;
+
+ private:
+ std::shared_ptr<ConverterInterface<TMetadata, TMapKey>> wrapped_converter_;
+ std::map<TMapKey, TV4L2> conversion_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(MapConverter);
+};
+
+// -----------------------------------------------------------------------------
+
+template <typename TMetadata, typename TV4L2, typename TMapKey>
+MapConverter<TMetadata, TV4L2, TMapKey>::MapConverter(
+ std::shared_ptr<ConverterInterface<TMetadata, TMapKey>> wrapped_converter,
+ std::map<TMapKey, TV4L2> conversion_map)
+ : wrapped_converter_(std::move(wrapped_converter)),
+ conversion_map_(conversion_map) {
+ HAL_LOG_ENTER();
+}
+
+template <typename TMetadata, typename TV4L2, typename TMapKey>
+int MapConverter<TMetadata, TV4L2, TMapKey>::MetadataToV4L2(TMetadata value,
+ TV4L2* conversion) {
+ HAL_LOG_ENTER();
+
+ if (conversion_map_.empty()) {
+ HAL_LOGE("Empty conversion map.");
+ return -EINVAL;
+ }
+
+ TMapKey raw_conversion = 0;
+ int res = wrapped_converter_->MetadataToV4L2(value, &raw_conversion);
+ if (res) {
+ HAL_LOGE("Failed to perform underlying conversion.");
+ return res;
+ }
+
+ // Find nearest key.
+ auto kv = conversion_map_.lower_bound(raw_conversion);
+ // lower_bound finds the first >= element.
+ if (kv == conversion_map_.begin()) {
+ // Searching for less than the smallest key, so that will be the nearest.
+ *conversion = kv->second;
+ } else if (kv == conversion_map_.end()) {
+ // Searching for greater than the largest key, so that will be the nearest.
+ --kv;
+ *conversion = kv->second;
+ } else {
+ // Since kv points to the first >= element, either that or the previous
+ // element will be nearest.
+ *conversion = kv->second;
+ TMapKey diff = kv->first - raw_conversion;
+
+ // Now compare to the previous. This element will be < raw conversion,
+ // so reverse the order of the subtraction.
+ --kv;
+ if (raw_conversion - kv->first < diff) {
+ *conversion = kv->second;
+ }
+ }
+
+ return 0;
+}
+
+template <typename TMetadata, typename TV4L2, typename TMapKey>
+int MapConverter<TMetadata, TV4L2, TMapKey>::V4L2ToMetadata(
+ TV4L2 value, TMetadata* conversion) {
+ HAL_LOG_ENTER();
+
+ // Unfortunately no bi-directional map lookup in C++.
+ // Breaking on second, not first found so that a warning
+ // can be given if there are multiple values.
+ size_t count = 0;
+ int res;
+ for (auto kv : conversion_map_) {
+ if (kv.second == value) {
+ ++count;
+ if (count == 1) {
+ // First match.
+ res = wrapped_converter_->V4L2ToMetadata(kv.first, conversion);
+ } else {
+ // second match.
+ break;
+ }
+ }
+ }
+
+ if (count == 0) {
+ HAL_LOGE("Couldn't find map conversion of V4L2 value %d.", value);
+ return -EINVAL;
+ } else if (count > 1) {
+ HAL_LOGW("Multiple map conversions found for V4L2 value %d, using first.",
+ value);
+ }
+ return res;
+}
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_METADATA_MAP_CONVERTER_H_
diff --git a/modules/camera/3_4/metadata/map_converter_test.cpp b/modules/camera/3_4/metadata/map_converter_test.cpp
new file mode 100644
index 0000000..0361810
--- /dev/null
+++ b/modules/camera/3_4/metadata/map_converter_test.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "map_converter.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "converter_interface_mock.h"
+
+using testing::Return;
+using testing::SetArgPointee;
+using testing::Test;
+using testing::_;
+
+namespace v4l2_camera_hal {
+
+class MapConverterTest : public Test {
+ protected:
+ virtual void SetUp() {
+ converter_.reset(new ConverterInterfaceMock<int, int32_t>());
+ dut_.reset(new MapConverter<int, int32_t, int32_t>(converter_, map_));
+ }
+
+ virtual void ExpectConvertToV4L2(int32_t converted, int32_t expected) {
+ int initial = 99;
+ EXPECT_CALL(*converter_, MetadataToV4L2(initial, _))
+ .WillOnce(DoAll(SetArgPointee<1>(converted), Return(0)));
+
+ int32_t actual = expected + 1; // Initialize to non-expected value.
+ ASSERT_EQ(dut_->MetadataToV4L2(initial, &actual), 0);
+ EXPECT_EQ(actual, expected);
+ }
+
+ std::shared_ptr<ConverterInterfaceMock<int, int32_t>> converter_;
+ std::unique_ptr<MapConverter<int, int32_t, int32_t>> dut_;
+
+ const std::map<int32_t, int32_t> map_{{10, 1}, {40, 4}, {20, 2}, {30, 3}};
+};
+
+TEST_F(MapConverterTest, NormalConversionToV4L2) {
+ // A value that matches the map perfectly.
+ auto kv = map_.begin();
+ ExpectConvertToV4L2(kv->first, kv->second);
+}
+
+TEST_F(MapConverterTest, RoundingDownConversionToV4L2) {
+ // A value that's in range but not an exact key value.
+ auto kv = map_.begin();
+ ExpectConvertToV4L2(kv->first + 1, kv->second);
+}
+
+TEST_F(MapConverterTest, RoundingUpConversionToV4L2) {
+ // A value that's in range but not an exact key value.
+ auto kv = map_.begin();
+ ++kv;
+ ExpectConvertToV4L2(kv->first - 1, kv->second);
+}
+
+TEST_F(MapConverterTest, ClampUpConversionToV4L2) {
+ // A value that's below range.
+ auto kv = map_.begin();
+ ExpectConvertToV4L2(kv->first - 1, kv->second);
+}
+
+TEST_F(MapConverterTest, ClampDownConversionToV4L2) {
+ // A value that's above range (even after fitting to step).
+ auto kv = map_.rbegin();
+ ExpectConvertToV4L2(kv->first + 1, kv->second);
+}
+
+TEST_F(MapConverterTest, ConversionErrorToV4L2) {
+ int initial = 99;
+ int err = -99;
+ EXPECT_CALL(*converter_, MetadataToV4L2(initial, _)).WillOnce(Return(err));
+
+ int32_t unused;
+ EXPECT_EQ(dut_->MetadataToV4L2(initial, &unused), err);
+}
+
+TEST_F(MapConverterTest, NormalConversionToMetadata) {
+ auto kv = map_.begin();
+ int expected = 99;
+ EXPECT_CALL(*converter_, V4L2ToMetadata(kv->first, _))
+ .WillOnce(DoAll(SetArgPointee<1>(expected), Return(0)));
+
+ int actual = expected + 1; // Initialize to non-expected value.
+ ASSERT_EQ(dut_->V4L2ToMetadata(kv->second, &actual), 0);
+ EXPECT_EQ(actual, expected);
+}
+
+TEST_F(MapConverterTest, NotFoundConversionToMetadata) {
+ int unused;
+ ASSERT_EQ(dut_->V4L2ToMetadata(100, &unused), -EINVAL);
+}
+
+} // namespace v4l2_camera_hal
diff --git a/modules/camera/3_4/metadata/menu_control_options.h b/modules/camera/3_4/metadata/menu_control_options.h
new file mode 100644
index 0000000..c972dc6
--- /dev/null
+++ b/modules/camera/3_4/metadata/menu_control_options.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef V4L2_CAMERA_HAL_METADATA_MENU_CONTROL_OPTIONS_H_
+#define V4L2_CAMERA_HAL_METADATA_MENU_CONTROL_OPTIONS_H_
+
+#include <errno.h>
+
+#include "../common.h"
+#include "control_options_interface.h"
+#include "default_option_delegate.h"
+
+namespace v4l2_camera_hal {
+
+// MenuControlOptions offer a fixed list of acceptable values.
+template <typename T>
+class MenuControlOptions : public ControlOptionsInterface<T> {
+ public:
+ // |options| must be non-empty.
+ MenuControlOptions(std::vector<T> options,
+ std::shared_ptr<DefaultOptionDelegate<T>> defaults)
+ : options_(options), defaults_(defaults){};
+ MenuControlOptions(std::vector<T> options, std::map<int, T> defaults)
+ : options_(options),
+ defaults_(std::make_shared<DefaultOptionDelegate<T>>(defaults)){};
+
+ virtual std::vector<T> MetadataRepresentation() override { return options_; };
+ virtual bool IsSupported(const T& option) override {
+ return (std::find(options_.begin(), options_.end(), option) !=
+ options_.end());
+ };
+ virtual int DefaultValueForTemplate(int template_type,
+ T* default_value) override {
+ // Default to the first option.
+ if (options_.empty()) {
+ HAL_LOGE("Can't get default value, options are empty.");
+ return -ENODEV;
+ }
+
+ // Try to get it from the defaults delegate.
+ if (defaults_->DefaultValueForTemplate(template_type, default_value) &&
+ IsSupported(*default_value)) {
+ return 0;
+ }
+
+ // Fall back to the first available.
+ *default_value = options_[0];
+ return 0;
+ };
+
+ private:
+ std::vector<T> options_;
+ std::shared_ptr<DefaultOptionDelegate<T>> defaults_;
+};
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_METADATA_MENU_CONTROL_OPTIONS_H_
diff --git a/modules/camera/3_4/metadata/menu_control_options_test.cpp b/modules/camera/3_4/metadata/menu_control_options_test.cpp
new file mode 100644
index 0000000..1a6ce6e
--- /dev/null
+++ b/modules/camera/3_4/metadata/menu_control_options_test.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "menu_control_options.h"
+
+#include <memory>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hardware/camera3.h>
+
+#include "default_option_delegate_mock.h"
+
+using testing::Return;
+using testing::SetArgPointee;
+using testing::Test;
+using testing::_;
+
+namespace v4l2_camera_hal {
+
+class MenuControlOptionsTest : public Test {
+ protected:
+ virtual void SetUp() {
+ mock_defaults_.reset(new DefaultOptionDelegateMock<int>());
+ dut_.reset(new MenuControlOptions<int>(options_, mock_defaults_));
+ }
+
+ std::unique_ptr<MenuControlOptions<int>> dut_;
+ const std::vector<int> options_{1, 10, 19, 30};
+ std::shared_ptr<DefaultOptionDelegateMock<int>> mock_defaults_;
+};
+
+TEST_F(MenuControlOptionsTest, MetadataRepresentation) {
+ // Technically order doesn't matter, but this is faster to write,
+ // and still passes.
+ EXPECT_EQ(dut_->MetadataRepresentation(), options_);
+}
+
+TEST_F(MenuControlOptionsTest, IsSupported) {
+ for (auto option : options_) {
+ EXPECT_TRUE(dut_->IsSupported(option));
+ }
+ // And at least one unsupported.
+ EXPECT_FALSE(dut_->IsSupported(99));
+}
+
+TEST_F(MenuControlOptionsTest, DelegateDefaultValue) {
+ int template_index = 3;
+ int expected = options_[2];
+ ASSERT_TRUE(dut_->IsSupported(expected));
+ EXPECT_CALL(*mock_defaults_, DefaultValueForTemplate(template_index, _))
+ .WillOnce(DoAll(SetArgPointee<1>(expected), Return(true)));
+ int actual = expected - 1;
+ EXPECT_EQ(dut_->DefaultValueForTemplate(template_index, &actual), 0);
+ EXPECT_EQ(actual, expected);
+}
+
+TEST_F(MenuControlOptionsTest, InvalidDelegateDefaultValue) {
+ // -1 is not a supported option.
+ int template_index = 3;
+ int default_val = -1;
+ ASSERT_FALSE(dut_->IsSupported(default_val));
+
+ EXPECT_CALL(*mock_defaults_, DefaultValueForTemplate(template_index, _))
+ .WillOnce(DoAll(SetArgPointee<1>(default_val), Return(true)));
+
+ int actual = default_val;
+ EXPECT_EQ(dut_->DefaultValueForTemplate(template_index, &actual), 0);
+ // Should just give any supported option instead.
+ EXPECT_TRUE(dut_->IsSupported(actual));
+}
+
+TEST_F(MenuControlOptionsTest, NoDelegateDefaultValue) {
+ int template_index = 3;
+ int actual = -1;
+ ASSERT_FALSE(dut_->IsSupported(actual));
+
+ // Have delegate error.
+ EXPECT_CALL(*mock_defaults_, DefaultValueForTemplate(template_index, _))
+ .WillOnce(Return(false));
+
+ // Should still give *some* supported value.
+ EXPECT_EQ(dut_->DefaultValueForTemplate(template_index, &actual), 0);
+ EXPECT_TRUE(dut_->IsSupported(actual));
+}
+
+TEST_F(MenuControlOptionsTest, NoDefaultValue) {
+ // Invalid options don't have a valid default.
+ MenuControlOptions<int> bad_options({}, mock_defaults_);
+ for (int i = 1; i < CAMERA3_TEMPLATE_COUNT; ++i) {
+ int value = -1;
+ EXPECT_EQ(bad_options.DefaultValueForTemplate(i, &value), -ENODEV);
+ }
+}
+
+} // namespace v4l2_camera_hal
diff --git a/modules/camera/3_4/metadata/metadata.cpp b/modules/camera/3_4/metadata/metadata.cpp
new file mode 100644
index 0000000..efc9959
--- /dev/null
+++ b/modules/camera/3_4/metadata/metadata.cpp
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "metadata.h"
+
+#include <camera/CameraMetadata.h>
+#include <hardware/camera3.h>
+
+#include "../common.h"
+#include "metadata_common.h"
+
+namespace v4l2_camera_hal {
+
+Metadata::Metadata(PartialMetadataSet components)
+ : components_(std::move(components)) {
+ HAL_LOG_ENTER();
+}
+
+Metadata::~Metadata() {
+ HAL_LOG_ENTER();
+}
+
+int Metadata::FillStaticMetadata(android::CameraMetadata* metadata) {
+ HAL_LOG_ENTER();
+ if (!metadata) {
+ HAL_LOGE("Can't fill null metadata.");
+ return -EINVAL;
+ }
+
+ std::vector<int32_t> static_tags;
+ std::vector<int32_t> control_tags;
+ std::vector<int32_t> dynamic_tags;
+ int res = 0;
+
+ for (auto& component : components_) {
+ // Prevent components from potentially overriding others.
+ android::CameraMetadata additional_metadata;
+ // Populate the fields.
+ res = component->PopulateStaticFields(&additional_metadata);
+ if (res) {
+ HAL_LOGE("Failed to get all static properties.");
+ return res;
+ }
+ // Add it to the overall result.
+ if (!additional_metadata.isEmpty()) {
+ res = metadata->append(additional_metadata);
+ if (res != android::OK) {
+ HAL_LOGE("Failed to append all static properties.");
+ return res;
+ }
+ }
+
+ // Note what tags the component adds.
+ std::vector<int32_t> tags = component->StaticTags();
+ std::move(tags.begin(),
+ tags.end(),
+ std::inserter(static_tags, static_tags.end()));
+ tags = component->ControlTags();
+ std::move(tags.begin(),
+ tags.end(),
+ std::inserter(control_tags, control_tags.end()));
+ tags = component->DynamicTags();
+ std::move(tags.begin(),
+ tags.end(),
+ std::inserter(dynamic_tags, dynamic_tags.end()));
+ }
+
+ // Populate the meta fields.
+ static_tags.push_back(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS);
+ res = UpdateMetadata(
+ metadata, ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS, control_tags);
+ if (res != android::OK) {
+ HAL_LOGE("Failed to add request keys meta key.");
+ return -ENODEV;
+ }
+ static_tags.push_back(ANDROID_REQUEST_AVAILABLE_RESULT_KEYS);
+ res = UpdateMetadata(
+ metadata, ANDROID_REQUEST_AVAILABLE_RESULT_KEYS, dynamic_tags);
+ if (res != android::OK) {
+ HAL_LOGE("Failed to add result keys meta key.");
+ return -ENODEV;
+ }
+ static_tags.push_back(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS);
+ res = UpdateMetadata(
+ metadata, ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS, static_tags);
+ if (res != android::OK) {
+ HAL_LOGE("Failed to add characteristics keys meta key.");
+ return -ENODEV;
+ }
+
+ // TODO(b/31018853): cache result.
+ return 0;
+}
+
+bool Metadata::IsValidRequest(const android::CameraMetadata& metadata) {
+ HAL_LOG_ENTER();
+
+ // Empty means "use previous settings", which are inherently valid.
+ if (metadata.isEmpty())
+ return true;
+
+ for (auto& component : components_) {
+ // Check that all components support the values requested of them.
+ bool valid_request = component->SupportsRequestValues(metadata);
+ if (!valid_request) {
+ // Exit early if possible.
+ return false;
+ }
+ }
+
+ return true;
+}
+
+int Metadata::GetRequestTemplate(int template_type,
+ android::CameraMetadata* template_metadata) {
+ HAL_LOG_ENTER();
+ if (!template_metadata) {
+ HAL_LOGE("Can't fill null template.");
+ return -EINVAL;
+ }
+
+ // Templates are numbered 1 through COUNT-1 for some reason.
+ if (template_type < 1 || template_type >= CAMERA3_TEMPLATE_COUNT) {
+ HAL_LOGE("Unrecognized template type %d.", template_type);
+ return -EINVAL;
+ }
+
+ for (auto& component : components_) {
+ // Prevent components from potentially overriding others.
+ android::CameraMetadata additional_metadata;
+ int res =
+ component->PopulateTemplateRequest(template_type, &additional_metadata);
+ if (res) {
+ HAL_LOGE("Failed to get all default request fields.");
+ return res;
+ }
+ // Add it to the overall result.
+ if (!additional_metadata.isEmpty()) {
+ res = template_metadata->append(additional_metadata);
+ if (res != android::OK) {
+ HAL_LOGE("Failed to append all default request fields.");
+ return res;
+ }
+ }
+ }
+
+ // TODO(b/31018853): cache result.
+ return 0;
+}
+
+int Metadata::SetRequestSettings(const android::CameraMetadata& metadata) {
+ HAL_LOG_ENTER();
+
+ // Empty means "use previous settings".
+ if (metadata.isEmpty())
+ return 0;
+
+ for (auto& component : components_) {
+ int res = component->SetRequestValues(metadata);
+ if (res) {
+ HAL_LOGE("Failed to set all requested settings.");
+ return res;
+ }
+ }
+
+ return 0;
+}
+
+int Metadata::FillResultMetadata(android::CameraMetadata* metadata) {
+ HAL_LOG_ENTER();
+ if (!metadata) {
+ HAL_LOGE("Can't fill null metadata.");
+ return -EINVAL;
+ }
+
+ for (auto& component : components_) {
+ // Prevent components from potentially overriding others.
+ android::CameraMetadata additional_metadata;
+ int res = component->PopulateDynamicFields(&additional_metadata);
+ if (res) {
+ HAL_LOGE("Failed to get all dynamic result fields.");
+ return res;
+ }
+ // Add it to the overall result.
+ if (!additional_metadata.isEmpty()) {
+ res = metadata->append(additional_metadata);
+ if (res != android::OK) {
+ HAL_LOGE("Failed to append all dynamic result fields.");
+ return res;
+ }
+ }
+ }
+
+ return 0;
+}
+
+} // namespace v4l2_camera_hal
diff --git a/modules/camera/3_4/metadata/metadata.h b/modules/camera/3_4/metadata/metadata.h
new file mode 100644
index 0000000..e2232b5
--- /dev/null
+++ b/modules/camera/3_4/metadata/metadata.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef V4L2_CAMERA_HAL_METADATA_H_
+#define V4L2_CAMERA_HAL_METADATA_H_
+
+#include <set>
+
+#include <camera/CameraMetadata.h>
+#include <hardware/camera3.h>
+
+#include "../common.h"
+#include "metadata_common.h"
+
+namespace v4l2_camera_hal {
+class Metadata {
+ public:
+ Metadata(PartialMetadataSet components);
+ virtual ~Metadata();
+
+ int FillStaticMetadata(android::CameraMetadata* metadata);
+ bool IsValidRequest(const android::CameraMetadata& metadata);
+ int GetRequestTemplate(int template_type,
+ android::CameraMetadata* template_metadata);
+ int SetRequestSettings(const android::CameraMetadata& metadata);
+ int FillResultMetadata(android::CameraMetadata* metadata);
+
+ private:
+ // The overall metadata is broken down into several distinct pieces.
+ // Note: it is undefined behavior if multiple components share tags.
+ PartialMetadataSet components_;
+
+ DISALLOW_COPY_AND_ASSIGN(Metadata);
+};
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_V4L2_METADATA_H_
diff --git a/modules/camera/3_4/metadata/metadata_common.h b/modules/camera/3_4/metadata/metadata_common.h
new file mode 100644
index 0000000..34b7777
--- /dev/null
+++ b/modules/camera/3_4/metadata/metadata_common.h
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef V4L2_CAMERA_HAL_METADATA_METADATA_COMMON_H_
+#define V4L2_CAMERA_HAL_METADATA_METADATA_COMMON_H_
+
+#include <array>
+#include <memory>
+#include <set>
+#include <vector>
+
+#include <camera/CameraMetadata.h>
+
+#include "array_vector.h"
+#include "partial_metadata_interface.h"
+
+namespace v4l2_camera_hal {
+
+typedef std::set<std::unique_ptr<PartialMetadataInterface>> PartialMetadataSet;
+
+// Templated helper functions effectively extending android::CameraMetadata.
+// Will cause a compile-time errors if CameraMetadata doesn't support
+// using the templated type. Templates are provided to extend this support
+// to std::arrays, std::vectors, and ArrayVectors of supported types as
+// appropriate.
+
+// UpdateMetadata(metadata, tag, data):
+//
+// Updates the entry for |tag| in |metadata| (functionally similar to
+// android::CameraMetadata::update).
+//
+// Args:
+// metadata: the android::CameraMetadata to update.
+// tag: the tag within |metadata| to update.
+// data: A reference to the data to update |tag| with.
+//
+// Returns:
+// 0: Success.
+// -ENODEV: The type of |data| does not match the expected type for |tag|,
+// or another error occured. Note: no errors are given for updating a
+// metadata entry with an incorrect amount of data (e.g. filling a tag
+// that expects to have only one value with multiple values), as this
+// information is not encoded in the type associated with the tag by
+// get_camera_metadata_tag_type (from <system/camera_metadata.h>).
+
+// Generic (pointer & size).
+template <typename T>
+static int UpdateMetadata(android::CameraMetadata* metadata,
+ int32_t tag,
+ const T* data,
+ size_t count) {
+ int res = metadata->update(tag, data, count);
+ if (res) {
+ HAL_LOGE("Failed to update metadata tag %d", tag);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+// Generic (single item reference).
+template <typename T>
+static int UpdateMetadata(android::CameraMetadata* metadata,
+ int32_t tag,
+ const T& val) {
+ return UpdateMetadata(metadata, tag, &val, 1);
+}
+
+// Specialization for vectors.
+template <typename T>
+static int UpdateMetadata(android::CameraMetadata* metadata,
+ int32_t tag,
+ const std::vector<T>& val) {
+ return UpdateMetadata(metadata, tag, val.data(), val.size());
+}
+
+// Specialization for arrays.
+template <typename T, size_t N>
+static int UpdateMetadata(android::CameraMetadata* metadata,
+ int32_t tag,
+ const std::array<T, N>& val) {
+ return UpdateMetadata(metadata, tag, val.data(), N);
+}
+
+// Specialization for ArrayVectors.
+template <typename T, size_t N>
+static int UpdateMetadata(android::CameraMetadata* metadata,
+ int32_t tag,
+ const ArrayVector<T, N>& val) {
+ return UpdateMetadata(metadata, tag, val.data(), val.total_num_elements());
+}
+
+// Specialization for vectors of arrays.
+template <typename T, size_t N>
+static int UpdateMetadata(android::CameraMetadata* metadata,
+ int32_t tag,
+ const std::vector<std::array<T, N>>& val) {
+ // Convert to array vector so we know all the elements are contiguous.
+ ArrayVector<T, N> array_vector;
+ for (const auto& array : val) {
+ array_vector.push_back(array);
+ }
+ return UpdateMetadata(metadata, tag, array_vector);
+}
+
+// GetDataPointer(entry, val)
+//
+// A helper for other methods in this file.
+// Gets the data pointer of a given metadata entry into |*val|.
+
+static void GetDataPointer(camera_metadata_ro_entry_t& entry,
+ const uint8_t** val) {
+ *val = entry.data.u8;
+}
+
+static void GetDataPointer(camera_metadata_ro_entry_t& entry,
+ const int32_t** val) {
+ *val = entry.data.i32;
+}
+
+static void GetDataPointer(camera_metadata_ro_entry_t& entry,
+ const float** val) {
+ *val = entry.data.f;
+}
+
+static void GetDataPointer(camera_metadata_ro_entry_t& entry,
+ const int64_t** val) {
+ *val = entry.data.i64;
+}
+
+static void GetDataPointer(camera_metadata_ro_entry_t& entry,
+ const double** val) {
+ *val = entry.data.d;
+}
+
+static void GetDataPointer(camera_metadata_ro_entry_t& entry,
+ const camera_metadata_rational_t** val) {
+ *val = entry.data.r;
+}
+
+// SingleTagValue(metadata, tag, val)
+//
+// Get the value of the |tag| entry in |metadata|.
+// |tag| is expected to refer to an entry with a single item
+// of the templated type (a "single item" is exactly N values
+// if the templated type is an array of size N). An error will be
+// returned if it the wrong number of items are present.
+//
+// Returns:
+// -ENOENT: The tag couldn't be found or was empty.
+// -EINVAL: The tag contained more than one item, or |val| is null.
+// -ENODEV: The tag claims to be non-empty, but the data pointer is null.
+// 0: Success. |*val| will contain the value for |tag|.
+
+// Singleton.
+template <typename T>
+static int SingleTagValue(const android::CameraMetadata& metadata,
+ int32_t tag,
+ T* val) {
+ if (!val) {
+ HAL_LOGE("Null pointer passed to SingleTagValue.");
+ return -EINVAL;
+ }
+ camera_metadata_ro_entry_t entry = metadata.find(tag);
+ if (entry.count == 0) {
+ HAL_LOGE("Metadata tag %d is empty.", tag);
+ return -ENOENT;
+ } else if (entry.count != 1) {
+ HAL_LOGE(
+ "Error: expected metadata tag %d to contain exactly 1 value "
+ "(had %d).",
+ tag,
+ entry.count);
+ return -EINVAL;
+ }
+ const T* data = nullptr;
+ GetDataPointer(entry, &data);
+ if (data == nullptr) {
+ HAL_LOGE("Metadata tag %d is empty.", tag);
+ return -ENODEV;
+ }
+ *val = *data;
+ return 0;
+}
+
+// Specialization for std::array.
+template <typename T, size_t N>
+static int SingleTagValue(const android::CameraMetadata& metadata,
+ int32_t tag,
+ std::array<T, N>* val) {
+ if (!val) {
+ HAL_LOGE("Null pointer passed to SingleTagValue.");
+ return -EINVAL;
+ }
+ camera_metadata_ro_entry_t entry = metadata.find(tag);
+ if (entry.count == 0) {
+ HAL_LOGE("Metadata tag %d is empty.", tag);
+ return -ENOENT;
+ } else if (entry.count != N) {
+ HAL_LOGE(
+ "Error: expected metadata tag %d to contain a single array of "
+ "exactly %d values (had %d).",
+ tag,
+ N,
+ entry.count);
+ return -EINVAL;
+ }
+ const T* data = nullptr;
+ GetDataPointer(entry, &data);
+ if (data == nullptr) {
+ HAL_LOGE("Metadata tag %d is empty.", tag);
+ return -ENODEV;
+ }
+ // Fill in the array.
+ for (size_t i = 0; i < N; ++i) {
+ (*val)[i] = data[i];
+ }
+ return 0;
+}
+
+// VectorTagValue(metadata, tag, val)
+//
+// Get the value of the |tag| entry in |metadata|.
+// |tag| is expected to refer to an entry with a vector
+// of the templated type. For arrays, an error will be
+// returned if it the wrong number of items are present.
+//
+// Returns:
+// -ENOENT: The tag couldn't be found or was empty. While technically an
+// empty vector may be valid, this error is returned for consistency
+// with SingleTagValue.
+// -EINVAL: The tag contained an invalid number of entries (e.g. 6 entries for
+// a vector of length 4 arrays), or |val| is null.
+// -ENODEV: The tag claims to be non-empty, but the data pointer is null.
+// 0: Success. |*val| will contain the values for |tag|.
+template <typename T>
+static int VectorTagValue(const android::CameraMetadata& metadata,
+ int32_t tag,
+ std::vector<T>* val) {
+ if (!val) {
+ HAL_LOGE("Null pointer passed to VectorTagValue.");
+ return -EINVAL;
+ }
+ camera_metadata_ro_entry_t entry = metadata.find(tag);
+ if (entry.count == 0) {
+ return -ENOENT;
+ }
+ const T* data = nullptr;
+ GetDataPointer(entry, &data);
+ if (data == nullptr) {
+ HAL_LOGE("Metadata tag %d claims to have elements but is empty.", tag);
+ return -ENODEV;
+ }
+ // Copy the data for |tag| into the output vector.
+ *val = std::vector<T>(data, data + entry.count);
+ return 0;
+}
+
+// Specialization for std::array.
+template <typename T, size_t N>
+static int VectorTagValue(const android::CameraMetadata& metadata,
+ int32_t tag,
+ std::vector<std::array<T, N>>* val) {
+ if (!val) {
+ HAL_LOGE("Null pointer passed to VectorTagValue.");
+ return -EINVAL;
+ }
+ camera_metadata_ro_entry_t entry = metadata.find(tag);
+ if (entry.count == 0) {
+ return -ENOENT;
+ }
+ if (entry.count % N != 0) {
+ HAL_LOGE(
+ "Error: expected metadata tag %d to contain a vector of arrays of "
+ "length %d (had %d entries, which is not divisible by %d).",
+ tag,
+ N,
+ entry.count,
+ N);
+ return -EINVAL;
+ }
+ const T* data = nullptr;
+ GetDataPointer(entry, &data);
+ if (data == nullptr) {
+ HAL_LOGE("Metadata tag %d claims to have elements but is empty.", tag);
+ return -ENODEV;
+ }
+ // Copy the data for |tag| into separate arrays for the output vector.
+ size_t num_arrays = entry.count / N;
+ *val = std::vector<std::array<T, N>>(num_arrays);
+ for (size_t i = 0; i < num_arrays; ++i) {
+ for (size_t j = 0; j < N; ++j) {
+ val->at(i)[j] = data[i * N + j];
+ }
+ }
+ return 0;
+}
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_METADATA_METADATA_COMMON_H_
diff --git a/modules/camera/3_4/metadata/metadata_reader.cpp b/modules/camera/3_4/metadata/metadata_reader.cpp
new file mode 100644
index 0000000..fe2ff85
--- /dev/null
+++ b/modules/camera/3_4/metadata/metadata_reader.cpp
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "metadata_reader.h"
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "MetadataReader"
+#include <cutils/log.h>
+#include <system/camera.h>
+
+#include "metadata_common.h"
+
+namespace default_camera_hal {
+
+MetadataReader::MetadataReader(
+ std::unique_ptr<const android::CameraMetadata> metadata)
+ : metadata_(std::move(metadata)) {}
+
+MetadataReader::~MetadataReader() {}
+
+int MetadataReader::Facing(int* facing) const {
+ uint8_t metadata_facing = 0;
+ int res = v4l2_camera_hal::SingleTagValue(
+ *metadata_, ANDROID_LENS_FACING, &metadata_facing);
+ if (res) {
+ ALOGE("%s: Failed to get facing from static metadata.", __func__);
+ return res;
+ }
+
+ switch (metadata_facing) {
+ case (ANDROID_LENS_FACING_FRONT):
+ *facing = CAMERA_FACING_FRONT;
+ break;
+ case (ANDROID_LENS_FACING_BACK):
+ *facing = CAMERA_FACING_BACK;
+ break;
+ case (ANDROID_LENS_FACING_EXTERNAL):
+ *facing = CAMERA_FACING_EXTERNAL;
+ break;
+ default:
+ ALOGE("%s: Invalid facing from static metadata: %d.",
+ __func__,
+ metadata_facing);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int MetadataReader::Orientation(int* orientation) const {
+ int32_t metadata_orientation = 0;
+ int res = v4l2_camera_hal::SingleTagValue(
+ *metadata_, ANDROID_SENSOR_ORIENTATION, &metadata_orientation);
+ if (res) {
+ ALOGE("%s: Failed to get orientation from static metadata.", __func__);
+ return res;
+ }
+
+ // Orientation must be 0, 90, 180, or 270.
+ if (metadata_orientation < 0 || metadata_orientation > 270 ||
+ metadata_orientation % 90 != 0) {
+ ALOGE(
+ "%s: Invalid orientation %d "
+ "(must be a 90-degree increment in [0, 360)).",
+ __func__,
+ metadata_orientation);
+ return -EINVAL;
+ }
+
+ *orientation = static_cast<int>(metadata_orientation);
+ return 0;
+}
+
+int MetadataReader::MaxInputStreams(int32_t* max_input) const {
+ int res = v4l2_camera_hal::SingleTagValue(
+ *metadata_, ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS, max_input);
+ if (res == -ENOENT) {
+ // Not required; default to 0.
+ *max_input = 0;
+ } else if (res) {
+ ALOGE("%s: Failed to get max output streams from static metadata.",
+ __func__);
+ return res;
+ }
+ return 0;
+}
+
+int MetadataReader::MaxOutputStreams(int32_t* max_raw,
+ int32_t* max_non_stalling,
+ int32_t* max_stalling) const {
+ std::array<int32_t, 3> max_output_streams;
+ int res = v4l2_camera_hal::SingleTagValue(
+ *metadata_, ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS, &max_output_streams);
+ if (res) {
+ ALOGE("%s: Failed to get max output streams from static metadata.",
+ __func__);
+ return res;
+ }
+ *max_raw = max_output_streams[0];
+ *max_non_stalling = max_output_streams[1];
+ *max_stalling = max_output_streams[2];
+ return 0;
+}
+
+int MetadataReader::RequestCapabilities(std::set<uint8_t>* capabilities) const {
+ std::vector<uint8_t> raw_capabilities;
+ int res = v4l2_camera_hal::VectorTagValue(
+ *metadata_, ANDROID_REQUEST_AVAILABLE_CAPABILITIES, &raw_capabilities);
+ if (res) {
+ ALOGE("%s: Failed to get request capabilities from static metadata.",
+ __func__);
+ return res;
+ }
+
+ // Move from vector to set.
+ capabilities->insert(raw_capabilities.begin(), raw_capabilities.end());
+ return 0;
+}
+
+int MetadataReader::StreamConfigurations(
+ std::vector<StreamConfiguration>* configs) const {
+ std::vector<RawStreamConfiguration> raw_stream_configs;
+ int res = v4l2_camera_hal::VectorTagValue(
+ *metadata_,
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
+ &raw_stream_configs);
+ if (res) {
+ ALOGE("%s: Failed to get stream configs from static metadata.", __func__);
+ return res;
+ }
+
+ // TODO(b/31384253): check for required configs.
+
+ // Convert from raw.
+ configs->insert(
+ configs->end(), raw_stream_configs.begin(), raw_stream_configs.end());
+
+ // Check that all configs are valid.
+ for (const auto& config : *configs) {
+ // Must have positive dimensions.
+ if (config.spec.width < 1 || config.spec.height < 1) {
+ ALOGE("%s: Invalid stream config: non-positive dimensions (%d, %d).",
+ __func__,
+ config.spec.width,
+ config.spec.height);
+ return -EINVAL;
+ }
+ // Must have a known direction enum.
+ switch (config.direction) {
+ case ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT:
+ case ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT:
+ break;
+ default:
+ ALOGE("%s: Invalid stream config direction: %d.",
+ __func__,
+ config.direction);
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+int MetadataReader::StreamStallDurations(
+ std::vector<StreamStallDuration>* stalls) const {
+ std::vector<RawStreamStallDuration> raw_stream_stall_durations;
+ int res =
+ v4l2_camera_hal::VectorTagValue(*metadata_,
+ ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,
+ &raw_stream_stall_durations);
+ if (res) {
+ ALOGE("%s: Failed to get stall durations from static metadata.", __func__);
+ return res;
+ }
+
+ // Convert from raw.
+ stalls->insert(stalls->end(),
+ raw_stream_stall_durations.begin(),
+ raw_stream_stall_durations.end());
+ // Check that all stalls are valid.
+ for (const auto& stall : *stalls) {
+ // Must have positive dimensions.
+ if (stall.spec.width < 1 || stall.spec.height < 1) {
+ ALOGE("%s: Invalid stall duration: non-positive dimensions (%d, %d).",
+ __func__,
+ stall.spec.width,
+ stall.spec.height);
+ return -EINVAL;
+ }
+ // Must have a non-negative stall.
+ if (stall.duration < 0) {
+ ALOGE("%s: Invalid stall duration: negative stall %d.",
+ __func__,
+ stall.duration);
+ return -EINVAL;
+ }
+ // TODO(b/31384253): YUV_420_888, RAW10, RAW12, RAW_OPAQUE,
+ // and IMPLEMENTATION_DEFINED must have 0 stall duration.
+ }
+
+ return 0;
+}
+
+int MetadataReader::ReprocessFormats(ReprocessFormatMap* reprocess_map) const {
+ std::vector<int32_t> input_output_formats;
+ int res = v4l2_camera_hal::VectorTagValue(
+ *metadata_,
+ ANDROID_SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP,
+ &input_output_formats);
+ if (res) {
+ ALOGE("%s: Failed to get input output format map from static metadata.",
+ __func__);
+ return res;
+ }
+
+ // Convert from the raw vector.
+ for (size_t i = 0; i < input_output_formats.size();) {
+ // The map is represented as variable-length entries of the format
+ // input, num_outputs, <outputs>.
+
+ // Get the input format.
+ int32_t input_format = input_output_formats[i++];
+
+ // Find the output begin and end for this format.
+ int32_t num_output_formats = input_output_formats[i++];
+ if (num_output_formats < 1) {
+ ALOGE(
+ "%s: No output formats for input format %d.", __func__, input_format);
+ return -EINVAL;
+ }
+ size_t outputs_end = i + num_output_formats;
+ if (outputs_end > input_output_formats.size()) {
+ ALOGE("%s: Input format %d requests more data than available.",
+ __func__,
+ input_format);
+ return -EINVAL;
+ }
+
+ // Copy all the output formats into the map.
+ (*reprocess_map)[input_format].insert(
+ input_output_formats.data() + i,
+ input_output_formats.data() + outputs_end);
+
+ // Move on to the next entry.
+ i = outputs_end;
+ }
+
+ // TODO(b/31384253): check for required mappings.
+
+ return 0;
+}
+
+} // namespace default_camera_hal
diff --git a/modules/camera/3_4/metadata/metadata_reader.h b/modules/camera/3_4/metadata/metadata_reader.h
new file mode 100644
index 0000000..996bf8b
--- /dev/null
+++ b/modules/camera/3_4/metadata/metadata_reader.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DEFAULT_CAMERA_HAL_METADATA_METADATA_READER_H_
+#define DEFAULT_CAMERA_HAL_METADATA_METADATA_READER_H_
+
+#include <map>
+#include <memory>
+#include <set>
+#include <vector>
+
+#include <camera/CameraMetadata.h>
+
+#include "../common.h"
+#include "types.h"
+
+namespace default_camera_hal {
+
+// A MetadataReader reads and converts/validates various metadata entries.
+class MetadataReader {
+ public:
+ MetadataReader(std::unique_ptr<const android::CameraMetadata> metadata);
+ virtual ~MetadataReader();
+
+ // Get a pointer to the underlying metadata being read.
+ // The pointer is valid only as long as this object is alive.
+ // The "locking" here only causes non-const methods to fail,
+ // which is not a problem since the CameraMetadata being locked
+ // is already const. This could be a problem if the metadata was
+ // shared more widely, but |metadata_| is a unique_ptr,
+ // guaranteeing the safety of this. Destructing automatically "unlocks".
+ virtual const camera_metadata_t* raw_metadata() const {
+ return metadata_->getAndLock();
+ }
+
+ // All accessor methods must be given a valid pointer. They will return:
+ // 0: Success.
+ // -ENOENT: The necessary entry is missing.
+ // -EINVAL: The entry value is invalid.
+ // -ENODEV: Some other error occured.
+
+ // The |facing| returned will be one of the enum values from system/camera.h.
+ virtual int Facing(int* facing) const;
+ virtual int Orientation(int* orientation) const;
+ virtual int MaxInputStreams(int32_t* max_input_streams) const;
+ virtual int MaxOutputStreams(int32_t* max_raw_output_streams,
+ int32_t* max_non_stalling_output_streams,
+ int32_t* max_stalling_output_streams) const;
+ virtual int RequestCapabilities(std::set<uint8_t>* capabilites) const;
+ virtual int StreamConfigurations(
+ std::vector<StreamConfiguration>* configs) const;
+ virtual int StreamStallDurations(
+ std::vector<StreamStallDuration>* stalls) const;
+ virtual int ReprocessFormats(ReprocessFormatMap* reprocess_map) const;
+
+ private:
+ std::unique_ptr<const android::CameraMetadata> metadata_;
+
+ DISALLOW_COPY_AND_ASSIGN(MetadataReader);
+};
+
+} // namespace default_camera_hal
+
+#endif // DEFAULT_CAMERA_HAL_METADATA_METADATA_READER_H_
diff --git a/modules/camera/3_4/metadata/metadata_reader_mock.h b/modules/camera/3_4/metadata/metadata_reader_mock.h
new file mode 100644
index 0000000..19895a7
--- /dev/null
+++ b/modules/camera/3_4/metadata/metadata_reader_mock.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Mock for metadata readers.
+
+#ifndef DEFAULT_CAMERA_HAL_METADATA_METADATA_READER_MOCK_H_
+#define DEFAULT_CAMERA_HAL_METADATA_METADATA_READER_MOCK_H_
+
+#include <gmock/gmock.h>
+
+#include "metadata_reader.h"
+
+namespace default_camera_hal {
+
+class MetadataReaderMock : public MetadataReader {
+ public:
+ MetadataReaderMock() : MetadataReader(nullptr){};
+ MOCK_CONST_METHOD0(raw_metadata, const camera_metadata_t*());
+ MOCK_CONST_METHOD1(Facing, int(int*));
+ MOCK_CONST_METHOD1(Orientation, int(int*));
+ MOCK_CONST_METHOD1(MaxInputStreams, int(int32_t*));
+ MOCK_CONST_METHOD3(MaxOutputStreams, int(int32_t*, int32_t*, int32_t*));
+ MOCK_CONST_METHOD1(RequestCapabilities, int(std::set<uint8_t>*));
+ MOCK_CONST_METHOD1(StreamConfigurations,
+ int(std::vector<StreamConfiguration>*));
+ MOCK_CONST_METHOD1(StreamStallDurations,
+ int(std::vector<StreamStallDuration>*));
+ MOCK_CONST_METHOD1(ReprocessFormats, int(ReprocessFormatMap*));
+};
+
+} // namespace default_camera_hal
+
+#endif // DEFAULT_CAMERA_HAL_METADATA_METADATA_READER_MOCK_H_
diff --git a/modules/camera/3_4/metadata/metadata_reader_test.cpp b/modules/camera/3_4/metadata/metadata_reader_test.cpp
new file mode 100644
index 0000000..5b9cc63
--- /dev/null
+++ b/modules/camera/3_4/metadata/metadata_reader_test.cpp
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "metadata_reader.h"
+
+#include <camera/CameraMetadata.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <system/camera.h>
+
+#include "array_vector.h"
+#include "metadata_common.h"
+
+using testing::AtMost;
+using testing::Expectation;
+using testing::Return;
+using testing::Test;
+
+namespace default_camera_hal {
+
+class MetadataReaderTest : public Test {
+ protected:
+ void SetUp() {
+ ResetMetadata();
+ // FillDUT should be called before using the device under test.
+ dut_.reset();
+ }
+
+ void ResetMetadata() {
+ metadata_ = std::make_unique<android::CameraMetadata>();
+ }
+
+ void FillDUT() {
+ dut_ = std::make_unique<MetadataReader>(std::move(metadata_));
+ ResetMetadata();
+ }
+
+ std::unique_ptr<MetadataReader> dut_;
+ std::unique_ptr<android::CameraMetadata> metadata_;
+
+ const int32_t facing_tag_ = ANDROID_LENS_FACING;
+ const int32_t orientation_tag_ = ANDROID_SENSOR_ORIENTATION;
+ const int32_t max_inputs_tag_ = ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS;
+ const int32_t max_outputs_tag_ = ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS;
+ const int32_t configs_tag_ = ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS;
+ const int32_t stalls_tag_ = ANDROID_SCALER_AVAILABLE_STALL_DURATIONS;
+ const int32_t reprocess_formats_tag_ =
+ ANDROID_SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP;
+
+ const std::vector<int32_t> valid_orientations_ = {0, 90, 180, 270};
+ // TODO(b/31384253): check for required configs/reprocess formats.
+};
+
+TEST_F(MetadataReaderTest, FacingTranslations) {
+ // Check that the enums are converting properly.
+ std::map<uint8_t, int> translations{
+ {ANDROID_LENS_FACING_FRONT, CAMERA_FACING_FRONT},
+ {ANDROID_LENS_FACING_BACK, CAMERA_FACING_BACK},
+ {ANDROID_LENS_FACING_EXTERNAL, CAMERA_FACING_EXTERNAL}};
+ for (const auto& translation : translations) {
+ ASSERT_EQ(v4l2_camera_hal::UpdateMetadata(
+ metadata_.get(), facing_tag_, translation.first),
+ 0);
+ FillDUT();
+
+ int expected = translation.second;
+ int actual = expected + 1;
+ EXPECT_EQ(dut_->Facing(&actual), 0);
+ EXPECT_EQ(actual, expected);
+ }
+}
+
+TEST_F(MetadataReaderTest, InvalidFacing) {
+ uint8_t invalid = 99;
+ ASSERT_EQ(
+ v4l2_camera_hal::UpdateMetadata(metadata_.get(), facing_tag_, invalid),
+ 0);
+ FillDUT();
+ int actual = 0;
+ EXPECT_EQ(dut_->Facing(&actual), -EINVAL);
+}
+
+TEST_F(MetadataReaderTest, EmptyFacing) {
+ FillDUT();
+ int actual = 0;
+ EXPECT_EQ(dut_->Facing(&actual), -ENOENT);
+}
+
+TEST_F(MetadataReaderTest, ValidOrientations) {
+ for (int32_t orientation : valid_orientations_) {
+ ASSERT_EQ(v4l2_camera_hal::UpdateMetadata(
+ metadata_.get(), orientation_tag_, orientation),
+ 0);
+ FillDUT();
+
+ int actual = orientation + 1;
+ EXPECT_EQ(dut_->Orientation(&actual), 0);
+ EXPECT_EQ(actual, orientation);
+ }
+}
+
+TEST_F(MetadataReaderTest, InvalidOrientations) {
+ // High.
+ for (int32_t orientation : valid_orientations_) {
+ ASSERT_EQ(v4l2_camera_hal::UpdateMetadata(
+ metadata_.get(), orientation_tag_, orientation + 1),
+ 0);
+ FillDUT();
+ int actual = 0;
+ EXPECT_EQ(dut_->Orientation(&actual), -EINVAL);
+ }
+ // Low.
+ for (int32_t orientation : valid_orientations_) {
+ ASSERT_EQ(v4l2_camera_hal::UpdateMetadata(
+ metadata_.get(), orientation_tag_, orientation - 1),
+ 0);
+ FillDUT();
+ int actual = 0;
+ EXPECT_EQ(dut_->Orientation(&actual), -EINVAL);
+ }
+}
+
+TEST_F(MetadataReaderTest, EmptyOrientation) {
+ FillDUT();
+ int actual = 0;
+ EXPECT_EQ(dut_->Orientation(&actual), -ENOENT);
+}
+
+TEST_F(MetadataReaderTest, MaxInputs) {
+ int32_t expected = 12;
+ ASSERT_EQ(v4l2_camera_hal::UpdateMetadata(
+ metadata_.get(), max_inputs_tag_, expected),
+ 0);
+ FillDUT();
+ int32_t actual = expected + 1;
+ ASSERT_EQ(dut_->MaxInputStreams(&actual), 0);
+ EXPECT_EQ(actual, expected);
+}
+
+TEST_F(MetadataReaderTest, EmptyMaxInputs) {
+ FillDUT();
+ // Max inputs is an optional key; if not present the default is 0.
+ int32_t expected = 0;
+ int32_t actual = expected + 1;
+ ASSERT_EQ(dut_->MaxInputStreams(&actual), 0);
+ EXPECT_EQ(actual, expected);
+}
+
+TEST_F(MetadataReaderTest, MaxOutputs) {
+ std::array<int32_t, 3> expected = {{12, 34, 56}};
+ ASSERT_EQ(v4l2_camera_hal::UpdateMetadata(
+ metadata_.get(), max_outputs_tag_, expected),
+ 0);
+ FillDUT();
+ std::array<int32_t, 3> actual;
+ ASSERT_EQ(dut_->MaxOutputStreams(&actual[0], &actual[1], &actual[2]), 0);
+ EXPECT_EQ(actual, expected);
+}
+
+TEST_F(MetadataReaderTest, InvalidMaxOutputs) {
+ // Must be a 3-tuple to be valid.
+ std::array<int32_t, 4> invalid = {{12, 34, 56, 78}};
+ ASSERT_EQ(v4l2_camera_hal::UpdateMetadata(
+ metadata_.get(), max_outputs_tag_, invalid),
+ 0);
+ FillDUT();
+ int32_t actual;
+ // Don't mind the aliasing since we don't care about the value.
+ ASSERT_EQ(dut_->MaxOutputStreams(&actual, &actual, &actual), -EINVAL);
+}
+
+TEST_F(MetadataReaderTest, EmptyMaxOutputs) {
+ FillDUT();
+ int32_t actual;
+ // Don't mind the aliasing since we don't care about the value.
+ ASSERT_EQ(dut_->MaxOutputStreams(&actual, &actual, &actual), -ENOENT);
+}
+
+TEST_F(MetadataReaderTest, StreamConfigurations) {
+ v4l2_camera_hal::ArrayVector<int32_t, 4> configs;
+ std::array<int32_t, 4> config1{
+ {1, 2, 3, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT}};
+ std::array<int32_t, 4> config2{
+ {5, 6, 7, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT}};
+ configs.push_back(config1);
+ configs.push_back(config2);
+ ASSERT_EQ(
+ v4l2_camera_hal::UpdateMetadata(metadata_.get(), configs_tag_, configs),
+ 0);
+ FillDUT();
+ std::vector<StreamConfiguration> actual;
+ ASSERT_EQ(dut_->StreamConfigurations(&actual), 0);
+ ASSERT_EQ(actual.size(), configs.num_arrays());
+ EXPECT_EQ(actual[0].spec.format, config1[0]);
+ EXPECT_EQ(actual[0].spec.width, config1[1]);
+ EXPECT_EQ(actual[0].spec.height, config1[2]);
+ EXPECT_EQ(actual[0].direction, config1[3]);
+ EXPECT_EQ(actual[1].spec.format, config2[0]);
+ EXPECT_EQ(actual[1].spec.width, config2[1]);
+ EXPECT_EQ(actual[1].spec.height, config2[2]);
+ EXPECT_EQ(actual[1].direction, config2[3]);
+}
+
+TEST_F(MetadataReaderTest, InvalidStreamConfigurationDirection) {
+ // -1 is not a valid direction.
+ std::array<int32_t, 4> config{{1, 2, 3, -1}};
+ ASSERT_EQ(
+ v4l2_camera_hal::UpdateMetadata(metadata_.get(), configs_tag_, config),
+ 0);
+ FillDUT();
+ std::vector<StreamConfiguration> actual;
+ ASSERT_EQ(dut_->StreamConfigurations(&actual), -EINVAL);
+}
+
+TEST_F(MetadataReaderTest, InvalidStreamConfigurationSize) {
+ // Both size dimensions must be > 0.
+ std::array<int32_t, 4> config{
+ {1, 2, 0, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT}};
+ ASSERT_EQ(
+ v4l2_camera_hal::UpdateMetadata(metadata_.get(), configs_tag_, config),
+ 0);
+ FillDUT();
+ std::vector<StreamConfiguration> actual;
+ ASSERT_EQ(dut_->StreamConfigurations(&actual), -EINVAL);
+}
+
+TEST_F(MetadataReaderTest, InvalidStreamConfigurationNumElements) {
+ // Should be a multiple of 4.
+ std::array<int32_t, 5> config{
+ {1, 2, 3, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT, 5}};
+ ASSERT_EQ(
+ v4l2_camera_hal::UpdateMetadata(metadata_.get(), configs_tag_, config),
+ 0);
+ FillDUT();
+ std::vector<StreamConfiguration> actual;
+ ASSERT_EQ(dut_->StreamConfigurations(&actual), -EINVAL);
+}
+
+// TODO(b/31384253): Test that failure occurs if
+// required configurations are not present.
+
+TEST_F(MetadataReaderTest, NoStreamConfigurations) {
+ FillDUT();
+ std::vector<StreamConfiguration> actual;
+ ASSERT_EQ(dut_->StreamConfigurations(&actual), -ENOENT);
+}
+
+TEST_F(MetadataReaderTest, StreamStallDurations) {
+ v4l2_camera_hal::ArrayVector<int64_t, 4> stalls;
+ std::array<int64_t, 4> stall1{{1, 2, 3, 4}};
+ std::array<int64_t, 4> stall2{{5, 6, 7, 8}};
+ stalls.push_back(stall1);
+ stalls.push_back(stall2);
+ ASSERT_EQ(
+ v4l2_camera_hal::UpdateMetadata(metadata_.get(), stalls_tag_, stalls), 0);
+ FillDUT();
+ std::vector<StreamStallDuration> actual;
+ ASSERT_EQ(dut_->StreamStallDurations(&actual), 0);
+ ASSERT_EQ(actual.size(), stalls.num_arrays());
+ EXPECT_EQ(actual[0].spec.format, stall1[0]);
+ EXPECT_EQ(actual[0].spec.width, stall1[1]);
+ EXPECT_EQ(actual[0].spec.height, stall1[2]);
+ EXPECT_EQ(actual[0].duration, stall1[3]);
+ EXPECT_EQ(actual[1].spec.format, stall2[0]);
+ EXPECT_EQ(actual[1].spec.width, stall2[1]);
+ EXPECT_EQ(actual[1].spec.height, stall2[2]);
+ EXPECT_EQ(actual[1].duration, stall2[3]);
+}
+
+TEST_F(MetadataReaderTest, InvalidStreamStallDurationDuration) {
+ // -1 is not a valid duration.
+ std::array<int64_t, 4> stall{{1, 2, 3, -1}};
+ ASSERT_EQ(
+ v4l2_camera_hal::UpdateMetadata(metadata_.get(), stalls_tag_, stall), 0);
+ FillDUT();
+ std::vector<StreamStallDuration> actual;
+ ASSERT_EQ(dut_->StreamStallDurations(&actual), -EINVAL);
+}
+
+TEST_F(MetadataReaderTest, InvalidStreamStallDurationSize) {
+ // Both size dimensions must be > 0.
+ std::array<int64_t, 4> stall{{1, 2, 0, 3}};
+ ASSERT_EQ(
+ v4l2_camera_hal::UpdateMetadata(metadata_.get(), stalls_tag_, stall), 0);
+ FillDUT();
+ std::vector<StreamStallDuration> actual;
+ ASSERT_EQ(dut_->StreamStallDurations(&actual), -EINVAL);
+}
+
+TEST_F(MetadataReaderTest, InvalidStreamStallDurationNumElements) {
+ // Should be a multiple of 4.
+ std::array<int64_t, 5> stall{{1, 2, 3, 4, 5}};
+ ASSERT_EQ(
+ v4l2_camera_hal::UpdateMetadata(metadata_.get(), stalls_tag_, stall), 0);
+ FillDUT();
+ std::vector<StreamStallDuration> actual;
+ ASSERT_EQ(dut_->StreamStallDurations(&actual), -EINVAL);
+}
+
+// TODO(b/31384253): Test that failure occurs if
+// YUV_420_888, RAW10, RAW12, RAW_OPAQUE, or IMPLEMENTATION_DEFINED
+// formats have stall durations > 0.
+
+TEST_F(MetadataReaderTest, NoStreamStallDurations) {
+ FillDUT();
+ std::vector<StreamStallDuration> actual;
+ ASSERT_EQ(dut_->StreamStallDurations(&actual), -ENOENT);
+}
+
+TEST_F(MetadataReaderTest, ReprocessFormats) {
+ ReprocessFormatMap expected{{1, {4}}, {2, {5, 6}}, {3, {7, 8, 9}}};
+ std::vector<int32_t> raw;
+ for (const auto& input_outputs : expected) {
+ raw.push_back(input_outputs.first);
+ raw.push_back(input_outputs.second.size());
+ raw.insert(
+ raw.end(), input_outputs.second.begin(), input_outputs.second.end());
+ }
+ ASSERT_EQ(v4l2_camera_hal::UpdateMetadata(
+ metadata_.get(), reprocess_formats_tag_, raw),
+ 0);
+ FillDUT();
+ ReprocessFormatMap actual;
+ ASSERT_EQ(dut_->ReprocessFormats(&actual), 0);
+ EXPECT_EQ(actual, expected);
+}
+
+TEST_F(MetadataReaderTest, ReprocessFormatsNoOutputs) {
+ // 0 indicates that there are 0 output formats for input format 1,
+ // which is not ok.
+ std::vector<int32_t> raw{1, 0};
+ ASSERT_EQ(v4l2_camera_hal::UpdateMetadata(
+ metadata_.get(), reprocess_formats_tag_, raw),
+ 0);
+ FillDUT();
+ ReprocessFormatMap actual;
+ ASSERT_EQ(dut_->ReprocessFormats(&actual), -EINVAL);
+}
+
+TEST_F(MetadataReaderTest, ReprocessFormatsPastEnd) {
+ // 3 indicates that there are 3 output formats for input format 1,
+ // which is not ok since there are only 2 here.
+ std::vector<int32_t> raw{1, 3, 0, 0};
+ ASSERT_EQ(v4l2_camera_hal::UpdateMetadata(
+ metadata_.get(), reprocess_formats_tag_, raw),
+ 0);
+ FillDUT();
+ ReprocessFormatMap actual;
+ ASSERT_EQ(dut_->ReprocessFormats(&actual), -EINVAL);
+}
+
+TEST_F(MetadataReaderTest, EmptyReprocessFormats) {
+ FillDUT();
+ ReprocessFormatMap actual;
+ ASSERT_EQ(dut_->ReprocessFormats(&actual), -ENOENT);
+}
+
+} // namespace default_camera_hal
diff --git a/modules/camera/3_4/metadata/metadata_test.cpp b/modules/camera/3_4/metadata/metadata_test.cpp
new file mode 100644
index 0000000..508884c
--- /dev/null
+++ b/modules/camera/3_4/metadata/metadata_test.cpp
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "metadata.h"
+
+#include <memory>
+#include <set>
+#include <vector>
+
+#include <camera/CameraMetadata.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "metadata_common.h"
+#include "partial_metadata_interface_mock.h"
+
+using testing::AtMost;
+using testing::Return;
+using testing::Test;
+using testing::_;
+
+namespace v4l2_camera_hal {
+
+class MetadataTest : public Test {
+ protected:
+ virtual void SetUp() {
+ // Clear the DUT. AddComponents must be called before using it.
+ dut_.reset();
+
+ component1_.reset(new PartialMetadataInterfaceMock());
+ component2_.reset(new PartialMetadataInterfaceMock());
+ metadata_.reset(new android::CameraMetadata());
+ non_empty_metadata_.reset(new android::CameraMetadata());
+ uint8_t val = 1;
+ non_empty_metadata_->update(ANDROID_COLOR_CORRECTION_MODE, &val, 1);
+ }
+
+ // Once the component mocks have had expectations set,
+ // add them to the device under test.
+ virtual void AddComponents() {
+ // Don't mind moving; Gmock/Gtest fails on leaked mocks unless disabled by
+ // runtime flags.
+ PartialMetadataSet components;
+ components.insert(std::move(component1_));
+ components.insert(std::move(component2_));
+ dut_.reset(new Metadata(std::move(components)));
+ }
+
+ virtual void CompareTags(const std::set<int32_t>& expected,
+ const camera_metadata_entry_t& actual) {
+ ASSERT_EQ(expected.size(), actual.count);
+ for (size_t i = 0; i < actual.count; ++i) {
+ EXPECT_NE(expected.find(actual.data.i32[i]), expected.end());
+ }
+ }
+
+ // Device under test.
+ std::unique_ptr<Metadata> dut_;
+ // Mocks.
+ std::unique_ptr<PartialMetadataInterfaceMock> component1_;
+ std::unique_ptr<PartialMetadataInterfaceMock> component2_;
+ // Metadata.
+ std::unique_ptr<android::CameraMetadata> metadata_;
+ std::unique_ptr<android::CameraMetadata> non_empty_metadata_;
+ // An empty vector to use as necessary.
+ std::vector<int32_t> empty_tags_;
+};
+
+TEST_F(MetadataTest, FillStaticSuccess) {
+ // Should populate all the component static pieces.
+ EXPECT_CALL(*component1_, PopulateStaticFields(_)).WillOnce(Return(0));
+ EXPECT_CALL(*component2_, PopulateStaticFields(_)).WillOnce(Return(0));
+
+ // Should populate the meta keys, by polling each component's keys.
+ std::vector<int32_t> static_tags_1({1, 2});
+ std::vector<int32_t> static_tags_2({3, 4});
+ std::vector<int32_t> control_tags_1({5, 6});
+ std::vector<int32_t> control_tags_2({7, 8});
+ std::vector<int32_t> dynamic_tags_1({9, 10});
+ std::vector<int32_t> dynamic_tags_2({11, 12});
+ EXPECT_CALL(*component1_, StaticTags()).WillOnce(Return(static_tags_1));
+ EXPECT_CALL(*component1_, ControlTags()).WillOnce(Return(control_tags_1));
+ EXPECT_CALL(*component1_, DynamicTags()).WillOnce(Return(dynamic_tags_1));
+ EXPECT_CALL(*component2_, StaticTags()).WillOnce(Return(static_tags_2));
+ EXPECT_CALL(*component2_, ControlTags()).WillOnce(Return(control_tags_2));
+ EXPECT_CALL(*component2_, DynamicTags()).WillOnce(Return(dynamic_tags_2));
+
+ AddComponents();
+ // Should succeed. If it didn't, no reason to continue checking output.
+ ASSERT_EQ(dut_->FillStaticMetadata(metadata_.get()), 0);
+
+ // Meta keys should be filled correctly.
+ // Note: sets are used here, but it is undefined behavior if
+ // the class has multiple componenets reporting overlapping tags.
+
+ // Get the expected tags = combined tags of all components.
+ std::set<int32_t> static_tags(static_tags_1.begin(), static_tags_1.end());
+ static_tags.insert(static_tags_2.begin(), static_tags_2.end());
+ std::set<int32_t> control_tags(control_tags_1.begin(), control_tags_1.end());
+ control_tags.insert(control_tags_2.begin(), control_tags_2.end());
+ std::set<int32_t> dynamic_tags(dynamic_tags_1.begin(), dynamic_tags_1.end());
+ dynamic_tags.insert(dynamic_tags_2.begin(), dynamic_tags_2.end());
+
+ // Static tags includes not only all component static tags, but also
+ // the meta AVAILABLE_*_KEYS (* = [REQUEST, RESULT, CHARACTERISTICS]).
+ static_tags.emplace(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS);
+ static_tags.emplace(ANDROID_REQUEST_AVAILABLE_RESULT_KEYS);
+ static_tags.emplace(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS);
+
+ // Check against what was filled in in the metadata.
+ CompareTags(static_tags,
+ metadata_->find(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS));
+ CompareTags(control_tags,
+ metadata_->find(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS));
+ CompareTags(dynamic_tags,
+ metadata_->find(ANDROID_REQUEST_AVAILABLE_RESULT_KEYS));
+}
+
+TEST_F(MetadataTest, FillStaticFail) {
+ int err = -99;
+ // Order undefined, and may or may not exit early; use AtMost.
+ EXPECT_CALL(*component1_, PopulateStaticFields(_))
+ .Times(AtMost(1))
+ .WillOnce(Return(0));
+ EXPECT_CALL(*component2_, PopulateStaticFields(_)).WillOnce(Return(err));
+
+ // May or may not exit early, may still try to populate meta tags.
+ EXPECT_CALL(*component1_, StaticTags())
+ .Times(AtMost(1))
+ .WillOnce(Return(empty_tags_));
+ EXPECT_CALL(*component1_, ControlTags())
+ .Times(AtMost(1))
+ .WillOnce(Return(empty_tags_));
+ EXPECT_CALL(*component1_, DynamicTags())
+ .Times(AtMost(1))
+ .WillOnce(Return(empty_tags_));
+ EXPECT_CALL(*component2_, StaticTags())
+ .Times(AtMost(1))
+ .WillOnce(Return(empty_tags_));
+ EXPECT_CALL(*component2_, ControlTags())
+ .Times(AtMost(1))
+ .WillOnce(Return(empty_tags_));
+ EXPECT_CALL(*component2_, DynamicTags())
+ .Times(AtMost(1))
+ .WillOnce(Return(empty_tags_));
+
+ AddComponents();
+ // If any component errors, error should be returned
+ EXPECT_EQ(dut_->FillStaticMetadata(metadata_.get()), err);
+}
+
+TEST_F(MetadataTest, FillStaticNull) {
+ AddComponents();
+ EXPECT_EQ(dut_->FillStaticMetadata(nullptr), -EINVAL);
+}
+
+TEST_F(MetadataTest, IsValidSuccess) {
+ // Should check if all the component request values are valid.
+ EXPECT_CALL(*component1_, SupportsRequestValues(_)).WillOnce(Return(true));
+ EXPECT_CALL(*component2_, SupportsRequestValues(_)).WillOnce(Return(true));
+
+ AddComponents();
+ // Should succeed.
+ // Note: getAndLock is a lock against pointer invalidation, not concurrency,
+ // and unlocks on object destruction.
+ EXPECT_TRUE(dut_->IsValidRequest(*non_empty_metadata_));
+}
+
+TEST_F(MetadataTest, IsValidFail) {
+ // Should check if all the component request values are valid.
+ // Order undefined, and may or may not exit early; use AtMost.
+ EXPECT_CALL(*component1_, SupportsRequestValues(_))
+ .Times(AtMost(1))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*component2_, SupportsRequestValues(_)).WillOnce(Return(false));
+
+ AddComponents();
+ // Should fail since one of the components failed.
+ // Note: getAndLock is a lock against pointer invalidation, not concurrency,
+ // and unlocks on object destruction.
+ EXPECT_FALSE(dut_->IsValidRequest(*non_empty_metadata_));
+}
+
+TEST_F(MetadataTest, IsValidEmpty) {
+ // Setting null settings is a special case indicating to use the
+ // previous (valid) settings. As such it is inherently valid.
+ // Should not try to check any components.
+ EXPECT_CALL(*component1_, SupportsRequestValues(_)).Times(0);
+ EXPECT_CALL(*component2_, SupportsRequestValues(_)).Times(0);
+
+ AddComponents();
+ EXPECT_TRUE(dut_->IsValidRequest(*metadata_));
+}
+
+TEST_F(MetadataTest, GetTemplateSuccess) {
+ int template_type = 3;
+
+ // Should check if all the components fill the template successfully.
+ EXPECT_CALL(*component1_, PopulateTemplateRequest(template_type, _))
+ .WillOnce(Return(0));
+ EXPECT_CALL(*component2_, PopulateTemplateRequest(template_type, _))
+ .WillOnce(Return(0));
+
+ AddComponents();
+ // Should succeed.
+ EXPECT_EQ(dut_->GetRequestTemplate(template_type, metadata_.get()), 0);
+}
+
+TEST_F(MetadataTest, GetTemplateFail) {
+ int err = -99;
+ int template_type = 3;
+
+ // Should check if all the components fill the template successfully.
+ // Order undefined, and may or may not exit early; use AtMost.
+ EXPECT_CALL(*component1_, PopulateTemplateRequest(template_type, _))
+ .Times(AtMost(1))
+ .WillOnce(Return(0));
+ EXPECT_CALL(*component2_, PopulateTemplateRequest(template_type, _))
+ .WillOnce(Return(err));
+
+ AddComponents();
+ // Should fail since one of the components failed.
+ EXPECT_EQ(dut_->GetRequestTemplate(template_type, metadata_.get()), err);
+}
+
+TEST_F(MetadataTest, GetTemplateNull) {
+ AddComponents();
+ EXPECT_EQ(dut_->GetRequestTemplate(1, nullptr), -EINVAL);
+}
+
+TEST_F(MetadataTest, GetTemplateInvalid) {
+ int template_type = 99; // Invalid template type.
+
+ AddComponents();
+ // Should fail fast since template type is invalid.
+ EXPECT_EQ(dut_->GetRequestTemplate(template_type, metadata_.get()), -EINVAL);
+}
+
+TEST_F(MetadataTest, SetSettingsSuccess) {
+ // Should check if all the components set successfully.
+ EXPECT_CALL(*component1_, SetRequestValues(_)).WillOnce(Return(0));
+ EXPECT_CALL(*component2_, SetRequestValues(_)).WillOnce(Return(0));
+
+ AddComponents();
+ // Should succeed.
+ // Note: getAndLock is a lock against pointer invalidation, not concurrency,
+ // and unlocks on object destruction.
+ EXPECT_EQ(dut_->SetRequestSettings(*non_empty_metadata_), 0);
+}
+
+TEST_F(MetadataTest, SetSettingsFail) {
+ int err = -99;
+
+ // Should check if all the components set successfully.
+ // Order undefined, and may or may not exit early; use AtMost.
+ EXPECT_CALL(*component1_, SetRequestValues(_))
+ .Times(AtMost(1))
+ .WillOnce(Return(0));
+ EXPECT_CALL(*component2_, SetRequestValues(_)).WillOnce(Return(err));
+
+ AddComponents();
+ // Should fail since one of the components failed.
+ // Note: getAndLock is a lock against pointer invalidation, not concurrency,
+ // and unlocks on object destruction.
+ EXPECT_EQ(dut_->SetRequestSettings(*non_empty_metadata_), err);
+}
+
+TEST_F(MetadataTest, SetSettingsEmpty) {
+ // Setting null settings is a special case indicating to use the
+ // previous settings. Should not try to set any components.
+ EXPECT_CALL(*component1_, SetRequestValues(_)).Times(0);
+ EXPECT_CALL(*component2_, SetRequestValues(_)).Times(0);
+
+ AddComponents();
+ // Should succeed.
+ EXPECT_EQ(dut_->SetRequestSettings(*metadata_), 0);
+}
+
+TEST_F(MetadataTest, FillResultSuccess) {
+ // Should check if all the components fill results successfully.
+ EXPECT_CALL(*component1_, PopulateDynamicFields(_)).WillOnce(Return(0));
+ EXPECT_CALL(*component2_, PopulateDynamicFields(_)).WillOnce(Return(0));
+
+ AddComponents();
+ // Should succeed.
+ EXPECT_EQ(dut_->FillResultMetadata(metadata_.get()), 0);
+}
+
+TEST_F(MetadataTest, FillResultFail) {
+ int err = -99;
+
+ // Should check if all the components fill results successfully.
+ // Order undefined, and may or may not exit early; use AtMost.
+ EXPECT_CALL(*component1_, PopulateDynamicFields(_))
+ .Times(AtMost(1))
+ .WillOnce(Return(0));
+ EXPECT_CALL(*component2_, PopulateDynamicFields(_)).WillOnce(Return(err));
+
+ AddComponents();
+ // Should fail since one of the components failed.
+ EXPECT_EQ(dut_->FillResultMetadata(metadata_.get()), err);
+}
+
+TEST_F(MetadataTest, FillResultNull) {
+ AddComponents();
+ EXPECT_EQ(dut_->FillResultMetadata(nullptr), -EINVAL);
+}
+
+} // namespace v4l2_camera_hal
diff --git a/modules/camera/3_4/metadata/no_effect_control_delegate.h b/modules/camera/3_4/metadata/no_effect_control_delegate.h
new file mode 100644
index 0000000..e1936f1
--- /dev/null
+++ b/modules/camera/3_4/metadata/no_effect_control_delegate.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef V4L2_CAMERA_HAL_METADATA_NO_EFFECT_CONTROL_DELEGATE_H_
+#define V4L2_CAMERA_HAL_METADATA_NO_EFFECT_CONTROL_DELEGATE_H_
+
+#include "control_delegate_interface.h"
+
+namespace v4l2_camera_hal {
+
+// A NoEffectControlDelegate, as the name implies, has no effect.
+// The value can be gotten and set, but it does nothing.
+template <typename T>
+class NoEffectControlDelegate : public ControlDelegateInterface<T> {
+ public:
+ NoEffectControlDelegate(T default_value) : value_(default_value){};
+
+ int GetValue(T* value) override {
+ *value = value_;
+ return 0;
+ };
+ int SetValue(const T& value) override {
+ value_ = value;
+ return 0;
+ };
+
+ private:
+ T value_;
+};
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_METADATA_NO_EFFECT_CONTROL_DELEGATE_H_
diff --git a/modules/camera/3_4/metadata/no_effect_control_delegate_test.cpp b/modules/camera/3_4/metadata/no_effect_control_delegate_test.cpp
new file mode 100644
index 0000000..0a7a24c
--- /dev/null
+++ b/modules/camera/3_4/metadata/no_effect_control_delegate_test.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "no_effect_control_delegate.h"
+
+#include <gtest/gtest.h>
+
+using testing::Test;
+
+namespace v4l2_camera_hal {
+
+TEST(NoEffectControlDelegateTest, DefaultGet) {
+ int32_t value = 12;
+ NoEffectControlDelegate<int32_t> control(value);
+ int32_t actual = 0;
+ ASSERT_EQ(control.GetValue(&actual), 0);
+ EXPECT_EQ(actual, value);
+}
+
+TEST(NoEffectControlDelegateTest, GetAndSet) {
+ int32_t value = 12;
+ NoEffectControlDelegate<int32_t> control(value);
+ int32_t new_value = 13;
+ ASSERT_EQ(control.SetValue(new_value), 0);
+ int32_t actual = 0;
+ ASSERT_EQ(control.GetValue(&actual), 0);
+ EXPECT_EQ(actual, new_value);
+}
+
+} // namespace v4l2_camera_hal
diff --git a/modules/camera/3_4/metadata/partial_metadata_factory.h b/modules/camera/3_4/metadata/partial_metadata_factory.h
new file mode 100644
index 0000000..63bf2f5
--- /dev/null
+++ b/modules/camera/3_4/metadata/partial_metadata_factory.h
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef V4L2_CAMERA_HAL_METADATA_CONTROL_FACTORY_H_
+#define V4L2_CAMERA_HAL_METADATA_CONTROL_FACTORY_H_
+
+#include "../common.h"
+#include "control.h"
+#include "menu_control_options.h"
+#include "no_effect_control_delegate.h"
+#include "ranged_converter.h"
+#include "slider_control_options.h"
+#include "state.h"
+#include "tagged_control_delegate.h"
+#include "tagged_control_options.h"
+#include "v4l2_control_delegate.h"
+
+namespace v4l2_camera_hal {
+
+enum class ControlType { kMenu, kSlider };
+
+// Static functions to create partial metadata. Nullptr is returned on failures.
+
+// FixedState: A state that doesn't change.
+template <typename T>
+static std::unique_ptr<State<T>> FixedState(int32_t tag, T value);
+
+// NoEffectOptionlessControl: A control that accepts any value,
+// and has no effect. A default value is given.
+template <typename T>
+static std::unique_ptr<Control<T>> NoEffectOptionlessControl(
+ int32_t delegate_tag, T default_value);
+
+// NoEffectMenuControl: Some menu options, but they have no effect.
+template <typename T>
+static std::unique_ptr<Control<T>> NoEffectMenuControl(
+ int32_t delegate_tag,
+ int32_t options_tag,
+ const std::vector<T>& options,
+ std::map<int, T> default_values = {});
+
+// NoEffectSliderControl: A slider of options, but they have no effect.
+template <typename T>
+static std::unique_ptr<Control<T>> NoEffectSliderControl(
+ int32_t delegate_tag,
+ int32_t options_tag,
+ T min,
+ T max,
+ std::map<int, T> default_values = {});
+
+// NoEffectControl: A control with no effect and only a single allowable
+// value. Chooses an appropriate ControlOptionsInterface depending on type.
+template <typename T>
+static std::unique_ptr<Control<T>> NoEffectControl(
+ ControlType type,
+ int32_t delegate_tag,
+ int32_t options_tag,
+ T value,
+ std::map<int, T> default_values = {});
+
+// V4L2Control: A control corresponding to a V4L2 control.
+template <typename T>
+static std::unique_ptr<Control<T>> V4L2Control(
+ ControlType type,
+ int32_t delegate_tag,
+ int32_t options_tag,
+ std::shared_ptr<V4L2Wrapper> device,
+ int control_id,
+ std::shared_ptr<ConverterInterface<T, int32_t>> converter,
+ std::map<int, T> default_values = {});
+
+// V4L2ControlOrDefault: Like V4L2Control, but if the V4L2Control fails to
+// initialize for some reason, this method will fall back to NoEffectControl
+// with an initial value defined by |fallback_default|.
+template <typename T>
+static std::unique_ptr<Control<T>> V4L2ControlOrDefault(
+ ControlType type,
+ int32_t delegate_tag,
+ int32_t options_tag,
+ std::shared_ptr<V4L2Wrapper> device,
+ int control_id,
+ std::shared_ptr<ConverterInterface<T, int32_t>> converter,
+ T fallback_default,
+ std::map<int, T> default_values = {});
+
+// -----------------------------------------------------------------------------
+
+template <typename T>
+std::unique_ptr<State<T>> FixedState(int32_t tag, T value) {
+ HAL_LOG_ENTER();
+
+ // Take advantage of ControlDelegate inheriting from StateDelegate;
+ // This will only expose GetValue, not SetValue, so the default will
+ // always be returned.
+ return std::make_unique<State<T>>(
+ tag, std::make_unique<NoEffectControlDelegate<T>>(value));
+}
+
+template <typename T>
+std::unique_ptr<Control<T>> NoEffectOptionlessControl(int32_t delegate_tag,
+ T default_value) {
+ HAL_LOG_ENTER();
+
+ return std::make_unique<Control<T>>(
+ std::make_unique<TaggedControlDelegate<T>>(
+ delegate_tag,
+ std::make_unique<NoEffectControlDelegate<T>>(default_value)),
+ nullptr);
+}
+
+template <typename T>
+std::unique_ptr<Control<T>> NoEffectMenuControl(
+ int32_t delegate_tag,
+ int32_t options_tag,
+ const std::vector<T>& options,
+ std::map<int, T> default_values) {
+ HAL_LOG_ENTER();
+
+ if (options.empty()) {
+ HAL_LOGE("At least one option must be provided.");
+ return nullptr;
+ }
+
+ return std::make_unique<Control<T>>(
+ std::make_unique<TaggedControlDelegate<T>>(
+ delegate_tag,
+ std::make_unique<NoEffectControlDelegate<T>>(options[0])),
+ std::make_unique<TaggedControlOptions<T>>(
+ options_tag,
+ std::make_unique<MenuControlOptions<T>>(options, default_values)));
+}
+
+template <typename T>
+std::unique_ptr<Control<T>> NoEffectSliderControl(
+ int32_t delegate_tag,
+ int32_t options_tag,
+ T min,
+ T max,
+ std::map<int, T> default_values) {
+ HAL_LOG_ENTER();
+
+ return std::make_unique<Control<T>>(
+ std::make_unique<TaggedControlDelegate<T>>(
+ delegate_tag, std::make_unique<NoEffectControlDelegate<T>>(min)),
+ std::make_unique<TaggedControlOptions<T>>(
+ options_tag,
+ std::make_unique<SliderControlOptions<T>>(min, max, default_values)));
+}
+
+template <typename T>
+std::unique_ptr<Control<T>> NoEffectControl(ControlType type,
+ int32_t delegate_tag,
+ int32_t options_tag,
+ T value,
+ std::map<int, T> default_values) {
+ HAL_LOG_ENTER();
+
+ switch (type) {
+ case ControlType::kMenu:
+ return NoEffectMenuControl<T>(
+ delegate_tag, options_tag, {value}, default_values);
+ case ControlType::kSlider:
+ return NoEffectSliderControl(
+ delegate_tag, options_tag, value, value, default_values);
+ }
+}
+
+template <typename T>
+std::unique_ptr<Control<T>> V4L2Control(
+ ControlType type,
+ int32_t delegate_tag,
+ int32_t options_tag,
+ std::shared_ptr<V4L2Wrapper> device,
+ int control_id,
+ std::shared_ptr<ConverterInterface<T, int32_t>> converter,
+ std::map<int, T> default_values) {
+ HAL_LOG_ENTER();
+
+ // Query the device.
+ v4l2_query_ext_ctrl control_query;
+ int res = device->QueryControl(control_id, &control_query);
+ if (res) {
+ HAL_LOGE("Failed to query control %d.", control_id);
+ return nullptr;
+ }
+
+ int32_t control_min = static_cast<int32_t>(control_query.minimum);
+ int32_t control_max = static_cast<int32_t>(control_query.maximum);
+ int32_t control_step = static_cast<int32_t>(control_query.step);
+ if (control_min > control_max) {
+ HAL_LOGE("No acceptable values (min %d is greater than max %d).",
+ control_min,
+ control_max);
+ return nullptr;
+ }
+
+ // Variables needed by the various switch statements.
+ std::vector<T> options;
+ T metadata_val;
+ T metadata_min;
+ T metadata_max;
+ // Set up the result converter and result options based on type.
+ std::shared_ptr<ConverterInterface<T, int32_t>> result_converter(converter);
+ std::unique_ptr<ControlOptionsInterface<T>> result_options;
+ switch (control_query.type) {
+ case V4L2_CTRL_TYPE_BOOLEAN:
+ if (type != ControlType::kMenu) {
+ HAL_LOGE(
+ "V4L2 control %d is of type %d, which isn't compatible with "
+ "desired metadata control type %d",
+ control_id,
+ control_query.type,
+ type);
+ return nullptr;
+ }
+
+ // Convert each available option,
+ // ignoring ones without a known conversion.
+ for (int32_t i = control_min; i <= control_max; i += control_step) {
+ res = converter->V4L2ToMetadata(i, &metadata_val);
+ if (res == -EINVAL) {
+ HAL_LOGV("V4L2 value %d for control %d has no metadata equivalent.",
+ i,
+ control_id);
+ continue;
+ } else if (res) {
+ HAL_LOGE("Error converting value %d for control %d.", i, control_id);
+ return nullptr;
+ }
+ options.push_back(metadata_val);
+ }
+ // Check to make sure there's at least one option.
+ if (options.empty()) {
+ HAL_LOGE("No valid options for control %d.", control_id);
+ return nullptr;
+ }
+
+ result_options.reset(new MenuControlOptions<T>(options, default_values));
+ // No converter changes necessary.
+ break;
+ case V4L2_CTRL_TYPE_INTEGER:
+ if (type != ControlType::kSlider) {
+ HAL_LOGE(
+ "V4L2 control %d is of type %d, which isn't compatible with "
+ "desired metadata control type %d",
+ control_id,
+ control_query.type,
+ type);
+ return nullptr;
+ }
+
+ // Upgrade to a range/step-clamping converter.
+ result_converter.reset(new RangedConverter<T, int32_t>(
+ converter, control_min, control_max, control_step));
+
+ // Convert the min and max.
+ res = result_converter->V4L2ToMetadata(control_min, &metadata_min);
+ if (res) {
+ HAL_LOGE(
+ "Failed to convert V4L2 min value %d for control %d to metadata.",
+ control_min,
+ control_id);
+ return nullptr;
+ }
+ res = result_converter->V4L2ToMetadata(control_max, &metadata_max);
+ if (res) {
+ HAL_LOGE(
+ "Failed to convert V4L2 max value %d for control %d to metadata.",
+ control_max,
+ control_id);
+ return nullptr;
+ }
+ result_options.reset(new SliderControlOptions<T>(
+ metadata_min, metadata_max, default_values));
+ break;
+ default:
+ HAL_LOGE("Control %d (%s) is of unsupported type %d",
+ control_id,
+ control_query.name,
+ control_query.type);
+ return nullptr;
+ }
+
+ // Construct the control.
+ return std::make_unique<Control<T>>(
+ std::make_unique<TaggedControlDelegate<T>>(
+ delegate_tag,
+ std::make_unique<V4L2ControlDelegate<T>>(
+ device, control_id, result_converter)),
+ std::make_unique<TaggedControlOptions<T>>(options_tag,
+ std::move(result_options)));
+}
+
+template <typename T>
+std::unique_ptr<Control<T>> V4L2ControlOrDefault(
+ ControlType type,
+ int32_t delegate_tag,
+ int32_t options_tag,
+ std::shared_ptr<V4L2Wrapper> device,
+ int control_id,
+ std::shared_ptr<ConverterInterface<T, int32_t>> converter,
+ T fallback_default,
+ std::map<int, T> default_values) {
+ HAL_LOG_ENTER();
+
+ std::unique_ptr<Control<T>> result = V4L2Control(type,
+ delegate_tag,
+ options_tag,
+ device,
+ control_id,
+ converter,
+ default_values);
+ if (!result) {
+ result = NoEffectControl(
+ type, delegate_tag, options_tag, fallback_default, default_values);
+ }
+ return result;
+}
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_METADATA_CONTROL_FACTORY_H_
diff --git a/modules/camera/3_4/metadata/partial_metadata_factory_test.cpp b/modules/camera/3_4/metadata/partial_metadata_factory_test.cpp
new file mode 100644
index 0000000..9ca3a38
--- /dev/null
+++ b/modules/camera/3_4/metadata/partial_metadata_factory_test.cpp
@@ -0,0 +1,454 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <camera/CameraMetadata.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "../v4l2_wrapper_mock.h"
+#include "converter_interface_mock.h"
+#include "metadata_common.h"
+#include "partial_metadata_factory.h"
+#include "test_common.h"
+
+using testing::AtMost;
+using testing::Expectation;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::Test;
+using testing::_;
+
+namespace v4l2_camera_hal {
+
+class PartialMetadataFactoryTest : public Test {
+ protected:
+ virtual void SetUp() {
+ mock_device_.reset(new V4L2WrapperMock());
+ mock_converter_.reset(new ConverterInterfaceMock<uint8_t, int32_t>());
+ // Nullify control so an error will be thrown
+ // if a test doesn't construct it.
+ control_.reset();
+ }
+
+ virtual void ExpectControlTags() {
+ ASSERT_EQ(control_->StaticTags().size(), 1);
+ EXPECT_EQ(control_->StaticTags()[0], options_tag_);
+ ASSERT_EQ(control_->ControlTags().size(), 1);
+ EXPECT_EQ(control_->ControlTags()[0], delegate_tag_);
+ ASSERT_EQ(control_->DynamicTags().size(), 1);
+ EXPECT_EQ(control_->DynamicTags()[0], delegate_tag_);
+ }
+
+ virtual void ExpectControlOptions(const std::vector<uint8_t>& options) {
+ // Options should be available.
+ android::CameraMetadata metadata;
+ ASSERT_EQ(control_->PopulateStaticFields(&metadata), 0);
+ EXPECT_EQ(metadata.entryCount(), 1);
+ ExpectMetadataEq(metadata, options_tag_, options);
+ }
+
+ virtual void ExpectControlValue(uint8_t value) {
+ android::CameraMetadata metadata;
+ ASSERT_EQ(control_->PopulateDynamicFields(&metadata), 0);
+ EXPECT_EQ(metadata.entryCount(), 1);
+ ExpectMetadataEq(metadata, delegate_tag_, value);
+ }
+
+ std::unique_ptr<Control<uint8_t>> control_;
+ std::shared_ptr<ConverterInterfaceMock<uint8_t, int32_t>> mock_converter_;
+ std::shared_ptr<V4L2WrapperMock> mock_device_;
+
+ // Need tags that match the data type (uint8_t) being passed.
+ const int32_t delegate_tag_ = ANDROID_COLOR_CORRECTION_ABERRATION_MODE;
+ const int32_t options_tag_ =
+ ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES;
+};
+
+class DISABLED_PartialMetadataFactoryTest : public PartialMetadataFactoryTest {
+};
+
+TEST_F(PartialMetadataFactoryTest, FixedState) {
+ uint8_t value = 13;
+ std::unique_ptr<State<uint8_t>> state = FixedState(delegate_tag_, value);
+
+ ASSERT_EQ(state->StaticTags().size(), 0);
+ ASSERT_EQ(state->ControlTags().size(), 0);
+ ASSERT_EQ(state->DynamicTags().size(), 1);
+ EXPECT_EQ(state->DynamicTags()[0], delegate_tag_);
+
+ android::CameraMetadata metadata;
+ ASSERT_EQ(state->PopulateDynamicFields(&metadata), 0);
+ EXPECT_EQ(metadata.entryCount(), 1);
+ ExpectMetadataEq(metadata, delegate_tag_, value);
+}
+
+TEST_F(PartialMetadataFactoryTest, NoEffectMenu) {
+ std::vector<uint8_t> test_options = {9, 8, 12};
+ control_ =
+ NoEffectMenuControl<uint8_t>(delegate_tag_, options_tag_, test_options);
+ ASSERT_NE(control_, nullptr);
+
+ ExpectControlTags();
+
+ // Options should be available.
+ ExpectControlOptions(test_options);
+ // Default value should be test_options[0].
+ ExpectControlValue(test_options[0]);
+}
+
+TEST_F(PartialMetadataFactoryTest, NoEffectGenericMenu) {
+ uint8_t default_val = 9;
+ control_ = NoEffectControl<uint8_t>(
+ ControlType::kMenu, delegate_tag_, options_tag_, default_val);
+ ASSERT_NE(control_, nullptr);
+
+ ExpectControlTags();
+
+ // Options should be available.
+ ExpectControlOptions({default_val});
+ // |default_val| should be default option.
+ ExpectControlValue(default_val);
+}
+
+TEST_F(PartialMetadataFactoryTest, NoEffectSlider) {
+ std::vector<uint8_t> test_range = {9, 12};
+ control_ = NoEffectSliderControl<uint8_t>(
+ delegate_tag_, options_tag_, test_range[0], test_range[1]);
+ ASSERT_NE(control_, nullptr);
+
+ ExpectControlTags();
+
+ // Single option should be available.
+ ExpectControlOptions(test_range);
+ // Default value should be the minimum (test_range[0]).
+ ExpectControlValue(test_range[0]);
+}
+
+TEST_F(PartialMetadataFactoryTest, NoEffectGenericSlider) {
+ uint8_t default_val = 9;
+ control_ = NoEffectControl<uint8_t>(
+ ControlType::kSlider, delegate_tag_, options_tag_, default_val);
+ ASSERT_NE(control_, nullptr);
+
+ ExpectControlTags();
+
+ // Range containing only |default_val| should be available.
+ ExpectControlOptions({default_val, default_val});
+ // |default_val| should be default option.
+ ExpectControlValue(default_val);
+}
+
+TEST_F(PartialMetadataFactoryTest, V4L2FactoryQueryFail) {
+ int control_id = 55;
+ // Should query the device.
+ EXPECT_CALL(*mock_device_, QueryControl(control_id, _)).WillOnce(Return(-1));
+ control_ = V4L2Control<uint8_t>(ControlType::kMenu,
+ delegate_tag_,
+ options_tag_,
+ mock_device_,
+ control_id,
+ mock_converter_);
+ // Failure, should return null.
+ ASSERT_EQ(control_, nullptr);
+}
+
+TEST_F(PartialMetadataFactoryTest, V4L2FactoryQueryBadType) {
+ int control_id = 55;
+ v4l2_query_ext_ctrl query_result;
+ query_result.type = V4L2_CTRL_TYPE_CTRL_CLASS;
+ // Should query the device.
+ EXPECT_CALL(*mock_device_, QueryControl(control_id, _))
+ .WillOnce(DoAll(SetArgPointee<1>(query_result), Return(0)));
+ control_ = V4L2Control<uint8_t>(ControlType::kMenu,
+ delegate_tag_,
+ options_tag_,
+ mock_device_,
+ control_id,
+ mock_converter_);
+ // Failure, should return null.
+ ASSERT_EQ(control_, nullptr);
+}
+
+TEST_F(PartialMetadataFactoryTest, V4L2FactoryQueryBadRange) {
+ int control_id = 55;
+ v4l2_query_ext_ctrl query_result;
+ query_result.type = V4L2_CTRL_TYPE_MENU;
+ query_result.minimum = 10;
+ query_result.maximum = 1; // Less than minimum.
+ // Should query the device.
+ EXPECT_CALL(*mock_device_, QueryControl(control_id, _))
+ .WillOnce(DoAll(SetArgPointee<1>(query_result), Return(0)));
+ control_ = V4L2Control<uint8_t>(ControlType::kMenu,
+ delegate_tag_,
+ options_tag_,
+ mock_device_,
+ control_id,
+ mock_converter_);
+ // Failure, should return null.
+ ASSERT_EQ(control_, nullptr);
+}
+
+TEST_F(PartialMetadataFactoryTest, V4L2FactoryTypeRequestMenuMismatch) {
+ int control_id = 55;
+ v4l2_query_ext_ctrl query_result;
+ query_result.type = V4L2_CTRL_TYPE_INTEGER;
+ query_result.minimum = 1;
+ query_result.maximum = 7;
+ query_result.step = 2;
+ // Have conversions for values 1-5, by step size 2.
+ std::map<int32_t, uint8_t> conversion_map = {{1, 10}, {3, 30}, {5, 50}};
+
+ // Should query the device.
+ EXPECT_CALL(*mock_device_, QueryControl(control_id, _))
+ .WillOnce(DoAll(SetArgPointee<1>(query_result), Return(0)));
+
+ // If you ask for a Menu, but the V4L2 control is a slider type, that's bad.
+ control_ = V4L2Control<uint8_t>(ControlType::kMenu,
+ delegate_tag_,
+ options_tag_,
+ mock_device_,
+ control_id,
+ mock_converter_);
+ ASSERT_EQ(control_, nullptr);
+}
+
+TEST_F(PartialMetadataFactoryTest, V4L2FactoryTypeRequestSliderMismatch) {
+ int control_id = 55;
+ v4l2_query_ext_ctrl query_result;
+ query_result.type = V4L2_CTRL_TYPE_MENU;
+ query_result.minimum = 1;
+ query_result.maximum = 7;
+ query_result.step = 2;
+ // Have conversions for values 1-5, by step size 2.
+ std::map<int32_t, uint8_t> conversion_map = {{1, 10}, {3, 30}, {5, 50}};
+
+ // Should query the device.
+ EXPECT_CALL(*mock_device_, QueryControl(control_id, _))
+ .WillOnce(DoAll(SetArgPointee<1>(query_result), Return(0)));
+
+ // If you ask for a Slider and get a Menu, that's bad.
+ control_ = V4L2Control<uint8_t>(ControlType::kSlider,
+ delegate_tag_,
+ options_tag_,
+ mock_device_,
+ control_id,
+ mock_converter_);
+ ASSERT_EQ(control_, nullptr);
+}
+
+TEST_F(DISABLED_PartialMetadataFactoryTest, V4L2FactoryMenu) {
+ // TODO(b/30921166): Correct Menu support so this can be re-enabled.
+ int control_id = 55;
+ v4l2_query_ext_ctrl query_result;
+ query_result.type = V4L2_CTRL_TYPE_MENU;
+ query_result.minimum = 1;
+ query_result.maximum = 7;
+ query_result.step = 2;
+ // Have conversions for values 1-5, by step size 2.
+ std::map<int32_t, uint8_t> conversion_map = {{1, 10}, {3, 30}, {5, 50}};
+
+ // Should query the device.
+ EXPECT_CALL(*mock_device_, QueryControl(control_id, _))
+ .WillOnce(DoAll(SetArgPointee<1>(query_result), Return(0)));
+ // Should convert values.
+ std::vector<uint8_t> expected_options;
+ for (auto kv : conversion_map) {
+ EXPECT_CALL(*mock_converter_, V4L2ToMetadata(kv.first, _))
+ .WillOnce(DoAll(SetArgPointee<1>(kv.second), Return(0)));
+ expected_options.push_back(kv.second);
+ }
+ // Will fail to convert 7 with -EINVAL, shouldn't matter.
+ EXPECT_CALL(*mock_converter_, V4L2ToMetadata(7, _)).WillOnce(Return(-EINVAL));
+
+ control_ = V4L2Control<uint8_t>(ControlType::kMenu,
+ delegate_tag_,
+ options_tag_,
+ mock_device_,
+ control_id,
+ mock_converter_);
+ ASSERT_NE(control_, nullptr);
+
+ ExpectControlTags();
+ ExpectControlOptions(expected_options);
+}
+
+TEST_F(DISABLED_PartialMetadataFactoryTest, V4L2FactoryMenuConversionFail) {
+ // TODO(b/30921166): Correct Menu support so this can be re-enabled.
+ int control_id = 55;
+ v4l2_query_ext_ctrl query_result;
+ query_result.type = V4L2_CTRL_TYPE_MENU;
+ query_result.minimum = 1;
+ query_result.maximum = 7;
+ query_result.step = 2;
+
+ // Should query the device.
+ EXPECT_CALL(*mock_device_, QueryControl(control_id, _))
+ .WillOnce(DoAll(SetArgPointee<1>(query_result), Return(0)));
+ // Conversion fails with non-EINVAL error.
+ EXPECT_CALL(*mock_converter_, V4L2ToMetadata(_, _)).WillOnce(Return(-1));
+
+ control_ = V4L2Control<uint8_t>(ControlType::kMenu,
+ delegate_tag_,
+ options_tag_,
+ mock_device_,
+ control_id,
+ mock_converter_);
+ ASSERT_EQ(control_, nullptr);
+}
+
+TEST_F(DISABLED_PartialMetadataFactoryTest, V4L2FactoryMenuNoConversions) {
+ // TODO(b/30921166): Correct Menu support so this can be re-enabled.
+ int control_id = 55;
+ v4l2_query_ext_ctrl query_result;
+ query_result.type = V4L2_CTRL_TYPE_MENU;
+ query_result.minimum = 1;
+ query_result.maximum = 1;
+ query_result.step = 1;
+
+ // Should query the device.
+ EXPECT_CALL(*mock_device_, QueryControl(control_id, _))
+ .WillOnce(DoAll(SetArgPointee<1>(query_result), Return(0)));
+ // Conversion fails with -EINVAL error.
+ EXPECT_CALL(*mock_converter_, V4L2ToMetadata(1, _)).WillOnce(Return(-EINVAL));
+
+ control_ = V4L2Control<uint8_t>(ControlType::kMenu,
+ delegate_tag_,
+ options_tag_,
+ mock_device_,
+ control_id,
+ mock_converter_);
+ // Since there were no convertable options, should fail.
+ ASSERT_EQ(control_, nullptr);
+}
+
+TEST_F(PartialMetadataFactoryTest, V4L2FactoryInteger) {
+ int control_id = 55;
+ v4l2_query_ext_ctrl query_result;
+ query_result.type = V4L2_CTRL_TYPE_INTEGER;
+ query_result.minimum = 1;
+ query_result.maximum = 7;
+ query_result.step = 2;
+ // Have conversions for values 1 & 7.
+ std::map<int32_t, uint8_t> conversion_map = {{1, 10}, {7, 70}};
+
+ // Should query the device.
+ EXPECT_CALL(*mock_device_, QueryControl(control_id, _))
+ .WillOnce(DoAll(SetArgPointee<1>(query_result), Return(0)));
+ // Should convert values.
+ std::vector<uint8_t> expected_options;
+ for (auto kv : conversion_map) {
+ EXPECT_CALL(*mock_converter_, V4L2ToMetadata(kv.first, _))
+ .WillOnce(DoAll(SetArgPointee<1>(kv.second), Return(0)));
+ expected_options.push_back(kv.second);
+ }
+
+ control_ = V4L2Control<uint8_t>(ControlType::kSlider,
+ delegate_tag_,
+ options_tag_,
+ mock_device_,
+ control_id,
+ mock_converter_);
+ ASSERT_NE(control_, nullptr);
+
+ ExpectControlTags();
+ ExpectControlOptions(expected_options);
+
+ // Should be fitting converted values to steps.
+ uint8_t set_val = 10;
+ android::CameraMetadata metadata;
+ EXPECT_EQ(UpdateMetadata(&metadata, delegate_tag_, set_val), 0);
+ EXPECT_CALL(*mock_converter_, MetadataToV4L2(set_val, _))
+ .WillOnce(DoAll(SetArgPointee<1>(4), Return(0)));
+ // When it calls into the device, the 4 returned above should be
+ // rounded down to the step value of 3.
+ EXPECT_CALL(*mock_device_, SetControl(control_id, 3, _)).WillOnce(Return(0));
+ EXPECT_EQ(control_->SetRequestValues(metadata), 0);
+}
+
+TEST_F(PartialMetadataFactoryTest, V4L2FactoryIntegerFailedConversion) {
+ int control_id = 55;
+ v4l2_query_ext_ctrl query_result;
+ query_result.type = V4L2_CTRL_TYPE_INTEGER;
+ query_result.minimum = 1;
+ query_result.maximum = 7;
+ query_result.step = 2;
+
+ // Should query the device.
+ EXPECT_CALL(*mock_device_, QueryControl(control_id, _))
+ .WillOnce(DoAll(SetArgPointee<1>(query_result), Return(0)));
+ // Fail to convert a value. Even -EINVAL is bad in this case.
+ EXPECT_CALL(*mock_converter_, V4L2ToMetadata(1, _)).WillOnce(Return(-EINVAL));
+
+ control_ = V4L2Control<uint8_t>(ControlType::kSlider,
+ delegate_tag_,
+ options_tag_,
+ mock_device_,
+ control_id,
+ mock_converter_);
+ ASSERT_EQ(control_, nullptr);
+}
+
+TEST_F(PartialMetadataFactoryTest, V4L2FallbackMenu) {
+ uint8_t default_val = 9;
+ int control_id = 55;
+
+ // Should query the device.
+ EXPECT_CALL(*mock_device_, QueryControl(control_id, _)).WillOnce(Return(-1));
+
+ // Shouldn't fail, should fall back to menu control.
+ control_ = V4L2ControlOrDefault<uint8_t>(ControlType::kMenu,
+ delegate_tag_,
+ options_tag_,
+ mock_device_,
+ control_id,
+ mock_converter_,
+ default_val);
+ ASSERT_NE(control_, nullptr);
+
+ ExpectControlTags();
+
+ // Options should be available.
+ ExpectControlOptions({default_val});
+ // |default_val| should be default option.
+ ExpectControlValue(default_val);
+}
+
+TEST_F(PartialMetadataFactoryTest, V4L2FallbackSlider) {
+ uint8_t default_val = 9;
+ int control_id = 55;
+
+ // Should query the device.
+ EXPECT_CALL(*mock_device_, QueryControl(control_id, _)).WillOnce(Return(-1));
+
+ // Shouldn't fail, should fall back to slider control.
+ control_ = V4L2ControlOrDefault<uint8_t>(ControlType::kSlider,
+ delegate_tag_,
+ options_tag_,
+ mock_device_,
+ control_id,
+ mock_converter_,
+ default_val);
+ ASSERT_NE(control_, nullptr);
+
+ ExpectControlTags();
+
+ // Range containing only |default_val| should be available.
+ ExpectControlOptions({default_val, default_val});
+ // |default_val| should be default option.
+ ExpectControlValue(default_val);
+}
+
+} // namespace v4l2_camera_hal
diff --git a/modules/camera/3_4/metadata/partial_metadata_interface.h b/modules/camera/3_4/metadata/partial_metadata_interface.h
new file mode 100644
index 0000000..f6e9138
--- /dev/null
+++ b/modules/camera/3_4/metadata/partial_metadata_interface.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef V4L2_CAMERA_HAL_METADATA_PARTIAL_METADATA_INTERFACE_H_
+#define V4L2_CAMERA_HAL_METADATA_PARTIAL_METADATA_INTERFACE_H_
+
+#include <array>
+#include <vector>
+
+#include <camera/CameraMetadata.h>
+
+#include "../common.h"
+#include "array_vector.h"
+
+namespace v4l2_camera_hal {
+
+// A subset of metadata.
+class PartialMetadataInterface {
+ public:
+ virtual ~PartialMetadataInterface(){};
+
+ // The metadata tags this partial metadata is responsible for.
+ // See system/media/camera/docs/docs.html for descriptions of each tag.
+ virtual std::vector<int32_t> StaticTags() const = 0;
+ virtual std::vector<int32_t> ControlTags() const = 0;
+ virtual std::vector<int32_t> DynamicTags() const = 0;
+
+ // Add all the static properties this partial metadata
+ // is responsible for to |metadata|.
+ virtual int PopulateStaticFields(android::CameraMetadata* metadata) const = 0;
+ // Add all the dynamic states this partial metadata
+ // is responsible for to |metadata|.
+ virtual int PopulateDynamicFields(
+ android::CameraMetadata* metadata) const = 0;
+ // Add default request values for a given template type for all the controls
+ // this partial metadata owns.
+ virtual int PopulateTemplateRequest(
+ int template_type, android::CameraMetadata* metadata) const = 0;
+ // Check if the requested control values from |metadata| (for controls
+ // this partial metadata owns) are supported. Empty/null values for owned
+ // control tags indicate no change, and are thus inherently supported.
+ // If |metadata| is empty all controls are implicitly supported.
+ virtual bool SupportsRequestValues(
+ const android::CameraMetadata& metadata) const = 0;
+ // Set all the controls this partial metadata
+ // is responsible for from |metadata|. Empty/null values for owned control
+ // tags indicate no change. If |metadata| is empty no controls should
+ // be changed.
+ virtual int SetRequestValues(const android::CameraMetadata& metadata) = 0;
+};
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_METADATA_PARTIAL_METADATA_INTERFACE_H_
diff --git a/modules/camera/3_4/metadata/partial_metadata_interface_mock.h b/modules/camera/3_4/metadata/partial_metadata_interface_mock.h
new file mode 100644
index 0000000..9e822a1
--- /dev/null
+++ b/modules/camera/3_4/metadata/partial_metadata_interface_mock.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Mock for partial metadata interfaces.
+
+#ifndef V4L2_CAMERA_HAL_PARTIAL_METADATA_INTERFACE_MOCK_H_
+#define V4L2_CAMERA_HAL_PARTIAL_METADATA_INTERFACE_MOCK_H_
+
+#include <gmock/gmock.h>
+
+#include "partial_metadata_interface.h"
+
+namespace v4l2_camera_hal {
+
+class PartialMetadataInterfaceMock : public PartialMetadataInterface {
+ public:
+ PartialMetadataInterfaceMock() : PartialMetadataInterface(){};
+ MOCK_CONST_METHOD0(StaticTags, std::vector<int32_t>());
+ MOCK_CONST_METHOD0(ControlTags, std::vector<int32_t>());
+ MOCK_CONST_METHOD0(DynamicTags, std::vector<int32_t>());
+ MOCK_CONST_METHOD1(PopulateStaticFields, int(android::CameraMetadata*));
+ MOCK_CONST_METHOD1(PopulateDynamicFields, int(android::CameraMetadata*));
+ MOCK_CONST_METHOD2(PopulateTemplateRequest,
+ int(int, android::CameraMetadata*));
+ MOCK_CONST_METHOD1(SupportsRequestValues,
+ bool(const android::CameraMetadata&));
+ MOCK_METHOD1(SetRequestValues, int(const android::CameraMetadata&));
+};
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_PARTIAL_METADATA_INTERFACE_MOCK_H_
diff --git a/modules/camera/3_4/metadata/property.h b/modules/camera/3_4/metadata/property.h
new file mode 100644
index 0000000..6884c7d
--- /dev/null
+++ b/modules/camera/3_4/metadata/property.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef V4L2_CAMERA_HAL_METADATA_PROPERTY_H_
+#define V4L2_CAMERA_HAL_METADATA_PROPERTY_H_
+
+#include "../common.h"
+#include "metadata_common.h"
+#include "partial_metadata_interface.h"
+
+namespace v4l2_camera_hal {
+
+// A Property is a PartialMetadata that only has a single static tag.
+template <typename T>
+class Property : public PartialMetadataInterface {
+ public:
+ Property(int32_t tag, T value) : tag_(tag), value_(std::move(value)){};
+
+ virtual std::vector<int32_t> StaticTags() const override { return {tag_}; };
+
+ virtual std::vector<int32_t> ControlTags() const override { return {}; };
+
+ virtual std::vector<int32_t> DynamicTags() const override { return {}; };
+
+ virtual int PopulateStaticFields(
+ android::CameraMetadata* metadata) const override {
+ return UpdateMetadata(metadata, tag_, value_);
+ };
+
+ virtual int PopulateDynamicFields(
+ android::CameraMetadata* metadata) const override {
+ return 0;
+ };
+
+ virtual int PopulateTemplateRequest(
+ int template_type, android::CameraMetadata* metadata) const override {
+ return 0;
+ };
+
+ virtual bool SupportsRequestValues(
+ const android::CameraMetadata& metadata) const override {
+ return true;
+ };
+
+ virtual int SetRequestValues(
+ const android::CameraMetadata& metadata) override {
+ return 0;
+ };
+
+ private:
+ int32_t tag_;
+ T value_;
+};
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_METADATA_PROPERTY_H_
diff --git a/modules/camera/3_4/metadata/property_test.cpp b/modules/camera/3_4/metadata/property_test.cpp
new file mode 100644
index 0000000..8e947ea
--- /dev/null
+++ b/modules/camera/3_4/metadata/property_test.cpp
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "property.h"
+
+#include <array>
+#include <vector>
+
+#include <camera/CameraMetadata.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hardware/camera3.h>
+
+#include "array_vector.h"
+#include "metadata_common.h"
+#include "test_common.h"
+
+using testing::AtMost;
+using testing::Return;
+using testing::ReturnRef;
+using testing::Test;
+using testing::_;
+
+namespace v4l2_camera_hal {
+
+class PropertyTest : public Test {
+ protected:
+ // Need tags that match the data types being passed.
+ static constexpr int32_t byte_tag_ = ANDROID_CONTROL_SCENE_MODE_OVERRIDES;
+ static constexpr int32_t float_tag_ = ANDROID_COLOR_CORRECTION_GAINS;
+ static constexpr int32_t int_tag_ = ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION;
+ static constexpr int32_t int_tag2_ = ANDROID_JPEG_ORIENTATION;
+};
+
+TEST_F(PropertyTest, Tags) {
+ Property<int32_t> property(int_tag_, 1);
+
+ // Should have only the single tag it was constructed with.
+ EXPECT_EQ(property.ControlTags().size(), 0);
+ EXPECT_EQ(property.DynamicTags().size(), 0);
+ ASSERT_EQ(property.StaticTags().size(), 1);
+ // The macro doesn't like the int_tag_ variable being passed in directly.
+ int32_t expected_tag = int_tag_;
+ EXPECT_EQ(property.StaticTags()[0], expected_tag);
+}
+
+TEST_F(PropertyTest, PopulateStaticSingleNumber) {
+ // Set up a fixed property.
+ int32_t data = 1234;
+ Property<int32_t> property(int_tag_, data);
+
+ // Populate static fields.
+ android::CameraMetadata metadata;
+ ASSERT_EQ(property.PopulateStaticFields(&metadata), 0);
+
+ // Check the results.
+ // Should only have added 1 entry.
+ EXPECT_EQ(metadata.entryCount(), 1);
+ // Should have added the right entry.
+ ExpectMetadataEq(metadata, int_tag_, data);
+}
+
+// TODO(b/30839858): These tests are really testing the metadata_common.h
+// UpdateMetadata methods, and shouldn't be conducted here.
+TEST_F(PropertyTest, PopulateStaticVector) {
+ // Set up a fixed property.
+ std::vector<float> data({0.1, 2.3, 4.5, 6.7});
+ Property<std::vector<float>> property(float_tag_, data);
+
+ // Populate static fields.
+ android::CameraMetadata metadata;
+ ASSERT_EQ(property.PopulateStaticFields(&metadata), 0);
+
+ // Check the results.
+ // Should only have added 1 entry.
+ EXPECT_EQ(metadata.entryCount(), 1);
+ // Should have added the right entry.
+ ExpectMetadataEq(metadata, float_tag_, data);
+}
+
+TEST_F(PropertyTest, PopulateStaticArray) {
+ // Set up a fixed property.
+ std::array<float, 4> data({{0.1, 2.3, 4.5, 6.7}});
+ Property<std::array<float, 4>> property(float_tag_, data);
+
+ // Populate static fields.
+ android::CameraMetadata metadata;
+ ASSERT_EQ(property.PopulateStaticFields(&metadata), 0);
+
+ // Check the results.
+ // Should only have added 1 entry.
+ EXPECT_EQ(metadata.entryCount(), 1);
+ // Should have added the right entry.
+ ExpectMetadataEq(metadata, float_tag_, data);
+}
+
+TEST_F(PropertyTest, PopulateStaticArrayVector) {
+ // Set up a fixed property.
+ ArrayVector<uint8_t, 3> data;
+ data.push_back({{1, 2, 3}});
+ data.push_back({{4, 5, 6}});
+ Property<ArrayVector<uint8_t, 3>> property(byte_tag_, data);
+
+ // Populate static fields.
+ android::CameraMetadata metadata;
+ ASSERT_EQ(property.PopulateStaticFields(&metadata), 0);
+
+ // Check the results.
+ // Should only have added 1 entry.
+ EXPECT_EQ(metadata.entryCount(), 1);
+ // Should have added the right entry.
+ ExpectMetadataEq(metadata, byte_tag_, data);
+}
+
+TEST_F(PropertyTest, PopulateDynamic) {
+ Property<int32_t> property(int_tag_, 1);
+
+ android::CameraMetadata metadata;
+ EXPECT_EQ(property.PopulateDynamicFields(&metadata), 0);
+
+ // Shouldn't have added anything.
+ EXPECT_TRUE(metadata.isEmpty());
+}
+
+TEST_F(PropertyTest, PopulateTemplate) {
+ Property<int32_t> property(int_tag_, 1);
+
+ for (int i = 1; i < CAMERA3_TEMPLATE_COUNT; ++i) {
+ android::CameraMetadata metadata;
+ EXPECT_EQ(property.PopulateTemplateRequest(i, &metadata), 0);
+ // Shouldn't have added anything.
+ EXPECT_TRUE(metadata.isEmpty());
+ }
+}
+
+TEST_F(PropertyTest, SupportsRequest) {
+ Property<int32_t> property(int_tag_, 1);
+ android::CameraMetadata metadata;
+ EXPECT_EQ(property.SupportsRequestValues(metadata), true);
+}
+
+TEST_F(PropertyTest, SetRequest) {
+ Property<int32_t> property(int_tag_, 1);
+ android::CameraMetadata metadata;
+ EXPECT_EQ(property.SetRequestValues(metadata), 0);
+}
+
+} // namespace v4l2_camera_hal
diff --git a/modules/camera/3_4/metadata/ranged_converter.h b/modules/camera/3_4/metadata/ranged_converter.h
new file mode 100644
index 0000000..115ac2a
--- /dev/null
+++ b/modules/camera/3_4/metadata/ranged_converter.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef V4L2_CAMERA_HAL_METADATA_RANGED_CONVERTER_H_
+#define V4L2_CAMERA_HAL_METADATA_RANGED_CONVERTER_H_
+
+#include <memory>
+
+#include "../common.h"
+#include "converter_interface.h"
+
+namespace v4l2_camera_hal {
+
+// An RangedConverter fits values converted by a wrapped converter
+// to a stepped range (when going from metadata -> v4l2. The other
+// direction remains unchanged).
+template <typename TMetadata, typename TV4L2>
+class RangedConverter : public ConverterInterface<TMetadata, TV4L2> {
+ public:
+ RangedConverter(
+ std::shared_ptr<ConverterInterface<TMetadata, TV4L2>> wrapped_converter,
+ TV4L2 min,
+ TV4L2 max,
+ TV4L2 step);
+
+ virtual int MetadataToV4L2(TMetadata value, TV4L2* conversion) override;
+ virtual int V4L2ToMetadata(TV4L2 value, TMetadata* conversion) override;
+
+ private:
+ std::shared_ptr<ConverterInterface<TMetadata, TV4L2>> wrapped_converter_;
+ const TV4L2 min_;
+ const TV4L2 max_;
+ const TV4L2 step_;
+
+ DISALLOW_COPY_AND_ASSIGN(RangedConverter);
+};
+
+// -----------------------------------------------------------------------------
+
+template <typename TMetadata, typename TV4L2>
+RangedConverter<TMetadata, TV4L2>::RangedConverter(
+ std::shared_ptr<ConverterInterface<TMetadata, TV4L2>> wrapped_converter,
+ TV4L2 min,
+ TV4L2 max,
+ TV4L2 step)
+ : wrapped_converter_(std::move(wrapped_converter)),
+ min_(min),
+ max_(max),
+ step_(step) {
+ HAL_LOG_ENTER();
+}
+
+template <typename TMetadata, typename TV4L2>
+int RangedConverter<TMetadata, TV4L2>::MetadataToV4L2(TMetadata value,
+ TV4L2* conversion) {
+ HAL_LOG_ENTER();
+
+ TV4L2 raw_conversion = 0;
+ int res = wrapped_converter_->MetadataToV4L2(value, &raw_conversion);
+ if (res) {
+ HAL_LOGE("Failed to perform underlying conversion.");
+ return res;
+ }
+
+ // Round down to step (steps start at min_).
+ raw_conversion -= (raw_conversion - min_) % step_;
+
+ // Clamp to range.
+ if (raw_conversion < min_) {
+ raw_conversion = min_;
+ } else if (raw_conversion > max_) {
+ raw_conversion = max_;
+ }
+
+ *conversion = raw_conversion;
+ return 0;
+}
+
+template <typename TMetadata, typename TV4L2>
+int RangedConverter<TMetadata, TV4L2>::V4L2ToMetadata(TV4L2 value,
+ TMetadata* conversion) {
+ HAL_LOG_ENTER();
+
+ return wrapped_converter_->V4L2ToMetadata(value, conversion);
+}
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_METADATA_RANGED_CONVERTER_H_
diff --git a/modules/camera/3_4/metadata/ranged_converter_test.cpp b/modules/camera/3_4/metadata/ranged_converter_test.cpp
new file mode 100644
index 0000000..2b5ccc6
--- /dev/null
+++ b/modules/camera/3_4/metadata/ranged_converter_test.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ranged_converter.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "converter_interface_mock.h"
+
+using testing::Return;
+using testing::SetArgPointee;
+using testing::Test;
+using testing::_;
+
+namespace v4l2_camera_hal {
+
+class RangedConverterTest : public Test {
+ protected:
+ virtual void SetUp() {
+ converter_.reset(new ConverterInterfaceMock<int, int32_t>());
+ dut_.reset(
+ new RangedConverter<int, int32_t>(converter_, min_, max_, step_));
+ }
+
+ virtual void ExpectConvert(int32_t converted, int32_t expected) {
+ int initial = 99;
+ EXPECT_CALL(*converter_, MetadataToV4L2(initial, _))
+ .WillOnce(DoAll(SetArgPointee<1>(converted), Return(0)));
+
+ int32_t actual = expected + 1; // Initialize to non-expected value.
+ ASSERT_EQ(dut_->MetadataToV4L2(initial, &actual), 0);
+ EXPECT_EQ(actual, expected);
+ }
+
+ std::shared_ptr<ConverterInterfaceMock<int, int32_t>> converter_;
+ std::unique_ptr<RangedConverter<int, int32_t>> dut_;
+
+ const int32_t min_ = -11;
+ const int32_t max_ = 10;
+ const int32_t step_ = 3;
+};
+
+TEST_F(RangedConverterTest, NormalConversion) {
+ // A value that's in range and on step.
+ ExpectConvert(max_ - step_, max_ - step_);
+}
+
+TEST_F(RangedConverterTest, RoundingConversion) {
+ // A value that's in range but off step.
+ ExpectConvert(max_ - step_ + 1, max_ - step_);
+}
+
+TEST_F(RangedConverterTest, ClampUpConversion) {
+ // A value that's below range.
+ ExpectConvert(min_ - 1, min_);
+}
+
+TEST_F(RangedConverterTest, ClampDownConversion) {
+ // A value that's above range (even after fitting to step).
+ ExpectConvert(max_ + step_, max_);
+}
+
+TEST_F(RangedConverterTest, ConversionError) {
+ int initial = 99;
+ int err = -99;
+ EXPECT_CALL(*converter_, MetadataToV4L2(initial, _)).WillOnce(Return(err));
+
+ int32_t unused;
+ EXPECT_EQ(dut_->MetadataToV4L2(initial, &unused), err);
+}
+
+} // namespace v4l2_camera_hal
diff --git a/modules/camera/3_4/metadata/scaling_converter.h b/modules/camera/3_4/metadata/scaling_converter.h
new file mode 100644
index 0000000..3087167
--- /dev/null
+++ b/modules/camera/3_4/metadata/scaling_converter.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef V4L2_CAMERA_HAL_METADATA_SCALING_CONVERTER_H_
+#define V4L2_CAMERA_HAL_METADATA_SCALING_CONVERTER_H_
+
+#include "../common.h"
+#include "converter_interface.h"
+
+namespace v4l2_camera_hal {
+
+// An ScalingConverter scales values up or down.
+template <typename TMetadata, typename TV4L2>
+class ScalingConverter : public ConverterInterface<TMetadata, TV4L2> {
+ public:
+ ScalingConverter(TMetadata v4l2_to_metadata_numerator,
+ TMetadata v4l2_to_metadata_denominator);
+
+ virtual int MetadataToV4L2(TMetadata value, TV4L2* conversion) override;
+ virtual int V4L2ToMetadata(TV4L2 value, TMetadata* conversion) override;
+
+ private:
+ const TMetadata v4l2_to_metadata_numerator_;
+ const TMetadata v4l2_to_metadata_denominator_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScalingConverter);
+};
+
+// -----------------------------------------------------------------------------
+
+template <typename TMetadata, typename TV4L2>
+ScalingConverter<TMetadata, TV4L2>::ScalingConverter(
+ TMetadata v4l2_to_metadata_numerator,
+ TMetadata v4l2_to_metadata_denominator)
+ : v4l2_to_metadata_numerator_(v4l2_to_metadata_numerator),
+ v4l2_to_metadata_denominator_(v4l2_to_metadata_denominator) {
+ HAL_LOG_ENTER();
+}
+
+template <typename TMetadata, typename TV4L2>
+int ScalingConverter<TMetadata, TV4L2>::MetadataToV4L2(TMetadata value,
+ TV4L2* conversion) {
+ HAL_LOG_ENTER();
+
+ *conversion = static_cast<TV4L2>(value * v4l2_to_metadata_denominator_ /
+ v4l2_to_metadata_numerator_);
+ return 0;
+}
+
+template <typename TMetadata, typename TV4L2>
+int ScalingConverter<TMetadata, TV4L2>::V4L2ToMetadata(TV4L2 value,
+ TMetadata* conversion) {
+ HAL_LOG_ENTER();
+
+ *conversion = static_cast<TMetadata>(value) * v4l2_to_metadata_numerator_ /
+ v4l2_to_metadata_denominator_;
+ return 0;
+}
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_METADATA_SCALING_CONVERTER_H_
diff --git a/modules/camera/3_4/metadata/slider_control_options.h b/modules/camera/3_4/metadata/slider_control_options.h
new file mode 100644
index 0000000..88c1651
--- /dev/null
+++ b/modules/camera/3_4/metadata/slider_control_options.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef V4L2_CAMERA_HAL_METADATA_SLIDER_CONTROL_OPTIONS_H_
+#define V4L2_CAMERA_HAL_METADATA_SLIDER_CONTROL_OPTIONS_H_
+
+#include <errno.h>
+
+#include <vector>
+
+#include "../common.h"
+#include "control_options_interface.h"
+#include "default_option_delegate.h"
+
+namespace v4l2_camera_hal {
+
+// SliderControlOptions offer a range of acceptable values, inclusive.
+template <typename T>
+class SliderControlOptions : public ControlOptionsInterface<T> {
+ public:
+ // |min| must be <= |max|.
+ SliderControlOptions(const T& min,
+ const T& max,
+ std::shared_ptr<DefaultOptionDelegate<T>> defaults)
+ : min_(min), max_(max), defaults_(defaults){};
+ SliderControlOptions(const T& min, const T& max, std::map<int, T> defaults)
+ : min_(min),
+ max_(max),
+ defaults_(std::make_shared<DefaultOptionDelegate<T>>(defaults)){};
+
+ virtual std::vector<T> MetadataRepresentation() override {
+ return {min_, max_};
+ };
+ virtual bool IsSupported(const T& option) override {
+ return option >= min_ && option <= max_;
+ };
+ virtual int DefaultValueForTemplate(int template_type,
+ T* default_value) override {
+ if (min_ > max_) {
+ HAL_LOGE("No valid default slider option, min is greater than max.");
+ return -ENODEV;
+ }
+
+ if (defaults_->DefaultValueForTemplate(template_type, default_value)) {
+ // Get as close as we can to the desired value.
+ if (*default_value < min_) {
+ *default_value = min_;
+ } else if (*default_value > max_) {
+ *default_value = max_;
+ }
+ return 0;
+ }
+
+ // No default given, just fall back to the min of the range.
+ *default_value = min_;
+ return 0;
+ };
+
+ private:
+ T min_;
+ T max_;
+ std::shared_ptr<DefaultOptionDelegate<T>> defaults_;
+};
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_METADATA_SLIDER_CONTROL_OPTIONS_H_
diff --git a/modules/camera/3_4/metadata/slider_control_options_test.cpp b/modules/camera/3_4/metadata/slider_control_options_test.cpp
new file mode 100644
index 0000000..b7cef5a
--- /dev/null
+++ b/modules/camera/3_4/metadata/slider_control_options_test.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "slider_control_options.h"
+
+#include <memory>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hardware/camera3.h>
+
+#include "default_option_delegate_mock.h"
+
+using testing::Return;
+using testing::SetArgPointee;
+using testing::Test;
+using testing::_;
+
+namespace v4l2_camera_hal {
+
+class SliderControlOptionsTest : public Test {
+ protected:
+ virtual void SetUp() {
+ mock_defaults_.reset(new DefaultOptionDelegateMock<int>());
+ dut_.reset(new SliderControlOptions<int>(min_, max_, mock_defaults_));
+ }
+
+ std::unique_ptr<SliderControlOptions<int>> dut_;
+ std::shared_ptr<DefaultOptionDelegateMock<int>> mock_defaults_;
+ const int min_ = 1;
+ const int max_ = 10;
+};
+
+TEST_F(SliderControlOptionsTest, MetadataRepresentation) {
+ // Technically order doesn't matter, but this is faster to write,
+ // and still passes.
+ std::vector<int> expected{min_, max_};
+ EXPECT_EQ(dut_->MetadataRepresentation(), expected);
+}
+
+TEST_F(SliderControlOptionsTest, IsSupported) {
+ for (int i = min_; i <= max_; ++i) {
+ EXPECT_TRUE(dut_->IsSupported(i));
+ }
+ // Out of range unsupported.
+ EXPECT_FALSE(dut_->IsSupported(min_ - 1));
+ EXPECT_FALSE(dut_->IsSupported(max_ + 1));
+}
+
+TEST_F(SliderControlOptionsTest, DelegateDefaultValue) {
+ int template_index = 3;
+ int expected = max_ - 1;
+ ASSERT_TRUE(dut_->IsSupported(expected));
+ EXPECT_CALL(*mock_defaults_, DefaultValueForTemplate(template_index, _))
+ .WillOnce(DoAll(SetArgPointee<1>(expected), Return(true)));
+ int actual = expected - 1;
+ EXPECT_EQ(dut_->DefaultValueForTemplate(template_index, &actual), 0);
+ EXPECT_EQ(actual, expected);
+}
+
+TEST_F(SliderControlOptionsTest, LowDelegateDefaultValue) {
+ int template_index = 3;
+ // min - 1 is below the valid range.
+ int default_val = min_ - 1;
+ // Should get bumped up into range.
+ int expected = min_;
+ ASSERT_FALSE(dut_->IsSupported(default_val));
+ ASSERT_TRUE(dut_->IsSupported(expected));
+
+ EXPECT_CALL(*mock_defaults_, DefaultValueForTemplate(template_index, _))
+ .WillOnce(DoAll(SetArgPointee<1>(default_val), Return(true)));
+ int actual = default_val;
+ EXPECT_EQ(dut_->DefaultValueForTemplate(template_index, &actual), 0);
+ EXPECT_EQ(actual, expected);
+}
+
+TEST_F(SliderControlOptionsTest, HighDelegateDefaultValue) {
+ int template_index = 3;
+ // max + 1 is above the valid range.
+ int default_val = max_ + 1;
+ // Should get bumped down into range.
+ int expected = max_;
+ ASSERT_FALSE(dut_->IsSupported(default_val));
+ ASSERT_TRUE(dut_->IsSupported(expected));
+
+ EXPECT_CALL(*mock_defaults_, DefaultValueForTemplate(template_index, _))
+ .WillOnce(DoAll(SetArgPointee<1>(default_val), Return(true)));
+ int actual = default_val;
+ EXPECT_EQ(dut_->DefaultValueForTemplate(template_index, &actual), 0);
+ EXPECT_EQ(actual, expected);
+}
+
+TEST_F(SliderControlOptionsTest, NoDelegateDefaultValue) {
+ int template_index = 3;
+ int actual = min_ - 1;
+ ASSERT_FALSE(dut_->IsSupported(actual));
+
+ // Have delegate error.
+ EXPECT_CALL(*mock_defaults_, DefaultValueForTemplate(template_index, _))
+ .WillOnce(Return(false));
+
+ // Should still give *some* supported value.
+ EXPECT_EQ(dut_->DefaultValueForTemplate(template_index, &actual), 0);
+ EXPECT_TRUE(dut_->IsSupported(actual));
+}
+
+TEST_F(SliderControlOptionsTest, NoDefaultValue) {
+ // Invalid options don't have a valid default.
+ SliderControlOptions<int> bad_options(10, 9, mock_defaults_); // min > max.
+ for (int i = 1; i < CAMERA3_TEMPLATE_COUNT; ++i) {
+ int value = -1;
+ EXPECT_EQ(bad_options.DefaultValueForTemplate(i, &value), -ENODEV);
+ }
+}
+
+} // namespace v4l2_camera_hal
diff --git a/modules/camera/3_4/metadata/state.h b/modules/camera/3_4/metadata/state.h
new file mode 100644
index 0000000..54f66e4
--- /dev/null
+++ b/modules/camera/3_4/metadata/state.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef V4L2_CAMERA_HAL_METADATA_STATE_H_
+#define V4L2_CAMERA_HAL_METADATA_STATE_H_
+
+#include "../common.h"
+#include "metadata_common.h"
+#include "partial_metadata_interface.h"
+#include "state_delegate_interface.h"
+
+namespace v4l2_camera_hal {
+
+// A State is a PartialMetadata that only has a single dynamic value.
+template <typename T>
+class State : public PartialMetadataInterface {
+ public:
+ State(int32_t tag, std::unique_ptr<StateDelegateInterface<T>> delegate)
+ : tag_(tag), delegate_(std::move(delegate)){};
+
+ virtual std::vector<int32_t> StaticTags() const override { return {}; };
+ virtual std::vector<int32_t> ControlTags() const override { return {}; };
+ virtual std::vector<int32_t> DynamicTags() const override { return {tag_}; };
+
+ virtual int PopulateStaticFields(
+ android::CameraMetadata* metadata) const override;
+ virtual int PopulateDynamicFields(
+ android::CameraMetadata* metadata) const override;
+ virtual int PopulateTemplateRequest(
+ int template_type, android::CameraMetadata* metadata) const override;
+ virtual bool SupportsRequestValues(
+ const android::CameraMetadata& metadata) const override;
+ virtual int SetRequestValues(
+ const android::CameraMetadata& metadata) override;
+
+ private:
+ int32_t tag_;
+ std::unique_ptr<StateDelegateInterface<T>> delegate_;
+};
+
+// -----------------------------------------------------------------------------
+
+template <typename T>
+int State<T>::PopulateStaticFields(android::CameraMetadata* metadata) const {
+ HAL_LOG_ENTER();
+ return 0;
+}
+
+template <typename T>
+int State<T>::PopulateDynamicFields(android::CameraMetadata* metadata) const {
+ HAL_LOG_ENTER();
+
+ T value;
+ int res = delegate_->GetValue(&value);
+ if (res) {
+ return res;
+ }
+ return UpdateMetadata(metadata, tag_, value);
+};
+
+template <typename T>
+int State<T>::PopulateTemplateRequest(int template_type,
+ android::CameraMetadata* metadata) const {
+ HAL_LOG_ENTER();
+ return 0;
+};
+
+template <typename T>
+bool State<T>::SupportsRequestValues(
+ const android::CameraMetadata& metadata) const {
+ HAL_LOG_ENTER();
+ return true;
+};
+
+template <typename T>
+int State<T>::SetRequestValues(const android::CameraMetadata& metadata) {
+ HAL_LOG_ENTER();
+ return 0;
+};
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_METADATA_STATE_H_
diff --git a/modules/camera/3_4/metadata/state_delegate_interface.h b/modules/camera/3_4/metadata/state_delegate_interface.h
new file mode 100644
index 0000000..c18ee3c
--- /dev/null
+++ b/modules/camera/3_4/metadata/state_delegate_interface.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef V4L2_CAMERA_HAL_METADATA_STATE_DELEGATE_INTERFACE_H_
+#define V4L2_CAMERA_HAL_METADATA_STATE_DELEGATE_INTERFACE_H_
+
+namespace v4l2_camera_hal {
+
+// A StateDelegate is simply a dynamic value that can be queried.
+// The value may change between queries.
+template <typename T>
+class StateDelegateInterface {
+ public:
+ virtual ~StateDelegateInterface(){};
+ // Returns 0 on success, error code on failure.
+ virtual int GetValue(T* value) = 0;
+};
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_METADATA_STATE_DELEGATE_INTERFACE_H_
diff --git a/modules/camera/3_4/metadata/state_delegate_interface_mock.h b/modules/camera/3_4/metadata/state_delegate_interface_mock.h
new file mode 100644
index 0000000..5064b83
--- /dev/null
+++ b/modules/camera/3_4/metadata/state_delegate_interface_mock.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Mock for state delegate interfaces.
+
+#ifndef V4L2_CAMERA_HAL_METADATA_STATE_DELEGATE_INTERFACE_MOCK_H_
+#define V4L2_CAMERA_HAL_METADATA_STATE_DELEGATE_INTERFACE_MOCK_H_
+
+#include <gmock/gmock.h>
+
+#include "state_delegate_interface.h"
+
+namespace v4l2_camera_hal {
+
+template <typename T>
+class StateDelegateInterfaceMock : public StateDelegateInterface<T> {
+ public:
+ StateDelegateInterfaceMock(){};
+ MOCK_METHOD1_T(GetValue, int(T*));
+};
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_METADATA_CONTROL_DELEGATE_INTERFACE_MOCK_H_
diff --git a/modules/camera/3_4/metadata/state_test.cpp b/modules/camera/3_4/metadata/state_test.cpp
new file mode 100644
index 0000000..5c308bc
--- /dev/null
+++ b/modules/camera/3_4/metadata/state_test.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "state.h"
+
+#include <camera/CameraMetadata.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "metadata_common.h"
+#include "state_delegate_interface_mock.h"
+#include "test_common.h"
+
+using testing::AtMost;
+using testing::Expectation;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::Test;
+using testing::_;
+
+namespace v4l2_camera_hal {
+
+class StateTest : public Test {
+ protected:
+ virtual void SetUp() {
+ mock_delegate_.reset(new StateDelegateInterfaceMock<uint8_t>());
+ // Nullify state so an error will be thrown if a test doesn't call
+ // PrepareState.
+ state_.reset();
+ }
+
+ virtual void PrepareState() {
+ // Use this method after all the EXPECT_CALLs to pass ownership of the mocks
+ // to the device.
+ state_.reset(new State<uint8_t>(tag_, std::move(mock_delegate_)));
+ }
+
+ std::unique_ptr<State<uint8_t>> state_;
+ std::unique_ptr<StateDelegateInterfaceMock<uint8_t>> mock_delegate_;
+
+ // Need tag that matches the data type (uint8_t) being passed.
+ const int32_t tag_ = ANDROID_CONTROL_AF_STATE;
+};
+
+TEST_F(StateTest, Tags) {
+ PrepareState();
+ EXPECT_TRUE(state_->StaticTags().empty());
+ EXPECT_TRUE(state_->ControlTags().empty());
+ ASSERT_EQ(state_->DynamicTags().size(), 1);
+ EXPECT_EQ(state_->DynamicTags()[0], tag_);
+}
+
+TEST_F(StateTest, PopulateStatic) {
+ PrepareState();
+ android::CameraMetadata metadata;
+ ASSERT_EQ(state_->PopulateStaticFields(&metadata), 0);
+ EXPECT_TRUE(metadata.isEmpty());
+}
+
+TEST_F(StateTest, PopulateDynamic) {
+ uint8_t expected = 99;
+ EXPECT_CALL(*mock_delegate_, GetValue(_))
+ .WillOnce(DoAll(SetArgPointee<0>(expected), Return(0)));
+
+ PrepareState();
+
+ android::CameraMetadata metadata;
+ ASSERT_EQ(state_->PopulateDynamicFields(&metadata), 0);
+ EXPECT_EQ(metadata.entryCount(), 1);
+ ExpectMetadataEq(metadata, tag_, expected);
+}
+
+TEST_F(StateTest, PopulateDynamicFail) {
+ int err = 123;
+ EXPECT_CALL(*mock_delegate_, GetValue(_)).WillOnce(Return(err));
+
+ PrepareState();
+
+ android::CameraMetadata metadata;
+ ASSERT_EQ(state_->PopulateDynamicFields(&metadata), err);
+}
+
+TEST_F(StateTest, PopulateTemplate) {
+ int template_type = 3;
+ PrepareState();
+ android::CameraMetadata metadata;
+ ASSERT_EQ(state_->PopulateTemplateRequest(template_type, &metadata), 0);
+ EXPECT_TRUE(metadata.isEmpty());
+}
+
+TEST_F(StateTest, SupportsRequest) {
+ PrepareState();
+ android::CameraMetadata metadata;
+ EXPECT_TRUE(state_->SupportsRequestValues(metadata));
+}
+
+TEST_F(StateTest, SetRequest) {
+ PrepareState();
+ android::CameraMetadata metadata;
+ ASSERT_EQ(state_->SetRequestValues(metadata), 0);
+}
+
+} // namespace v4l2_camera_hal
diff --git a/modules/camera/3_4/metadata/tagged_control_delegate.h b/modules/camera/3_4/metadata/tagged_control_delegate.h
new file mode 100644
index 0000000..40677f9
--- /dev/null
+++ b/modules/camera/3_4/metadata/tagged_control_delegate.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef V4L2_CAMERA_HAL_METADATA_TAGGED_CONTROL_DELEGATE_H_
+#define V4L2_CAMERA_HAL_METADATA_TAGGED_CONTROL_DELEGATE_H_
+
+#include <memory>
+
+#include "control_delegate_interface.h"
+
+namespace v4l2_camera_hal {
+
+// A TaggedControlDelegate wraps a ControlDelegate and adds a tag.
+template <typename T>
+class TaggedControlDelegate : public ControlDelegateInterface<T> {
+ public:
+ TaggedControlDelegate(int32_t tag,
+ std::unique_ptr<ControlDelegateInterface<T>> delegate)
+ : tag_(tag), delegate_(std::move(delegate)){};
+
+ int32_t tag() { return tag_; };
+
+ virtual int GetValue(T* value) override {
+ return delegate_->GetValue(value);
+ };
+ virtual int SetValue(const T& value) override {
+ return delegate_->SetValue(value);
+ };
+
+ private:
+ const int32_t tag_;
+ std::unique_ptr<ControlDelegateInterface<T>> delegate_;
+};
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_METADATA_CONTROL_DELEGATE_INTERFACE_H_
diff --git a/modules/camera/3_4/metadata/tagged_control_delegate_test.cpp b/modules/camera/3_4/metadata/tagged_control_delegate_test.cpp
new file mode 100644
index 0000000..ba29ab7
--- /dev/null
+++ b/modules/camera/3_4/metadata/tagged_control_delegate_test.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "tagged_control_delegate.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "control_delegate_interface_mock.h"
+
+using testing::Return;
+using testing::SetArgPointee;
+using testing::Test;
+using testing::_;
+
+namespace v4l2_camera_hal {
+
+class TaggedControlDelegateTest : public Test {
+ protected:
+ virtual void SetUp() {
+ mock_delegate_.reset(new ControlDelegateInterfaceMock<uint8_t>());
+ // Nullify dut so an error will be thrown if a test doesn't call PrepareDUT.
+ dut_.reset();
+ }
+
+ virtual void PrepareDUT() {
+ // Use this method after all the EXPECT_CALLs to pass ownership of the
+ // delegate
+ // to the device.
+ dut_.reset(
+ new TaggedControlDelegate<uint8_t>(tag_, std::move(mock_delegate_)));
+ }
+
+ std::unique_ptr<TaggedControlDelegate<uint8_t>> dut_;
+ std::unique_ptr<ControlDelegateInterfaceMock<uint8_t>> mock_delegate_;
+ const int32_t tag_ = 123;
+};
+
+TEST_F(TaggedControlDelegateTest, GetTag) {
+ PrepareDUT();
+ EXPECT_EQ(dut_->tag(), tag_);
+}
+
+TEST_F(TaggedControlDelegateTest, GetSuccess) {
+ uint8_t expected = 3;
+ EXPECT_CALL(*mock_delegate_, GetValue(_))
+ .WillOnce(DoAll(SetArgPointee<0>(expected), Return(0)));
+ PrepareDUT();
+ uint8_t actual = expected + 1; // Initialize to an incorrect value.
+ ASSERT_EQ(dut_->GetValue(&actual), 0);
+ EXPECT_EQ(actual, expected);
+}
+
+TEST_F(TaggedControlDelegateTest, GetFailure) {
+ int err = 3;
+ EXPECT_CALL(*mock_delegate_, GetValue(_)).WillOnce(Return(err));
+ PrepareDUT();
+ uint8_t unused = 0;
+ ASSERT_EQ(dut_->GetValue(&unused), err);
+}
+
+TEST_F(TaggedControlDelegateTest, SetSuccess) {
+ uint8_t value = 3;
+ EXPECT_CALL(*mock_delegate_, SetValue(value)).WillOnce(Return(0));
+ PrepareDUT();
+ ASSERT_EQ(dut_->SetValue(value), 0);
+}
+
+TEST_F(TaggedControlDelegateTest, SetFailure) {
+ int err = 3;
+ uint8_t value = 12;
+ EXPECT_CALL(*mock_delegate_, SetValue(value)).WillOnce(Return(err));
+ PrepareDUT();
+ ASSERT_EQ(dut_->SetValue(value), err);
+}
+
+} // namespace v4l2_camera_hal
diff --git a/modules/camera/3_4/metadata/tagged_control_options.h b/modules/camera/3_4/metadata/tagged_control_options.h
new file mode 100644
index 0000000..3d900ae
--- /dev/null
+++ b/modules/camera/3_4/metadata/tagged_control_options.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef V4L2_CAMERA_HAL_METADATA_TAGGED_CONTROL_OPTIONS_H_
+#define V4L2_CAMERA_HAL_METADATA_TAGGED_CONTROL_OPTIONS_H_
+
+#include <memory>
+
+#include "control_options_interface.h"
+
+namespace v4l2_camera_hal {
+
+// A constant tag with a value not used as a real tag
+// (since all real tags are unsigned), to indicate options
+// that should not be reported.
+// Any class working with TaggedControlOptions should check
+// the tag against this value before using it.
+static int32_t DO_NOT_REPORT_OPTIONS = -1;
+
+// A TaggedControlOptions wraps a ControlOptions and adds a tag.
+template <typename T>
+class TaggedControlOptions : public ControlOptionsInterface<T> {
+ public:
+ TaggedControlOptions(int32_t tag,
+ std::unique_ptr<ControlOptionsInterface<T>> options)
+ : tag_(tag), options_(std::move(options)){};
+
+ int32_t tag() { return tag_; };
+
+ virtual std::vector<T> MetadataRepresentation() override {
+ return options_->MetadataRepresentation();
+ };
+ virtual bool IsSupported(const T& value) override {
+ return options_->IsSupported(value);
+ };
+ virtual int DefaultValueForTemplate(int template_type,
+ T* default_value) override {
+ return options_->DefaultValueForTemplate(template_type, default_value);
+ }
+
+ private:
+ const int32_t tag_;
+ std::unique_ptr<ControlOptionsInterface<T>> options_;
+};
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_METADATA_CONTROL_OPTIONS_INTERFACE_H_
diff --git a/modules/camera/3_4/metadata/tagged_control_options_test.cpp b/modules/camera/3_4/metadata/tagged_control_options_test.cpp
new file mode 100644
index 0000000..845426a
--- /dev/null
+++ b/modules/camera/3_4/metadata/tagged_control_options_test.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "tagged_control_options.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "control_options_interface_mock.h"
+
+using testing::Return;
+using testing::SetArgPointee;
+using testing::Test;
+using testing::_;
+
+namespace v4l2_camera_hal {
+
+class TaggedControlOptionsTest : public Test {
+ protected:
+ virtual void SetUp() {
+ mock_options_.reset(new ControlOptionsInterfaceMock<uint8_t>());
+ // Nullify dut so an error will be thrown if a test doesn't call PrepareDUT.
+ dut_.reset();
+ }
+
+ virtual void PrepareDUT() {
+ // Use this method after all the EXPECT_CALLs to pass ownership of the
+ // options
+ // to the device.
+ dut_.reset(
+ new TaggedControlOptions<uint8_t>(tag_, std::move(mock_options_)));
+ }
+
+ std::unique_ptr<TaggedControlOptions<uint8_t>> dut_;
+ std::unique_ptr<ControlOptionsInterfaceMock<uint8_t>> mock_options_;
+ const int32_t tag_ = 123;
+};
+
+TEST_F(TaggedControlOptionsTest, GetTag) {
+ PrepareDUT();
+ EXPECT_EQ(dut_->tag(), tag_);
+}
+
+TEST_F(TaggedControlOptionsTest, MetadataRepresentation) {
+ std::vector<uint8_t> expected{3, 4, 5};
+ EXPECT_CALL(*mock_options_, MetadataRepresentation())
+ .WillOnce(Return(expected));
+ PrepareDUT();
+ ASSERT_EQ(dut_->MetadataRepresentation(), expected);
+}
+
+TEST_F(TaggedControlOptionsTest, IsSupportedTrue) {
+ bool supported = true;
+ uint8_t value = 3;
+ EXPECT_CALL(*mock_options_, IsSupported(value)).WillOnce(Return(supported));
+ PrepareDUT();
+ ASSERT_EQ(dut_->IsSupported(value), supported);
+}
+
+TEST_F(TaggedControlOptionsTest, IsSupportedFalse) {
+ bool supported = false;
+ uint8_t value = 3;
+ EXPECT_CALL(*mock_options_, IsSupported(value)).WillOnce(Return(supported));
+ PrepareDUT();
+ ASSERT_EQ(dut_->IsSupported(value), supported);
+}
+
+TEST_F(TaggedControlOptionsTest, DefaultValue) {
+ uint8_t value = 99;
+ int template_id = 3;
+ EXPECT_CALL(*mock_options_, DefaultValueForTemplate(template_id, _))
+ .WillOnce(DoAll(SetArgPointee<1>(value), Return(0)));
+ PrepareDUT();
+ uint8_t actual = value + 1;
+ EXPECT_EQ(dut_->DefaultValueForTemplate(template_id, &actual), 0);
+ EXPECT_EQ(actual, value);
+}
+
+TEST_F(TaggedControlOptionsTest, DefaultValueFail) {
+ int err = 12;
+ int template_id = 3;
+ EXPECT_CALL(*mock_options_, DefaultValueForTemplate(template_id, _))
+ .WillOnce(Return(err));
+ PrepareDUT();
+ uint8_t unused;
+ EXPECT_EQ(dut_->DefaultValueForTemplate(template_id, &unused), err);
+}
+
+} // namespace v4l2_camera_hal
diff --git a/modules/camera/3_4/metadata/test_common.h b/modules/camera/3_4/metadata/test_common.h
new file mode 100644
index 0000000..489990f
--- /dev/null
+++ b/modules/camera/3_4/metadata/test_common.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef V4L2_CAMERA_HAL_METADATA_TEST_COMMON_H_
+#define V4L2_CAMERA_HAL_METADATA_TEST_COMMON_H_
+
+#include <array>
+#include <vector>
+
+#include <camera/CameraMetadata.h>
+#include <gtest/gtest.h>
+
+#include "array_vector.h"
+#include "metadata_common.h"
+
+namespace v4l2_camera_hal {
+
+// Check that metadata of a given tag matches expectations.
+// Generic.
+template <typename T>
+static void ExpectMetadataEq(const android::CameraMetadata& metadata,
+ int32_t tag,
+ const T* expected,
+ size_t size) {
+ camera_metadata_ro_entry_t entry = metadata.find(tag);
+ ASSERT_EQ(entry.count, size);
+ const T* data = nullptr;
+ GetDataPointer(entry, &data);
+ ASSERT_NE(data, nullptr);
+ for (size_t i = 0; i < size; ++i) {
+ EXPECT_EQ(data[i], expected[i]);
+ }
+}
+
+// Single item.
+template <typename T>
+static void ExpectMetadataEq(const android::CameraMetadata& metadata,
+ int32_t tag,
+ T expected) {
+ ExpectMetadataEq(metadata, tag, &expected, 1);
+}
+
+// Vector of items.
+template <typename T>
+static void ExpectMetadataEq(const android::CameraMetadata& metadata,
+ int32_t tag,
+ const std::vector<T>& expected) {
+ ExpectMetadataEq(metadata, tag, expected.data(), expected.size());
+}
+
+// Array of items.
+template <typename T, size_t N>
+static void ExpectMetadataEq(const android::CameraMetadata& metadata,
+ int32_t tag,
+ const std::array<T, N>& expected) {
+ ExpectMetadataEq(metadata, tag, expected.data(), N);
+}
+
+// ArrayVector.
+template <typename T, size_t N>
+static void ExpectMetadataEq(const android::CameraMetadata& metadata,
+ int32_t tag,
+ const ArrayVector<T, N>& expected) {
+ ExpectMetadataEq(
+ metadata, tag, expected.data(), expected.total_num_elements());
+}
+
+// Vector of arrays.
+template <typename T, size_t N>
+static int ExpectMetadataEq(const android::CameraMetadata& metadata,
+ int32_t tag,
+ const std::vector<std::array<T, N>>& expected) {
+ // Convert to array vector so we know all the elements are contiguous.
+ ArrayVector<T, N> array_vector;
+ for (const auto& array : expected) {
+ array_vector.push_back(array);
+ }
+ ExpectMetadataEq(metadata, tag, array_vector);
+}
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_METADATA_TEST_COMMON_H_
diff --git a/modules/camera/3_4/metadata/types.h b/modules/camera/3_4/metadata/types.h
new file mode 100644
index 0000000..093fe01
--- /dev/null
+++ b/modules/camera/3_4/metadata/types.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DEFAULT_CAMERA_HAL_METADATA_TYPES_H_
+#define DEFAULT_CAMERA_HAL_METADATA_TYPES_H_
+
+#include <array>
+#include <map>
+#include <set>
+
+#include <hardware/camera3.h>
+
+namespace default_camera_hal {
+
+// A variety of Structs describing more complex metadata entries.
+
+// StreamSpec describe the attributes of a single stream.
+struct StreamSpec {
+ int32_t format;
+ int32_t width;
+ int32_t height;
+
+ StreamSpec(int32_t f, int32_t w, int32_t h)
+ : format(f), width(w), height(h) {}
+ StreamSpec(const camera3_stream_t* stream)
+ : format(stream->format), width(stream->width), height(stream->height) {}
+
+ struct Compare {
+ bool operator()(const StreamSpec& left, const StreamSpec& right) const {
+ // Base equality/comparison first on format, then on width, then height.
+ return left.format < right.format ||
+ (left.format == right.format &&
+ (left.width < right.width ||
+ (left.width == right.width && left.height < right.height)));
+ }
+ };
+};
+
+// StreamConfigurations indicate a possible direction configuration for
+// a given set of stream specifications.
+typedef std::array<int32_t, 4> RawStreamConfiguration;
+struct StreamConfiguration {
+ StreamSpec spec;
+ int32_t direction;
+
+ StreamConfiguration(const RawStreamConfiguration& raw)
+ : spec({raw[0], raw[1], raw[2]}), direction(raw[3]) {}
+};
+
+// StreamStallDurations indicate the stall duration (in ns) for
+// when a stream with a given set of specifications is used as output.
+typedef std::array<int64_t, 4> RawStreamStallDuration;
+struct StreamStallDuration {
+ StreamSpec spec;
+ int64_t duration;
+
+ StreamStallDuration(const RawStreamStallDuration& raw)
+ : spec({static_cast<int32_t>(raw[0]),
+ static_cast<int32_t>(raw[1]),
+ static_cast<int32_t>(raw[2])}),
+ duration(raw[3]) {}
+};
+
+// Map input formats to their supported reprocess output formats.
+typedef std::map<int32_t, std::set<int32_t>> ReprocessFormatMap;
+
+} // namespace default_camera_hal
+
+#endif // DEFAULT_CAMERA_HAL_METADATA_TYPES_H_
diff --git a/modules/camera/3_4/metadata/v4l2_control_delegate.h b/modules/camera/3_4/metadata/v4l2_control_delegate.h
new file mode 100644
index 0000000..3f45f9c
--- /dev/null
+++ b/modules/camera/3_4/metadata/v4l2_control_delegate.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef V4L2_CAMERA_HAL_METADATA_V4L2_CONTROL_DELEGATE_H_
+#define V4L2_CAMERA_HAL_METADATA_V4L2_CONTROL_DELEGATE_H_
+
+#include "../v4l2_wrapper.h"
+#include "control_delegate_interface.h"
+#include "converter_interface.h"
+
+namespace v4l2_camera_hal {
+
+// A V4L2ControlDelegate routes getting and setting through V4L2
+template <typename TMetadata, typename TV4L2 = int32_t>
+class V4L2ControlDelegate : public ControlDelegateInterface<TMetadata> {
+ public:
+ V4L2ControlDelegate(
+ std::shared_ptr<V4L2Wrapper> device,
+ int control_id,
+ std::shared_ptr<ConverterInterface<TMetadata, TV4L2>> converter)
+ : device_(std::move(device)),
+ control_id_(control_id),
+ converter_(std::move(converter)){};
+
+ int GetValue(TMetadata* value) override {
+ TV4L2 v4l2_value;
+ int res = device_->GetControl(control_id_, &v4l2_value);
+ if (res) {
+ HAL_LOGE("Failed to get device value for control %d.", control_id_);
+ return res;
+ }
+ return converter_->V4L2ToMetadata(v4l2_value, value);
+ };
+
+ int SetValue(const TMetadata& value) override {
+ TV4L2 v4l2_value;
+ int res = converter_->MetadataToV4L2(value, &v4l2_value);
+ if (res) {
+ HAL_LOGE("Failed to convert metadata value to V4L2.");
+ return res;
+ }
+ return device_->SetControl(control_id_, v4l2_value);
+ };
+
+ private:
+ std::shared_ptr<V4L2Wrapper> device_;
+ int control_id_;
+ std::shared_ptr<ConverterInterface<TMetadata, TV4L2>> converter_;
+};
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_METADATA_V4L2_CONTROL_DELEGATE_H_
diff --git a/modules/camera/3_4/metadata/v4l2_control_delegate_test.cpp b/modules/camera/3_4/metadata/v4l2_control_delegate_test.cpp
new file mode 100644
index 0000000..2f14d6f
--- /dev/null
+++ b/modules/camera/3_4/metadata/v4l2_control_delegate_test.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "v4l2_control_delegate.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "../v4l2_wrapper_mock.h"
+#include "converter_interface_mock.h"
+
+using testing::Return;
+using testing::SetArgPointee;
+using testing::Test;
+using testing::_;
+
+namespace v4l2_camera_hal {
+
+class V4L2ControlDelegateTest : public Test {
+ protected:
+ virtual void SetUp() {
+ mock_device_.reset(new V4L2WrapperMock());
+ mock_converter_.reset(new ConverterInterfaceMock<uint8_t, int32_t>());
+ dut_.reset(new V4L2ControlDelegate<uint8_t>(
+ mock_device_, control_id_, mock_converter_));
+ }
+
+ std::unique_ptr<V4L2ControlDelegate<uint8_t>> dut_;
+ std::shared_ptr<V4L2WrapperMock> mock_device_;
+ std::shared_ptr<ConverterInterfaceMock<uint8_t, int32_t>> mock_converter_;
+ const int control_id_ = 123;
+};
+
+TEST_F(V4L2ControlDelegateTest, GetSuccess) {
+ int32_t device_result = 99;
+ uint8_t conversion_result = 10;
+ EXPECT_CALL(*mock_device_, GetControl(control_id_, _))
+ .WillOnce(DoAll(SetArgPointee<1>(device_result), Return(0)));
+ EXPECT_CALL(*mock_converter_, V4L2ToMetadata(device_result, _))
+ .WillOnce(DoAll(SetArgPointee<1>(conversion_result), Return(0)));
+
+ uint8_t actual = conversion_result + 1; // Something incorrect.
+ ASSERT_EQ(dut_->GetValue(&actual), 0);
+ EXPECT_EQ(actual, conversion_result);
+}
+
+TEST_F(V4L2ControlDelegateTest, GetConverterFailure) {
+ int32_t device_result = 99;
+ EXPECT_CALL(*mock_device_, GetControl(control_id_, _))
+ .WillOnce(DoAll(SetArgPointee<1>(device_result), Return(0)));
+ int err = -99;
+ EXPECT_CALL(*mock_converter_, V4L2ToMetadata(device_result, _))
+ .WillOnce(Return(err));
+
+ uint8_t unused = 1;
+ ASSERT_EQ(dut_->GetValue(&unused), err);
+}
+
+TEST_F(V4L2ControlDelegateTest, GetDeviceFailure) {
+ int err = -99;
+ EXPECT_CALL(*mock_device_, GetControl(control_id_, _)).WillOnce(Return(err));
+
+ uint8_t unused = 1;
+ ASSERT_EQ(dut_->GetValue(&unused), err);
+}
+
+TEST_F(V4L2ControlDelegateTest, SetSuccess) {
+ uint8_t input = 10;
+ int32_t conversion_result = 99;
+ EXPECT_CALL(*mock_converter_, MetadataToV4L2(input, _))
+ .WillOnce(DoAll(SetArgPointee<1>(conversion_result), Return(0)));
+ EXPECT_CALL(*mock_device_, SetControl(control_id_, conversion_result, _))
+ .WillOnce(Return(0));
+
+ ASSERT_EQ(dut_->SetValue(input), 0);
+}
+
+TEST_F(V4L2ControlDelegateTest, SetConverterFailure) {
+ uint8_t input = 10;
+ int err = 12;
+ EXPECT_CALL(*mock_converter_, MetadataToV4L2(input, _)).WillOnce(Return(err));
+ ASSERT_EQ(dut_->SetValue(input), err);
+}
+
+TEST_F(V4L2ControlDelegateTest, SetDeviceFailure) {
+ uint8_t input = 10;
+ int32_t conversion_result = 99;
+ EXPECT_CALL(*mock_converter_, MetadataToV4L2(input, _))
+ .WillOnce(DoAll(SetArgPointee<1>(conversion_result), Return(0)));
+ int err = 66;
+ EXPECT_CALL(*mock_device_, SetControl(control_id_, conversion_result, _))
+ .WillOnce(Return(err));
+
+ ASSERT_EQ(dut_->SetValue(input), err);
+}
+
+} // namespace v4l2_camera_hal
diff --git a/modules/camera/3_4/request_tracker.cpp b/modules/camera/3_4/request_tracker.cpp
new file mode 100644
index 0000000..09f634d
--- /dev/null
+++ b/modules/camera/3_4/request_tracker.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "request_tracker.h"
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "RequestTracker"
+#include <cutils/log.h>
+
+namespace default_camera_hal {
+
+RequestTracker::RequestTracker() {}
+
+RequestTracker::~RequestTracker() {}
+
+void RequestTracker::SetStreamConfiguration(
+ const camera3_stream_configuration_t& config) {
+ // Clear the old configuration.
+ ClearStreamConfiguration();
+ // Add an entry to the buffer tracking map for each configured stream.
+ for (size_t i = 0; i < config.num_streams; ++i) {
+ buffers_in_flight_.emplace(config.streams[i], 0);
+ }
+}
+
+void RequestTracker::ClearStreamConfiguration() {
+ // The keys of the in flight buffer map are the configured streams.
+ buffers_in_flight_.clear();
+}
+
+// Helper: get the streams used by a request.
+std::set<camera3_stream_t*> RequestStreams(const CaptureRequest& request) {
+ std::set<camera3_stream_t*> result;
+ if (request.input_buffer) {
+ result.insert(request.input_buffer->stream);
+ }
+ for (const auto& output_buffer : request.output_buffers) {
+ result.insert(output_buffer.stream);
+ }
+ return std::move(result);
+}
+
+bool RequestTracker::Add(std::shared_ptr<CaptureRequest> request) {
+ if (!CanAddRequest(*request)) {
+ return false;
+ }
+
+ // Add to the count for each stream used.
+ for (const auto stream : RequestStreams(*request)) {
+ ++buffers_in_flight_[stream];
+ }
+
+ // Store the request.
+ frames_in_flight_[request->frame_number] = request;
+
+ return true;
+}
+
+bool RequestTracker::Remove(std::shared_ptr<CaptureRequest> request) {
+ if (!request) {
+ return false;
+ }
+
+ // Get the request.
+ const auto frame_number_request =
+ frames_in_flight_.find(request->frame_number);
+ if (frame_number_request == frames_in_flight_.end()) {
+ ALOGE("%s: Frame %u is not in flight.", __func__, request->frame_number);
+ return false;
+ } else if (request != frame_number_request->second) {
+ ALOGE(
+ "%s: Request for frame %u cannot be removed: "
+ "does not matched the stored request.",
+ __func__,
+ request->frame_number);
+ return false;
+ }
+
+ frames_in_flight_.erase(frame_number_request);
+
+ // Decrement the counts of used streams.
+ for (const auto stream : RequestStreams(*request)) {
+ --buffers_in_flight_[stream];
+ }
+
+ return true;
+}
+
+void RequestTracker::Clear(
+ std::set<std::shared_ptr<CaptureRequest>>* requests) {
+ // If desired, extract all the currently in-flight requests.
+ if (requests) {
+ for (auto& frame_number_request : frames_in_flight_) {
+ requests->insert(frame_number_request.second);
+ }
+ }
+
+ // Clear out all tracking.
+ frames_in_flight_.clear();
+ // Maintain the configuration, but reset counts.
+ for (auto& stream_count : buffers_in_flight_) {
+ stream_count.second = 0;
+ }
+}
+
+bool RequestTracker::CanAddRequest(const CaptureRequest& request) const {
+ // Check that it's not a duplicate.
+ if (frames_in_flight_.count(request.frame_number) > 0) {
+ ALOGE("%s: Already tracking a request with frame number %d.",
+ __func__,
+ request.frame_number);
+ return false;
+ }
+
+ // Check that each stream has space
+ // (which implicitly checks if it is configured).
+ bool result = true;
+ for (const auto stream : RequestStreams(request)) {
+ if (StreamFull(stream)) {
+ ALOGE("%s: Stream %p is full.", __func__, stream);
+ return false;
+ }
+ }
+ return true;
+}
+
+bool RequestTracker::StreamFull(const camera3_stream_t* handle) const {
+ const auto it = buffers_in_flight_.find(handle);
+ if (it == buffers_in_flight_.end()) {
+ // Unconfigured streams are implicitly full.
+ ALOGV("%s: Stream %p is not a configured stream.", __func__, handle);
+ return true;
+ } else {
+ return it->second >= it->first->max_buffers;
+ }
+}
+
+bool RequestTracker::InFlight(uint32_t frame_number) const {
+ return frames_in_flight_.count(frame_number) > 0;
+}
+
+bool RequestTracker::Empty() const {
+ return frames_in_flight_.empty();
+}
+
+} // namespace default_camera_hal
diff --git a/modules/camera/3_4/request_tracker.h b/modules/camera/3_4/request_tracker.h
new file mode 100644
index 0000000..a632a61
--- /dev/null
+++ b/modules/camera/3_4/request_tracker.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DEFAULT_CAMERA_HAL_REQUEST_TRACKER_H_
+#define DEFAULT_CAMERA_HAL_REQUEST_TRACKER_H_
+
+#include <map>
+#include <memory>
+#include <set>
+
+#include <hardware/camera3.h>
+
+#include "capture_request.h"
+#include "common.h"
+
+namespace default_camera_hal {
+
+// Keep track of what requests and streams are in flight.
+class RequestTracker {
+ public:
+ RequestTracker();
+ virtual ~RequestTracker();
+
+ // Configuration methods. Both have undefined effects on in-flight requests,
+ // and should only be called when empty.
+ // Add configured streams. Replaces the previous configuration if any.
+ virtual void SetStreamConfiguration(
+ const camera3_stream_configuration_t& config);
+ // Reset to no configured streams.
+ virtual void ClearStreamConfiguration();
+
+ // Tracking methods.
+ // Track a request.
+ // False if a request of the same frame number is already being tracked
+ virtual bool Add(std::shared_ptr<CaptureRequest> request);
+ // Stop tracking a request.
+ // False if the given request is not being tracked.
+ virtual bool Remove(std::shared_ptr<CaptureRequest> request = nullptr);
+ // Empty out all requests being tracked.
+ virtual void Clear(
+ std::set<std::shared_ptr<CaptureRequest>>* requests = nullptr);
+
+ // Accessors to check availability.
+ // Check that a request isn't already in flight, and won't overflow any
+ // streams.
+ virtual bool CanAddRequest(const CaptureRequest& request) const;
+ // True if the given stream is already at max capacity.
+ virtual bool StreamFull(const camera3_stream_t* handle) const;
+ // True if a request is being tracked for the given frame number.
+ virtual bool InFlight(uint32_t frame_number) const;
+ // True if no requests being tracked.
+ virtual bool Empty() const;
+
+ private:
+ // Track for each stream, how many buffers are in flight.
+ std::map<const camera3_stream_t*, size_t> buffers_in_flight_;
+ // Track the frames in flight.
+ std::map<uint32_t, std::shared_ptr<CaptureRequest>> frames_in_flight_;
+
+ DISALLOW_COPY_AND_ASSIGN(RequestTracker);
+};
+
+} // namespace default_camera_hal
+
+#endif // DEFAULT_CAMERA_HAL_REQUEST_TRACKER_H_
diff --git a/modules/camera/3_4/request_tracker_test.cpp b/modules/camera/3_4/request_tracker_test.cpp
new file mode 100644
index 0000000..a68ff57
--- /dev/null
+++ b/modules/camera/3_4/request_tracker_test.cpp
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "request_tracker.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using testing::AtMost;
+using testing::Expectation;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::Test;
+using testing::_;
+
+namespace default_camera_hal {
+
+class RequestTrackerTest : public Test {
+ protected:
+ void SetUp() {
+ stream1_.max_buffers = 3;
+ stream2_.max_buffers = 3;
+ dut_.reset(new RequestTracker());
+ streams_ = {&stream1_, &stream2_};
+ camera3_stream_configuration_t config{streams_.size(), streams_.data(), 0};
+ dut_->SetStreamConfiguration(config);
+ }
+
+ std::shared_ptr<CaptureRequest> GenerateCaptureRequest(
+ uint32_t frame, std::vector<camera3_stream_t*> streams) {
+ std::shared_ptr<CaptureRequest> request =
+ std::make_shared<CaptureRequest>();
+
+ // Set the frame number and buffers.
+ request->frame_number = frame;
+ for (const auto stream : streams) {
+ // All we really care about for the buffers is which stream they're for.
+ camera3_stream_buffer_t buffer{stream, nullptr, 0, -1, -1};
+ request->output_buffers.push_back(buffer);
+ }
+
+ return request;
+ }
+
+ void AddRequest(uint32_t frame,
+ std::vector<camera3_stream_t*> streams,
+ bool expected = true) {
+ std::shared_ptr<CaptureRequest> request =
+ GenerateCaptureRequest(frame, streams);
+ EXPECT_EQ(dut_->CanAddRequest(*request), expected);
+ if (expected) {
+ EXPECT_FALSE(dut_->InFlight(frame));
+ }
+ EXPECT_EQ(dut_->Add(request), expected);
+ if (expected) {
+ EXPECT_TRUE(dut_->InFlight(frame));
+ }
+ }
+
+ camera3_stream_t stream1_;
+ camera3_stream_t stream2_;
+ std::vector<camera3_stream_t*> streams_;
+ std::shared_ptr<RequestTracker> dut_;
+};
+
+TEST_F(RequestTrackerTest, AddValid) {
+ uint32_t frame = 34;
+ EXPECT_FALSE(dut_->InFlight(frame));
+ AddRequest(frame, {&stream1_});
+}
+
+TEST_F(RequestTrackerTest, AddInput) {
+ EXPECT_TRUE(dut_->Empty());
+
+ // Add a request
+ uint32_t frame = 42;
+ std::shared_ptr<CaptureRequest> expected = GenerateCaptureRequest(frame, {});
+ // Set the input buffer instead of any outputs.
+ expected->input_buffer.reset(
+ new camera3_stream_buffer_t{&stream1_, nullptr, 0, -1, -1});
+ stream1_.max_buffers = 1;
+
+ EXPECT_TRUE(dut_->Add(expected));
+ EXPECT_TRUE(dut_->InFlight(frame));
+ // Should have added to the count of buffers for stream 1.
+ EXPECT_TRUE(dut_->StreamFull(&stream1_));
+}
+
+TEST_F(RequestTrackerTest, AddMultipleStreams) {
+ stream1_.max_buffers = 1;
+ stream2_.max_buffers = 1;
+
+ EXPECT_FALSE(dut_->StreamFull(&stream1_));
+ EXPECT_FALSE(dut_->StreamFull(&stream2_));
+
+ // Add a request using both streams.
+ AddRequest(99, {&stream1_, &stream2_});
+
+ // Should both have been counted.
+ EXPECT_TRUE(dut_->StreamFull(&stream1_));
+ EXPECT_TRUE(dut_->StreamFull(&stream2_));
+}
+
+TEST_F(RequestTrackerTest, AddUnconfigured) {
+ camera3_stream_t stream;
+ // Unconfigured should be considered full.
+ EXPECT_TRUE(dut_->StreamFull(&stream));
+ AddRequest(1, {&stream}, false);
+}
+
+TEST_F(RequestTrackerTest, AddPastCapacity) {
+ // Set the limit of stream 2 to 1.
+ stream2_.max_buffers = 1;
+
+ for (size_t i = 0; i < stream1_.max_buffers; ++i) {
+ EXPECT_FALSE(dut_->StreamFull(&stream1_));
+ EXPECT_FALSE(dut_->StreamFull(&stream2_));
+ AddRequest(i, {&stream1_});
+ }
+ // Filled up stream 1.
+ EXPECT_TRUE(dut_->StreamFull(&stream1_));
+ // Stream 2 should still not be full since nothing was added.
+ EXPECT_FALSE(dut_->StreamFull(&stream2_));
+
+ // Limit has been hit, can't add more.
+ AddRequest(stream1_.max_buffers, {&stream1_, &stream2_}, false);
+ EXPECT_TRUE(dut_->StreamFull(&stream1_));
+ // Should not have added to the count of stream 2.
+ EXPECT_FALSE(dut_->StreamFull(&stream2_));
+}
+
+TEST_F(RequestTrackerTest, AddDuplicate) {
+ uint32_t frame = 42;
+ AddRequest(frame, {&stream1_});
+ // Can't add a duplicate.
+ AddRequest(frame, {&stream2_}, false);
+}
+
+TEST_F(RequestTrackerTest, RemoveValid) {
+ EXPECT_TRUE(dut_->Empty());
+
+ // Add a request
+ uint32_t frame = 42;
+ std::shared_ptr<CaptureRequest> request =
+ GenerateCaptureRequest(frame, {&stream1_});
+ EXPECT_TRUE(dut_->Add(request));
+ EXPECT_TRUE(dut_->InFlight(frame));
+ AddRequest(frame + 1, {&stream1_});
+ EXPECT_FALSE(dut_->Empty());
+
+ // Remove it.
+ EXPECT_TRUE(dut_->Remove(request));
+ // Should have removed only the desired request.
+ EXPECT_FALSE(dut_->Empty());
+}
+
+TEST_F(RequestTrackerTest, RemoveInvalidFrame) {
+ EXPECT_TRUE(dut_->Empty());
+
+ // Add a request
+ uint32_t frame = 42;
+ AddRequest(frame, {&stream1_});
+ EXPECT_FALSE(dut_->Empty());
+
+ // Try to remove a different one.
+ uint32_t bad_frame = frame + 1;
+ std::shared_ptr<CaptureRequest> bad_request =
+ GenerateCaptureRequest(bad_frame, {&stream1_});
+ EXPECT_FALSE(dut_->InFlight(bad_frame));
+ EXPECT_FALSE(dut_->Remove(bad_request));
+ EXPECT_FALSE(dut_->Empty());
+}
+
+TEST_F(RequestTrackerTest, RemoveInvalidData) {
+ EXPECT_TRUE(dut_->Empty());
+
+ // Add a request
+ uint32_t frame = 42;
+ AddRequest(frame, {&stream1_});
+ EXPECT_FALSE(dut_->Empty());
+
+ // Try to remove a different one.
+ // Even though this request looks the same, that fact that it is
+ // a pointer to a different object means it should fail.
+ std::shared_ptr<CaptureRequest> bad_request =
+ GenerateCaptureRequest(frame, {&stream1_});
+ EXPECT_TRUE(dut_->InFlight(frame));
+ EXPECT_FALSE(dut_->Remove(bad_request));
+ EXPECT_FALSE(dut_->Empty());
+}
+
+TEST_F(RequestTrackerTest, RemoveNull) {
+ EXPECT_FALSE(dut_->Remove(nullptr));
+}
+
+TEST_F(RequestTrackerTest, ClearRequests) {
+ // Create some requests.
+ uint32_t frame1 = 42;
+ uint32_t frame2 = frame1 + 1;
+ std::shared_ptr<CaptureRequest> request1 =
+ GenerateCaptureRequest(frame1, {&stream1_});
+ std::shared_ptr<CaptureRequest> request2 =
+ GenerateCaptureRequest(frame2, {&stream2_});
+ std::set<std::shared_ptr<CaptureRequest>> expected;
+ expected.insert(request1);
+ expected.insert(request2);
+
+ // Insert them.
+ EXPECT_TRUE(dut_->Add(request1));
+ EXPECT_TRUE(dut_->Add(request2));
+ EXPECT_TRUE(dut_->InFlight(frame1));
+ EXPECT_TRUE(dut_->InFlight(frame2));
+ EXPECT_FALSE(dut_->Empty());
+ std::set<std::shared_ptr<CaptureRequest>> actual;
+
+ // Clear them out.
+ dut_->Clear(&actual);
+ EXPECT_TRUE(dut_->Empty());
+ EXPECT_EQ(actual, expected);
+
+ // Configuration (max values) should not have been cleared.
+ EXPECT_TRUE(dut_->Add(request1));
+}
+
+TEST_F(RequestTrackerTest, ClearRequestsNoResult) {
+ // Add some requests.
+ EXPECT_TRUE(dut_->Empty());
+ AddRequest(1, {&stream1_});
+ AddRequest(2, {&stream2_});
+ EXPECT_FALSE(dut_->Empty());
+ // Don't bother getting the cleared requests.
+ dut_->Clear();
+ EXPECT_TRUE(dut_->Empty());
+}
+
+TEST_F(RequestTrackerTest, ClearConfiguration) {
+ EXPECT_FALSE(dut_->StreamFull(&stream1_));
+ EXPECT_FALSE(dut_->StreamFull(&stream2_));
+
+ // Clear the configuration.
+ dut_->ClearStreamConfiguration();
+
+ // Both streams should be considered full now, since neither is configured.
+ EXPECT_TRUE(dut_->StreamFull(&stream1_));
+ EXPECT_TRUE(dut_->StreamFull(&stream2_));
+}
+
+} // namespace default_camera_hal
diff --git a/modules/camera/3_4/static_properties.cpp b/modules/camera/3_4/static_properties.cpp
new file mode 100644
index 0000000..5be9dcd
--- /dev/null
+++ b/modules/camera/3_4/static_properties.cpp
@@ -0,0 +1,494 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "static_properties.h"
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "StaticProperties"
+#include <cutils/log.h>
+#include <hardware/camera3.h>
+#include <system/camera.h>
+
+#include "metadata/metadata_reader.h"
+
+namespace default_camera_hal {
+
+// Build stream capabilities from configs + stall durations.
+static bool ConstructStreamCapabilities(
+ const std::vector<StreamConfiguration>& configs,
+ const std::vector<StreamStallDuration>& stalls,
+ StaticProperties::CapabilitiesMap* capabilities) {
+ // Extract directional capabilities from the configs.
+ for (const auto& config : configs) {
+ switch (config.direction) {
+ case ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT:
+ (*capabilities)[config.spec].output_supported = true;
+ break;
+ case ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT:
+ (*capabilities)[config.spec].input_supported = true;
+ break;
+ default:
+ // Should never happen when using the MetadataReader;
+ // it should validate directions.
+ ALOGE("%s: Unrecognized stream config direction %d.",
+ __func__,
+ config.direction);
+ return false;
+ }
+ }
+
+ // Extract stall durations from the stalls.
+ for (const auto& stall : stalls) {
+ (*capabilities)[stall.spec].stall_duration = stall.duration;
+ }
+
+ return true;
+}
+
+// Check that each output config has a valid corresponding stall duration
+// (extra durations not matching any output config are ignored).
+static bool ValidateStreamCapabilities(
+ StaticProperties::CapabilitiesMap capabilities) {
+ for (const auto& spec_capabilities : capabilities) {
+ // Only non-negative stall durations are valid. This should only happen
+ // due to output streams without an associated stall duration, as
+ // MetadataReader validates the metadata stall durations.
+ if (spec_capabilities.second.output_supported &&
+ spec_capabilities.second.stall_duration < 0) {
+ ALOGE(
+ "%s: Static metadata does not have a stall duration for "
+ "each output configuration. ",
+ __func__);
+ return false;
+ }
+ }
+ return true;
+}
+
+// Validate that the input/output formats map matches up with
+// the capabilities listed for all formats.
+bool ValidateReprocessFormats(
+ const StaticProperties::CapabilitiesMap& capabilities,
+ const ReprocessFormatMap& reprocess_map) {
+ // Get input formats.
+ std::set<int32_t> all_input_formats;
+ std::set<int32_t> all_output_formats;
+ for (const auto& spec_capabilities : capabilities) {
+ if (spec_capabilities.second.input_supported) {
+ all_input_formats.insert(spec_capabilities.first.format);
+ }
+ if (spec_capabilities.second.output_supported) {
+ all_output_formats.insert(spec_capabilities.first.format);
+ }
+ }
+
+ // Must be at least one input format.
+ if (all_input_formats.size() < 1) {
+ ALOGE("%s: No input formats, reprocessing can't be supported.", __func__);
+ return false;
+ }
+
+ // Check that the reprocess map input formats are exactly all available
+ // input formats (check size here, then checking for actual value
+ // matches will happen as part of the loop below).
+ if (all_input_formats.size() != reprocess_map.size()) {
+ ALOGE(
+ "%s: Stream configuration input formats do not match "
+ "input/output format map input formats.",
+ __func__);
+ return false;
+ }
+
+ // Check that each input format has at least one matching output format.
+ for (const auto& input_format : all_input_formats) {
+ const auto input_outputs_iterator = reprocess_map.find(input_format);
+ if (input_outputs_iterator == reprocess_map.end()) {
+ ALOGE(
+ "%s: No output formats for input format %d.", __func__, input_format);
+ return false;
+ }
+ // No need to check that the output formats vector is non-empty;
+ // MetadataReader validates this. Instead just check that
+ // all outputs are actually output formats.
+ for (const auto& output_format : input_outputs_iterator->second) {
+ if (all_output_formats.count(output_format) < 1) {
+ ALOGE(
+ "%s: Output format %d for input format %d "
+ "is not a supported output format.",
+ __func__,
+ input_format,
+ output_format);
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+StaticProperties* StaticProperties::NewStaticProperties(
+ std::unique_ptr<const MetadataReader> metadata_reader) {
+ int facing = 0;
+ int orientation = 0;
+ int32_t max_input_streams = 0;
+ int32_t max_raw_output_streams = 0;
+ int32_t max_non_stalling_output_streams = 0;
+ int32_t max_stalling_output_streams = 0;
+ std::set<uint8_t> request_capabilities;
+ std::vector<StreamConfiguration> configs;
+ std::vector<StreamStallDuration> stalls;
+ CapabilitiesMap stream_capabilities;
+ ReprocessFormatMap reprocess_map;
+
+ // If reading any data returns an error, something is wrong.
+ if (metadata_reader->Facing(&facing) ||
+ metadata_reader->Orientation(&orientation) ||
+ metadata_reader->MaxInputStreams(&max_input_streams) ||
+ metadata_reader->MaxOutputStreams(&max_raw_output_streams,
+ &max_non_stalling_output_streams,
+ &max_stalling_output_streams) ||
+ metadata_reader->RequestCapabilities(&request_capabilities) ||
+ metadata_reader->StreamConfigurations(&configs) ||
+ metadata_reader->StreamStallDurations(&stalls) ||
+ !ConstructStreamCapabilities(configs, stalls, &stream_capabilities) ||
+ // MetadataReader validates configs and stall seperately,
+ // but not that they match.
+ !ValidateStreamCapabilities(stream_capabilities) ||
+ // Reprocessing metadata only necessary if input streams are allowed.
+ (max_input_streams > 0 &&
+ (metadata_reader->ReprocessFormats(&reprocess_map) ||
+ // MetadataReader validates configs and the reprocess map seperately,
+ // but not that they match.
+ !ValidateReprocessFormats(stream_capabilities, reprocess_map)))) {
+ return nullptr;
+ }
+
+ return new StaticProperties(std::move(metadata_reader),
+ facing,
+ orientation,
+ max_input_streams,
+ max_raw_output_streams,
+ max_non_stalling_output_streams,
+ max_stalling_output_streams,
+ std::move(request_capabilities),
+ std::move(stream_capabilities),
+ std::move(reprocess_map));
+}
+
+StaticProperties::StaticProperties(
+ std::unique_ptr<const MetadataReader> metadata_reader,
+ int facing,
+ int orientation,
+ int32_t max_input_streams,
+ int32_t max_raw_output_streams,
+ int32_t max_non_stalling_output_streams,
+ int32_t max_stalling_output_streams,
+ std::set<uint8_t> request_capabilities,
+ CapabilitiesMap stream_capabilities,
+ ReprocessFormatMap supported_reprocess_outputs)
+ : metadata_reader_(std::move(metadata_reader)),
+ facing_(facing),
+ orientation_(orientation),
+ max_input_streams_(max_input_streams),
+ max_raw_output_streams_(max_raw_output_streams),
+ max_non_stalling_output_streams_(max_non_stalling_output_streams),
+ max_stalling_output_streams_(max_stalling_output_streams),
+ request_capabilities_(std::move(request_capabilities)),
+ stream_capabilities_(std::move(stream_capabilities)),
+ supported_reprocess_outputs_(std::move(supported_reprocess_outputs)) {}
+
+bool StaticProperties::TemplateSupported(int type) {
+ uint8_t required_capability = 0;
+ switch (type) {
+ case CAMERA3_TEMPLATE_PREVIEW:
+ // Preview is always supported.
+ return true;
+ case CAMERA3_TEMPLATE_MANUAL:
+ required_capability =
+ ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR;
+ break;
+ case CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG:
+ required_capability =
+ ANDROID_REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING;
+ break;
+ default:
+ required_capability =
+ ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE;
+ return true;
+ }
+
+ return request_capabilities_.count(required_capability) > 0;
+}
+
+// Helper functions for checking stream properties when verifying support.
+static bool IsInputType(int stream_type) {
+ return stream_type == CAMERA3_STREAM_INPUT ||
+ stream_type == CAMERA3_STREAM_BIDIRECTIONAL;
+}
+
+static bool IsOutputType(int stream_type) {
+ return stream_type == CAMERA3_STREAM_OUTPUT ||
+ stream_type == CAMERA3_STREAM_BIDIRECTIONAL;
+}
+
+static bool IsRawFormat(int format) {
+ return format == HAL_PIXEL_FORMAT_RAW10 || format == HAL_PIXEL_FORMAT_RAW12 ||
+ format == HAL_PIXEL_FORMAT_RAW16 ||
+ format == HAL_PIXEL_FORMAT_RAW_OPAQUE;
+}
+
+bool StaticProperties::StreamConfigurationSupported(
+ const camera3_stream_configuration_t* stream_config) {
+ return SanityCheckStreamConfiguration(stream_config) &&
+ InputStreamsSupported(stream_config) &&
+ OutputStreamsSupported(stream_config) &&
+ OperationModeSupported(stream_config);
+}
+
+bool StaticProperties::SanityCheckStreamConfiguration(
+ const camera3_stream_configuration_t* stream_config) {
+ // Check for null/empty values.
+ if (stream_config == nullptr) {
+ ALOGE("%s: NULL stream configuration array", __func__);
+ return false;
+ } else if (stream_config->num_streams == 0) {
+ ALOGE("%s: Empty stream configuration array", __func__);
+ return false;
+ } else if (stream_config->streams == nullptr) {
+ ALOGE("%s: NULL stream configuration streams", __func__);
+ return false;
+ }
+
+ // Check that all streams are either inputs or outputs (or both).
+ for (size_t i = 0; i < stream_config->num_streams; ++i) {
+ const camera3_stream_t* stream = stream_config->streams[i];
+ if (stream == nullptr) {
+ ALOGE("%s: Stream %d is null", __func__, i);
+ return false;
+ } else if (!IsInputType(stream->stream_type) &&
+ !IsOutputType(stream->stream_type)) {
+ ALOGE("%s: Stream %d type %d is neither an input nor an output type",
+ __func__,
+ i,
+ stream->stream_type);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool StaticProperties::InputStreamsSupported(
+ const camera3_stream_configuration_t* stream_config) {
+ // Find the input stream(s).
+ size_t num_input_streams = 0;
+ int input_format = -1;
+ for (size_t i = 0; i < stream_config->num_streams; ++i) {
+ const camera3_stream_t* stream = stream_config->streams[i];
+ if (IsInputType(stream->stream_type)) {
+ // Check that this stream is valid as an input.
+ const auto capabilities_iterator = stream_capabilities_.find(stream);
+ if (capabilities_iterator == stream_capabilities_.end() ||
+ !capabilities_iterator->second.input_supported) {
+ ALOGE("%s: %d x %d stream of format %d is not a supported input setup.",
+ __func__,
+ stream->width,
+ stream->height,
+ stream->format);
+ return false;
+ }
+
+ // Valid input stream; count it.
+ ++num_input_streams;
+ input_format = stream->format;
+ }
+ }
+
+ // Check the count.
+ if (num_input_streams > max_input_streams_) {
+ ALOGE(
+ "%s: Requested number of input streams %d is greater than "
+ "the maximum number supported by the device (%d).",
+ __func__,
+ num_input_streams,
+ max_input_streams_);
+ return false;
+ }
+ if (num_input_streams > 1) {
+ ALOGE("%s: Camera HAL 3.4 only supports 1 input stream max.", __func__);
+ return false;
+ }
+
+ // If there's an input stream, the configuration must have at least one
+ // supported output format for reprocessing that input.
+ if (num_input_streams > 0) {
+ const auto input_output_formats_iterator =
+ supported_reprocess_outputs_.find(input_format);
+ if (input_output_formats_iterator == supported_reprocess_outputs_.end()) {
+ // Should never happen; factory should verify that all valid inputs
+ // have one or more valid outputs.
+ ALOGE("%s: No valid output formats for input format %d.",
+ __func__,
+ input_format);
+ return false;
+ }
+ bool match_found = false;
+ // Go through outputs looking for a supported one.
+ for (size_t i = 0; i < stream_config->num_streams; ++i) {
+ const camera3_stream_t* stream = stream_config->streams[i];
+ if (IsOutputType(stream->stream_type)) {
+ if (input_output_formats_iterator->second.count(stream->format) > 0) {
+ match_found = true;
+ break;
+ }
+ }
+ }
+ if (!match_found) {
+ ALOGE("%s: No supported output format provided for input format %d.",
+ __func__,
+ input_format);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool StaticProperties::OutputStreamsSupported(
+ const camera3_stream_configuration_t* stream_config) {
+ // Find and count output streams.
+ size_t num_raw = 0;
+ size_t num_stalling = 0;
+ size_t num_non_stalling = 0;
+ for (int i = 0; i < stream_config->num_streams; ++i) {
+ const camera3_stream_t* stream = stream_config->streams[i];
+ if (IsOutputType(stream->stream_type)) {
+ // Check that this stream is valid as an output.
+ const auto capabilities_iterator = stream_capabilities_.find(stream);
+ if (capabilities_iterator == stream_capabilities_.end() ||
+ !capabilities_iterator->second.output_supported) {
+ ALOGE(
+ "%s: %d x %d stream of format %d "
+ "is not a supported output setup.",
+ __func__,
+ stream->width,
+ stream->height,
+ stream->format);
+ return false;
+ }
+
+ // Valid output; count it.
+ if (IsRawFormat(stream->format)) {
+ ++num_raw;
+ } else if (capabilities_iterator->second.stall_duration > 0) {
+ ++num_stalling;
+ } else {
+ ++num_non_stalling;
+ }
+ }
+ }
+
+ // Check that the counts are within bounds.
+ if (num_raw > max_raw_output_streams_) {
+ ALOGE(
+ "%s: Requested stream configuration exceeds maximum supported "
+ "raw output streams %d (requested %d).",
+ __func__,
+ max_raw_output_streams_,
+ num_raw);
+ return false;
+ } else if (num_stalling > max_stalling_output_streams_) {
+ ALOGE(
+ "%s: Requested stream configuration exceeds maximum supported "
+ "stalling output streams %d (requested %d).",
+ __func__,
+ max_stalling_output_streams_,
+ num_stalling);
+ return false;
+ } else if (num_non_stalling > max_non_stalling_output_streams_) {
+ ALOGE(
+ "%s: Requested stream configuration exceeds maximum supported "
+ "non-stalling output streams %d (requested %d).",
+ __func__,
+ max_non_stalling_output_streams_,
+ num_non_stalling);
+ return false;
+ }
+
+ return true;
+}
+
+bool StaticProperties::OperationModeSupported(
+ const camera3_stream_configuration_t* stream_config) {
+ switch (stream_config->operation_mode) {
+ case CAMERA3_STREAM_CONFIGURATION_NORMAL_MODE:
+ return true;
+ case CAMERA3_STREAM_CONFIGURATION_CONSTRAINED_HIGH_SPEED_MODE:
+ // TODO(b/31370792): Check metadata for high speed support,
+ // check that requested streams have support for high speed.
+ ALOGE("%s: Support for CONSTRAINED_HIGH_SPEED not implemented", __func__);
+ return false;
+ default:
+ ALOGE("%s: Unrecognized stream configuration mode: %d",
+ __func__,
+ stream_config->operation_mode);
+ return false;
+ }
+}
+
+bool StaticProperties::ReprocessingSupported(
+ const camera3_stream_t* input_stream,
+ const std::set<const camera3_stream_t*>& output_streams) {
+ // There must be an input.
+ if (!input_stream) {
+ ALOGE("%s: No input stream.", __func__);
+ return false;
+ }
+ // There must be an output.
+ if (output_streams.size() < 1) {
+ ALOGE("%s: No output stream.", __func__);
+ return false;
+ }
+
+ const auto input_output_formats =
+ supported_reprocess_outputs_.find(input_stream->format);
+ if (input_output_formats == supported_reprocess_outputs_.end()) {
+ // Should never happen for a valid input stream.
+ ALOGE("%s: Input format %d does not support any output formats.",
+ __func__,
+ input_stream->format);
+ return false;
+ }
+
+ // Check that all output streams can be outputs for the input stream.
+ const std::set<int32_t>& supported_output_formats =
+ input_output_formats->second;
+ for (const auto output_stream : output_streams) {
+ if (supported_output_formats.count(output_stream->format) < 1) {
+ ALOGE(
+ "%s: Output format %d is not a supported output "
+ "for request input format %d.",
+ __func__,
+ output_stream->format,
+ input_stream->format);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace default_camera_hal
diff --git a/modules/camera/3_4/static_properties.h b/modules/camera/3_4/static_properties.h
new file mode 100644
index 0000000..25c8205
--- /dev/null
+++ b/modules/camera/3_4/static_properties.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DEFAULT_CAMERA_HAL_STATIC_PROPERTIES_H_
+#define DEFAULT_CAMERA_HAL_STATIC_PROPERTIES_H_
+
+#include <memory>
+#include <set>
+
+#include <hardware/camera3.h>
+
+#include "common.h"
+#include "metadata/metadata_reader.h"
+#include "metadata/types.h"
+
+namespace default_camera_hal {
+
+// StaticProperties provides a wrapper around useful static metadata entries.
+class StaticProperties {
+ public:
+ // Helpful types for interpreting some static properties.
+ struct StreamCapabilities {
+ int64_t stall_duration;
+ int32_t input_supported;
+ int32_t output_supported;
+ // Default constructor ensures no support
+ // and an invalid stall duration.
+ StreamCapabilities()
+ : stall_duration(-1), input_supported(0), output_supported(0) {}
+ };
+ // Map stream spec (format, size) to their
+ // capabilities (input, output, stall).
+ typedef std::map<StreamSpec, StreamCapabilities, StreamSpec::Compare>
+ CapabilitiesMap;
+
+ // Use this method to create StaticProperties objects.
+ // Functionally equivalent to "new StaticProperties",
+ // except that it may return nullptr in case of failure (missing entries).
+ static StaticProperties* NewStaticProperties(
+ std::unique_ptr<const MetadataReader> metadata_reader);
+ static StaticProperties* NewStaticProperties(
+ std::unique_ptr<android::CameraMetadata> metadata) {
+ return NewStaticProperties(
+ std::make_unique<MetadataReader>(std::move(metadata)));
+ }
+ virtual ~StaticProperties(){};
+
+ // Simple accessors.
+ int facing() const { return facing_; };
+ int orientation() const { return orientation_; };
+ // Carrying on the promise of the underlying reader,
+ // the returned pointer is valid only as long as this object is alive.
+ const camera_metadata_t* raw_metadata() const {
+ return metadata_reader_->raw_metadata();
+ };
+
+ // Check if a given template type is supported.
+ bool TemplateSupported(int type);
+ // Validators (check that values are consistent with the capabilities
+ // this object represents/base requirements of the camera HAL).
+ bool StreamConfigurationSupported(
+ const camera3_stream_configuration_t* stream_config);
+ // Check that the inputs and outputs for a request don't conflict.
+ bool ReprocessingSupported(
+ const camera3_stream_t* input_stream,
+ const std::set<const camera3_stream_t*>& output_streams);
+
+ private:
+ // Constructor private to allow failing on bad input.
+ // Use NewStaticProperties instead.
+ StaticProperties(std::unique_ptr<const MetadataReader> metadata_reader,
+ int facing,
+ int orientation,
+ int32_t max_input_streams,
+ int32_t max_raw_output_streams,
+ int32_t max_non_stalling_output_streams,
+ int32_t max_stalling_output_streams,
+ std::set<uint8_t> request_capabilities,
+ CapabilitiesMap stream_capabilities,
+ ReprocessFormatMap supported_reprocess_outputs);
+
+ // Helper functions for StreamConfigurationSupported.
+ bool SanityCheckStreamConfiguration(
+ const camera3_stream_configuration_t* stream_config);
+ bool InputStreamsSupported(
+ const camera3_stream_configuration_t* stream_config);
+ bool OutputStreamsSupported(
+ const camera3_stream_configuration_t* stream_config);
+ bool OperationModeSupported(
+ const camera3_stream_configuration_t* stream_config);
+
+ const std::unique_ptr<const MetadataReader> metadata_reader_;
+ const int facing_;
+ const int orientation_;
+ const int32_t max_input_streams_;
+ const int32_t max_raw_output_streams_;
+ const int32_t max_non_stalling_output_streams_;
+ const int32_t max_stalling_output_streams_;
+ const std::set<uint8_t> request_capabilities_;
+ const CapabilitiesMap stream_capabilities_;
+ const ReprocessFormatMap supported_reprocess_outputs_;
+
+ DISALLOW_COPY_AND_ASSIGN(StaticProperties);
+};
+
+} // namespace default_camera_hal
+
+#endif // DEFAULT_CAMERA_HAL_STATIC_PROPERTIES_H_
diff --git a/modules/camera/3_4/static_properties_test.cpp b/modules/camera/3_4/static_properties_test.cpp
new file mode 100644
index 0000000..e78e343
--- /dev/null
+++ b/modules/camera/3_4/static_properties_test.cpp
@@ -0,0 +1,667 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "static_properties.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hardware/camera3.h>
+#include <system/camera.h>
+
+#include "metadata/metadata_reader_mock.h"
+
+using testing::AtMost;
+using testing::Expectation;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::Test;
+using testing::_;
+
+namespace default_camera_hal {
+
+class StaticPropertiesTest : public Test {
+ protected:
+ virtual void SetUp() {
+ // Ensure tests will probably fail if PrepareDUT isn't called.
+ dut_.reset();
+ mock_reader_ = std::make_unique<MetadataReaderMock>();
+ }
+
+ void PrepareDUT() {
+ dut_.reset(StaticProperties::NewStaticProperties(std::move(mock_reader_)));
+ }
+
+ void PrepareDefaultDUT() {
+ SetDefaultExpectations();
+ PrepareDUT();
+ ASSERT_NE(dut_, nullptr);
+ }
+
+ void SetDefaultExpectations() {
+ EXPECT_CALL(*mock_reader_, Facing(_))
+ .Times(AtMost(1))
+ .WillOnce(DoAll(SetArgPointee<0>(test_facing_), Return(0)));
+ EXPECT_CALL(*mock_reader_, Orientation(_))
+ .Times(AtMost(1))
+ .WillOnce(DoAll(SetArgPointee<0>(test_orientation_), Return(0)));
+ EXPECT_CALL(*mock_reader_, MaxInputStreams(_))
+ .Times(AtMost(1))
+ .WillOnce(DoAll(SetArgPointee<0>(test_max_inputs_), Return(0)));
+ EXPECT_CALL(*mock_reader_, MaxOutputStreams(_, _, _))
+ .Times(AtMost(1))
+ .WillOnce(DoAll(SetArgPointee<0>(test_max_raw_outputs_),
+ SetArgPointee<1>(test_max_non_stalling_outputs_),
+ SetArgPointee<2>(test_max_stalling_outputs_),
+ Return(0)));
+ EXPECT_CALL(*mock_reader_, RequestCapabilities(_))
+ .Times(AtMost(1))
+ .WillOnce(
+ DoAll(SetArgPointee<0>(test_request_capabilities_), Return(0)));
+ EXPECT_CALL(*mock_reader_, StreamConfigurations(_))
+ .Times(AtMost(1))
+ .WillOnce(DoAll(SetArgPointee<0>(test_configs_), Return(0)));
+ EXPECT_CALL(*mock_reader_, StreamStallDurations(_))
+ .Times(AtMost(1))
+ .WillOnce(DoAll(SetArgPointee<0>(test_stalls_), Return(0)));
+ EXPECT_CALL(*mock_reader_, ReprocessFormats(_))
+ .Times(AtMost(1))
+ .WillOnce(DoAll(SetArgPointee<0>(test_reprocess_map_), Return(0)));
+ }
+
+ camera3_stream_t MakeStream(int32_t format,
+ bool output = true,
+ bool input = false,
+ int32_t width = kWidth,
+ int32_t height = kHeight) {
+ int type = -1;
+ if (output && input) {
+ type = CAMERA3_STREAM_BIDIRECTIONAL;
+ } else if (output) {
+ type = CAMERA3_STREAM_OUTPUT;
+ } else if (input) {
+ type = CAMERA3_STREAM_INPUT;
+ }
+ return {static_cast<int>(type),
+ static_cast<uint32_t>(width),
+ static_cast<uint32_t>(height),
+ static_cast<int>(format)};
+ }
+
+ void ExpectConfigurationSupported(std::vector<camera3_stream_t>& streams,
+ bool expected) {
+ std::vector<camera3_stream_t*> stream_addresses;
+ for (size_t i = 0; i < streams.size(); ++i) {
+ stream_addresses.push_back(&streams[i]);
+ }
+ camera3_stream_configuration_t config = {
+ stream_addresses.size(),
+ stream_addresses.data(),
+ CAMERA3_STREAM_CONFIGURATION_NORMAL_MODE};
+ PrepareDefaultDUT();
+ EXPECT_EQ(dut_->StreamConfigurationSupported(&config), expected);
+ }
+
+ std::unique_ptr<StaticProperties> dut_;
+ std::unique_ptr<MetadataReaderMock> mock_reader_;
+
+ // Some helper values used for stream testing.
+ static constexpr int32_t kWidth = 320;
+ static constexpr int32_t kHeight = 240;
+ static constexpr int32_t kAlternateWidth = 640;
+ static constexpr int32_t kAlternateHeight = 480;
+
+ const int test_facing_ = CAMERA_FACING_FRONT;
+ const int test_orientation_ = 90;
+ const int32_t test_max_inputs_ = 3;
+ const int32_t test_max_raw_outputs_ = 1;
+ const int32_t test_max_non_stalling_outputs_ = 2;
+ const int32_t test_max_stalling_outputs_ = 3;
+ const std::set<uint8_t> test_request_capabilities_ = {
+ ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE,
+ ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR,
+ ANDROID_REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING};
+
+ // Some formats for various purposes (in various combinations,
+ // these types should be capable of testing all failure conditions).
+ const int32_t output_multisize_non_stalling_ = 1;
+ const int32_t bidirectional_self_supporting_stalling_ = 2;
+ const int32_t bidirectional_raw_ = HAL_PIXEL_FORMAT_RAW10;
+ const int32_t input_ = 3;
+ const int32_t other = input_;
+
+ const std::vector<StreamConfiguration> test_configs_ = {
+ {{{output_multisize_non_stalling_,
+ kWidth,
+ kHeight,
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT}}},
+ {{{output_multisize_non_stalling_,
+ kAlternateWidth,
+ kAlternateHeight,
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT}}},
+ {{{bidirectional_self_supporting_stalling_,
+ kWidth,
+ kHeight,
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT}}},
+ {{{bidirectional_self_supporting_stalling_,
+ kWidth,
+ kHeight,
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT}}},
+ {{{bidirectional_raw_,
+ kWidth,
+ kHeight,
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT}}},
+ {{{bidirectional_raw_,
+ kWidth,
+ kHeight,
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT}}},
+ {{{input_,
+ kWidth,
+ kHeight,
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT}}}};
+ // Raw having a stall duration shouldn't matter,
+ // it should still be counted as the raw type.
+ const std::vector<StreamStallDuration> test_stalls_ = {
+ {{{output_multisize_non_stalling_, kWidth, kHeight, 0}}},
+ {{{output_multisize_non_stalling_,
+ kAlternateWidth,
+ kAlternateHeight,
+ 0}}},
+ {{{bidirectional_self_supporting_stalling_, kWidth, kHeight, 10}}},
+ {{{bidirectional_raw_, kWidth, kHeight, 15}}}};
+ // Format 2 can go to itself or 1. 3 and RAW can only go to 1.
+ const ReprocessFormatMap test_reprocess_map_ = {
+ {bidirectional_self_supporting_stalling_,
+ {output_multisize_non_stalling_,
+ bidirectional_self_supporting_stalling_}},
+ {bidirectional_raw_, {output_multisize_non_stalling_}},
+ {input_, {output_multisize_non_stalling_}}};
+ // Codify the above information about format capabilities in some helpful
+ // vectors.
+ int32_t multi_size_format_ = 1;
+ const std::vector<int32_t> input_formats_ = {2, 3, HAL_PIXEL_FORMAT_RAW10};
+ const std::vector<int32_t> output_formats_ = {1, 2, HAL_PIXEL_FORMAT_RAW10};
+};
+
+TEST_F(StaticPropertiesTest, FactorySuccess) {
+ PrepareDefaultDUT();
+ EXPECT_EQ(dut_->facing(), test_facing_);
+ EXPECT_EQ(dut_->orientation(), test_orientation_);
+
+ // Stream configurations tested seperately.
+}
+
+TEST_F(StaticPropertiesTest, FactoryFailedFacing) {
+ SetDefaultExpectations();
+ // Override with a failure expectation.
+ EXPECT_CALL(*mock_reader_, Facing(_)).WillOnce(Return(99));
+ PrepareDUT();
+ EXPECT_EQ(dut_, nullptr);
+}
+
+TEST_F(StaticPropertiesTest, FactoryFailedOrientation) {
+ SetDefaultExpectations();
+ // Override with a failure expectation.
+ EXPECT_CALL(*mock_reader_, Orientation(_)).WillOnce(Return(99));
+ PrepareDUT();
+ EXPECT_EQ(dut_, nullptr);
+}
+
+TEST_F(StaticPropertiesTest, FactoryFailedMaxInputs) {
+ SetDefaultExpectations();
+ // Override with a failure expectation.
+ EXPECT_CALL(*mock_reader_, MaxInputStreams(_)).WillOnce(Return(99));
+ PrepareDUT();
+ EXPECT_EQ(dut_, nullptr);
+}
+
+TEST_F(StaticPropertiesTest, FactoryFailedMaxOutputs) {
+ SetDefaultExpectations();
+ // Override with a failure expectation.
+ EXPECT_CALL(*mock_reader_, MaxOutputStreams(_, _, _)).WillOnce(Return(99));
+ PrepareDUT();
+ EXPECT_EQ(dut_, nullptr);
+}
+
+TEST_F(StaticPropertiesTest, FactoryFailedRequestCapabilities) {
+ SetDefaultExpectations();
+ // Override with a failure expectation.
+ EXPECT_CALL(*mock_reader_, RequestCapabilities(_)).WillOnce(Return(99));
+ PrepareDUT();
+ EXPECT_EQ(dut_, nullptr);
+}
+
+TEST_F(StaticPropertiesTest, FactoryFailedStreamConfigs) {
+ SetDefaultExpectations();
+ // Override with a failure expectation.
+ EXPECT_CALL(*mock_reader_, StreamConfigurations(_)).WillOnce(Return(99));
+ PrepareDUT();
+ EXPECT_EQ(dut_, nullptr);
+}
+
+TEST_F(StaticPropertiesTest, FactoryFailedStallDurations) {
+ SetDefaultExpectations();
+ // Override with a failure expectation.
+ EXPECT_CALL(*mock_reader_, StreamStallDurations(_)).WillOnce(Return(99));
+ PrepareDUT();
+ EXPECT_EQ(dut_, nullptr);
+}
+
+TEST_F(StaticPropertiesTest, FactoryFailedReprocessFormats) {
+ SetDefaultExpectations();
+ // Override with a failure expectation.
+ EXPECT_CALL(*mock_reader_, ReprocessFormats(_)).WillOnce(Return(99));
+ PrepareDUT();
+ EXPECT_EQ(dut_, nullptr);
+}
+
+TEST_F(StaticPropertiesTest, FactoryNoReprocessFormats) {
+ // If there are no inputs allowed, the reprocess formats shouldn't matter.
+ SetDefaultExpectations();
+ // Override max inputs.
+ EXPECT_CALL(*mock_reader_, MaxInputStreams(_))
+ .WillOnce(DoAll(SetArgPointee<0>(0), Return(0)));
+ // Override reprocess formats with a failure expectation.
+ EXPECT_CALL(*mock_reader_, ReprocessFormats(_))
+ .Times(AtMost(1))
+ .WillOnce(Return(99));
+ PrepareDUT();
+ // Should be ok.
+ EXPECT_NE(dut_, nullptr);
+}
+
+TEST_F(StaticPropertiesTest, FactoryInvalidCapabilities) {
+ SetDefaultExpectations();
+ // Override configs with an extra output format.
+ std::vector<StreamConfiguration> configs = test_configs_;
+ configs.push_back(
+ {{{5,
+ kWidth,
+ kHeight,
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT}}});
+ EXPECT_CALL(*mock_reader_, StreamConfigurations(_))
+ .WillOnce(DoAll(SetArgPointee<0>(configs), Return(0)));
+ PrepareDUT();
+ // Should fail because not every output has a stall.
+ EXPECT_EQ(dut_, nullptr);
+}
+
+TEST_F(StaticPropertiesTest, InvalidReprocessNoInputs) {
+ SetDefaultExpectations();
+ // Override configs by removing all inputs.
+ std::vector<StreamConfiguration> configs = test_configs_;
+ for (auto it = configs.begin(); it != configs.end();) {
+ if ((*it).direction ==
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT) {
+ it = configs.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ EXPECT_CALL(*mock_reader_, StreamConfigurations(_))
+ .WillOnce(DoAll(SetArgPointee<0>(configs), Return(0)));
+ PrepareDUT();
+ // Should fail because inputs are supported but there are no input formats.
+ EXPECT_EQ(dut_, nullptr);
+}
+
+TEST_F(StaticPropertiesTest, InvalidReprocessExtraInput) {
+ SetDefaultExpectations();
+ // Override configs with an extra input format.
+ std::vector<StreamConfiguration> configs = test_configs_;
+ configs.push_back({{{5,
+ kWidth,
+ kHeight,
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT}}});
+ EXPECT_CALL(*mock_reader_, StreamConfigurations(_))
+ .WillOnce(DoAll(SetArgPointee<0>(configs), Return(0)));
+ PrepareDUT();
+ // Should fail because no reprocess outputs are listed for the extra input.
+ EXPECT_EQ(dut_, nullptr);
+}
+
+TEST_F(StaticPropertiesTest, InvalidReprocessExtraMapEntry) {
+ SetDefaultExpectations();
+ // Override the reprocess map with an extra entry.
+ ReprocessFormatMap reprocess_map = test_reprocess_map_;
+ reprocess_map[5] = {1};
+ EXPECT_CALL(*mock_reader_, ReprocessFormats(_))
+ .WillOnce(DoAll(SetArgPointee<0>(reprocess_map), Return(0)));
+ PrepareDUT();
+ // Should fail because the extra map entry doesn't correspond to an input.
+ EXPECT_EQ(dut_, nullptr);
+}
+
+TEST_F(StaticPropertiesTest, InvalidReprocessWrongMapEntries) {
+ SetDefaultExpectations();
+ // Override the reprocess map replacing the entry for the
+ // input-only format with the output-only format.
+ ReprocessFormatMap reprocess_map = test_reprocess_map_;
+ reprocess_map.erase(input_);
+ reprocess_map[output_multisize_non_stalling_] = {1};
+ EXPECT_CALL(*mock_reader_, ReprocessFormats(_))
+ .WillOnce(DoAll(SetArgPointee<0>(reprocess_map), Return(0)));
+ PrepareDUT();
+ // Should fail because not all input formats are present/
+ // one of the map "input" formats is output only.
+ EXPECT_EQ(dut_, nullptr);
+}
+
+TEST_F(StaticPropertiesTest, InvalidReprocessNotAnOutput) {
+ SetDefaultExpectations();
+ // Override the reprocess map with a non-output output entry.
+ ReprocessFormatMap reprocess_map = test_reprocess_map_;
+ reprocess_map[input_].insert(input_);
+ EXPECT_CALL(*mock_reader_, ReprocessFormats(_))
+ .WillOnce(DoAll(SetArgPointee<0>(reprocess_map), Return(0)));
+ PrepareDUT();
+ // Should fail because a specified output format doesn't support output.
+ EXPECT_EQ(dut_, nullptr);
+}
+
+TEST_F(StaticPropertiesTest, TemplatesValid) {
+ PrepareDefaultDUT();
+ for (int i = 1; i < CAMERA3_TEMPLATE_COUNT; ++i) {
+ EXPECT_TRUE(dut_->TemplateSupported(i));
+ }
+}
+
+TEST_F(StaticPropertiesTest, ConfigureSingleOutput) {
+ std::vector<camera3_stream_t> streams;
+ streams.push_back(MakeStream(output_multisize_non_stalling_));
+ ExpectConfigurationSupported(streams, true);
+}
+
+TEST_F(StaticPropertiesTest, ConfigureMultipleOutputs) {
+ std::vector<camera3_stream_t> streams;
+ // 2 outputs, of different sizes.
+ streams.push_back(MakeStream(bidirectional_raw_));
+ // Use the alternate size.
+ streams.push_back(MakeStream(output_multisize_non_stalling_,
+ true,
+ false,
+ kAlternateWidth,
+ kAlternateHeight));
+ ExpectConfigurationSupported(streams, true);
+}
+
+TEST_F(StaticPropertiesTest, ConfigureInput) {
+ std::vector<camera3_stream_t> streams;
+ // Single input -> different output.
+ streams.push_back(MakeStream(input_, false, true));
+ // Use the alternate size, it should be ok.
+ streams.push_back(MakeStream(output_multisize_non_stalling_,
+ true,
+ false,
+ kAlternateWidth,
+ kAlternateHeight));
+ ExpectConfigurationSupported(streams, true);
+}
+
+TEST_F(StaticPropertiesTest, ConfigureBidirectional) {
+ std::vector<camera3_stream_t> streams;
+ // Single input -> same output.
+ streams.push_back(
+ MakeStream(bidirectional_self_supporting_stalling_, true, true));
+ ExpectConfigurationSupported(streams, true);
+}
+
+TEST_F(StaticPropertiesTest, ConfigureMultipleReprocess) {
+ std::vector<camera3_stream_t> streams;
+ // Single input -> multiple outputs.
+ streams.push_back(
+ MakeStream(bidirectional_self_supporting_stalling_, true, true));
+ streams.push_back(MakeStream(output_multisize_non_stalling_));
+ ExpectConfigurationSupported(streams, true);
+}
+
+TEST_F(StaticPropertiesTest, ConfigureNull) {
+ PrepareDefaultDUT();
+ EXPECT_FALSE(dut_->StreamConfigurationSupported(nullptr));
+}
+
+TEST_F(StaticPropertiesTest, ConfigureEmptyStreams) {
+ std::vector<camera3_stream_t*> streams(1);
+ camera3_stream_configuration_t config = {
+ 0, streams.data(), CAMERA3_STREAM_CONFIGURATION_NORMAL_MODE};
+ PrepareDefaultDUT();
+ EXPECT_FALSE(dut_->StreamConfigurationSupported(&config));
+}
+
+TEST_F(StaticPropertiesTest, ConfigureNullStreams) {
+ std::vector<camera3_stream_t*> streams(2, nullptr);
+ camera3_stream_configuration_t config = {
+ streams.size(), streams.data(), CAMERA3_STREAM_CONFIGURATION_NORMAL_MODE};
+ PrepareDefaultDUT();
+ EXPECT_FALSE(dut_->StreamConfigurationSupported(&config));
+}
+
+TEST_F(StaticPropertiesTest, ConfigureNullStreamVector) {
+ // Even if the camera claims to have multiple streams, check for null.
+ camera3_stream_configuration_t config = {
+ 3, nullptr, CAMERA3_STREAM_CONFIGURATION_NORMAL_MODE};
+ PrepareDefaultDUT();
+ EXPECT_FALSE(dut_->StreamConfigurationSupported(&config));
+}
+
+TEST_F(StaticPropertiesTest, ConfigureNoOutput) {
+ std::vector<camera3_stream_t> streams;
+ // Only an input stream, no output.
+ streams.push_back(MakeStream(input_, false, true));
+ ExpectConfigurationSupported(streams, false);
+}
+
+TEST_F(StaticPropertiesTest, ConfigureInvalidType) {
+ std::vector<camera3_stream_t> streams;
+ // Not input, output, or bidirectional.
+ streams.push_back(MakeStream(output_multisize_non_stalling_, false, false));
+ ExpectConfigurationSupported(streams, false);
+}
+
+TEST_F(StaticPropertiesTest, ConfigureSpecFormatDoesNotExist) {
+ std::vector<camera3_stream_t> streams;
+ // Format 99 is not supported in any form.
+ streams.push_back(MakeStream(99));
+ ExpectConfigurationSupported(streams, false);
+}
+
+TEST_F(StaticPropertiesTest, ConfigureSpecSizeDoesNotExist) {
+ std::vector<camera3_stream_t> streams;
+ // Size 99 x 99 not supported for the output format.
+ streams.push_back(
+ MakeStream(output_multisize_non_stalling_, true, false, 99, 99));
+ ExpectConfigurationSupported(streams, false);
+}
+
+TEST_F(StaticPropertiesTest, ConfigureNotAnInput) {
+ std::vector<camera3_stream_t> streams;
+ streams.push_back(MakeStream(output_multisize_non_stalling_));
+ // Can't use output-only format as an input.
+ streams.push_back(MakeStream(output_multisize_non_stalling_, false, true));
+ ExpectConfigurationSupported(streams, false);
+}
+
+TEST_F(StaticPropertiesTest, ConfigureNotAnOutput) {
+ std::vector<camera3_stream_t> streams;
+ // Can't use input-only format as an output.
+ streams.push_back(MakeStream(input_));
+ ExpectConfigurationSupported(streams, false);
+}
+
+TEST_F(StaticPropertiesTest, ConfigureTooManyInputs) {
+ std::vector<camera3_stream_t> streams;
+ // At the threshold is ok.
+ for (int32_t i = 0; i < test_max_inputs_; ++i) {
+ streams.push_back(MakeStream(input_, false, true));
+ }
+ // Have a valid output still.
+ streams.push_back(MakeStream(output_multisize_non_stalling_));
+ ExpectConfigurationSupported(streams, false);
+
+ // Reset.
+ mock_reader_ = std::make_unique<MetadataReaderMock>();
+ streams.clear();
+
+ // Try again with too many.
+ for (int32_t i = 0; i <= test_max_inputs_; ++i) {
+ streams.push_back(MakeStream(input_, false, true));
+ }
+ // Have a valid output still.
+ streams.push_back(MakeStream(output_multisize_non_stalling_));
+ ExpectConfigurationSupported(streams, false);
+}
+
+TEST_F(StaticPropertiesTest, ConfigureTooManyRaw) {
+ std::vector<camera3_stream_t> streams;
+ // At the threshold is ok.
+ for (int32_t i = 0; i < test_max_raw_outputs_; ++i) {
+ streams.push_back(MakeStream(bidirectional_raw_));
+ }
+ ExpectConfigurationSupported(streams, true);
+
+ // Reset.
+ mock_reader_ = std::make_unique<MetadataReaderMock>();
+ streams.clear();
+
+ // Try again with too many.
+ for (int32_t i = 0; i <= test_max_raw_outputs_; ++i) {
+ streams.push_back(MakeStream(bidirectional_raw_));
+ }
+ ExpectConfigurationSupported(streams, false);
+}
+
+TEST_F(StaticPropertiesTest, ConfigureTooManyStalling) {
+ std::vector<camera3_stream_t> streams;
+ // At the threshold is ok.
+ for (int32_t i = 0; i < test_max_stalling_outputs_; ++i) {
+ streams.push_back(MakeStream(bidirectional_self_supporting_stalling_));
+ }
+ ExpectConfigurationSupported(streams, true);
+
+ // Reset.
+ mock_reader_ = std::make_unique<MetadataReaderMock>();
+ streams.clear();
+
+ // Try again with too many.
+ for (int32_t i = 0; i <= test_max_stalling_outputs_; ++i) {
+ streams.push_back(MakeStream(bidirectional_self_supporting_stalling_));
+ }
+ ExpectConfigurationSupported(streams, false);
+}
+
+TEST_F(StaticPropertiesTest, ConfigureTooManyNonStalling) {
+ std::vector<camera3_stream_t> streams;
+ // At the threshold is ok.
+ for (int32_t i = 0; i < test_max_non_stalling_outputs_; ++i) {
+ streams.push_back(MakeStream(output_multisize_non_stalling_));
+ }
+ ExpectConfigurationSupported(streams, true);
+
+ // Reset.
+ mock_reader_ = std::make_unique<MetadataReaderMock>();
+ streams.clear();
+
+ // Try again with too many.
+ for (int32_t i = 0; i <= test_max_non_stalling_outputs_; ++i) {
+ streams.push_back(MakeStream(output_multisize_non_stalling_));
+ }
+ ExpectConfigurationSupported(streams, false);
+}
+
+TEST_F(StaticPropertiesTest, ConfigureUnuspportedInput) {
+ std::vector<camera3_stream_t> streams;
+ streams.push_back(MakeStream(input_, false, true));
+ streams.push_back(MakeStream(bidirectional_raw_));
+ // No matching output format for input.
+ ExpectConfigurationSupported(streams, false);
+}
+
+TEST_F(StaticPropertiesTest, ConfigureUnsupportedOutput) {
+ std::vector<camera3_stream_t> streams;
+ streams.push_back(MakeStream(input_, false, true));
+ // The universal output does match input.
+ streams.push_back(MakeStream(output_multisize_non_stalling_));
+ // Raw does not match input.
+ streams.push_back(MakeStream(bidirectional_raw_));
+ // Input is matched; it's ok that raw doesn't match (only the actual
+ // requests care).
+ ExpectConfigurationSupported(streams, true);
+}
+
+TEST_F(StaticPropertiesTest, ConfigureUnsupportedBidirectional) {
+ std::vector<camera3_stream_t> streams;
+ // The test raw format, while supporting both input and output,
+ // does not actually support itself.
+ streams.push_back(MakeStream(bidirectional_raw_, true, true));
+ ExpectConfigurationSupported(streams, false);
+}
+
+TEST_F(StaticPropertiesTest, ConfigureBadOperationMode) {
+ // A valid stream set.
+ camera3_stream_t stream = MakeStream(output_multisize_non_stalling_);
+ camera3_stream_t* stream_address = &stream;
+ // But not a valid config.
+ camera3_stream_configuration_t config = {
+ 1,
+ &stream_address,
+ 99 // Not a valid operation mode.
+ };
+ PrepareDefaultDUT();
+ EXPECT_FALSE(dut_->StreamConfigurationSupported(&config));
+}
+
+TEST_F(StaticPropertiesTest, ReprocessingSingleOutput) {
+ camera3_stream_t input_stream = MakeStream(input_);
+ camera3_stream_t output_stream = MakeStream(output_multisize_non_stalling_);
+ PrepareDefaultDUT();
+ EXPECT_TRUE(dut_->ReprocessingSupported(&input_stream, {&output_stream}));
+}
+
+TEST_F(StaticPropertiesTest, ReprocessingMultipleOutputs) {
+ camera3_stream_t input_stream =
+ MakeStream(bidirectional_self_supporting_stalling_, false, true);
+ // Bi-directional self-supporting supports the universal output and itself.
+ camera3_stream_t output_stream1 = MakeStream(output_multisize_non_stalling_);
+ camera3_stream_t output_stream2 =
+ MakeStream(bidirectional_self_supporting_stalling_);
+ PrepareDefaultDUT();
+ EXPECT_TRUE(dut_->ReprocessingSupported(&input_stream,
+ {&output_stream1, &output_stream2}));
+}
+
+TEST_F(StaticPropertiesTest, ReprocessingNoInput) {
+ camera3_stream_t output_stream = MakeStream(output_multisize_non_stalling_);
+ PrepareDefaultDUT();
+ EXPECT_FALSE(dut_->ReprocessingSupported(nullptr, {&output_stream}));
+}
+
+TEST_F(StaticPropertiesTest, ReprocessingNoOutput) {
+ camera3_stream_t input_stream = MakeStream(input_);
+ PrepareDefaultDUT();
+ EXPECT_FALSE(dut_->ReprocessingSupported(&input_stream, {}));
+}
+
+TEST_F(StaticPropertiesTest, ReprocessingInvalidOutput) {
+ camera3_stream_t input_stream = MakeStream(input_, false, true);
+ // The universal output does match input.
+ camera3_stream_t output_stream1 = MakeStream(output_multisize_non_stalling_);
+ // Raw does not match input.
+ camera3_stream_t output_stream2 = MakeStream(bidirectional_raw_);
+ PrepareDefaultDUT();
+ EXPECT_FALSE(dut_->ReprocessingSupported(&input_stream,
+ {&output_stream1, &output_stream2}));
+}
+
+} // namespace default_camera_hal
diff --git a/modules/camera/3_4/stream_format.cpp b/modules/camera/3_4/stream_format.cpp
new file mode 100644
index 0000000..c85c26b
--- /dev/null
+++ b/modules/camera/3_4/stream_format.cpp
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "stream_format.h"
+
+#include <linux/videodev2.h>
+
+#include <system/graphics.h>
+
+#include "common.h"
+
+namespace v4l2_camera_hal {
+
+StreamFormat::StreamFormat(int format, uint32_t width, uint32_t height)
+ // TODO(b/30000211): multiplanar support.
+ : type_(V4L2_BUF_TYPE_VIDEO_CAPTURE),
+ v4l2_pixel_format_(StreamFormat::HalToV4L2PixelFormat(format)),
+ width_(width),
+ height_(height),
+ bytes_per_line_(0),
+ min_buffer_size_(0) {}
+
+StreamFormat::StreamFormat(const v4l2_format& format)
+ : type_(format.type),
+ // TODO(b/30000211): multiplanar support.
+ v4l2_pixel_format_(format.fmt.pix.pixelformat),
+ width_(format.fmt.pix.width),
+ height_(format.fmt.pix.height),
+ bytes_per_line_(format.fmt.pix.bytesperline),
+ min_buffer_size_(format.fmt.pix.sizeimage) {}
+
+void StreamFormat::FillFormatRequest(v4l2_format* format) const {
+ memset(format, 0, sizeof(*format));
+ format->type = type_;
+ format->fmt.pix.pixelformat = v4l2_pixel_format_;
+ format->fmt.pix.width = width_;
+ format->fmt.pix.height = height_;
+ // Bytes per line and min buffer size are outputs set by the driver,
+ // not part of the request.
+}
+
+FormatCategory StreamFormat::Category() const {
+ switch (v4l2_pixel_format_) {
+ case V4L2_PIX_FMT_JPEG:
+ return kFormatCategoryStalling;
+ case V4L2_PIX_FMT_YUV420: // Fall through.
+ case V4L2_PIX_FMT_BGR32:
+ return kFormatCategoryNonStalling;
+ default:
+ // Note: currently no supported RAW formats.
+ return kFormatCategoryUnknown;
+ }
+}
+
+bool StreamFormat::operator==(const StreamFormat& other) const {
+ // Used to check that a requested format was actually set, so
+ // don't compare bytes per line or min buffer size.
+ return (type_ == other.type_ &&
+ v4l2_pixel_format_ == other.v4l2_pixel_format_ &&
+ width_ == other.width_ && height_ == other.height_);
+}
+
+bool StreamFormat::operator!=(const StreamFormat& other) const {
+ return !(*this == other);
+}
+
+int StreamFormat::V4L2ToHalPixelFormat(uint32_t v4l2_pixel_format) {
+ // Translate V4L2 format to HAL format.
+ int hal_pixel_format = -1;
+ switch (v4l2_pixel_format) {
+ case V4L2_PIX_FMT_JPEG:
+ hal_pixel_format = HAL_PIXEL_FORMAT_BLOB;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ hal_pixel_format = HAL_PIXEL_FORMAT_YCbCr_420_888;
+ break;
+ case V4L2_PIX_FMT_BGR32:
+ hal_pixel_format = HAL_PIXEL_FORMAT_RGBA_8888;
+ break;
+ default:
+ // Unrecognized format.
+ HAL_LOGV("Unrecognized v4l2 pixel format %u", v4l2_pixel_format);
+ break;
+ }
+ return hal_pixel_format;
+}
+
+uint32_t StreamFormat::HalToV4L2PixelFormat(int hal_pixel_format) {
+ // Translate HAL format to V4L2 format.
+ uint32_t v4l2_pixel_format = 0;
+ switch (hal_pixel_format) {
+ case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED: // fall-through.
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ // Should be RGB32, but RPi doesn't support that.
+ // For now we accept that the colors will be off.
+ v4l2_pixel_format = V4L2_PIX_FMT_BGR32;
+ break;
+ case HAL_PIXEL_FORMAT_YCbCr_420_888:
+ v4l2_pixel_format = V4L2_PIX_FMT_YUV420;
+ break;
+ case HAL_PIXEL_FORMAT_BLOB:
+ v4l2_pixel_format = V4L2_PIX_FMT_JPEG;
+ break;
+ default:
+ // Unrecognized format.
+ HAL_LOGV("Unrecognized HAL pixel format %d", hal_pixel_format);
+ break;
+ }
+ return v4l2_pixel_format;
+}
+
+} // namespace v4l2_camera_hal
diff --git a/modules/camera/3_4/stream_format.h b/modules/camera/3_4/stream_format.h
new file mode 100644
index 0000000..66c5965
--- /dev/null
+++ b/modules/camera/3_4/stream_format.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef V4L2_CAMERA_HAL_STREAM_FORMAT_H_
+#define V4L2_CAMERA_HAL_STREAM_FORMAT_H_
+
+#include <string.h>
+
+#include <linux/videodev2.h>
+
+#include "common.h"
+
+namespace v4l2_camera_hal {
+
+enum FormatCategory {
+ kFormatCategoryRaw,
+ kFormatCategoryStalling,
+ kFormatCategoryNonStalling,
+ kFormatCategoryUnknown,
+};
+
+class StreamFormat {
+ public:
+ StreamFormat(int format, uint32_t width, uint32_t height);
+ StreamFormat(const v4l2_format& format);
+ virtual ~StreamFormat() = default;
+ // Only uint32_t members, use default generated copy and assign.
+
+ void FillFormatRequest(v4l2_format* format) const;
+ FormatCategory Category() const;
+
+ // Accessors.
+ inline uint32_t type() const { return type_; };
+ inline uint32_t bytes_per_line() const { return bytes_per_line_; };
+
+ bool operator==(const StreamFormat& other) const;
+ bool operator!=(const StreamFormat& other) const;
+
+ // HAL <-> V4L2 conversions
+ // Returns 0 for unrecognized.
+ static uint32_t HalToV4L2PixelFormat(int hal_pixel_format);
+ // Returns -1 for unrecognized.
+ static int V4L2ToHalPixelFormat(uint32_t v4l2_pixel_format);
+
+ private:
+ uint32_t type_;
+ uint32_t v4l2_pixel_format_;
+ uint32_t width_;
+ uint32_t height_;
+ uint32_t bytes_per_line_;
+ uint32_t min_buffer_size_;
+};
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_STREAM_FORMAT_H_
diff --git a/modules/camera/3_4/v4l2_camera.cpp b/modules/camera/3_4/v4l2_camera.cpp
new file mode 100644
index 0000000..3e5b859
--- /dev/null
+++ b/modules/camera/3_4/v4l2_camera.cpp
@@ -0,0 +1,451 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "v4l2_camera.h"
+
+#include <fcntl.h>
+#include <linux/videodev2.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <cstdlib>
+
+#include <camera/CameraMetadata.h>
+#include <hardware/camera3.h>
+
+#include "common.h"
+#include "function_thread.h"
+#include "metadata/metadata_common.h"
+#include "stream_format.h"
+#include "v4l2_metadata_factory.h"
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
+
+namespace v4l2_camera_hal {
+
+// Helper function for managing metadata.
+static std::vector<int32_t> getMetadataKeys(const camera_metadata_t* metadata) {
+ std::vector<int32_t> keys;
+ size_t num_entries = get_camera_metadata_entry_count(metadata);
+ for (size_t i = 0; i < num_entries; ++i) {
+ camera_metadata_ro_entry_t entry;
+ get_camera_metadata_ro_entry(metadata, i, &entry);
+ keys.push_back(entry.tag);
+ }
+ return keys;
+}
+
+V4L2Camera* V4L2Camera::NewV4L2Camera(int id, const std::string path) {
+ HAL_LOG_ENTER();
+
+ std::shared_ptr<V4L2Wrapper> v4l2_wrapper(V4L2Wrapper::NewV4L2Wrapper(path));
+ if (!v4l2_wrapper) {
+ HAL_LOGE("Failed to initialize V4L2 wrapper.");
+ return nullptr;
+ }
+
+ std::unique_ptr<Metadata> metadata;
+ int res = GetV4L2Metadata(v4l2_wrapper, &metadata);
+ if (res) {
+ HAL_LOGE("Failed to initialize V4L2 metadata: %d", res);
+ return nullptr;
+ }
+
+ return new V4L2Camera(id, std::move(v4l2_wrapper), std::move(metadata));
+}
+
+V4L2Camera::V4L2Camera(int id,
+ std::shared_ptr<V4L2Wrapper> v4l2_wrapper,
+ std::unique_ptr<Metadata> metadata)
+ : default_camera_hal::Camera(id),
+ device_(std::move(v4l2_wrapper)),
+ metadata_(std::move(metadata)),
+ max_input_streams_(0),
+ max_output_streams_({{0, 0, 0}}),
+ buffer_enqueuer_(new FunctionThread(
+ std::bind(&V4L2Camera::enqueueRequestBuffers, this))),
+ buffer_dequeuer_(new FunctionThread(
+ std::bind(&V4L2Camera::dequeueRequestBuffers, this))) {
+ HAL_LOG_ENTER();
+}
+
+V4L2Camera::~V4L2Camera() {
+ HAL_LOG_ENTER();
+}
+
+int V4L2Camera::connect() {
+ HAL_LOG_ENTER();
+
+ if (connection_) {
+ HAL_LOGE("Already connected. Please disconnect and try again.");
+ return -EIO;
+ }
+
+ connection_.reset(new V4L2Wrapper::Connection(device_));
+ if (connection_->status()) {
+ HAL_LOGE("Failed to connect to device.");
+ return connection_->status();
+ }
+
+ // TODO(b/29185945): confirm this is a supported device.
+ // This is checked by the HAL, but the device at |device_|'s path may
+ // not be the same one that was there when the HAL was loaded.
+ // (Alternatively, better hotplugging support may make this unecessary
+ // by disabling cameras that get disconnected and checking newly connected
+ // cameras, so connect() is never called on an unsupported camera)
+
+ // TODO(b/29158098): Inform service of any flashes that are no longer
+ // available because this camera is in use.
+ return 0;
+}
+
+void V4L2Camera::disconnect() {
+ HAL_LOG_ENTER();
+
+ connection_.reset();
+
+ // TODO(b/29158098): Inform service of any flashes that are available again
+ // because this camera is no longer in use.
+}
+
+int V4L2Camera::flushBuffers() {
+ HAL_LOG_ENTER();
+
+ int res = device_->StreamOff();
+
+ // This is not strictly necessary, but prevents a buildup of aborted
+ // requests in the in_flight_ map. These should be cleared
+ // whenever the stream is turned off.
+ std::lock_guard<std::mutex> guard(in_flight_lock_);
+ in_flight_.clear();
+
+ return res;
+}
+
+int V4L2Camera::initStaticInfo(android::CameraMetadata* out) {
+ HAL_LOG_ENTER();
+
+ int res = metadata_->FillStaticMetadata(out);
+ if (res) {
+ HAL_LOGE("Failed to get static metadata.");
+ return res;
+ }
+
+ // Extract max streams for use in verifying stream configs.
+ res = SingleTagValue(
+ *out, ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS, &max_input_streams_);
+ if (res) {
+ HAL_LOGE("Failed to get max num input streams from static metadata.");
+ return res;
+ }
+ res = SingleTagValue(
+ *out, ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS, &max_output_streams_);
+ if (res) {
+ HAL_LOGE("Failed to get max num output streams from static metadata.");
+ return res;
+ }
+
+ return 0;
+}
+
+int V4L2Camera::initTemplate(int type, android::CameraMetadata* out) {
+ HAL_LOG_ENTER();
+
+ return metadata_->GetRequestTemplate(type, out);
+}
+
+void V4L2Camera::initDeviceInfo(camera_info_t* info) {
+ HAL_LOG_ENTER();
+
+ // TODO(b/31044975): move this into device interface.
+ // For now, just constants.
+ info->resource_cost = 100;
+ info->conflicting_devices = nullptr;
+ info->conflicting_devices_length = 0;
+}
+
+int V4L2Camera::initDevice() {
+ HAL_LOG_ENTER();
+
+ // Start the buffer enqueue/dequeue threads if they're not already running.
+ if (!buffer_enqueuer_->isRunning()) {
+ android::status_t res = buffer_enqueuer_->run("Enqueue buffers");
+ if (res != android::OK) {
+ HAL_LOGE("Failed to start buffer enqueue thread: %d", res);
+ return -ENODEV;
+ }
+ }
+ if (!buffer_dequeuer_->isRunning()) {
+ android::status_t res = buffer_dequeuer_->run("Dequeue buffers");
+ if (res != android::OK) {
+ HAL_LOGE("Failed to start buffer dequeue thread: %d", res);
+ return -ENODEV;
+ }
+ }
+
+ return 0;
+}
+
+int V4L2Camera::enqueueRequest(
+ std::shared_ptr<default_camera_hal::CaptureRequest> request) {
+ HAL_LOG_ENTER();
+
+ // Assume request validated before calling this function.
+ // (For now, always exactly 1 output buffer, no inputs).
+ {
+ std::lock_guard<std::mutex> guard(request_queue_lock_);
+ request_queue_.push(request);
+ requests_available_.notify_one();
+ }
+
+ return 0;
+}
+
+std::shared_ptr<default_camera_hal::CaptureRequest>
+V4L2Camera::dequeueRequest() {
+ std::unique_lock<std::mutex> lock(request_queue_lock_);
+ while (request_queue_.empty()) {
+ requests_available_.wait(lock);
+ }
+
+ std::shared_ptr<default_camera_hal::CaptureRequest> request =
+ request_queue_.front();
+ request_queue_.pop();
+ return request;
+}
+
+bool V4L2Camera::enqueueRequestBuffers() {
+ // Get a request from the queue (blocks this thread until one is available).
+ std::shared_ptr<default_camera_hal::CaptureRequest> request =
+ dequeueRequest();
+
+ // Assume request validated before being added to the queue
+ // (For now, always exactly 1 output buffer, no inputs).
+
+ // Setting and getting settings are best effort here,
+ // since there's no way to know through V4L2 exactly what
+ // settings are used for a buffer unless we were to enqueue them
+ // one at a time, which would be too slow.
+
+ // Set the requested settings
+ int res = metadata_->SetRequestSettings(request->settings);
+ if (res) {
+ HAL_LOGE("Failed to set settings.");
+ completeRequest(request, res);
+ return true;
+ }
+
+ // Replace the requested settings with a snapshot of
+ // the used settings/state immediately before enqueue.
+ res = metadata_->FillResultMetadata(&request->settings);
+ if (res) {
+ // Note: since request is a shared pointer, this may happen if another
+ // thread has already decided to complete the request (e.g. via flushing),
+ // since that locks the metadata (in that case, this failing is fine,
+ // and completeRequest will simply do nothing).
+ HAL_LOGE("Failed to fill result metadata.");
+ completeRequest(request, res);
+ return true;
+ }
+
+ // Actually enqueue the buffer for capture.
+ {
+ std::lock_guard<std::mutex> guard(in_flight_lock_);
+
+ uint32_t index;
+ res = device_->EnqueueBuffer(&request->output_buffers[0], &index);
+ if (res) {
+ HAL_LOGE("Device failed to enqueue buffer.");
+ completeRequest(request, res);
+ return true;
+ }
+
+ // Make sure the stream is on (no effect if already on).
+ res = device_->StreamOn();
+ if (res) {
+ HAL_LOGE("Device failed to turn on stream.");
+ // Don't really want to send an error for only the request here,
+ // since this is a full device error.
+ // TODO: Should trigger full flush.
+ return true;
+ }
+
+ // Note: the request should be dequeued/flushed from the device
+ // before removal from in_flight_.
+ in_flight_.emplace(index, request);
+ buffers_in_flight_.notify_one();
+ }
+
+ return true;
+}
+
+bool V4L2Camera::dequeueRequestBuffers() {
+ // Dequeue a buffer.
+ uint32_t result_index;
+ int res = device_->DequeueBuffer(&result_index);
+ if (res) {
+ if (res == -EAGAIN) {
+ // EAGAIN just means nothing to dequeue right now.
+ // Wait until something is available before looping again.
+ std::unique_lock<std::mutex> lock(in_flight_lock_);
+ while (in_flight_.empty()) {
+ buffers_in_flight_.wait(lock);
+ }
+ } else {
+ HAL_LOGW("Device failed to dequeue buffer: %d", res);
+ }
+ return true;
+ }
+
+ // Find the associated request and complete it.
+ std::lock_guard<std::mutex> guard(in_flight_lock_);
+ auto index_request = in_flight_.find(result_index);
+ if (index_request != in_flight_.end()) {
+ completeRequest(index_request->second, 0);
+ in_flight_.erase(index_request);
+ } else {
+ HAL_LOGW(
+ "Dequeued non in-flight buffer index %d. "
+ "This buffer may have been flushed from the HAL but not the device.",
+ index_request->first);
+ }
+ return true;
+}
+
+bool V4L2Camera::validateDataspacesAndRotations(
+ const camera3_stream_configuration_t* stream_config) {
+ HAL_LOG_ENTER();
+
+ for (uint32_t i = 0; i < stream_config->num_streams; ++i) {
+ if (stream_config->streams[i]->rotation != CAMERA3_STREAM_ROTATION_0) {
+ HAL_LOGV("Rotation %d for stream %d not supported",
+ stream_config->streams[i]->rotation,
+ i);
+ return false;
+ }
+ // Accept all dataspaces, as it will just be overwritten below anyways.
+ }
+ return true;
+}
+
+int V4L2Camera::setupStreams(camera3_stream_configuration_t* stream_config) {
+ HAL_LOG_ENTER();
+
+ std::lock_guard<std::mutex> guard(in_flight_lock_);
+ // The framework should be enforcing this, but doesn't hurt to be safe.
+ if (!in_flight_.empty()) {
+ HAL_LOGE("Can't set device format while frames are in flight.");
+ return -EINVAL;
+ }
+
+ // stream_config should have been validated; assume at least 1 stream.
+ camera3_stream_t* stream = stream_config->streams[0];
+ int format = stream->format;
+ uint32_t width = stream->width;
+ uint32_t height = stream->height;
+
+ if (stream_config->num_streams > 1) {
+ // TODO(b/29939583): V4L2 doesn't actually support more than 1
+ // stream at a time. If not all streams are the same format
+ // and size, error. Note that this means the HAL is not spec-compliant.
+ // Technically, this error should be thrown during validation, but
+ // since it isn't a spec-valid error validation isn't set up to check it.
+ for (uint32_t i = 1; i < stream_config->num_streams; ++i) {
+ stream = stream_config->streams[i];
+ if (stream->format != format || stream->width != width ||
+ stream->height != height) {
+ HAL_LOGE(
+ "V4L2 only supports 1 stream configuration at a time "
+ "(stream 0 is format %d, width %u, height %u, "
+ "stream %d is format %d, width %u, height %u).",
+ format,
+ width,
+ height,
+ i,
+ stream->format,
+ stream->width,
+ stream->height);
+ return -EINVAL;
+ }
+ }
+ }
+
+ // Ensure the stream is off.
+ int res = device_->StreamOff();
+ if (res) {
+ HAL_LOGE("Device failed to turn off stream for reconfiguration: %d.", res);
+ return -ENODEV;
+ }
+
+ StreamFormat stream_format(format, width, height);
+ uint32_t max_buffers = 0;
+ res = device_->SetFormat(stream_format, &max_buffers);
+ if (res) {
+ HAL_LOGE("Failed to set device to correct format for stream: %d.", res);
+ return -ENODEV;
+ }
+
+ // Sanity check.
+ if (max_buffers < 1) {
+ HAL_LOGE("Setting format resulted in an invalid maximum of %u buffers.",
+ max_buffers);
+ return -ENODEV;
+ }
+
+ // Set all the streams dataspaces, usages, and max buffers.
+ for (uint32_t i = 0; i < stream_config->num_streams; ++i) {
+ stream = stream_config->streams[i];
+
+ // Max buffers as reported by the device.
+ stream->max_buffers = max_buffers;
+
+ // Usage: currently using sw graphics.
+ switch (stream->stream_type) {
+ case CAMERA3_STREAM_INPUT:
+ stream->usage = GRALLOC_USAGE_SW_READ_OFTEN;
+ break;
+ case CAMERA3_STREAM_OUTPUT:
+ stream->usage = GRALLOC_USAGE_SW_WRITE_OFTEN;
+ break;
+ case CAMERA3_STREAM_BIDIRECTIONAL:
+ stream->usage =
+ GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN;
+ break;
+ default:
+ // nothing to do.
+ break;
+ }
+
+ // Doesn't matter what was requested, we always use dataspace V0_JFIF.
+ // Note: according to camera3.h, this isn't allowed, but the camera
+ // framework team claims it's underdocumented; the implementation lets the
+ // HAL overwrite it. If this is changed, change the validation above.
+ stream->data_space = HAL_DATASPACE_V0_JFIF;
+ }
+
+ return 0;
+}
+
+bool V4L2Camera::isValidRequestSettings(
+ const android::CameraMetadata& settings) {
+ if (!metadata_->IsValidRequest(settings)) {
+ HAL_LOGE("Invalid request settings.");
+ return false;
+ }
+ return true;
+}
+
+} // namespace v4l2_camera_hal
diff --git a/modules/camera/3_4/v4l2_camera.h b/modules/camera/3_4/v4l2_camera.h
new file mode 100644
index 0000000..1db8d40
--- /dev/null
+++ b/modules/camera/3_4/v4l2_camera.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Loosely based on hardware/libhardware/modules/camera/ExampleCamera.h
+
+#ifndef V4L2_CAMERA_HAL_V4L2_CAMERA_H_
+#define V4L2_CAMERA_HAL_V4L2_CAMERA_H_
+
+#include <array>
+#include <condition_variable>
+#include <map>
+#include <queue>
+#include <string>
+
+#include <camera/CameraMetadata.h>
+#include <utils/StrongPointer.h>
+#include <utils/Thread.h>
+
+#include "camera.h"
+#include "common.h"
+#include "metadata/metadata.h"
+#include "v4l2_wrapper.h"
+
+namespace v4l2_camera_hal {
+// V4L2Camera is a specific V4L2-supported camera device. The Camera object
+// contains all logic common between all cameras (e.g. front and back cameras),
+// while a specific camera device (e.g. V4L2Camera) holds all specific
+// metadata and logic about that device.
+class V4L2Camera : public default_camera_hal::Camera {
+ public:
+ // Use this method to create V4L2Camera objects. Functionally equivalent
+ // to "new V4L2Camera", except that it may return nullptr in case of failure.
+ static V4L2Camera* NewV4L2Camera(int id, const std::string path);
+ ~V4L2Camera();
+
+ private:
+ // Constructor private to allow failing on bad input.
+ // Use NewV4L2Camera instead.
+ V4L2Camera(int id,
+ std::shared_ptr<V4L2Wrapper> v4l2_wrapper,
+ std::unique_ptr<Metadata> metadata);
+
+ // default_camera_hal::Camera virtual methods.
+ // Connect to the device: open dev nodes, etc.
+ int connect() override;
+ // Disconnect from the device: close dev nodes, etc.
+ void disconnect() override;
+ // Initialize static camera characteristics for individual device.
+ int initStaticInfo(android::CameraMetadata* out) override;
+ // Initialize a template of the given type.
+ int initTemplate(int type, android::CameraMetadata* out) override;
+ // Initialize device info: resource cost and conflicting devices
+ // (/conflicting devices length).
+ void initDeviceInfo(camera_info_t* info) override;
+ // Extra initialization of device when opened.
+ int initDevice() override;
+ // Verify stream configuration dataspaces and rotation values
+ bool validateDataspacesAndRotations(
+ const camera3_stream_configuration_t* stream_config) override;
+ // Set up the streams, including seting usage & max_buffers
+ int setupStreams(camera3_stream_configuration_t* stream_config) override;
+ // Verify settings are valid for a capture or reprocessing.
+ bool isValidRequestSettings(const android::CameraMetadata& settings) override;
+ // Enqueue a request to receive data from the camera.
+ int enqueueRequest(
+ std::shared_ptr<default_camera_hal::CaptureRequest> request) override;
+ // Flush in flight buffers.
+ int flushBuffers() override;
+
+ // Async request processing helpers.
+ // Dequeue a request from the waiting queue.
+ // Blocks until a request is available.
+ std::shared_ptr<default_camera_hal::CaptureRequest> dequeueRequest();
+
+ // Thread functions. Return true to loop, false to exit.
+ // Pass buffers for enqueued requests to the device.
+ bool enqueueRequestBuffers();
+ // Retreive buffers from the device.
+ bool dequeueRequestBuffers();
+
+ // V4L2 helper.
+ std::shared_ptr<V4L2Wrapper> device_;
+ std::unique_ptr<V4L2Wrapper::Connection> connection_;
+ std::unique_ptr<Metadata> metadata_;
+ std::mutex request_queue_lock_;
+ std::queue<std::shared_ptr<default_camera_hal::CaptureRequest>>
+ request_queue_;
+ std::mutex in_flight_lock_;
+ // Maps buffer index : request.
+ std::map<uint32_t, std::shared_ptr<default_camera_hal::CaptureRequest>>
+ in_flight_;
+ // Threads require holding an Android strong pointer.
+ android::sp<android::Thread> buffer_enqueuer_;
+ android::sp<android::Thread> buffer_dequeuer_;
+ std::condition_variable requests_available_;
+ std::condition_variable buffers_in_flight_;
+
+ int32_t max_input_streams_;
+ std::array<int, 3> max_output_streams_; // {raw, non-stalling, stalling}.
+
+ DISALLOW_COPY_AND_ASSIGN(V4L2Camera);
+};
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_V4L2_CAMERA_H_
diff --git a/modules/camera/3_4/v4l2_camera_hal.cpp b/modules/camera/3_4/v4l2_camera_hal.cpp
new file mode 100644
index 0000000..bfc8712
--- /dev/null
+++ b/modules/camera/3_4/v4l2_camera_hal.cpp
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Modified from hardware/libhardware/modules/camera/CameraHAL.cpp
+
+#include "v4l2_camera_hal.h"
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <linux/videodev2.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <cstdlib>
+#include <unordered_set>
+
+#include <android-base/parseint.h>
+
+#include "common.h"
+#include "v4l2_camera.h"
+
+/*
+ * This file serves as the entry point to the HAL. It is modified from the
+ * example default HAL available in hardware/libhardware/modules/camera.
+ * It contains the module structure and functions used by the framework
+ * to load and interface to this HAL, as well as the handles to the individual
+ * camera devices.
+ */
+
+namespace v4l2_camera_hal {
+
+// Default global camera hal.
+static V4L2CameraHAL gCameraHAL;
+
+V4L2CameraHAL::V4L2CameraHAL() : mCameras(), mCallbacks(NULL) {
+ HAL_LOG_ENTER();
+ // Adds all available V4L2 devices.
+ // List /dev nodes.
+ DIR* dir = opendir("/dev");
+ if (dir == NULL) {
+ HAL_LOGE("Failed to open /dev");
+ return;
+ }
+ // Find /dev/video* nodes.
+ dirent* ent;
+ std::vector<std::string> nodes;
+ while ((ent = readdir(dir))) {
+ std::string desired = "video";
+ size_t len = desired.size();
+ if (strncmp(desired.c_str(), ent->d_name, len) == 0) {
+ if (strlen(ent->d_name) > len && isdigit(ent->d_name[len])) {
+ // ent is a numbered video node.
+ nodes.push_back(std::string("/dev/") + ent->d_name);
+ HAL_LOGV("Found video node %s.", nodes.back().c_str());
+ }
+ }
+ }
+ // Test each for V4L2 support and uniqueness.
+ std::unordered_set<std::string> buses;
+ std::string bus;
+ v4l2_capability cap;
+ int fd;
+ int id = 0;
+ for (const auto& node : nodes) {
+ // Open the node.
+ fd = TEMP_FAILURE_RETRY(open(node.c_str(), O_RDWR));
+ if (fd < 0) {
+ HAL_LOGE("failed to open %s (%s).", node.c_str(), strerror(errno));
+ continue;
+ }
+ // Read V4L2 capabilities.
+ if (TEMP_FAILURE_RETRY(ioctl(fd, VIDIOC_QUERYCAP, &cap)) != 0) {
+ HAL_LOGE(
+ "VIDIOC_QUERYCAP on %s fail: %s.", node.c_str(), strerror(errno));
+ } else if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
+ HAL_LOGE("%s is not a V4L2 video capture device.", node.c_str());
+ } else {
+ // If the node is unique, add a camera for it.
+ bus = reinterpret_cast<char*>(cap.bus_info);
+ if (buses.insert(bus).second) {
+ HAL_LOGV("Found unique bus at %s.", node.c_str());
+ std::unique_ptr<V4L2Camera> cam(V4L2Camera::NewV4L2Camera(id++, node));
+ if (cam) {
+ mCameras.push_back(std::move(cam));
+ } else {
+ HAL_LOGE("Failed to initialize camera at %s.", node.c_str());
+ }
+ }
+ }
+ TEMP_FAILURE_RETRY(close(fd));
+ }
+}
+
+V4L2CameraHAL::~V4L2CameraHAL() {
+ HAL_LOG_ENTER();
+}
+
+int V4L2CameraHAL::getNumberOfCameras() {
+ HAL_LOGV("returns %d", mCameras.size());
+ return mCameras.size();
+}
+
+int V4L2CameraHAL::getCameraInfo(int id, camera_info_t* info) {
+ HAL_LOG_ENTER();
+ if (id < 0 || id >= mCameras.size()) {
+ return -EINVAL;
+ }
+ // TODO(b/29185945): Hotplugging: return -EINVAL if unplugged.
+ return mCameras[id]->getInfo(info);
+}
+
+int V4L2CameraHAL::setCallbacks(const camera_module_callbacks_t* callbacks) {
+ HAL_LOG_ENTER();
+ mCallbacks = callbacks;
+ return 0;
+}
+
+void V4L2CameraHAL::getVendorTagOps(vendor_tag_ops_t* ops) {
+ HAL_LOG_ENTER();
+ // No vendor ops for this HAL. From <hardware/camera_common.h>:
+ // "leave ops unchanged if no vendor tags are defined."
+}
+
+int V4L2CameraHAL::openLegacy(const hw_module_t* module,
+ const char* id,
+ uint32_t halVersion,
+ hw_device_t** device) {
+ HAL_LOG_ENTER();
+ // Not supported.
+ return -ENOSYS;
+}
+
+int V4L2CameraHAL::setTorchMode(const char* camera_id, bool enabled) {
+ HAL_LOG_ENTER();
+ // TODO(b/29158098): HAL is required to respond appropriately if
+ // the desired camera actually does support flash.
+ return -ENOSYS;
+}
+
+int V4L2CameraHAL::openDevice(const hw_module_t* module,
+ const char* name,
+ hw_device_t** device) {
+ HAL_LOG_ENTER();
+
+ if (module != &HAL_MODULE_INFO_SYM.common) {
+ HAL_LOGE(
+ "Invalid module %p expected %p", module, &HAL_MODULE_INFO_SYM.common);
+ return -EINVAL;
+ }
+
+ int id;
+ if (!android::base::ParseInt(name, &id, 0, getNumberOfCameras() - 1)) {
+ return -EINVAL;
+ }
+ // TODO(b/29185945): Hotplugging: return -EINVAL if unplugged.
+ return mCameras[id]->openDevice(module, device);
+}
+
+/*
+ * The framework calls the following wrappers, which in turn
+ * call the corresponding methods of the global HAL object.
+ */
+
+static int get_number_of_cameras() {
+ return gCameraHAL.getNumberOfCameras();
+}
+
+static int get_camera_info(int id, struct camera_info* info) {
+ return gCameraHAL.getCameraInfo(id, info);
+}
+
+static int set_callbacks(const camera_module_callbacks_t* callbacks) {
+ return gCameraHAL.setCallbacks(callbacks);
+}
+
+static void get_vendor_tag_ops(vendor_tag_ops_t* ops) {
+ return gCameraHAL.getVendorTagOps(ops);
+}
+
+static int open_legacy(const hw_module_t* module,
+ const char* id,
+ uint32_t halVersion,
+ hw_device_t** device) {
+ return gCameraHAL.openLegacy(module, id, halVersion, device);
+}
+
+static int set_torch_mode(const char* camera_id, bool enabled) {
+ return gCameraHAL.setTorchMode(camera_id, enabled);
+}
+
+static int open_dev(const hw_module_t* module,
+ const char* name,
+ hw_device_t** device) {
+ return gCameraHAL.openDevice(module, name, device);
+}
+
+} // namespace v4l2_camera_hal
+
+static hw_module_methods_t v4l2_module_methods = {
+ .open = v4l2_camera_hal::open_dev};
+
+camera_module_t HAL_MODULE_INFO_SYM __attribute__((visibility("default"))) = {
+ .common =
+ {
+ .tag = HARDWARE_MODULE_TAG,
+ .module_api_version = CAMERA_MODULE_API_VERSION_2_4,
+ .hal_api_version = HARDWARE_HAL_API_VERSION,
+ .id = CAMERA_HARDWARE_MODULE_ID,
+ .name = "V4L2 Camera HAL v3",
+ .author = "The Android Open Source Project",
+ .methods = &v4l2_module_methods,
+ .dso = nullptr,
+ .reserved = {0},
+ },
+ .get_number_of_cameras = v4l2_camera_hal::get_number_of_cameras,
+ .get_camera_info = v4l2_camera_hal::get_camera_info,
+ .set_callbacks = v4l2_camera_hal::set_callbacks,
+ .get_vendor_tag_ops = v4l2_camera_hal::get_vendor_tag_ops,
+ .open_legacy = v4l2_camera_hal::open_legacy,
+ .set_torch_mode = v4l2_camera_hal::set_torch_mode,
+ .init = nullptr,
+ .reserved = {nullptr, nullptr, nullptr, nullptr, nullptr}};
diff --git a/modules/camera/3_4/v4l2_camera_hal.h b/modules/camera/3_4/v4l2_camera_hal.h
new file mode 100644
index 0000000..1b4ef0f
--- /dev/null
+++ b/modules/camera/3_4/v4l2_camera_hal.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Modified from hardware/libhardware/modules/camera/CameraHAL.h
+
+#ifndef V4L2_CAMERA_HAL_V4L2_CAMERA_HAL_H_
+#define V4L2_CAMERA_HAL_V4L2_CAMERA_HAL_H_
+
+#include <vector>
+
+#include <hardware/camera_common.h>
+#include <hardware/hardware.h>
+
+#include "camera.h"
+#include "common.h"
+
+namespace v4l2_camera_hal {
+/*
+ * V4L2CameraHAL contains all module state that isn't specific to an
+ * individual camera device. This class is based off of the sample
+ * default CameraHAL from /hardware/libhardware/modules/camera.
+ */
+class V4L2CameraHAL {
+ public:
+ V4L2CameraHAL();
+ ~V4L2CameraHAL();
+
+ // Camera Module Interface (see <hardware/camera_common.h>).
+ int getNumberOfCameras();
+ int getCameraInfo(int camera_id, camera_info_t* info);
+ int setCallbacks(const camera_module_callbacks_t* callbacks);
+ void getVendorTagOps(vendor_tag_ops_t* ops);
+ int openLegacy(const hw_module_t* module,
+ const char* id,
+ uint32_t halVersion,
+ hw_device_t** device);
+ int setTorchMode(const char* camera_id, bool enabled);
+
+ // Hardware Module Interface (see <hardware/hardware.h>).
+ int openDevice(const hw_module_t* mod, const char* name, hw_device_t** dev);
+
+ private:
+ // Vector of cameras.
+ std::vector<std::unique_ptr<default_camera_hal::Camera>> mCameras;
+ // Callback handle.
+ const camera_module_callbacks_t* mCallbacks;
+
+ DISALLOW_COPY_AND_ASSIGN(V4L2CameraHAL);
+};
+
+} // namespace v4l2_camera_hal
+
+extern camera_module_t HAL_MODULE_INFO_SYM;
+
+#endif // V4L2_CAMERA_HAL_V4L2_CAMERA_HAL_H_
diff --git a/modules/camera/3_4/v4l2_gralloc.cpp b/modules/camera/3_4/v4l2_gralloc.cpp
new file mode 100644
index 0000000..7da3c4e
--- /dev/null
+++ b/modules/camera/3_4/v4l2_gralloc.cpp
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "v4l2_gralloc.h"
+
+#include <linux/videodev2.h>
+
+#include <cstdlib>
+
+#include <hardware/camera3.h>
+#include <hardware/gralloc.h>
+#include <system/graphics.h>
+
+#include "common.h"
+#include "stream_format.h"
+
+namespace v4l2_camera_hal {
+
+// Copy |height| lines from |src| to |dest|,
+// where |src| and |dest| may have different line lengths.
+void copyWithPadding(uint8_t* dest,
+ const uint8_t* src,
+ size_t dest_stride,
+ size_t src_stride,
+ size_t height) {
+ size_t copy_stride = dest_stride;
+ if (copy_stride > src_stride) {
+ // Adding padding, not reducing. 0 out the extra memory.
+ memset(dest, 0, src_stride * height);
+ copy_stride = src_stride;
+ }
+ uint8_t* dest_line_start = dest;
+ const uint8_t* src_line_start = src;
+ for (size_t row = 0; row < height;
+ ++row, dest_line_start += dest_stride, src_line_start += src_stride) {
+ memcpy(dest_line_start, src_line_start, copy_stride);
+ }
+}
+
+V4L2Gralloc* V4L2Gralloc::NewV4L2Gralloc() {
+ // Initialize and check the gralloc module.
+ const hw_module_t* module = nullptr;
+ int res = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
+ if (res || !module) {
+ HAL_LOGE("Couldn't get gralloc module.");
+ return nullptr;
+ }
+ const gralloc_module_t* gralloc =
+ reinterpret_cast<const gralloc_module_t*>(module);
+
+ // This class only supports Gralloc v0, not Gralloc V1.
+ if (gralloc->common.module_api_version > GRALLOC_MODULE_API_VERSION_0_3) {
+ HAL_LOGE(
+ "Invalid gralloc version %x. Only 0.3 (%x) "
+ "and below are supported by this HAL.",
+ gralloc->common.module_api_version,
+ GRALLOC_MODULE_API_VERSION_0_3);
+ return nullptr;
+ }
+
+ return new V4L2Gralloc(gralloc);
+}
+
+// Private. As checked by above factory, module will be non-null
+// and a supported version.
+V4L2Gralloc::V4L2Gralloc(const gralloc_module_t* module) : mModule(module) {}
+
+V4L2Gralloc::~V4L2Gralloc() {
+ // Unlock buffers that are still locked.
+ unlockAllBuffers();
+}
+
+int V4L2Gralloc::lock(const camera3_stream_buffer_t* camera_buffer,
+ uint32_t bytes_per_line,
+ v4l2_buffer* device_buffer) {
+ // Lock the camera buffer (varies depending on if the buffer is YUV or not).
+ std::unique_ptr<BufferData> buffer_data(
+ new BufferData{camera_buffer, nullptr, bytes_per_line});
+ buffer_handle_t buffer = *camera_buffer->buffer;
+ void* data;
+ camera3_stream_t* stream = camera_buffer->stream;
+ int ret = 0;
+ switch (StreamFormat::HalToV4L2PixelFormat(stream->format)) {
+ // TODO(b/30119452): support more YCbCr formats.
+ case V4L2_PIX_FMT_YUV420:
+ android_ycbcr yuv_data;
+ ret = mModule->lock_ycbcr(mModule,
+ buffer,
+ stream->usage,
+ 0,
+ 0,
+ stream->width,
+ stream->height,
+ &yuv_data);
+ if (ret) {
+ HAL_LOGE("Failed to lock ycbcr buffer: %d", ret);
+ return ret;
+ }
+
+ // Check if gralloc format matches v4l2 format
+ // (same padding, not interleaved, contiguous).
+ if (yuv_data.ystride == bytes_per_line &&
+ yuv_data.cstride == bytes_per_line / 2 && yuv_data.chroma_step == 1 &&
+ (reinterpret_cast<uint8_t*>(yuv_data.cb) ==
+ reinterpret_cast<uint8_t*>(yuv_data.y) +
+ (stream->height * yuv_data.ystride)) &&
+ (reinterpret_cast<uint8_t*>(yuv_data.cr) ==
+ reinterpret_cast<uint8_t*>(yuv_data.cb) +
+ (stream->height / 2 * yuv_data.cstride))) {
+ // If so, great, point to the beginning.
+ data = yuv_data.y;
+ } else {
+ // If not, allocate a contiguous buffer of appropriate size
+ // (to be transformed back upon unlock).
+ data = new uint8_t[device_buffer->length];
+ // Make a dynamically-allocated copy of yuv_data,
+ // since it will be needed at transform time.
+ buffer_data->transform_dest.reset(new android_ycbcr(yuv_data));
+ }
+ break;
+ case V4L2_PIX_FMT_JPEG:
+ // Jpeg buffers are just contiguous blobs; lock length * 1.
+ ret = mModule->lock(mModule,
+ buffer,
+ stream->usage,
+ 0,
+ 0,
+ device_buffer->length,
+ 1,
+ &data);
+ if (ret) {
+ HAL_LOGE("Failed to lock jpeg buffer: %d", ret);
+ return ret;
+ }
+ break;
+ case V4L2_PIX_FMT_BGR32: // Fall-through.
+ case V4L2_PIX_FMT_RGB32:
+ // RGB formats have nice agreed upon representation. Unless using android
+ // flex formats.
+ ret = mModule->lock(mModule,
+ buffer,
+ stream->usage,
+ 0,
+ 0,
+ stream->width,
+ stream->height,
+ &data);
+ if (ret) {
+ HAL_LOGE("Failed to lock RGB buffer: %d", ret);
+ return ret;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (!data) {
+ ALOGE("Gralloc lock returned null ptr");
+ return -ENODEV;
+ }
+
+ // Set up the device buffer.
+ static_assert(sizeof(unsigned long) >= sizeof(void*),
+ "void* must be able to fit in the v4l2_buffer m.userptr "
+ "field (unsigned long) for this code to work");
+ device_buffer->m.userptr = reinterpret_cast<unsigned long>(data);
+
+ // Note the mapping of data:buffer info for when unlock is called.
+ mBufferMap.emplace(data, buffer_data.release());
+
+ return 0;
+}
+
+int V4L2Gralloc::unlock(const v4l2_buffer* device_buffer) {
+ // TODO(b/30000211): support multi-planar data (video_capture_mplane).
+ if (device_buffer->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ return -EINVAL;
+ }
+
+ void* data = reinterpret_cast<void*>(device_buffer->m.userptr);
+
+ // Find and pop the matching entry in the map.
+ auto map_entry = mBufferMap.find(data);
+ if (map_entry == mBufferMap.end()) {
+ HAL_LOGE("No matching buffer for data at %p", data);
+ return -EINVAL;
+ }
+ std::unique_ptr<const BufferData> buffer_data(map_entry->second);
+ mBufferMap.erase(map_entry);
+
+ const camera3_stream_buffer_t* camera_buffer = buffer_data->camera_buffer;
+ const buffer_handle_t buffer = *camera_buffer->buffer;
+
+ // Check for transform.
+ if (buffer_data->transform_dest) {
+ HAL_LOGV("Transforming V4L2 YUV to gralloc YUV.");
+ // In this case data was allocated by this class, put it in a unique_ptr
+ // to ensure it gets cleaned up no matter which way this function exits.
+ std::unique_ptr<uint8_t[]> data_cleanup(reinterpret_cast<uint8_t*>(data));
+
+ uint32_t bytes_per_line = buffer_data->v4l2_bytes_per_line;
+ android_ycbcr* yuv_data = buffer_data->transform_dest.get();
+
+ // Should only occur in error situations.
+ if (device_buffer->bytesused == 0) {
+ return -EINVAL;
+ }
+
+ // Transform V4L2 to Gralloc, copying each plane to the correct place,
+ // adjusting padding, and interleaving if necessary.
+ uint32_t height = camera_buffer->stream->height;
+ // Y data first.
+ size_t y_len = bytes_per_line * height;
+ if (yuv_data->ystride == bytes_per_line) {
+ // Data should match exactly.
+ memcpy(yuv_data->y, data, y_len);
+ } else {
+ HAL_LOGV("Changing padding on Y plane from %u to %u.",
+ bytes_per_line,
+ yuv_data->ystride);
+ // Wrong padding from V4L2.
+ copyWithPadding(reinterpret_cast<uint8_t*>(yuv_data->y),
+ reinterpret_cast<uint8_t*>(data),
+ yuv_data->ystride,
+ bytes_per_line,
+ height);
+ }
+ // C data.
+ // TODO(b/30119452): These calculations assume YCbCr_420_888.
+ size_t c_len = y_len / 4;
+ uint32_t c_bytes_per_line = bytes_per_line / 2;
+ // V4L2 is packed, meaning the data is stored as contiguous {y, cb, cr}.
+ uint8_t* cb_device = reinterpret_cast<uint8_t*>(data) + y_len;
+ uint8_t* cr_device = cb_device + c_len;
+ size_t step = yuv_data->chroma_step;
+ if (step == 1) {
+ // Still planar.
+ if (yuv_data->cstride == c_bytes_per_line) {
+ // Data should match exactly.
+ memcpy(yuv_data->cb, cb_device, c_len);
+ memcpy(yuv_data->cr, cr_device, c_len);
+ } else {
+ HAL_LOGV("Changing padding on C plane from %u to %u.",
+ c_bytes_per_line,
+ yuv_data->cstride);
+ // Wrong padding from V4L2.
+ copyWithPadding(reinterpret_cast<uint8_t*>(yuv_data->cb),
+ cb_device,
+ yuv_data->cstride,
+ c_bytes_per_line,
+ height / 2);
+ copyWithPadding(reinterpret_cast<uint8_t*>(yuv_data->cr),
+ cr_device,
+ yuv_data->cstride,
+ c_bytes_per_line,
+ height / 2);
+ }
+ } else {
+ // Desire semiplanar (cb and cr interleaved).
+ HAL_LOGV("Interleaving cb and cr. Padding going from %u to %u.",
+ c_bytes_per_line,
+ yuv_data->cstride);
+ uint32_t c_height = height / 2;
+ uint32_t c_width = camera_buffer->stream->width / 2;
+ // Zero out destination
+ uint8_t* cb_gralloc = reinterpret_cast<uint8_t*>(yuv_data->cb);
+ uint8_t* cr_gralloc = reinterpret_cast<uint8_t*>(yuv_data->cr);
+ memset(cb_gralloc, 0, c_width * c_height * step);
+
+ // Interleaving means we need to copy the cb and cr bytes one by one.
+ for (size_t line = 0; line < c_height; ++line,
+ cb_gralloc += yuv_data->cstride,
+ cr_gralloc += yuv_data->cstride,
+ cb_device += c_bytes_per_line,
+ cr_device += c_bytes_per_line) {
+ for (size_t i = 0; i < c_width; ++i) {
+ *(cb_gralloc + (i * step)) = *(cb_device + i);
+ *(cr_gralloc + (i * step)) = *(cr_device + i);
+ }
+ }
+ }
+ }
+
+ // Unlock.
+ int res = mModule->unlock(mModule, buffer);
+ if (res) {
+ HAL_LOGE("Failed to unlock buffer at %p", buffer);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+int V4L2Gralloc::unlockAllBuffers() {
+ HAL_LOG_ENTER();
+
+ bool failed = false;
+ for (auto const& entry : mBufferMap) {
+ int res = mModule->unlock(mModule, *entry.second->camera_buffer->buffer);
+ if (res) {
+ failed = true;
+ }
+ // When there is a transform to be made, the buffer returned by lock()
+ // is dynamically allocated (to hold the pre-transform data).
+ if (entry.second->transform_dest) {
+ delete[] reinterpret_cast<uint8_t*>(entry.first);
+ }
+ // The BufferData entry is always dynamically allocated in lock().
+ delete entry.second;
+ }
+ mBufferMap.clear();
+
+ // If any unlock failed, return error.
+ if (failed) {
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+} // namespace default_camera_hal
diff --git a/modules/camera/3_4/v4l2_gralloc.h b/modules/camera/3_4/v4l2_gralloc.h
new file mode 100644
index 0000000..82129e3
--- /dev/null
+++ b/modules/camera/3_4/v4l2_gralloc.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef V4L2_CAMERA_HAL_V4L2_GRALLOC_H_
+#define V4L2_CAMERA_HAL_V4L2_GRALLOC_H_
+
+#include <linux/videodev2.h>
+
+#include <unordered_map>
+
+#include <hardware/camera3.h>
+#include <hardware/gralloc.h>
+#include <system/graphics.h>
+
+namespace v4l2_camera_hal {
+
+// Generously allow up to 6MB (the largest JPEG on the RPi camera is about 5MB).
+static constexpr size_t V4L2_MAX_JPEG_SIZE = 6000000;
+
+// V4L2Gralloc is a wrapper around relevant parts of a gralloc module,
+// with some assistive transformations.
+class V4L2Gralloc {
+ public:
+ // Use this method to create V4L2Gralloc objects. Functionally equivalent
+ // to "new V4L2Gralloc", except that it may return nullptr in case of failure.
+ static V4L2Gralloc* NewV4L2Gralloc();
+ virtual ~V4L2Gralloc();
+
+ // Lock a camera buffer. Uses device buffer length, sets user pointer.
+ int lock(const camera3_stream_buffer_t* camera_buffer,
+ uint32_t bytes_per_line,
+ v4l2_buffer* device_buffer);
+ // Unlock a buffer that was locked by this helper (equality determined
+ // based on buffer user pointer, not the specific object).
+ int unlock(const v4l2_buffer* device_buffer);
+ // Release all held locks.
+ int unlockAllBuffers();
+
+ private:
+ // Constructor is private to allow failing on bad input.
+ // Use NewV4L2Gralloc instead.
+ V4L2Gralloc(const gralloc_module_t* module);
+
+ const gralloc_module_t* mModule;
+
+ struct BufferData {
+ const camera3_stream_buffer_t* camera_buffer;
+ // Below fields only used when a ycbcr format transform is necessary.
+ std::unique_ptr<android_ycbcr> transform_dest; // nullptr if no transform.
+ uint32_t v4l2_bytes_per_line;
+ };
+ // Map data pointer : BufferData about that buffer.
+ std::unordered_map<void*, const BufferData*> mBufferMap;
+};
+
+} // namespace default_camera_hal
+
+#endif // V4L2_CAMERA_HAL_V4L2_GRALLOC_H_
diff --git a/modules/camera/3_4/v4l2_metadata_factory.cpp b/modules/camera/3_4/v4l2_metadata_factory.cpp
new file mode 100644
index 0000000..67556d9
--- /dev/null
+++ b/modules/camera/3_4/v4l2_metadata_factory.cpp
@@ -0,0 +1,571 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "v4l2_metadata_factory.h"
+
+#include <camera/CameraMetadata.h>
+
+#include "common.h"
+#include "format_metadata_factory.h"
+#include "metadata/boottime_state_delegate.h"
+#include "metadata/control.h"
+#include "metadata/enum_converter.h"
+#include "metadata/metadata_common.h"
+#include "metadata/partial_metadata_factory.h"
+#include "metadata/property.h"
+#include "metadata/scaling_converter.h"
+#include "v4l2_gralloc.h"
+
+namespace v4l2_camera_hal {
+
+// According to spec, each unit of V4L2_CID_AUTO_EXPOSURE_BIAS is 0.001 EV.
+const camera_metadata_rational_t kAeCompensationUnit = {1, 1000};
+// According to spec, each unit of V4L2_CID_EXPOSURE_ABSOLUTE is 100 us.
+const int64_t kV4L2ExposureTimeStepNs = 100000;
+// According to spec, each unit of V4L2_CID_ISO_SENSITIVITY is ISO/1000.
+const int32_t kV4L2SensitivityDenominator = 1000;
+
+int GetV4L2Metadata(std::shared_ptr<V4L2Wrapper> device,
+ std::unique_ptr<Metadata>* result) {
+ HAL_LOG_ENTER();
+
+ // Open a temporary connection to the device for all the V4L2 querying
+ // that will be happening (this could be done for each component individually,
+ // but doing it here prevents connecting and disconnecting for each one).
+ V4L2Wrapper::Connection temp_connection = V4L2Wrapper::Connection(device);
+ if (temp_connection.status()) {
+ HAL_LOGE("Failed to connect to device: %d.", temp_connection.status());
+ return temp_connection.status();
+ }
+
+ // TODO(b/30035628): Add states.
+
+ PartialMetadataSet components;
+
+ components.insert(NoEffectMenuControl<uint8_t>(
+ ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
+ ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
+ {ANDROID_COLOR_CORRECTION_ABERRATION_MODE_FAST,
+ ANDROID_COLOR_CORRECTION_ABERRATION_MODE_HIGH_QUALITY},
+ {{CAMERA3_TEMPLATE_STILL_CAPTURE,
+ ANDROID_COLOR_CORRECTION_ABERRATION_MODE_HIGH_QUALITY},
+ {OTHER_TEMPLATES, ANDROID_COLOR_CORRECTION_ABERRATION_MODE_FAST}}));
+
+ // TODO(b/30510395): subcomponents of 3A.
+ // In general, default to ON/AUTO since they imply pretty much nothing,
+ // while OFF implies guarantees about not hindering performance.
+ components.insert(std::unique_ptr<PartialMetadataInterface>(
+ new Property<std::array<int32_t, 3>>(ANDROID_CONTROL_MAX_REGIONS,
+ {{/*AE*/ 0, /*AWB*/ 0, /*AF*/ 0}})));
+ // TODO(b/30921166): V4L2_CID_AUTO_EXPOSURE_BIAS is an int menu, so
+ // this will be falling back to NoEffect until int menu support is added.
+ components.insert(V4L2ControlOrDefault<int32_t>(
+ ControlType::kSlider,
+ ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
+ ANDROID_CONTROL_AE_COMPENSATION_RANGE,
+ device,
+ V4L2_CID_AUTO_EXPOSURE_BIAS,
+ // No scaling necessary, AE_COMPENSATION_STEP handles this.
+ std::make_shared<ScalingConverter<int32_t, int32_t>>(1, 1),
+ 0,
+ {{OTHER_TEMPLATES, 0}}));
+ components.insert(std::unique_ptr<PartialMetadataInterface>(
+ new Property<camera_metadata_rational_t>(
+ ANDROID_CONTROL_AE_COMPENSATION_STEP, kAeCompensationUnit)));
+ // TODO(b/31021522): Autofocus subcomponent.
+ components.insert(
+ NoEffectMenuControl<uint8_t>(ANDROID_CONTROL_AF_MODE,
+ ANDROID_CONTROL_AF_AVAILABLE_MODES,
+ {ANDROID_CONTROL_AF_MODE_OFF}));
+ // TODO(b/31021522): Should read autofocus state from
+ // V4L2_CID_AUTO_FOCUS_STATUS bitmask. The framework gets a little more
+ // complex than that does; there's a whole state-machine table in
+ // the docs (system/media/camera/docs/docs.html).
+ components.insert(FixedState<uint8_t>(ANDROID_CONTROL_AF_STATE,
+ ANDROID_CONTROL_AF_STATE_INACTIVE));
+ // TODO(b/31022735): Correctly implement AE & AF triggers that
+ // actually do something. These no effect triggers are even worse than most
+ // of the useless controls in this class, since technically they should
+ // revert back to IDLE eventually after START/CANCEL, but for now they won't
+ // unless IDLE is requested.
+ components.insert(
+ NoEffectMenuControl<uint8_t>(ANDROID_CONTROL_AF_TRIGGER,
+ DO_NOT_REPORT_OPTIONS,
+ {ANDROID_CONTROL_AF_TRIGGER_IDLE,
+ ANDROID_CONTROL_AF_TRIGGER_START,
+ ANDROID_CONTROL_AF_TRIGGER_CANCEL}));
+ components.insert(NoEffectMenuControl<uint8_t>(
+ ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
+ DO_NOT_REPORT_OPTIONS,
+ {ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_IDLE,
+ ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_START,
+ ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL}));
+ components.insert(V4L2ControlOrDefault<uint8_t>(
+ ControlType::kMenu,
+ ANDROID_CONTROL_AE_ANTIBANDING_MODE,
+ ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,
+ device,
+ V4L2_CID_POWER_LINE_FREQUENCY,
+ std::shared_ptr<ConverterInterface<uint8_t, int32_t>>(
+ new EnumConverter({{V4L2_CID_POWER_LINE_FREQUENCY_DISABLED,
+ ANDROID_CONTROL_AE_ANTIBANDING_MODE_OFF},
+ {V4L2_CID_POWER_LINE_FREQUENCY_50HZ,
+ ANDROID_CONTROL_AE_ANTIBANDING_MODE_50HZ},
+ {V4L2_CID_POWER_LINE_FREQUENCY_60HZ,
+ ANDROID_CONTROL_AE_ANTIBANDING_MODE_60HZ},
+ {V4L2_CID_POWER_LINE_FREQUENCY_AUTO,
+ ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO}})),
+ ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO,
+ {{CAMERA3_TEMPLATE_MANUAL, ANDROID_CONTROL_AE_ANTIBANDING_MODE_OFF},
+ {OTHER_TEMPLATES, ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO}}));
+ std::unique_ptr<PartialMetadataInterface> exposure_time =
+ V4L2Control<int64_t>(ControlType::kSlider,
+ ANDROID_SENSOR_EXPOSURE_TIME,
+ ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE,
+ device,
+ V4L2_CID_EXPOSURE_ABSOLUTE,
+ std::make_shared<ScalingConverter<int64_t, int32_t>>(
+ kV4L2ExposureTimeStepNs, 1));
+ // TODO(b/31037072): Sensitivity has additional V4L2 controls
+ // (V4L2_CID_ISO_SENSITIVITY_AUTO), so this control currently has
+ // undefined behavior.
+ // TODO(b/30921166): V4L2_CID_ISO_SENSITIVITY is an int menu, so
+ // this will return nullptr until that is added.
+ std::unique_ptr<PartialMetadataInterface> sensitivity =
+ V4L2Control<int32_t>(ControlType::kSlider,
+ ANDROID_SENSOR_SENSITIVITY,
+ ANDROID_SENSOR_INFO_SENSITIVITY_RANGE,
+ device,
+ V4L2_CID_ISO_SENSITIVITY,
+ std::make_shared<ScalingConverter<int32_t, int32_t>>(
+ 1, kV4L2SensitivityDenominator));
+ std::multimap<int32_t, uint8_t> ae_mode_mapping = {
+ {V4L2_EXPOSURE_AUTO, ANDROID_CONTROL_AE_MODE_ON}};
+ if (exposure_time && sensitivity) {
+ // TODO(b/30510395): as part of coordinated 3A component,
+ // if these aren't available don't advertise AE mode OFF, only AUTO.
+ components.insert(std::move(exposure_time));
+ components.insert(std::move(sensitivity));
+ ae_mode_mapping.emplace(V4L2_EXPOSURE_MANUAL, ANDROID_CONTROL_AE_MODE_OFF);
+ }
+ components.insert(V4L2ControlOrDefault<uint8_t>(
+ ControlType::kMenu,
+ ANDROID_CONTROL_AE_MODE,
+ ANDROID_CONTROL_AE_AVAILABLE_MODES,
+ device,
+ V4L2_CID_EXPOSURE_AUTO,
+ std::shared_ptr<ConverterInterface<uint8_t, int32_t>>(
+ new EnumConverter(ae_mode_mapping)),
+ ANDROID_CONTROL_AE_MODE_ON,
+ {{CAMERA3_TEMPLATE_MANUAL, ANDROID_CONTROL_AE_MODE_OFF},
+ {OTHER_TEMPLATES, ANDROID_CONTROL_AE_MODE_ON}}));
+ // Can't get AE status from V4L2.
+ // TODO(b/30510395): If AE mode is OFF, this should switch to INACTIVE.
+ components.insert(FixedState<uint8_t>(ANDROID_CONTROL_AE_STATE,
+ ANDROID_CONTROL_AE_STATE_CONVERGED));
+ // V4L2 offers multiple white balance interfaces. Try the advanced one before
+ // falling
+ // back to the simpler version.
+ // Modes from each API that don't match up:
+ // Android: WARM_FLUORESCENT, TWILIGHT.
+ // V4L2: FLUORESCENT_H, HORIZON, FLASH.
+ std::unique_ptr<PartialMetadataInterface> awb(V4L2Control<uint8_t>(
+ ControlType::kMenu,
+ ANDROID_CONTROL_AWB_MODE,
+ ANDROID_CONTROL_AWB_AVAILABLE_MODES,
+ device,
+ V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE,
+ std::shared_ptr<ConverterInterface<uint8_t, int32_t>>(new EnumConverter(
+ {{V4L2_WHITE_BALANCE_MANUAL, ANDROID_CONTROL_AWB_MODE_OFF},
+ {V4L2_WHITE_BALANCE_AUTO, ANDROID_CONTROL_AWB_MODE_AUTO},
+ {V4L2_WHITE_BALANCE_INCANDESCENT,
+ ANDROID_CONTROL_AWB_MODE_INCANDESCENT},
+ {V4L2_WHITE_BALANCE_FLUORESCENT,
+ ANDROID_CONTROL_AWB_MODE_FLUORESCENT},
+ {V4L2_WHITE_BALANCE_DAYLIGHT, ANDROID_CONTROL_AWB_MODE_DAYLIGHT},
+ {V4L2_WHITE_BALANCE_CLOUDY,
+ ANDROID_CONTROL_AWB_MODE_CLOUDY_DAYLIGHT},
+ {V4L2_WHITE_BALANCE_SHADE, ANDROID_CONTROL_AWB_MODE_SHADE}})),
+ {{CAMERA3_TEMPLATE_MANUAL, ANDROID_CONTROL_AWB_MODE_OFF},
+ {OTHER_TEMPLATES, ANDROID_CONTROL_AWB_MODE_AUTO}}));
+ if (awb) {
+ components.insert(std::move(awb));
+ } else {
+ // Fall back to simpler AWB or even just an ignored control.
+ components.insert(V4L2ControlOrDefault<uint8_t>(
+ ControlType::kMenu,
+ ANDROID_CONTROL_AWB_MODE,
+ ANDROID_CONTROL_AWB_AVAILABLE_MODES,
+ device,
+ V4L2_CID_AUTO_WHITE_BALANCE,
+ std::shared_ptr<ConverterInterface<uint8_t, int32_t>>(
+ new EnumConverter({{0, ANDROID_CONTROL_AWB_MODE_OFF},
+ {1, ANDROID_CONTROL_AWB_MODE_AUTO}})),
+ ANDROID_CONTROL_AWB_MODE_AUTO,
+ {{CAMERA3_TEMPLATE_MANUAL, ANDROID_CONTROL_AWB_MODE_OFF},
+ {OTHER_TEMPLATES, ANDROID_CONTROL_AWB_MODE_AUTO}}));
+ }
+ // TODO(b/31041577): Handle AWB state machine correctly.
+ components.insert(FixedState<uint8_t>(ANDROID_CONTROL_AWB_STATE,
+ ANDROID_CONTROL_AWB_STATE_CONVERGED));
+ // TODO(b/31022153): 3A locks.
+ components.insert(std::unique_ptr<PartialMetadataInterface>(
+ new Property<uint8_t>(ANDROID_CONTROL_AE_LOCK_AVAILABLE,
+ ANDROID_CONTROL_AE_LOCK_AVAILABLE_FALSE)));
+ components.insert(
+ NoEffectMenuControl<uint8_t>(ANDROID_CONTROL_AE_LOCK,
+ DO_NOT_REPORT_OPTIONS,
+ {ANDROID_CONTROL_AE_LOCK_OFF}));
+ components.insert(std::unique_ptr<PartialMetadataInterface>(
+ new Property<uint8_t>(ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
+ ANDROID_CONTROL_AWB_LOCK_AVAILABLE_FALSE)));
+ components.insert(
+ NoEffectMenuControl<uint8_t>(ANDROID_CONTROL_AWB_LOCK,
+ DO_NOT_REPORT_OPTIONS,
+ {ANDROID_CONTROL_AWB_LOCK_OFF}));
+ // TODO(b/30510395): subcomponents of scene modes
+ // (may itself be a subcomponent of 3A).
+ // Modes from each API that don't match up:
+ // Android: FACE_PRIORITY, ACTION, NIGHT_PORTRAIT, THEATRE, STEADYPHOTO,
+ // BARCODE, HIGH_SPEED_VIDEO.
+ // V4L2: BACKLIGHT, DAWN_DUSK, FALL_COLORS, TEXT.
+ components.insert(V4L2ControlOrDefault<uint8_t>(
+ ControlType::kMenu,
+ ANDROID_CONTROL_SCENE_MODE,
+ ANDROID_CONTROL_AVAILABLE_SCENE_MODES,
+ device,
+ V4L2_CID_SCENE_MODE,
+ std::shared_ptr<ConverterInterface<uint8_t, int32_t>>(new EnumConverter(
+ {{V4L2_SCENE_MODE_NONE, ANDROID_CONTROL_SCENE_MODE_DISABLED},
+ {V4L2_SCENE_MODE_BEACH_SNOW, ANDROID_CONTROL_SCENE_MODE_BEACH},
+ {V4L2_SCENE_MODE_BEACH_SNOW, ANDROID_CONTROL_SCENE_MODE_SNOW},
+ {V4L2_SCENE_MODE_CANDLE_LIGHT,
+ ANDROID_CONTROL_SCENE_MODE_CANDLELIGHT},
+ {V4L2_SCENE_MODE_FIREWORKS, ANDROID_CONTROL_SCENE_MODE_FIREWORKS},
+ {V4L2_SCENE_MODE_LANDSCAPE, ANDROID_CONTROL_SCENE_MODE_LANDSCAPE},
+ {V4L2_SCENE_MODE_NIGHT, ANDROID_CONTROL_SCENE_MODE_NIGHT},
+ {V4L2_SCENE_MODE_PARTY_INDOOR, ANDROID_CONTROL_SCENE_MODE_PARTY},
+ {V4L2_SCENE_MODE_SPORTS, ANDROID_CONTROL_SCENE_MODE_SPORTS},
+ {V4L2_SCENE_MODE_SUNSET, ANDROID_CONTROL_SCENE_MODE_SUNSET}})),
+ ANDROID_CONTROL_SCENE_MODE_DISABLED));
+ // TODO(b/31022612): Scene mode overrides.
+ // Modes from each API that don't match up:
+ // Android: POSTERIZE, WHITEBOARD, BLACKBOARD.
+ // V4L2: ANTIQUE, ART_FREEZE, EMBOSS, GRASS_GREEN, SKETCH, SKIN_WHITEN,
+ // SKY_BLUE, SILHOUETTE, VIVID, SET_CBCR.
+ components.insert(V4L2ControlOrDefault<uint8_t>(
+ ControlType::kMenu,
+ ANDROID_CONTROL_EFFECT_MODE,
+ ANDROID_CONTROL_AVAILABLE_EFFECTS,
+ device,
+ V4L2_CID_COLORFX,
+ std::shared_ptr<ConverterInterface<uint8_t, int32_t>>(new EnumConverter(
+ {{V4L2_COLORFX_NONE, ANDROID_CONTROL_EFFECT_MODE_OFF},
+ {V4L2_COLORFX_BW, ANDROID_CONTROL_EFFECT_MODE_MONO},
+ {V4L2_COLORFX_NEGATIVE, ANDROID_CONTROL_EFFECT_MODE_NEGATIVE},
+ {V4L2_COLORFX_SOLARIZATION, ANDROID_CONTROL_EFFECT_MODE_SOLARIZE},
+ {V4L2_COLORFX_SEPIA, ANDROID_CONTROL_EFFECT_MODE_SEPIA},
+ {V4L2_COLORFX_AQUA, ANDROID_CONTROL_EFFECT_MODE_AQUA}})),
+ ANDROID_CONTROL_EFFECT_MODE_OFF));
+ // TODO(b/31021654): This should behave as a top level switch, not no effect.
+ // Should enforce being set to USE_SCENE_MODE when a scene mode is requested.
+ components.insert(NoEffectMenuControl<uint8_t>(
+ ANDROID_CONTROL_MODE,
+ ANDROID_CONTROL_AVAILABLE_MODES,
+ {ANDROID_CONTROL_MODE_AUTO, ANDROID_CONTROL_MODE_USE_SCENE_MODE}));
+
+ // Not sure if V4L2 does or doesn't do this, but HAL documentation says
+ // all devices must support FAST, and FAST can be equivalent to OFF, so
+ // either way it's fine to list. And if FAST is included, HIGH_QUALITY
+ // is supposed to be included as well.
+ components.insert(NoEffectMenuControl<uint8_t>(
+ ANDROID_EDGE_MODE,
+ ANDROID_EDGE_AVAILABLE_EDGE_MODES,
+ {ANDROID_EDGE_MODE_FAST, ANDROID_EDGE_MODE_HIGH_QUALITY},
+ {{CAMERA3_TEMPLATE_STILL_CAPTURE, ANDROID_EDGE_MODE_HIGH_QUALITY},
+ {OTHER_TEMPLATES, ANDROID_EDGE_MODE_FAST}}));
+
+ // TODO(b/31023454): subcomponents of flash.
+ components.insert(
+ std::unique_ptr<PartialMetadataInterface>(new Property<uint8_t>(
+ ANDROID_FLASH_INFO_AVAILABLE, ANDROID_FLASH_INFO_AVAILABLE_FALSE)));
+ components.insert(FixedState<uint8_t>(ANDROID_FLASH_STATE,
+ ANDROID_FLASH_STATE_UNAVAILABLE));
+ components.insert(NoEffectMenuControl<uint8_t>(
+ ANDROID_FLASH_MODE, DO_NOT_REPORT_OPTIONS, {ANDROID_FLASH_MODE_OFF}));
+
+ // TODO(30510395): subcomponents of hotpixel.
+ // No known V4L2 hot pixel correction. But it might be happening,
+ // so we report FAST/HIGH_QUALITY.
+ components.insert(NoEffectMenuControl<uint8_t>(
+ ANDROID_HOT_PIXEL_MODE,
+ ANDROID_HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES,
+ {ANDROID_HOT_PIXEL_MODE_FAST, ANDROID_HOT_PIXEL_MODE_HIGH_QUALITY}));
+ // ON only needs to be supported for RAW capable devices.
+ components.insert(NoEffectMenuControl<uint8_t>(
+ ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE,
+ ANDROID_STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES,
+ {ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE_OFF}));
+
+ // TODO(30510395): subcomponents focus/lens.
+ // No way to actually get the aperture and focal length
+ // in V4L2, but they're required keys, so fake them.
+ components.insert(
+ NoEffectMenuControl<float>(ANDROID_LENS_APERTURE,
+ ANDROID_LENS_INFO_AVAILABLE_APERTURES,
+ {2.0})); // RPi camera v2 is f/2.0.
+ // Always assume external-facing.
+ components.insert(
+ std::unique_ptr<PartialMetadataInterface>(new Property<uint8_t>(
+ ANDROID_LENS_FACING, ANDROID_LENS_FACING_EXTERNAL)));
+ components.insert(
+ NoEffectMenuControl<float>(ANDROID_LENS_FOCAL_LENGTH,
+ ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS,
+ {3.04})); // RPi camera v2 is 3.04mm.
+ // No known way to get filter densities from V4L2,
+ // report 0 to indicate this control is not supported.
+ components.insert(
+ NoEffectMenuControl<float>(ANDROID_LENS_FILTER_DENSITY,
+ ANDROID_LENS_INFO_AVAILABLE_FILTER_DENSITIES,
+ {0.0}));
+ // V4L2 focal units do not correspond to a particular physical unit.
+ components.insert(
+ std::unique_ptr<PartialMetadataInterface>(new Property<uint8_t>(
+ ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION,
+ ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION_UNCALIBRATED)));
+ // TODO(b/31022711): Focus distance component.
+ // Using a NoEffectMenuControl for now because for
+ // fixed-focus it meets expectations. Framework may allow
+ // setting any value and expect it to be clamped to 0, in which
+ // case this will have unexpected behavior (failing on non-0 settings).
+ components.insert(
+ NoEffectMenuControl<float>(ANDROID_LENS_FOCUS_DISTANCE,
+ ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
+ {0}));
+ // Hypefocal distance doesn't mean much for a fixed-focus uncalibrated device.
+ components.insert(std::make_unique<Property<float>>(
+ ANDROID_LENS_INFO_HYPERFOCAL_DISTANCE, 0));
+
+ // No way to know when the lens is moving or not in V4L2.
+ components.insert(
+ FixedState<uint8_t>(ANDROID_LENS_STATE, ANDROID_LENS_STATE_STATIONARY));
+ // No known V4L2 lens shading. But it might be happening,
+ // so report FAST/HIGH_QUALITY.
+ components.insert(NoEffectMenuControl<uint8_t>(
+ ANDROID_SHADING_MODE,
+ ANDROID_SHADING_AVAILABLE_MODES,
+ {ANDROID_SHADING_MODE_FAST, ANDROID_SHADING_MODE_HIGH_QUALITY}));
+ // ON only needs to be supported for RAW capable devices.
+ components.insert(NoEffectMenuControl<uint8_t>(
+ ANDROID_STATISTICS_LENS_SHADING_MAP_MODE,
+ ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES,
+ {ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF}));
+ // V4L2 doesn't differentiate between OPTICAL and VIDEO stabilization,
+ // so only report one (and report the other as OFF).
+ components.insert(V4L2ControlOrDefault<uint8_t>(
+ ControlType::kMenu,
+ ANDROID_CONTROL_VIDEO_STABILIZATION_MODE,
+ ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
+ device,
+ V4L2_CID_IMAGE_STABILIZATION,
+ std::shared_ptr<ConverterInterface<uint8_t, int32_t>>(new EnumConverter(
+ {{0, ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF},
+ {1, ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_ON}})),
+ ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF));
+ components.insert(NoEffectMenuControl<uint8_t>(
+ ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
+ ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
+ {ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF}));
+ // TODO(b/31017806): This should definitely have a different default depending
+ // on template.
+ components.insert(NoEffectMenuControl<uint8_t>(
+ ANDROID_CONTROL_CAPTURE_INTENT,
+ DO_NOT_REPORT_OPTIONS,
+ {ANDROID_CONTROL_CAPTURE_INTENT_CUSTOM,
+ ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW,
+ ANDROID_CONTROL_CAPTURE_INTENT_STILL_CAPTURE,
+ ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_RECORD,
+ ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT,
+ ANDROID_CONTROL_CAPTURE_INTENT_ZERO_SHUTTER_LAG,
+ ANDROID_CONTROL_CAPTURE_INTENT_MANUAL},
+ {{CAMERA3_TEMPLATE_PREVIEW, ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW},
+ {CAMERA3_TEMPLATE_STILL_CAPTURE,
+ ANDROID_CONTROL_CAPTURE_INTENT_STILL_CAPTURE},
+ {CAMERA3_TEMPLATE_VIDEO_RECORD,
+ ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_RECORD},
+ {CAMERA3_TEMPLATE_VIDEO_SNAPSHOT,
+ ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT},
+ {CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG,
+ ANDROID_CONTROL_CAPTURE_INTENT_ZERO_SHUTTER_LAG},
+ {CAMERA3_TEMPLATE_MANUAL, ANDROID_CONTROL_CAPTURE_INTENT_MANUAL},
+ {OTHER_TEMPLATES, ANDROID_CONTROL_CAPTURE_INTENT_CUSTOM}}));
+
+ // Unable to control noise reduction in V4L2 devices,
+ // but FAST is allowed to be the same as OFF,
+ // and HIGH_QUALITY can be the same as FAST.
+ components.insert(NoEffectMenuControl<uint8_t>(
+ ANDROID_NOISE_REDUCTION_MODE,
+ ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
+ {ANDROID_NOISE_REDUCTION_MODE_FAST,
+ ANDROID_NOISE_REDUCTION_MODE_HIGH_QUALITY},
+ {{CAMERA3_TEMPLATE_STILL_CAPTURE,
+ ANDROID_NOISE_REDUCTION_MODE_HIGH_QUALITY},
+ {OTHER_TEMPLATES, ANDROID_NOISE_REDUCTION_MODE_FAST}}));
+
+ // TODO(30510395): subcomponents of formats/streams.
+ // For now, no thumbnails available (only [0,0], the "no thumbnail" size).
+ // TODO(b/29580107): Could end up with a mismatch between request & result,
+ // since V4L2 doesn't actually allow for thumbnail size control.
+ components.insert(NoEffectMenuControl<std::array<int32_t, 2>>(
+ ANDROID_JPEG_THUMBNAIL_SIZE,
+ ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,
+ {{{0, 0}}}));
+ // TODO(b/31022752): Get this from the device,
+ // not constant (from V4L2Gralloc.h).
+ components.insert(std::unique_ptr<PartialMetadataInterface>(
+ new Property<int32_t>(ANDROID_JPEG_MAX_SIZE, V4L2_MAX_JPEG_SIZE)));
+ // TODO(b/31021672): Other JPEG controls (GPS, quality, orientation).
+ // TODO(b/29939583): V4L2 can only support 1 stream at a time.
+ // For now, just reporting minimum allowable for LIMITED devices.
+ components.insert(std::unique_ptr<PartialMetadataInterface>(
+ new Property<std::array<int32_t, 3>>(
+ ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,
+ {{/* Raw */ 0, /* Non-stalling */ 2, /* Stalling */ 1}})));
+ // Reprocessing not supported.
+ components.insert(std::unique_ptr<PartialMetadataInterface>(
+ new Property<int32_t>(ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS, 0)));
+ // No way to know pipeline depth for V4L2, so fake with max allowable latency.
+ // Doesn't mean much without per-frame controls anyways.
+ components.insert(std::unique_ptr<PartialMetadataInterface>(
+ new Property<uint8_t>(ANDROID_REQUEST_PIPELINE_MAX_DEPTH, 4)));
+ components.insert(FixedState<uint8_t>(ANDROID_REQUEST_PIPELINE_DEPTH, 4));
+ // "LIMITED devices are strongly encouraged to use a non-negative value.
+ // If UNKNOWN is used here then app developers do not have a way to know
+ // when sensor settings have been applied." - Unfortunately, V4L2 doesn't
+ // really help here either. Could even be that adjusting settings mid-stream
+ // blocks in V4L2, and should be avoided.
+ components.insert(
+ std::unique_ptr<PartialMetadataInterface>(new Property<int32_t>(
+ ANDROID_SYNC_MAX_LATENCY, ANDROID_SYNC_MAX_LATENCY_UNKNOWN)));
+ // Never know when controls are synced.
+ components.insert(FixedState<int64_t>(ANDROID_SYNC_FRAME_NUMBER,
+ ANDROID_SYNC_FRAME_NUMBER_UNKNOWN));
+
+ // TODO(b/31022480): subcomponents of cropping/sensors.
+ // Need ANDROID_SCALER_CROP_REGION control support.
+ // V4L2 VIDIOC_CROPCAP doesn't give a way to query this;
+ // it's driver dependent. For now, assume freeform, and
+ // some cameras may just behave badly.
+ // TODO(b/29579652): Figure out a way to determine this.
+ components.insert(std::unique_ptr<PartialMetadataInterface>(
+ new Property<float>(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM, 1)));
+ components.insert(std::unique_ptr<PartialMetadataInterface>(
+ new Property<uint8_t>(ANDROID_SCALER_CROPPING_TYPE,
+ ANDROID_SCALER_CROPPING_TYPE_FREEFORM)));
+ // Spoof pixel array size for now, eventually get from CROPCAP.
+ std::array<int32_t, 2> pixel_array_size = {{640, 480}};
+ components.insert(std::unique_ptr<PartialMetadataInterface>(
+ new Property<std::array<int32_t, 2>>(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
+ pixel_array_size)));
+ // Active array size is {x-offset, y-offset, width, height}, relative to
+ // the pixel array size, with {0, 0} being the top left. Since there's no way
+ // to get this in V4L2, assume the full pixel array is the active array.
+ std::array<int32_t, 4> active_array_size = {
+ {0, 0, pixel_array_size[0], pixel_array_size[1]}};
+ components.insert(std::unique_ptr<PartialMetadataInterface>(
+ new Property<std::array<int32_t, 4>>(
+ ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE, active_array_size)));
+ // This is really more freeform than a menu control, but since we're
+ // restricting it to not being used anyways this works for now.
+ components.insert(NoEffectMenuControl<std::array<int32_t, 4>>(
+ ANDROID_SCALER_CROP_REGION, DO_NOT_REPORT_OPTIONS, {active_array_size}));
+ // No way to get in V4L2, so faked. RPi camera v2 is 3.674 x 2.760 mm.
+ // Physical size is used in framework calculations (field of view,
+ // pixel pitch, etc.), so faking it may have unexpected results.
+ components.insert(std::unique_ptr<PartialMetadataInterface>(
+ new Property<std::array<float, 2>>(ANDROID_SENSOR_INFO_PHYSICAL_SIZE,
+ {{3.674, 2.760}})));
+ // HAL uses BOOTTIME timestamps.
+ // TODO(b/29457051): make sure timestamps are consistent throughout the HAL.
+ components.insert(std::unique_ptr<PartialMetadataInterface>(
+ new Property<uint8_t>(ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE,
+ ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN)));
+ components.insert(std::make_unique<State<int64_t>>(
+ ANDROID_SENSOR_TIMESTAMP, std::make_unique<BoottimeStateDelegate>()));
+ // No way to actually get shutter skew from V4L2.
+ components.insert(
+ FixedState<int64_t>(ANDROID_SENSOR_ROLLING_SHUTTER_SKEW, 0));
+ // No way to actually get orientation from V4L2.
+ components.insert(std::unique_ptr<PartialMetadataInterface>(
+ new Property<int32_t>(ANDROID_SENSOR_ORIENTATION, 0)));
+ // TODO(b/31023611): Sensor frame duration. Range should
+ // be dependent on the stream configuration being used.
+ // No test patterns supported.
+ components.insert(
+ NoEffectMenuControl<int32_t>(ANDROID_SENSOR_TEST_PATTERN_MODE,
+ ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES,
+ {ANDROID_SENSOR_TEST_PATTERN_MODE_OFF}));
+
+ // TODO(b/30510395): subcomponents of face detection.
+ // Face detection not supported.
+ components.insert(NoEffectMenuControl<uint8_t>(
+ ANDROID_STATISTICS_FACE_DETECT_MODE,
+ ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
+ {ANDROID_STATISTICS_FACE_DETECT_MODE_OFF}));
+ components.insert(std::unique_ptr<PartialMetadataInterface>(
+ new Property<int32_t>(ANDROID_STATISTICS_INFO_MAX_FACE_COUNT, 0)));
+
+ // No way to get detected scene flicker from V4L2.
+ components.insert(FixedState<uint8_t>(ANDROID_STATISTICS_SCENE_FLICKER,
+ ANDROID_STATISTICS_SCENE_FLICKER_NONE));
+
+ // TOOD(b/31023265): V4L2_CID_FLASH_INDICATOR_INTENSITY could be queried
+ // to see if there's a transmit LED. Would need to translate HAL off/on
+ // enum to slider min/max value. For now, no LEDs available.
+ components.insert(std::unique_ptr<PartialMetadataInterface>(
+ new Property<uint8_t>(ANDROID_LED_AVAILABLE_LEDS, {})));
+
+ /* Capabilities. */
+ // The V4L2Metadata pretends to at least meet the
+ // "LIMITED" and "BACKWARD_COMPATIBLE" functionality requirements.
+ components.insert(std::unique_ptr<PartialMetadataInterface>(
+ new Property<uint8_t>(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
+ ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)));
+ components.insert(std::unique_ptr<PartialMetadataInterface>(
+ new Property<std::vector<uint8_t>>(
+ ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
+ {ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE})));
+
+ // Request is unused, and can be any value,
+ // but that value needs to be propagated.
+ components.insert(NoEffectOptionlessControl<int32_t>(ANDROID_REQUEST_ID, 0));
+
+ // Metadata is returned in a single result; not multiple pieces.
+ components.insert(std::make_unique<Property<int32_t>>(
+ ANDROID_REQUEST_PARTIAL_RESULT_COUNT, 1));
+
+ int res =
+ AddFormatComponents(device, std::inserter(components, components.end()));
+ if (res) {
+ HAL_LOGE("Failed to initialize format components.");
+ return res;
+ }
+
+ *result = std::make_unique<Metadata>(std::move(components));
+ return 0;
+}
+
+} // namespace v4l2_camera_hal
diff --git a/modules/camera/3_4/v4l2_metadata_factory.h b/modules/camera/3_4/v4l2_metadata_factory.h
new file mode 100644
index 0000000..f25a370
--- /dev/null
+++ b/modules/camera/3_4/v4l2_metadata_factory.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef V4L2_CAMERA_HAL_V4L2_METADATA_FACTORY_H_
+#define V4L2_CAMERA_HAL_V4L2_METADATA_FACTORY_H_
+
+#include <memory>
+
+#include "metadata/metadata.h"
+#include "v4l2_wrapper.h"
+
+namespace v4l2_camera_hal {
+
+// A static function to get a Metadata object populated with V4L2 or other
+// controls as appropriate.
+int GetV4L2Metadata(std::shared_ptr<V4L2Wrapper> device,
+ std::unique_ptr<Metadata>* result);
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_V4L2_METADATA_FACTORY_H_
diff --git a/modules/camera/3_4/v4l2_wrapper.cpp b/modules/camera/3_4/v4l2_wrapper.cpp
new file mode 100644
index 0000000..3fafffd
--- /dev/null
+++ b/modules/camera/3_4/v4l2_wrapper.cpp
@@ -0,0 +1,629 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "v4l2_wrapper.h"
+
+#include <algorithm>
+#include <array>
+#include <limits>
+#include <mutex>
+#include <vector>
+
+#include <fcntl.h>
+#include <linux/videodev2.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <android-base/unique_fd.h>
+
+#include "common.h"
+#include "stream_format.h"
+#include "v4l2_gralloc.h"
+
+namespace v4l2_camera_hal {
+
+const int32_t kStandardSizes[][2] = {{640, 480}, {320, 240}};
+
+V4L2Wrapper* V4L2Wrapper::NewV4L2Wrapper(const std::string device_path) {
+ std::unique_ptr<V4L2Gralloc> gralloc(V4L2Gralloc::NewV4L2Gralloc());
+ if (!gralloc) {
+ HAL_LOGE("Failed to initialize gralloc helper.");
+ return nullptr;
+ }
+
+ return new V4L2Wrapper(device_path, std::move(gralloc));
+}
+
+V4L2Wrapper::V4L2Wrapper(const std::string device_path,
+ std::unique_ptr<V4L2Gralloc> gralloc)
+ : device_path_(std::move(device_path)),
+ gralloc_(std::move(gralloc)),
+ connection_count_(0) {}
+
+V4L2Wrapper::~V4L2Wrapper() {}
+
+int V4L2Wrapper::Connect() {
+ HAL_LOG_ENTER();
+ std::lock_guard<std::mutex> lock(connection_lock_);
+
+ if (connected()) {
+ HAL_LOGV("Camera device %s is already connected.", device_path_.c_str());
+ ++connection_count_;
+ return 0;
+ }
+
+ // Open in nonblocking mode (DQBUF may return EAGAIN).
+ int fd = TEMP_FAILURE_RETRY(open(device_path_.c_str(), O_RDWR | O_NONBLOCK));
+ if (fd < 0) {
+ HAL_LOGE("failed to open %s (%s)", device_path_.c_str(), strerror(errno));
+ return -ENODEV;
+ }
+ device_fd_.reset(fd);
+ ++connection_count_;
+
+ // Check if this connection has the extended control query capability.
+ v4l2_query_ext_ctrl query;
+ query.id = V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND;
+ extended_query_supported_ = (IoctlLocked(VIDIOC_QUERY_EXT_CTRL, &query) == 0);
+
+ // TODO(b/29185945): confirm this is a supported device.
+ // This is checked by the HAL, but the device at device_path_ may
+ // not be the same one that was there when the HAL was loaded.
+ // (Alternatively, better hotplugging support may make this unecessary
+ // by disabling cameras that get disconnected and checking newly connected
+ // cameras, so Connect() is never called on an unsupported camera)
+ return 0;
+}
+
+void V4L2Wrapper::Disconnect() {
+ HAL_LOG_ENTER();
+ std::lock_guard<std::mutex> lock(connection_lock_);
+
+ if (connection_count_ == 0) {
+ // Not connected.
+ HAL_LOGE("Camera device %s is not connected, cannot disconnect.",
+ device_path_.c_str());
+ return;
+ }
+
+ --connection_count_;
+ if (connection_count_ > 0) {
+ HAL_LOGV("Disconnected from camera device %s. %d connections remain.",
+ device_path_.c_str());
+ return;
+ }
+
+ device_fd_.reset(-1); // Includes close().
+ format_.reset();
+ buffers_.clear();
+ // Closing the device releases all queued buffers back to the user.
+ gralloc_->unlockAllBuffers();
+}
+
+// Helper function. Should be used instead of ioctl throughout this class.
+template <typename T>
+int V4L2Wrapper::IoctlLocked(int request, T data) {
+ // Potentially called so many times logging entry is a bad idea.
+ std::lock_guard<std::mutex> lock(device_lock_);
+
+ if (!connected()) {
+ HAL_LOGE("Device %s not connected.", device_path_.c_str());
+ return -ENODEV;
+ }
+ return TEMP_FAILURE_RETRY(ioctl(device_fd_.get(), request, data));
+}
+
+int V4L2Wrapper::StreamOn() {
+ if (!format_) {
+ HAL_LOGE("Stream format must be set before turning on stream.");
+ return -EINVAL;
+ }
+
+ int32_t type = format_->type();
+ if (IoctlLocked(VIDIOC_STREAMON, &type) < 0) {
+ HAL_LOGE("STREAMON fails: %s", strerror(errno));
+ return -ENODEV;
+ }
+
+ HAL_LOGV("Stream turned on.");
+ return 0;
+}
+
+int V4L2Wrapper::StreamOff() {
+ if (!format_) {
+ // Can't have turned on the stream without format being set,
+ // so nothing to turn off here.
+ return 0;
+ }
+
+ int32_t type = format_->type();
+ int res = IoctlLocked(VIDIOC_STREAMOFF, &type);
+ // Calling STREAMOFF releases all queued buffers back to the user.
+ int gralloc_res = gralloc_->unlockAllBuffers();
+ // No buffers in flight.
+ for (size_t i = 0; i < buffers_.size(); ++i) {
+ buffers_[i] = false;
+ }
+ if (res < 0) {
+ HAL_LOGE("STREAMOFF fails: %s", strerror(errno));
+ return -ENODEV;
+ }
+ if (gralloc_res < 0) {
+ HAL_LOGE("Failed to unlock all buffers after turning stream off.");
+ return gralloc_res;
+ }
+
+ HAL_LOGV("Stream turned off.");
+ return 0;
+}
+
+int V4L2Wrapper::QueryControl(uint32_t control_id,
+ v4l2_query_ext_ctrl* result) {
+ int res;
+
+ memset(result, 0, sizeof(*result));
+
+ if (extended_query_supported_) {
+ result->id = control_id;
+ res = IoctlLocked(VIDIOC_QUERY_EXT_CTRL, result);
+ // Assuming the operation was supported (not ENOTTY), no more to do.
+ if (errno != ENOTTY) {
+ if (res) {
+ HAL_LOGE("QUERY_EXT_CTRL fails: %s", strerror(errno));
+ return -ENODEV;
+ }
+ return 0;
+ }
+ }
+
+ // Extended control querying not supported, fall back to basic control query.
+ v4l2_queryctrl query;
+ query.id = control_id;
+ if (IoctlLocked(VIDIOC_QUERYCTRL, &query)) {
+ HAL_LOGE("QUERYCTRL fails: %s", strerror(errno));
+ return -ENODEV;
+ }
+
+ // Convert the basic result to the extended result.
+ result->id = query.id;
+ result->type = query.type;
+ memcpy(result->name, query.name, sizeof(query.name));
+ result->minimum = query.minimum;
+ if (query.type == V4L2_CTRL_TYPE_BITMASK) {
+ // According to the V4L2 documentation, when type is BITMASK,
+ // max and default should be interpreted as __u32. Practically,
+ // this means the conversion from 32 bit to 64 will pad with 0s not 1s.
+ result->maximum = static_cast<uint32_t>(query.maximum);
+ result->default_value = static_cast<uint32_t>(query.default_value);
+ } else {
+ result->maximum = query.maximum;
+ result->default_value = query.default_value;
+ }
+ result->step = static_cast<uint32_t>(query.step);
+ result->flags = query.flags;
+ result->elems = 1;
+ switch (result->type) {
+ case V4L2_CTRL_TYPE_INTEGER64:
+ result->elem_size = sizeof(int64_t);
+ break;
+ case V4L2_CTRL_TYPE_STRING:
+ result->elem_size = result->maximum + 1;
+ break;
+ default:
+ result->elem_size = sizeof(int32_t);
+ break;
+ }
+
+ return 0;
+}
+
+int V4L2Wrapper::GetControl(uint32_t control_id, int32_t* value) {
+ // For extended controls (any control class other than "user"),
+ // G_EXT_CTRL must be used instead of G_CTRL.
+ if (V4L2_CTRL_ID2CLASS(control_id) != V4L2_CTRL_CLASS_USER) {
+ v4l2_ext_control control;
+ v4l2_ext_controls controls;
+ memset(&control, 0, sizeof(control));
+ memset(&controls, 0, sizeof(controls));
+
+ control.id = control_id;
+ controls.ctrl_class = V4L2_CTRL_ID2CLASS(control_id);
+ controls.count = 1;
+ controls.controls = &control;
+
+ if (IoctlLocked(VIDIOC_G_EXT_CTRLS, &controls) < 0) {
+ HAL_LOGE("G_EXT_CTRLS fails: %s", strerror(errno));
+ return -ENODEV;
+ }
+ *value = control.value;
+ } else {
+ v4l2_control control{control_id, 0};
+ if (IoctlLocked(VIDIOC_G_CTRL, &control) < 0) {
+ HAL_LOGE("G_CTRL fails: %s", strerror(errno));
+ return -ENODEV;
+ }
+ *value = control.value;
+ }
+ return 0;
+}
+
+int V4L2Wrapper::SetControl(uint32_t control_id,
+ int32_t desired,
+ int32_t* result) {
+ int32_t result_value = 0;
+
+ // TODO(b/29334616): When async, this may need to check if the stream
+ // is on, and if so, lock it off while setting format. Need to look
+ // into if V4L2 supports adjusting controls while the stream is on.
+
+ // For extended controls (any control class other than "user"),
+ // S_EXT_CTRL must be used instead of S_CTRL.
+ if (V4L2_CTRL_ID2CLASS(control_id) != V4L2_CTRL_CLASS_USER) {
+ v4l2_ext_control control;
+ v4l2_ext_controls controls;
+ memset(&control, 0, sizeof(control));
+ memset(&controls, 0, sizeof(controls));
+
+ control.id = control_id;
+ control.value = desired;
+ controls.ctrl_class = V4L2_CTRL_ID2CLASS(control_id);
+ controls.count = 1;
+ controls.controls = &control;
+
+ if (IoctlLocked(VIDIOC_S_EXT_CTRLS, &controls) < 0) {
+ HAL_LOGE("S_EXT_CTRLS fails: %s", strerror(errno));
+ return -ENODEV;
+ }
+ result_value = control.value;
+ } else {
+ v4l2_control control{control_id, desired};
+ if (IoctlLocked(VIDIOC_S_CTRL, &control) < 0) {
+ HAL_LOGE("S_CTRL fails: %s", strerror(errno));
+ return -ENODEV;
+ }
+ result_value = control.value;
+ }
+
+ // If the caller wants to know the result, pass it back.
+ if (result != nullptr) {
+ *result = result_value;
+ }
+ return 0;
+}
+
+int V4L2Wrapper::GetFormats(std::set<uint32_t>* v4l2_formats) {
+ HAL_LOG_ENTER();
+
+ v4l2_fmtdesc format_query;
+ memset(&format_query, 0, sizeof(format_query));
+ // TODO(b/30000211): multiplanar support.
+ format_query.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ while (IoctlLocked(VIDIOC_ENUM_FMT, &format_query) >= 0) {
+ v4l2_formats->insert(format_query.pixelformat);
+ ++format_query.index;
+ }
+
+ if (errno != EINVAL) {
+ HAL_LOGE(
+ "ENUM_FMT fails at index %d: %s", format_query.index, strerror(errno));
+ return -ENODEV;
+ }
+ return 0;
+}
+
+int V4L2Wrapper::GetFormatFrameSizes(uint32_t v4l2_format,
+ std::set<std::array<int32_t, 2>>* sizes) {
+ v4l2_frmsizeenum size_query;
+ memset(&size_query, 0, sizeof(size_query));
+ size_query.pixel_format = v4l2_format;
+ if (IoctlLocked(VIDIOC_ENUM_FRAMESIZES, &size_query) < 0) {
+ HAL_LOGE("ENUM_FRAMESIZES failed: %s", strerror(errno));
+ return -ENODEV;
+ }
+ if (size_query.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
+ // Discrete: enumerate all sizes using VIDIOC_ENUM_FRAMESIZES.
+ // Assuming that a driver with discrete frame sizes has a reasonable number
+ // of them.
+ do {
+ sizes->insert({{{static_cast<int32_t>(size_query.discrete.width),
+ static_cast<int32_t>(size_query.discrete.height)}}});
+ ++size_query.index;
+ } while (IoctlLocked(VIDIOC_ENUM_FRAMESIZES, &size_query) >= 0);
+ if (errno != EINVAL) {
+ HAL_LOGE("ENUM_FRAMESIZES fails at index %d: %s",
+ size_query.index,
+ strerror(errno));
+ return -ENODEV;
+ }
+ } else {
+ // Continuous/Step-wise: based on the stepwise struct returned by the query.
+ // Fully listing all possible sizes, with large enough range/small enough
+ // step size, may produce far too many potential sizes. Instead, find the
+ // closest to a set of standard sizes.
+ for (const auto size : kStandardSizes) {
+ // Find the closest size, rounding up.
+ uint32_t desired_width = size[0];
+ uint32_t desired_height = size[1];
+ if (desired_width < size_query.stepwise.min_width ||
+ desired_height < size_query.stepwise.min_height) {
+ HAL_LOGV("Standard size %u x %u is too small for format %d",
+ desired_width,
+ desired_height,
+ v4l2_format);
+ continue;
+ } else if (desired_width > size_query.stepwise.max_width &&
+ desired_height > size_query.stepwise.max_height) {
+ HAL_LOGV("Standard size %u x %u is too big for format %d",
+ desired_width,
+ desired_height,
+ v4l2_format);
+ continue;
+ }
+
+ // Round up.
+ uint32_t width_steps = (desired_width - size_query.stepwise.min_width +
+ size_query.stepwise.step_width - 1) /
+ size_query.stepwise.step_width;
+ uint32_t height_steps = (desired_height - size_query.stepwise.min_height +
+ size_query.stepwise.step_height - 1) /
+ size_query.stepwise.step_height;
+ sizes->insert(
+ {{{static_cast<int32_t>(size_query.stepwise.min_width +
+ width_steps * size_query.stepwise.step_width),
+ static_cast<int32_t>(size_query.stepwise.min_height +
+ height_steps *
+ size_query.stepwise.step_height)}}});
+ }
+ }
+ return 0;
+}
+
+// Converts a v4l2_fract with units of seconds to an int64_t with units of ns.
+inline int64_t FractToNs(const v4l2_fract& fract) {
+ return (1000000000LL * fract.numerator) / fract.denominator;
+}
+
+int V4L2Wrapper::GetFormatFrameDurationRange(
+ uint32_t v4l2_format,
+ const std::array<int32_t, 2>& size,
+ std::array<int64_t, 2>* duration_range) {
+ // Potentially called so many times logging entry is a bad idea.
+
+ v4l2_frmivalenum duration_query;
+ memset(&duration_query, 0, sizeof(duration_query));
+ duration_query.pixel_format = v4l2_format;
+ duration_query.width = size[0];
+ duration_query.height = size[1];
+ if (IoctlLocked(VIDIOC_ENUM_FRAMEINTERVALS, &duration_query) < 0) {
+ HAL_LOGE("ENUM_FRAMEINTERVALS failed: %s", strerror(errno));
+ return -ENODEV;
+ }
+
+ int64_t min = std::numeric_limits<int64_t>::max();
+ int64_t max = std::numeric_limits<int64_t>::min();
+ if (duration_query.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
+ // Discrete: enumerate all durations using VIDIOC_ENUM_FRAMEINTERVALS.
+ do {
+ min = std::min(min, FractToNs(duration_query.discrete));
+ max = std::max(max, FractToNs(duration_query.discrete));
+ ++duration_query.index;
+ } while (IoctlLocked(VIDIOC_ENUM_FRAMEINTERVALS, &duration_query) >= 0);
+ if (errno != EINVAL) {
+ HAL_LOGE("ENUM_FRAMEINTERVALS fails at index %d: %s",
+ duration_query.index,
+ strerror(errno));
+ return -ENODEV;
+ }
+ } else {
+ // Continuous/Step-wise: simply convert the given min and max.
+ min = FractToNs(duration_query.stepwise.min);
+ max = FractToNs(duration_query.stepwise.max);
+ }
+ (*duration_range)[0] = min;
+ (*duration_range)[1] = max;
+ return 0;
+}
+
+int V4L2Wrapper::SetFormat(const StreamFormat& desired_format,
+ uint32_t* result_max_buffers) {
+ HAL_LOG_ENTER();
+
+ if (format_ && desired_format == *format_) {
+ HAL_LOGV("Already in correct format, skipping format setting.");
+ *result_max_buffers = buffers_.size();
+ return 0;
+ }
+
+ // Not in the correct format, set the new one.
+
+ if (format_) {
+ // If we had an old format, first request 0 buffers to inform the device
+ // we're no longer using any previously "allocated" buffers from the old
+ // format. This seems like it shouldn't be necessary for USERPTR memory,
+ // and/or should happen from turning the stream off, but the driver
+ // complained. May be a driver issue, or may be intended behavior.
+ int res = RequestBuffers(0);
+ if (res) {
+ return res;
+ }
+ }
+
+ // Set the camera to the new format.
+ v4l2_format new_format;
+ desired_format.FillFormatRequest(&new_format);
+ // TODO(b/29334616): When async, this will need to check if the stream
+ // is on, and if so, lock it off while setting format.
+
+ if (IoctlLocked(VIDIOC_S_FMT, &new_format) < 0) {
+ HAL_LOGE("S_FMT failed: %s", strerror(errno));
+ return -ENODEV;
+ }
+
+ // Check that the driver actually set to the requested values.
+ if (desired_format != new_format) {
+ HAL_LOGE("Device doesn't support desired stream configuration.");
+ return -EINVAL;
+ }
+
+ // Keep track of our new format.
+ format_.reset(new StreamFormat(new_format));
+
+ // Format changed, request new buffers.
+ int res = RequestBuffers(1);
+ if (res) {
+ HAL_LOGE("Requesting buffers for new format failed.");
+ return res;
+ }
+ *result_max_buffers = buffers_.size();
+ return 0;
+}
+
+int V4L2Wrapper::RequestBuffers(uint32_t num_requested) {
+ v4l2_requestbuffers req_buffers;
+ memset(&req_buffers, 0, sizeof(req_buffers));
+ req_buffers.type = format_->type();
+ req_buffers.memory = V4L2_MEMORY_USERPTR;
+ req_buffers.count = num_requested;
+
+ int res = IoctlLocked(VIDIOC_REQBUFS, &req_buffers);
+ // Calling REQBUFS releases all queued buffers back to the user.
+ int gralloc_res = gralloc_->unlockAllBuffers();
+ if (res < 0) {
+ HAL_LOGE("REQBUFS failed: %s", strerror(errno));
+ return -ENODEV;
+ }
+ if (gralloc_res < 0) {
+ HAL_LOGE("Failed to unlock all buffers when setting up new buffers.");
+ return gralloc_res;
+ }
+
+ // V4L2 will set req_buffers.count to a number of buffers it can handle.
+ if (num_requested > 0 && req_buffers.count < 1) {
+ HAL_LOGE("REQBUFS claims it can't handle any buffers.");
+ return -ENODEV;
+ }
+ buffers_.resize(req_buffers.count, false);
+
+ return 0;
+}
+
+int V4L2Wrapper::EnqueueBuffer(const camera3_stream_buffer_t* camera_buffer,
+ uint32_t* enqueued_index) {
+ if (!format_) {
+ HAL_LOGE("Stream format must be set before enqueuing buffers.");
+ return -ENODEV;
+ }
+
+ // Find a free buffer index. Could use some sort of persistent hinting
+ // here to improve expected efficiency, but buffers_.size() is expected
+ // to be low enough (<10 experimentally) that it's not worth it.
+ int index = -1;
+ {
+ std::lock_guard<std::mutex> guard(buffer_queue_lock_);
+ for (int i = 0; i < buffers_.size(); ++i) {
+ if (!buffers_[i]) {
+ index = i;
+ break;
+ }
+ }
+ }
+ if (index < 0) {
+ // Note: The HAL should be tracking the number of buffers in flight
+ // for each stream, and should never overflow the device.
+ HAL_LOGE("Cannot enqueue buffer: stream is already full.");
+ return -ENODEV;
+ }
+
+ // Set up a v4l2 buffer struct.
+ v4l2_buffer device_buffer;
+ memset(&device_buffer, 0, sizeof(device_buffer));
+ device_buffer.type = format_->type();
+ device_buffer.index = index;
+
+ // Use QUERYBUF to ensure our buffer/device is in good shape,
+ // and fill out remaining fields.
+ if (IoctlLocked(VIDIOC_QUERYBUF, &device_buffer) < 0) {
+ HAL_LOGE("QUERYBUF fails: %s", strerror(errno));
+ return -ENODEV;
+ }
+
+ // Lock the buffer for writing (fills in the user pointer field).
+ int res =
+ gralloc_->lock(camera_buffer, format_->bytes_per_line(), &device_buffer);
+ if (res) {
+ HAL_LOGE("Gralloc failed to lock buffer.");
+ return res;
+ }
+ if (IoctlLocked(VIDIOC_QBUF, &device_buffer) < 0) {
+ HAL_LOGE("QBUF fails: %s", strerror(errno));
+ gralloc_->unlock(&device_buffer);
+ return -ENODEV;
+ }
+
+ // Mark the buffer as in flight.
+ std::lock_guard<std::mutex> guard(buffer_queue_lock_);
+ buffers_[index] = true;
+
+ if (enqueued_index) {
+ *enqueued_index = index;
+ }
+ return 0;
+}
+
+int V4L2Wrapper::DequeueBuffer(uint32_t* dequeued_index) {
+ if (!format_) {
+ HAL_LOGV(
+ "Format not set, so stream can't be on, "
+ "so no buffers available for dequeueing");
+ return -EAGAIN;
+ }
+
+ v4l2_buffer buffer;
+ memset(&buffer, 0, sizeof(buffer));
+ buffer.type = format_->type();
+ buffer.memory = V4L2_MEMORY_USERPTR;
+ int res = IoctlLocked(VIDIOC_DQBUF, &buffer);
+ if (res) {
+ if (errno == EAGAIN) {
+ // Expected failure.
+ return -EAGAIN;
+ } else {
+ // Unexpected failure.
+ HAL_LOGE("DQBUF fails: %s", strerror(errno));
+ return -ENODEV;
+ }
+ }
+
+ // Mark the buffer as no longer in flight.
+ {
+ std::lock_guard<std::mutex> guard(buffer_queue_lock_);
+ buffers_[buffer.index] = false;
+ }
+
+ // Now that we're done painting the buffer, we can unlock it.
+ res = gralloc_->unlock(&buffer);
+ if (res) {
+ HAL_LOGE("Gralloc failed to unlock buffer after dequeueing.");
+ return res;
+ }
+
+ if (dequeued_index) {
+ *dequeued_index = buffer.index;
+ }
+ return 0;
+}
+
+} // namespace v4l2_camera_hal
diff --git a/modules/camera/3_4/v4l2_wrapper.h b/modules/camera/3_4/v4l2_wrapper.h
new file mode 100644
index 0000000..c3ad272
--- /dev/null
+++ b/modules/camera/3_4/v4l2_wrapper.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef V4L2_CAMERA_HAL_V4L2_WRAPPER_H_
+#define V4L2_CAMERA_HAL_V4L2_WRAPPER_H_
+
+#include <array>
+#include <memory>
+#include <mutex>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+
+#include "common.h"
+#include "stream_format.h"
+#include "v4l2_gralloc.h"
+
+namespace v4l2_camera_hal {
+class V4L2Wrapper {
+ public:
+ // Use this method to create V4L2Wrapper objects. Functionally equivalent
+ // to "new V4L2Wrapper", except that it may return nullptr in case of failure.
+ static V4L2Wrapper* NewV4L2Wrapper(const std::string device_path);
+ virtual ~V4L2Wrapper();
+
+ // Helper class to ensure all opened connections are closed.
+ class Connection {
+ public:
+ Connection(std::shared_ptr<V4L2Wrapper> device)
+ : device_(std::move(device)), connect_result_(device_->Connect()) {}
+ ~Connection() {
+ if (connect_result_ == 0) {
+ device_->Disconnect();
+ }
+ }
+ // Check whether the connection succeeded or not.
+ inline int status() const { return connect_result_; }
+
+ private:
+ std::shared_ptr<V4L2Wrapper> device_;
+ const int connect_result_;
+ };
+
+ // Turn the stream on or off.
+ virtual int StreamOn();
+ virtual int StreamOff();
+ // Manage controls.
+ virtual int QueryControl(uint32_t control_id, v4l2_query_ext_ctrl* result);
+ virtual int GetControl(uint32_t control_id, int32_t* value);
+ virtual int SetControl(uint32_t control_id,
+ int32_t desired,
+ int32_t* result = nullptr);
+ // Manage format.
+ virtual int GetFormats(std::set<uint32_t>* v4l2_formats);
+ virtual int GetFormatFrameSizes(uint32_t v4l2_format,
+ std::set<std::array<int32_t, 2>>* sizes);
+ // Durations are returned in ns.
+ virtual int GetFormatFrameDurationRange(
+ uint32_t v4l2_format,
+ const std::array<int32_t, 2>& size,
+ std::array<int64_t, 2>* duration_range);
+ virtual int SetFormat(const StreamFormat& desired_format,
+ uint32_t* result_max_buffers);
+ // Manage buffers.
+ virtual int EnqueueBuffer(const camera3_stream_buffer_t* camera_buffer,
+ uint32_t* enqueued_index = nullptr);
+ virtual int DequeueBuffer(uint32_t* dequeued_index = nullptr);
+
+ private:
+ // Constructor is private to allow failing on bad input.
+ // Use NewV4L2Wrapper instead.
+ V4L2Wrapper(const std::string device_path,
+ std::unique_ptr<V4L2Gralloc> gralloc);
+
+ // Connect or disconnect to the device. Access by creating/destroying
+ // a V4L2Wrapper::Connection object.
+ int Connect();
+ void Disconnect();
+ // Perform an ioctl call in a thread-safe fashion.
+ template <typename T>
+ int IoctlLocked(int request, T data);
+ // Request/release userspace buffer mode via VIDIOC_REQBUFS.
+ int RequestBuffers(uint32_t num_buffers);
+
+ inline bool connected() { return device_fd_.get() >= 0; }
+
+ // The camera device path. For example, /dev/video0.
+ const std::string device_path_;
+ // The opened device fd.
+ android::base::unique_fd device_fd_;
+ // The underlying gralloc module.
+ std::unique_ptr<V4L2Gralloc> gralloc_;
+ // Whether or not the device supports the extended control query.
+ bool extended_query_supported_;
+ // The format this device is set up for.
+ std::unique_ptr<StreamFormat> format_;
+ // Map indecies to buffer status. True if the index is in-flight.
+ // |buffers_.size()| will always be the maximum number of buffers this device
+ // can handle in its current format.
+ std::vector<bool> buffers_;
+ // Lock protecting use of the buffer tracker.
+ std::mutex buffer_queue_lock_;
+ // Lock protecting use of the device.
+ std::mutex device_lock_;
+ // Lock protecting connecting/disconnecting the device.
+ std::mutex connection_lock_;
+ // Reference count connections.
+ int connection_count_;
+
+ friend class Connection;
+ friend class V4L2WrapperMock;
+
+ DISALLOW_COPY_AND_ASSIGN(V4L2Wrapper);
+};
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_V4L2_WRAPPER_H_
diff --git a/modules/camera/3_4/v4l2_wrapper_mock.h b/modules/camera/3_4/v4l2_wrapper_mock.h
new file mode 100644
index 0000000..d423cc5
--- /dev/null
+++ b/modules/camera/3_4/v4l2_wrapper_mock.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Mock for wrapper class used to communicate with V4L2 devices.
+
+#ifndef V4L2_CAMERA_HAL_V4L2_WRAPPER_MOCK_H_
+#define V4L2_CAMERA_HAL_V4L2_WRAPPER_MOCK_H_
+
+#include <gmock/gmock.h>
+
+#include "v4l2_wrapper.h"
+
+namespace v4l2_camera_hal {
+
+class V4L2WrapperMock : public V4L2Wrapper {
+ public:
+ V4L2WrapperMock() : V4L2Wrapper("", nullptr){};
+ MOCK_METHOD0(StreamOn, int());
+ MOCK_METHOD0(StreamOff, int());
+ MOCK_METHOD2(QueryControl,
+ int(uint32_t control_id, v4l2_query_ext_ctrl* result));
+ MOCK_METHOD2(GetControl, int(uint32_t control_id, int32_t* value));
+ MOCK_METHOD3(SetControl,
+ int(uint32_t control_id, int32_t desired, int32_t* result));
+ MOCK_METHOD1(GetFormats, int(std::set<uint32_t>*));
+ MOCK_METHOD2(GetFormatFrameSizes,
+ int(uint32_t, std::set<std::array<int32_t, 2>>*));
+ MOCK_METHOD3(GetFormatFrameDurationRange,
+ int(uint32_t,
+ const std::array<int32_t, 2>&,
+ std::array<int64_t, 2>*));
+ MOCK_METHOD4(SetFormat,
+ int(int format,
+ uint32_t width,
+ uint32_t height,
+ uint32_t* result_max_buffers));
+ MOCK_METHOD2(EnqueueBuffer,
+ int(const camera3_stream_buffer_t* camera_buffer,
+ uint32_t* enqueued_index));
+ MOCK_METHOD1(DequeueBuffer, int(uint32_t* dequeued_index));
+};
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_V4L2_WRAPPER_MOCK_H_
diff --git a/modules/nfc-nci/Android.bp b/modules/nfc-nci/Android.bp
index 90d2a28..f4ef64e 100644
--- a/modules/nfc-nci/Android.bp
+++ b/modules/nfc-nci/Android.bp
@@ -16,7 +16,7 @@
name: "nfc_nci.default",
relative_install_path: "hw",
proprietary: true,
- srcs: ["nfc_nci_example.c"],
+ srcs: ["nfc_nci_example.cpp"],
shared_libs: [
"liblog",
"libcutils",
diff --git a/modules/nfc-nci/nfc_nci_example.c b/modules/nfc-nci/nfc_nci_example.cpp
similarity index 68%
rename from modules/nfc-nci/nfc_nci_example.c
rename to modules/nfc-nci/nfc_nci_example.cpp
index f3f60f6..0471b6b 100644
--- a/modules/nfc-nci/nfc_nci_example.c
+++ b/modules/nfc-nci/nfc_nci_example.cpp
@@ -23,61 +23,45 @@
#include <hardware/nfc.h>
/*
- * We want to silence the "unused argument" that gcc and clang give.
- * Other compilers generating this warning will need to provide their
- * custom attribute to silence this.
- */
-#if defined(__GNUC__) || defined(__clang__)
-#define UNUSED_ARGUMENT __attribute((unused))
-#else
-#define UNUSED_ARGUMENT
-#endif
-
-/*
* NCI HAL method implementations. These must be overriden
*/
-static int hal_open(const struct nfc_nci_device *dev UNUSED_ARGUMENT,
- nfc_stack_callback_t *p_cback UNUSED_ARGUMENT,
- nfc_stack_data_callback_t *p_data_cback UNUSED_ARGUMENT) {
+static int hal_open(const struct nfc_nci_device* /*dev*/,
+ nfc_stack_callback_t* /*p_cback*/,
+ nfc_stack_data_callback_t* /*p_data_cback*/) {
ALOGE("NFC-NCI HAL: %s", __FUNCTION__);
return 0;
}
-static int hal_write(const struct nfc_nci_device *dev UNUSED_ARGUMENT,
- uint16_t data_len UNUSED_ARGUMENT,
- const uint8_t *p_data UNUSED_ARGUMENT) {
+static int hal_write(const struct nfc_nci_device* /*dev*/,
+ uint16_t /*data_len*/, const uint8_t* /*p_data*/) {
ALOGE("NFC-NCI HAL: %s", __FUNCTION__);
return 0;
}
-static int hal_core_initialized(
- const struct nfc_nci_device *dev UNUSED_ARGUMENT,
- uint8_t* p_core_init_rsp_params UNUSED_ARGUMENT) {
+static int hal_core_initialized(const struct nfc_nci_device* /*dev*/,
+ uint8_t* /*p_core_init_rsp_params*/) {
ALOGE("NFC-NCI HAL: %s", __FUNCTION__);
return 0;
}
-static int hal_pre_discover(
- const struct nfc_nci_device *dev UNUSED_ARGUMENT) {
+static int hal_pre_discover(const struct nfc_nci_device* /*dev*/) {
ALOGE("NFC-NCI HAL: %s", __FUNCTION__);
return 0;
}
-static int hal_close(const struct nfc_nci_device *dev UNUSED_ARGUMENT) {
+static int hal_close(const struct nfc_nci_device* /*dev*/) {
ALOGE("NFC-NCI HAL: %s", __FUNCTION__);
return 0;
}
-static int hal_control_granted (
- const struct nfc_nci_device *p_dev UNUSED_ARGUMENT)
+static int hal_control_granted (const struct nfc_nci_device* /*p_dev*/)
{
ALOGE("NFC-NCI HAL: %s", __FUNCTION__);
return 0;
}
-static int hal_power_cycle (
- const struct nfc_nci_device *p_dev UNUSED_ARGUMENT)
+static int hal_power_cycle (const struct nfc_nci_device* /*p_dev*/)
{
ALOGE("NFC-NCI HAL: %s", __FUNCTION__);
return 0;
@@ -93,9 +77,10 @@
}
static int nfc_open(const hw_module_t* module, const char* name,
- hw_device_t** device) {
+ hw_device_t** device) {
if (strcmp(name, NFC_NCI_CONTROLLER) == 0) {
- nfc_nci_device_t *dev = calloc(1, sizeof(nfc_nci_device_t));
+ nfc_nci_device_t *dev = static_cast<nfc_nci_device_t*>(
+ calloc(1, sizeof(nfc_nci_device_t)));
dev->common.tag = HARDWARE_DEVICE_TAG;
dev->common.version = 0x00010000; // [31:16] major, [15:0] minor
diff --git a/modules/radio/radio_hw.c b/modules/radio/radio_hw.c
index 4376bf0..93b8c88 100644
--- a/modules/radio/radio_hw.c
+++ b/modules/radio/radio_hw.c
@@ -334,6 +334,7 @@
case CMD_CONFIG: {
tuner->config = cmd->config;
+ tuner->config.antenna_connected = true;
event.type = RADIO_EVENT_CONFIG;
event.config = tuner->config;
ALOGV("%s CMD_CONFIG type %d low %d up %d",
@@ -669,6 +670,7 @@
pthread_mutex_lock(&rdev->lock);
if (rdev->tuner != NULL) {
+ ALOGE("Can't open tuner twice");
status = -ENOSYS;
goto exit;
}
diff --git a/tests/camera2/AndroidTest.xml b/tests/camera2/AndroidTest.xml
new file mode 100644
index 0000000..693beb2
--- /dev/null
+++ b/tests/camera2/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for camera2_test">
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="camera2_test->/data/local/tmp/camera2_test" />
+ </target_preparer>
+ <option name="test-suite-tag" value="apct" />
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="camera2_test" />
+ </test>
+</configuration>
diff --git a/tests/hardware/struct-last.cpp b/tests/hardware/struct-last.cpp
index 44a7b2d..276d786 100644
--- a/tests/hardware/struct-last.cpp
+++ b/tests/hardware/struct-last.cpp
@@ -15,7 +15,6 @@
*/
#include <cstddef>
-#include <system/window.h>
#include <hardware/hardware.h>
#include <hardware/sensors.h>
#include <hardware/fb.h>
diff --git a/tests/hardware/struct-offset.cpp b/tests/hardware/struct-offset.cpp
index 7f7f2e0..6f86f03 100644
--- a/tests/hardware/struct-offset.cpp
+++ b/tests/hardware/struct-offset.cpp
@@ -15,7 +15,6 @@
*/
#include <cstddef>
-#include <system/window.h>
#include <hardware/hardware.h>
#include <hardware/sensors.h>
#include <hardware/fb.h>
diff --git a/tests/hardware/struct-size.cpp b/tests/hardware/struct-size.cpp
index acb9d2d..232b55d 100644
--- a/tests/hardware/struct-size.cpp
+++ b/tests/hardware/struct-size.cpp
@@ -15,7 +15,6 @@
*/
-#include <system/window.h>
#include <hardware/hardware.h>
#include <hardware/sensors.h>
#include <hardware/fb.h>
diff --git a/tests/hwc/Android.mk b/tests/hwc/Android.mk
index 367f5f4..0416ff1 100644
--- a/tests/hwc/Android.mk
+++ b/tests/hwc/Android.mk
@@ -4,12 +4,13 @@
LOCAL_MODULE := libcnativewindow
LOCAL_SRC_FILES := cnativewindow.c util.c
LOCAL_CFLAGS := -Wno-unused-parameter
+LOCAL_SHARED_LIBRARIES := libEGL libGLESv2 libdl libhardware libnativewindow
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := hwc-test-arrows
LOCAL_SRC_FILES := test-arrows.c
LOCAL_STATIC_LIBRARIES := libcnativewindow
-LOCAL_SHARED_LIBRARIES := libEGL libGLESv2 libdl libhardware
+LOCAL_SHARED_LIBRARIES := libEGL libGLESv2 libdl libhardware libnativewindow
LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES
include $(BUILD_EXECUTABLE)