Move V4L2 camera HAL work to libhardware

BUG: 31117512
diff --git a/modules/camera/3_4/Android.mk b/modules/camera/3_4/Android.mk
new file mode 100644
index 0000000..bf888be
--- /dev/null
+++ b/modules/camera/3_4/Android.mk
@@ -0,0 +1,104 @@
+#
+# 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 \
+  libnativehelper \
+  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 \
+  format_metadata_factory.cpp \
+  metadata/enum_converter.cpp \
+  metadata/metadata.cpp \
+  stream.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/enum_converter_test.cpp \
+  metadata/ignored_control_delegate_test.cpp \
+  metadata/map_converter_test.cpp \
+  metadata/menu_control_options_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 \
+
+# 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/camera.cpp b/modules/camera/3_4/camera.cpp
new file mode 100644
index 0000000..a1f7e86
--- /dev/null
+++ b/modules/camera/3_4/camera.cpp
@@ -0,0 +1,648 @@
+/*
+ * 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"
+#include "stream.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),
+    mStreams(NULL),
+    mNumStreams(0)
+{
+    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)
+{
+    android::Mutex::Autolock al(mStaticInfoLock);
+
+    info->device_version = mDevice.common.version;
+    initDeviceInfo(info);
+    if (mStaticInfo == NULL) {
+        std::unique_ptr<android::CameraMetadata> static_info =
+            std::make_unique<android::CameraMetadata>();
+        if (initStaticInfo(static_info.get())) {
+            return -ENODEV;
+        }
+        mStaticInfo = std::move(static_info);
+    }
+    // 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".
+    info->static_camera_characteristics = mStaticInfo->getAndLock();
+
+    // Get facing & orientation from the static info.
+    uint8_t facing = 0;
+    int res = v4l2_camera_hal::SingleTagValue(
+        *mStaticInfo, ANDROID_LENS_FACING, &facing);
+    if (res) {
+        ALOGE("%s:%d: Failed to get facing from static metadata.",
+              __func__, mId);
+        return res;
+    }
+    switch (facing) {
+        case (ANDROID_LENS_FACING_FRONT):
+            info->facing = CAMERA_FACING_FRONT;
+            break;
+        case (ANDROID_LENS_FACING_BACK):
+            info->facing = CAMERA_FACING_BACK;
+            break;
+        case (ANDROID_LENS_FACING_EXTERNAL):
+            info->facing = CAMERA_FACING_EXTERNAL;
+            break;
+        default:
+            ALOGE("%s:%d: Invalid facing from metadata: %d.",
+                  __func__, mId, facing);
+            return -ENODEV;
+    }
+    int32_t orientation = 0;
+    res = v4l2_camera_hal::SingleTagValue(
+        *mStaticInfo, ANDROID_SENSOR_ORIENTATION, &orientation);
+    if (res) {
+        ALOGE("%s:%d: Failed to get orientation from static metadata.",
+              __func__, mId);
+        return res;
+    }
+    info->orientation = static_cast<int>(orientation);
+
+    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;
+    }
+
+    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)
+{
+    camera3_stream_t *astream;
+    Stream **newStreams = NULL;
+    int res = 0;
+
+    // Must provide new settings after configureStreams.
+    mSettingsSet = false;
+
+    ALOGV("%s:%d: stream_config=%p", __func__, mId, stream_config);
+    ATRACE_CALL();
+    android::Mutex::Autolock al(mDeviceLock);
+
+    if (stream_config == NULL) {
+        ALOGE("%s:%d: NULL stream configuration array", __func__, mId);
+        return -EINVAL;
+    }
+    if (stream_config->num_streams == 0) {
+        ALOGE("%s:%d: Empty stream configuration array", __func__, mId);
+        return -EINVAL;
+    }
+
+    // Create new stream array
+    newStreams = new Stream*[stream_config->num_streams];
+    ALOGV("%s:%d: Number of Streams: %d", __func__, mId,
+            stream_config->num_streams);
+
+    // Mark all current streams unused for now
+    for (int i = 0; i < mNumStreams; i++)
+        mStreams[i]->mReuse = false;
+    // Fill new stream array with reused streams and new streams
+    for (unsigned int i = 0; i < stream_config->num_streams; i++) {
+        astream = stream_config->streams[i];
+        if (astream->max_buffers > 0) {
+            ALOGV("%s:%d: Reusing stream %d", __func__, mId, i);
+            newStreams[i] = reuseStream(astream);
+        } else {
+            ALOGV("%s:%d: Creating new stream %d", __func__, mId, i);
+            newStreams[i] = new Stream(mId, astream);
+        }
+
+        if (newStreams[i] == NULL) {
+            ALOGE("%s:%d: Error processing stream %d", __func__, mId, i);
+            goto err_out;
+        }
+        astream->priv = newStreams[i];
+    }
+
+    // Verify the set of streams in aggregate
+    if (!isValidStreamSet(newStreams, stream_config->num_streams,
+                          stream_config->operation_mode)) {
+        ALOGE("%s:%d: Invalid stream set", __func__, mId);
+        goto err_out;
+    }
+
+    // Set up all streams (calculate usage/max_buffers for each,
+    // do any device-specific initialization)
+    res = setupStreams(newStreams, stream_config->num_streams);
+    if (res) {
+        ALOGE("%s:%d: Failed to setup stream set", __func__, mId);
+        goto err_out;
+    }
+
+    // Destroy all old streams and replace stream array with new one
+    destroyStreams(mStreams, mNumStreams);
+    mStreams = newStreams;
+    mNumStreams = stream_config->num_streams;
+
+    return 0;
+
+err_out:
+    // Clean up temporary streams, preserve existing mStreams/mNumStreams
+    destroyStreams(newStreams, stream_config->num_streams);
+    // Set error if it wasn't specified.
+    if (!res) {
+      res = -EINVAL;
+    }
+    return res;
+}
+
+void Camera::destroyStreams(Stream **streams, int count)
+{
+    if (streams == NULL)
+        return;
+    for (int i = 0; i < count; i++) {
+        // Only destroy streams that weren't reused
+        if (streams[i] != NULL && !streams[i]->mReuse)
+            delete streams[i];
+    }
+    delete [] streams;
+}
+
+Stream *Camera::reuseStream(camera3_stream_t *astream)
+{
+    Stream *priv = reinterpret_cast<Stream*>(astream->priv);
+    // Verify the re-used stream's parameters match
+    if (!priv->isValidReuseStream(mId, astream)) {
+        ALOGE("%s:%d: Mismatched parameter in reused stream", __func__, mId);
+        return NULL;
+    }
+    // Mark stream to be reused
+    priv->mReuse = true;
+    return priv;
+}
+
+bool Camera::isValidStreamSet(Stream **streams, int count, uint32_t mode)
+{
+    int inputs = 0;
+    int outputs = 0;
+
+    if (streams == NULL) {
+        ALOGE("%s:%d: NULL stream configuration streams", __func__, mId);
+        return false;
+    }
+    if (count == 0) {
+        ALOGE("%s:%d: Zero count stream configuration streams", __func__, mId);
+        return false;
+    }
+    // Validate there is at most one input stream and at least one output stream
+    for (int i = 0; i < count; i++) {
+        // A stream may be both input and output (bidirectional)
+        if (streams[i]->isInputType())
+            inputs++;
+        if (streams[i]->isOutputType())
+            outputs++;
+    }
+    ALOGV("%s:%d: Configuring %d output streams and %d input streams",
+            __func__, mId, outputs, inputs);
+    if (outputs < 1) {
+        ALOGE("%s:%d: Stream config must have >= 1 output", __func__, mId);
+        return false;
+    }
+    if (inputs > 1) {
+        ALOGE("%s:%d: Stream config must have <= 1 input", __func__, mId);
+        return false;
+    }
+
+    // check for correct number of Bayer/YUV/JPEG/Encoder streams
+    return isSupportedStreamSet(streams, count, mode);
+}
+
+int Camera::setupStreams(Stream **streams, int count)
+{
+    /*
+     * This is where the HAL has to decide internally how to handle all of the
+     * streams, and then produce usage and max_buffer values for each stream.
+     * Note, the stream array has been checked before this point for ALL invalid
+     * conditions, so it must find a successful configuration for this stream
+     * array. The only errors should be from individual streams requesting
+     * unsupported features (such as data_space or rotation).
+     */
+    for (int i = 0; i < count; i++) {
+        uint32_t usage = 0;
+        if (streams[i]->isOutputType())
+            usage |= GRALLOC_USAGE_HW_CAMERA_WRITE;
+        if (streams[i]->isInputType())
+            usage |= GRALLOC_USAGE_HW_CAMERA_READ;
+        streams[i]->setUsage(usage);
+
+        uint32_t max_buffers;
+        int res = setupStream(streams[i], &max_buffers);
+        if (res) {
+          return res;
+        }
+        streams[i]->setMaxBuffers(max_buffers);
+    }
+    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]) {
+        // 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 *request)
+{
+    int res;
+
+    ALOGV("%s:%d: request=%p", __func__, mId, request);
+    ATRACE_CALL();
+
+    if (request == NULL) {
+        ALOGE("%s:%d: NULL request recieved", __func__, mId);
+        return -EINVAL;
+    }
+
+    ALOGV("%s:%d: Request Frame:%d Settings:%p", __func__, mId,
+            request->frame_number, request->settings);
+
+    // NULL indicates use last settings
+    if (request->settings == NULL && !mSettingsSet) {
+        ALOGE("%s:%d: NULL settings without previous set Frame:%d Req:%p",
+              __func__, mId, request->frame_number, request);
+        return -EINVAL;
+    }
+
+    if (request->input_buffer != NULL) {
+        ALOGV("%s:%d: Reprocessing input buffer %p", __func__, mId,
+                request->input_buffer);
+
+        if (!isValidReprocessSettings(request->settings)) {
+            ALOGE("%s:%d: Invalid settings for reprocess request: %p",
+                    __func__, mId, request->settings);
+            return -EINVAL;
+        }
+    } else {
+        ALOGV("%s:%d: Capturing new frame.", __func__, mId);
+
+        // Make a copy since CameraMetadata doesn't support weak ownership,
+        // but |request| is supposed to maintain ownership.
+        android::CameraMetadata request_settings;
+        request_settings = request->settings;
+        if (!isValidCaptureSettings(request_settings)) {
+            ALOGE("%s:%d: Invalid settings for capture request: %p",
+                    __func__, mId, request->settings);
+            return -EINVAL;
+        }
+        // Settings are valid, go ahead and set them.
+        res = setSettings(request_settings);
+        if (res) {
+          ALOGE("%s:%d: Failed to set valid settings for capture request: %p",
+                __func__, mId, request->settings);
+          return res;
+        }
+        mSettingsSet = true;
+    }
+
+    // Setup and process output buffers.
+    if (request->num_output_buffers <= 0) {
+        ALOGE("%s:%d: Invalid number of output buffers: %d", __func__, mId,
+                request->num_output_buffers);
+        return -EINVAL;
+    }
+    camera3_capture_result result;
+    result.num_output_buffers = request->num_output_buffers;
+    std::vector<camera3_stream_buffer_t> output_buffers(
+        result.num_output_buffers);
+    for (unsigned int i = 0; i < request->num_output_buffers; i++) {
+        res = processCaptureBuffer(&request->output_buffers[i],
+                                   &output_buffers[i]);
+        if (res)
+            return -ENODEV;
+    }
+    result.output_buffers = &output_buffers[0];
+
+    // Get metadata for this frame. Since the framework guarantees only
+    // one call to process_capture_request at a time, this call is guaranteed
+    // to correspond with the most recently enqueued buffer.
+
+    android::CameraMetadata result_metadata;
+    uint64_t timestamp = 0;
+    // TODO(b/29334616): this may also want to use a callback, since
+    // the shutter may not happen immediately.
+    res = getResultSettings(&result_metadata, &timestamp);
+    if (res) {
+        return res;
+    }
+    result.result = result_metadata.release();
+
+    // Notify the framework with the shutter time.
+    result.frame_number = request->frame_number;
+    notifyShutter(result.frame_number, timestamp);
+
+    // TODO(b/29334616): asynchronously return results (the following should
+    // be done once all enqueued buffers for the request complete and callback).
+    result.partial_result = 1;
+    mCallbackOps->process_capture_result(mCallbackOps, &result);
+
+    return 0;
+}
+
+bool Camera::isValidReprocessSettings(const camera_metadata_t* /*settings*/)
+{
+    // TODO: reject settings that cannot be reprocessed
+    // input buffers unimplemented, use this to reject reprocessing requests
+    ALOGE("%s:%d: Input buffer reprocessing not implemented", __func__, mId);
+    return false;
+}
+
+int Camera::processCaptureBuffer(const camera3_stream_buffer_t *in,
+        camera3_stream_buffer_t *out)
+{
+    int res;
+    // TODO(b/29334616): This probably should be non-blocking (currently blocks
+    // here and on gralloc lock). Perhaps caller should put "in" in a queue
+    // initially, then have a thread that dequeues from there and calls this
+    // function.
+    if (in->acquire_fence != -1) {
+        res = sync_wait(in->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;
+        }
+    }
+
+    out->stream = in->stream;
+    out->buffer = in->buffer;
+    out->status = CAMERA3_BUFFER_STATUS_OK;
+
+    // Enqueue buffer for software-painting
+    res = enqueueBuffer(out);
+    if (res) {
+      return res;
+    }
+
+    // TODO(b/29334616): This should be part of a callback made when the
+    // enqueued buffer finishes painting.
+    // TODO: use driver-backed release fences
+    out->acquire_fence = -1;
+    out->release_fence = -1;
+    return 0;
+}
+
+void Camera::notifyShutter(uint32_t frame_number, uint64_t timestamp)
+{
+    int res;
+    struct timespec ts;
+
+    // If timestamp is 0, get timestamp from right now instead
+    if (timestamp == 0) {
+        ALOGW("%s:%d: No timestamp provided, using CLOCK_BOOTTIME",
+                __func__, mId);
+        res = clock_gettime(CLOCK_BOOTTIME, &ts);
+        if (res == 0) {
+            timestamp = ts.tv_sec * 1000000000ULL + ts.tv_nsec;
+        } else {
+            ALOGE("%s:%d: No timestamp and failed to get CLOCK_BOOTTIME %s(%d)",
+                    __func__, mId, strerror(errno), errno);
+        }
+    }
+    camera3_notify_msg_t m;
+    memset(&m, 0, sizeof(m));
+    m.type = CAMERA3_MSG_SHUTTER;
+    m.message.shutter.frame_number = frame_number;
+    m.message.shutter.timestamp = timestamp;
+    mCallbackOps->notify(mCallbackOps, &m);
+}
+
+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
+
+    dprintf(fd, "Number of streams: %d\n", mNumStreams);
+    for (int i = 0; i < mNumStreams; i++) {
+        dprintf(fd, "Stream %d/%d:\n", i, mNumStreams);
+        mStreams[i]->dump(fd);
+    }
+}
+
+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*)
+{
+    // TODO(b/29937783)
+    ALOGE("%s: unimplemented.", __func__);
+    return -1;
+}
+
+} // 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..158c36d
--- /dev/null
+++ b/modules/camera/3_4/camera.h
@@ -0,0 +1,143 @@
+/*
+ * 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 "metadata/metadata.h"
+#include "stream.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 *request);
+        void dump(int fd);
+
+
+    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;
+        // Verify stream configuration is device-compatible
+        virtual bool isSupportedStreamSet(Stream** streams, int count,
+                                          uint32_t mode) = 0;
+        // Set up the device for a stream, and get the maximum number of
+        // buffers that stream can handle (max_buffers is an output parameter)
+        virtual int setupStream(Stream* stream, uint32_t* max_buffers) = 0;
+        // Verify settings are valid for a capture
+        virtual bool isValidCaptureSettings(
+            const android::CameraMetadata& settings) = 0;
+        // Set settings for a capture
+        virtual int setSettings(
+            const android::CameraMetadata& new_settings) = 0;
+        // Separate initialization method for individual devices when opened
+        virtual int initDevice() = 0;
+        // Enqueue a buffer to receive data from the camera
+        virtual int enqueueBuffer(
+            const camera3_stream_buffer_t *camera_buffer) = 0;
+        // Get the shutter time and updated settings for the most recent frame.
+        // The metadata parameter is both an input and output; frame-specific
+        // result fields should be appended to what is passed in.
+        virtual int getResultSettings(android::CameraMetadata* metadata,
+                                      uint64_t *timestamp) = 0;
+        // Prettyprint template names
+        const char* templateToString(int type);
+
+    private:
+        // Camera device handle returned to framework for use
+        camera3_device_t mDevice;
+        // Reuse a stream already created by this device
+        Stream *reuseStream(camera3_stream_t *astream);
+        // Destroy all streams in a stream array, and the array itself
+        void destroyStreams(Stream **array, int count);
+        // Verify a set of streams is valid in aggregate
+        bool isValidStreamSet(Stream **array, int count, uint32_t mode);
+        // Calculate usage and max_bufs of each stream
+        int setupStreams(Stream **array, int count);
+        // Verify settings are valid for reprocessing an input buffer
+        bool isValidReprocessSettings(const camera_metadata_t *settings);
+        // Process an output buffer
+        int processCaptureBuffer(const camera3_stream_buffer_t *in,
+                                 camera3_stream_buffer_t *out);
+        // Send a shutter notify message with start of exposure time
+        void notifyShutter(uint32_t frame_number, uint64_t timestamp);
+        // 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<const android::CameraMetadata> 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;
+        // Array of handles to streams currently in use by the device
+        Stream **mStreams;
+        // Number of streams in mStreams
+        int mNumStreams;
+        // Standard camera settings templates
+        std::unique_ptr<const android::CameraMetadata> mTemplates[CAMERA3_TEMPLATE_COUNT];
+};
+}  // namespace default_camera_hal
+
+#endif  // DEFAULT_CAMERA_HAL_CAMERA_H_
diff --git a/modules/camera/3_4/common.h b/modules/camera/3_4/common.h
new file mode 100644
index 0000000..e77c0b2
--- /dev/null
+++ b/modules/camera/3_4/common.h
@@ -0,0 +1,61 @@
+/*
+ * 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_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..673477b
--- /dev/null
+++ b/modules/camera/3_4/format_metadata_factory.cpp
@@ -0,0 +1,209 @@
+/*
+ * 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}});
+  fps_ranges.push_back({{max_yuv_fps, max_yuv_fps}});
+
+  // 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);
+
+  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..f033e37
--- /dev/null
+++ b/modules/camera/3_4/format_metadata_factory_test.cpp
@@ -0,0 +1,113 @@
+/*
+ * 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}}}}}};
+
+  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;
+    }
+  }
+}
+}  // namespace v4l2_camera_hal
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/control.h b/modules/camera/3_4/metadata/control.h
new file mode 100644
index 0000000..6442f90
--- /dev/null
+++ b/modules/camera/3_4/metadata/control.h
@@ -0,0 +1,199 @@
+/*
+ * 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)) {
+  HAL_LOG_ENTER();
+}
+
+template <typename T>
+std::vector<int32_t> Control<T>::StaticTags() const {
+  std::vector<int32_t> result;
+  if (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 {
+  HAL_LOG_ENTER();
+
+  if (!options_) {
+    HAL_LOGV("No options for control, nothing to populate.");
+    return 0;
+  }
+
+  return UpdateMetadata(
+      metadata, options_->tag(), options_->MetadataRepresentation());
+}
+
+template <typename T>
+int Control<T>::PopulateDynamicFields(android::CameraMetadata* metadata) const {
+  HAL_LOG_ENTER();
+
+  // 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 {
+  HAL_LOG_ENTER();
+
+  // 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 {
+  HAL_LOG_ENTER();
+  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) {
+  HAL_LOG_ENTER();
+  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..572f25f
--- /dev/null
+++ b/modules/camera/3_4/metadata/control_test.cpp
@@ -0,0 +1,349 @@
+/*
+ * 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(bool with_options = true) {
+    // 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>>(
+            options_tag_, std::move(mock_options_));
+    if (with_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(bool with_options = true) {
+    if (with_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);
+    EXPECT_EQ(metadata.entryCount(), 1);
+    ExpectMetadataEq(metadata, options_tag_, options);
+  }
+
+  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_;
+
+  // 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) {
+  PrepareControl(false);
+  ExpectTags(false);
+}
+
+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) {
+  PrepareControl(false);
+  android::CameraMetadata metadata;
+  ASSERT_EQ(control_->PopulateStaticFields(&metadata), 0);
+  // Should not have added any entry.
+  EXPECT_TRUE(metadata.isEmpty());
+}
+
+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.
+  uint8_t test_option = 99;
+  EXPECT_CALL(*mock_delegate_, GetValue(_))
+      .WillOnce(DoAll(SetArgPointee<0>(test_option), Return(0)));
+  PrepareControl(false);
+  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) {
+  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(false);
+
+  android::CameraMetadata metadata;
+  EXPECT_EQ(control_->PopulateTemplateRequest(template_type, &metadata), 0);
+  ExpectMetadataEq(metadata, delegate_tag_, value);
+}
+
+TEST_F(ControlTest, PopulateTemplateOptionlessFail) {
+  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(false);
+
+  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) {
+  android::CameraMetadata metadata;
+  uint8_t test_option = 123;
+  ASSERT_EQ(UpdateMetadata(&metadata, delegate_tag_, test_option), 0);
+  PrepareControl(false);
+
+  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, 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) {
+  // 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(false);
+  // 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) {
+  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(false);
+
+  // 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) {
+  // 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(false);
+  // 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/enum_converter.cpp b/modules/camera/3_4/metadata/enum_converter.cpp
new file mode 100644
index 0000000..f4c9819
--- /dev/null
+++ b/modules/camera/3_4/metadata/enum_converter.cpp
@@ -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.
+ */
+
+#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) {
+  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;
+  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) {
+  HAL_LOG_ENTER();
+
+  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..95ed3b6
--- /dev/null
+++ b/modules/camera/3_4/metadata/menu_control_options.h
@@ -0,0 +1,57 @@
+/*
+ * 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"
+
+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) : options_(options) {}
+
+  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 {
+    // TODO(b/31017806): More complex logic, depend on template_type.
+    // Default to the first option.
+    if (options_.empty()) {
+      HAL_LOGE("Can't get default value, options are empty.");
+      return -ENODEV;
+    }
+    *default_value = options_[0];
+    return 0;
+  }
+
+ private:
+  std::vector<T> options_;
+};
+
+}  // 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..24582f6
--- /dev/null
+++ b/modules/camera/3_4/metadata/menu_control_options_test.cpp
@@ -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.
+ */
+
+#include "menu_control_options.h"
+
+#include <memory>
+
+#include <gtest/gtest.h>
+#include <hardware/camera3.h>
+
+using testing::Test;
+
+namespace v4l2_camera_hal {
+
+class MenuControlOptionsTest : public Test {
+ protected:
+  virtual void SetUp() { dut_.reset(new MenuControlOptions<int>(options_)); }
+
+  std::unique_ptr<MenuControlOptions<int>> dut_;
+  const std::vector<int> options_{1, 10, 19, 30};
+};
+
+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, DefaultValue) {
+  // All default values should be supported.
+  // For some reason, the templates have values in the range [1, COUNT).
+  for (int i = 1; i < CAMERA3_TEMPLATE_COUNT; ++i) {
+    int value = -1;
+    EXPECT_EQ(dut_->DefaultValueForTemplate(i, &value), 0);
+    EXPECT_TRUE(dut_->IsSupported(value));
+  }
+}
+
+TEST_F(MenuControlOptionsTest, NoDefaultValue) {
+  // Invalid options don't have a valid default.
+  MenuControlOptions<int> bad_options({});
+  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..15f8084
--- /dev/null
+++ b/modules/camera/3_4/metadata/metadata_common.h
@@ -0,0 +1,227 @@
+/*
+ * 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.
+//   -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) {
+  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) {
+  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;
+}
+
+}  // namespace v4l2_camera_hal
+
+#endif  // V4L2_CAMERA_HAL_METADATA_METADATA_COMMON_H_
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..27ea6e8
--- /dev/null
+++ b/modules/camera/3_4/metadata/partial_metadata_factory.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_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.
+// The default value will be the first element of |options|.
+template <typename T>
+static std::unique_ptr<Control<T>> NoEffectMenuControl(
+    int32_t delegate_tag, int32_t options_tag, const std::vector<T>& options);
+
+// NoEffectSliderControl: A slider of options, but they have no effect.
+// The default value will be |min|.
+template <typename T>
+static std::unique_ptr<Control<T>> NoEffectSliderControl(int32_t delegate_tag,
+                                                         int32_t options_tag,
+                                                         T min,
+                                                         T max);
+
+// 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);
+
+// 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);
+
+// V4L2ControlOrDefault: Like V4L2Control, but if the V4L2Control fails to
+// initialize for some reason, this method will fall back to NoEffectControl.
+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,
+    const T& default_value);
+
+// -----------------------------------------------------------------------------
+
+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) {
+  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)));
+}
+
+template <typename T>
+std::unique_ptr<Control<T>> NoEffectSliderControl(int32_t delegate_tag,
+                                                  int32_t options_tag,
+                                                  T min,
+                                                  T max) {
+  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)));
+}
+
+template <typename T>
+std::unique_ptr<Control<T>> NoEffectControl(ControlType type,
+                                            int32_t delegate_tag,
+                                            int32_t options_tag,
+                                            T value) {
+  HAL_LOG_ENTER();
+
+  switch (type) {
+    case ControlType::kMenu:
+      return NoEffectMenuControl<T>(delegate_tag, options_tag, {value});
+    case ControlType::kSlider:
+      return NoEffectSliderControl(delegate_tag, options_tag, value, value);
+  }
+}
+
+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) {
+  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:  // Fall-through.
+    case V4L2_CTRL_TYPE_MENU:
+      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;
+        }
+        if (control_id == V4L2_CID_COLORFX) {
+          HAL_LOGE("Adding color effect %d (%d)", i, metadata_val);
+        }
+        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));
+      // 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));
+      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,
+    const T& default_value) {
+  HAL_LOG_ENTER();
+
+  std::unique_ptr<Control<T>> result = V4L2Control(
+      type, delegate_tag, options_tag, device, control_id, converter);
+  if (!result) {
+    result = NoEffectControl(type, delegate_tag, options_tag, default_value);
+  }
+  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..4c4a1b1
--- /dev/null
+++ b/modules/camera/3_4/metadata/partial_metadata_factory_test.cpp
@@ -0,0 +1,448 @@
+/*
+ * 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;
+};
+
+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(PartialMetadataFactoryTest, V4L2FactoryMenu) {
+  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(PartialMetadataFactoryTest, V4L2FactoryMenuConversionFail) {
+  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(PartialMetadataFactoryTest, V4L2FactoryMenuNoConversions) {
+  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..03da434
--- /dev/null
+++ b/modules/camera/3_4/metadata/property.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_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 {
+    HAL_LOG_ENTER();
+    return UpdateMetadata(metadata, tag_, value_);
+  };
+
+  virtual int PopulateDynamicFields(
+      android::CameraMetadata* metadata) const override {
+    HAL_LOG_ENTER();
+    return 0;
+  };
+
+  virtual int PopulateTemplateRequest(
+      int template_type, android::CameraMetadata* metadata) const override {
+    HAL_LOG_ENTER();
+    return 0;
+  };
+
+  virtual bool SupportsRequestValues(
+      const android::CameraMetadata& metadata) const override {
+    HAL_LOG_ENTER();
+    return true;
+  };
+
+  virtual int SetRequestValues(
+      const android::CameraMetadata& metadata) override {
+    HAL_LOG_ENTER();
+    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..2815dad
--- /dev/null
+++ b/modules/camera/3_4/metadata/slider_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_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"
+
+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(T min, T max) : min_(min), max_(max) {}
+
+  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 {
+    // TODO(b/31017806): More complex logic, depend on template_type.
+    if (min_ > max_) {
+      HAL_LOGE("No valid default slider option, min is greater than max.");
+      return -ENODEV;
+    }
+    // Default to the min value.
+    *default_value = min_;
+    return 0;
+  }
+
+ private:
+  T min_;
+  T max_;
+};
+
+}  // 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..a241ad6
--- /dev/null
+++ b/modules/camera/3_4/metadata/slider_control_options_test.cpp
@@ -0,0 +1,74 @@
+/*
+ * 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 <gtest/gtest.h>
+#include <hardware/camera3.h>
+
+using testing::Test;
+
+namespace v4l2_camera_hal {
+
+class SliderControlOptionsTest : public Test {
+ protected:
+  virtual void SetUp() {
+    dut_.reset(new SliderControlOptions<int>(min_, max_));
+  }
+
+  std::unique_ptr<SliderControlOptions<int>> dut_;
+  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, DefaultValue) {
+  // All default values should be supported.
+  // For some reason, the templates have values in the range [1, COUNT).
+  for (int i = 1; i < CAMERA3_TEMPLATE_COUNT; ++i) {
+    int value = -1;
+    EXPECT_EQ(dut_->DefaultValueForTemplate(i, &value), 0);
+    EXPECT_TRUE(dut_->IsSupported(value));
+  }
+}
+
+TEST_F(SliderControlOptionsTest, NoDefaultValue) {
+  // Invalid options don't have a valid default.
+  SliderControlOptions<int> bad_options(10, 9);  // 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..9204fea
--- /dev/null
+++ b/modules/camera/3_4/metadata/tagged_control_options.h
@@ -0,0 +1,54 @@
+/*
+ * 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 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/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/stream.cpp b/modules/camera/3_4/stream.cpp
new file mode 100644
index 0000000..97d8634
--- /dev/null
+++ b/modules/camera/3_4/stream.cpp
@@ -0,0 +1,199 @@
+/*
+ * 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/Stream.cpp
+
+#include <stdio.h>
+#include <hardware/camera3.h>
+#include <hardware/gralloc.h>
+#include <system/graphics.h>
+#include <utils/Mutex.h>
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Stream"
+#include <cutils/log.h>
+
+#define ATRACE_TAG (ATRACE_TAG_CAMERA | ATRACE_TAG_HAL)
+#include <utils/Trace.h>
+
+#include "stream.h"
+
+namespace default_camera_hal {
+
+Stream::Stream(int id, camera3_stream_t *s)
+  : mReuse(false),
+    mId(id),
+    mStream(s),
+    mType(s->stream_type),
+    mWidth(s->width),
+    mHeight(s->height),
+    mFormat(s->format),
+    mUsage(0),
+    mRotation(s->rotation),
+    mDataSpace(s->data_space),
+    mMaxBuffers(0)
+{
+}
+
+Stream::~Stream()
+{
+}
+
+void Stream::setUsage(uint32_t usage)
+{
+    android::Mutex::Autolock al(mLock);
+    if (usage != mUsage) {
+        mUsage = usage;
+        mStream->usage = usage;
+    }
+}
+
+void Stream::setMaxBuffers(uint32_t max_buffers)
+{
+    android::Mutex::Autolock al(mLock);
+    if (max_buffers != mMaxBuffers) {
+        mMaxBuffers = max_buffers;
+        mStream->max_buffers = max_buffers;
+    }
+}
+
+void Stream::setDataSpace(android_dataspace_t data_space)
+{
+    android::Mutex::Autolock al(mLock);
+    if (data_space != mDataSpace) {
+        mDataSpace = data_space;
+        mStream->data_space = data_space;
+    }
+}
+
+bool Stream::isInputType() const
+{
+    return mType == CAMERA3_STREAM_INPUT ||
+        mType == CAMERA3_STREAM_BIDIRECTIONAL;
+}
+
+bool Stream::isOutputType() const
+{
+    return mType == CAMERA3_STREAM_OUTPUT ||
+        mType == CAMERA3_STREAM_BIDIRECTIONAL;
+}
+
+const char* Stream::typeToString(int type)
+{
+    switch (type) {
+    case CAMERA3_STREAM_INPUT:
+        return "CAMERA3_STREAM_INPUT";
+    case CAMERA3_STREAM_OUTPUT:
+        return "CAMERA3_STREAM_OUTPUT";
+    case CAMERA3_STREAM_BIDIRECTIONAL:
+        return "CAMERA3_STREAM_BIDIRECTIONAL";
+    }
+    return "Invalid stream type!";
+}
+
+const char* Stream::formatToString(int format)
+{
+    // See <system/graphics.h> for full list
+    switch (format) {
+    case HAL_PIXEL_FORMAT_BGRA_8888:
+        return "BGRA 8888";
+    case HAL_PIXEL_FORMAT_RGBA_8888:
+        return "RGBA 8888";
+    case HAL_PIXEL_FORMAT_RGBX_8888:
+        return "RGBX 8888";
+    case HAL_PIXEL_FORMAT_RGB_888:
+        return "RGB 888";
+    case HAL_PIXEL_FORMAT_RGB_565:
+        return "RGB 565";
+    case HAL_PIXEL_FORMAT_Y8:
+        return "Y8";
+    case HAL_PIXEL_FORMAT_Y16:
+        return "Y16";
+    case HAL_PIXEL_FORMAT_YV12:
+        return "YV12";
+    case HAL_PIXEL_FORMAT_YCbCr_422_SP:
+        return "NV16";
+    case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+        return "NV21";
+    case HAL_PIXEL_FORMAT_YCbCr_422_I:
+        return "YUY2";
+    case HAL_PIXEL_FORMAT_RAW10:
+        return "RAW10";
+    case HAL_PIXEL_FORMAT_RAW16:
+        return "RAW16";
+    case HAL_PIXEL_FORMAT_BLOB:
+        return "BLOB";
+    case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
+        return "IMPLEMENTATION DEFINED";
+    case HAL_PIXEL_FORMAT_YCbCr_420_888:
+        return "FLEXIBLE YCbCr 420 888";
+    }
+    return "Invalid stream format!";
+}
+
+bool Stream::isValidReuseStream(int id, camera3_stream_t *s)
+{
+    if (id != mId) {
+        ALOGE("%s:%d: Invalid camera id for reuse. Got %d expect %d",
+                __func__, mId, id, mId);
+        return false;
+    }
+    if (s != mStream) {
+        ALOGE("%s:%d: Invalid stream handle for reuse. Got %p expect %p",
+                __func__, mId, s, mStream);
+        return false;
+    }
+    if (s->stream_type != mType) {
+        ALOGE("%s:%d: Mismatched type in reused stream. Got %s(%d) "
+                "expect %s(%d)", __func__, mId, typeToString(s->stream_type),
+                s->stream_type, typeToString(mType), mType);
+        return false;
+    }
+    if (s->format != mFormat) {
+        ALOGE("%s:%d: Mismatched format in reused stream. Got %s(%d) "
+                "expect %s(%d)", __func__, mId, formatToString(s->format),
+                s->format, formatToString(mFormat), mFormat);
+        return false;
+    }
+    if (s->width != mWidth) {
+        ALOGE("%s:%d: Mismatched width in reused stream. Got %d expect %d",
+                __func__, mId, s->width, mWidth);
+        return false;
+    }
+    if (s->height != mHeight) {
+        ALOGE("%s:%d: Mismatched height in reused stream. Got %d expect %d",
+                __func__, mId, s->height, mHeight);
+        return false;
+    }
+    return true;
+}
+
+void Stream::dump(int fd)
+{
+    android::Mutex::Autolock al(mLock);
+
+    dprintf(fd, "Stream ID: %d (%p)\n", mId, mStream);
+    dprintf(fd, "Stream Type: %s (%d)\n", typeToString(mType), mType);
+    dprintf(fd, "Width: %" PRIu32 " Height: %" PRIu32 "\n", mWidth, mHeight);
+    dprintf(fd, "Stream Format: %s (%d)", formatToString(mFormat), mFormat);
+    // ToDo: prettyprint usage mask flags
+    dprintf(fd, "Gralloc Usage Mask: %#" PRIx32 "\n", mUsage);
+    dprintf(fd, "Stream Rotation: %d\n", mRotation);
+    dprintf(fd, "Stream Dataspace: 0x%x\n", mDataSpace);
+    dprintf(fd, "Max Buffer Count: %" PRIu32 "\n", mMaxBuffers);
+}
+
+}  // namespace default_camera_hal
diff --git a/modules/camera/3_4/stream.h b/modules/camera/3_4/stream.h
new file mode 100644
index 0000000..781f946
--- /dev/null
+++ b/modules/camera/3_4/stream.h
@@ -0,0 +1,83 @@
+/*
+ * 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/Stream.h
+
+#ifndef DEFAULT_CAMERA_HAL_STREAM_H_
+#define DEFAULT_CAMERA_HAL_STREAM_H_
+
+#include <hardware/camera3.h>
+#include <hardware/gralloc.h>
+#include <system/graphics.h>
+#include <utils/Mutex.h>
+
+namespace default_camera_hal {
+// Stream represents a single input or output stream for a camera device.
+class Stream {
+    public:
+        Stream(int id, camera3_stream_t *s);
+        ~Stream();
+
+        // validate that astream's parameters match this stream's parameters
+        bool isValidReuseStream(int id, camera3_stream_t *s);
+
+        void setUsage(uint32_t usage);
+        void setMaxBuffers(uint32_t max_buffers);
+        void setDataSpace(android_dataspace_t data_space);
+
+        inline int getFormat() const { return mFormat; };
+        inline int getType() const { return mType; };
+        inline uint32_t getWidth() const { return mWidth; };
+        inline uint32_t getHeight() const { return mHeight; };
+        inline int getRotation() const { return mRotation; };
+
+        bool isInputType() const;
+        bool isOutputType() const;
+        const char* typeToString(int type);
+        const char* formatToString(int format);
+        void dump(int fd);
+
+        // This stream is being reused. Used in stream configuration passes
+        bool mReuse;
+
+    private:
+
+        // The camera device id this stream belongs to
+        const int mId;
+        // Handle to framework's stream, used as a cookie for buffers
+        camera3_stream_t *mStream;
+        // Stream type: CAMERA3_STREAM_* (see <hardware/camera3.h>)
+        const int mType;
+        // Width in pixels of the buffers in this stream
+        const uint32_t mWidth;
+        // Height in pixels of the buffers in this stream
+        const uint32_t mHeight;
+        // Gralloc format: HAL_PIXEL_FORMAT_* (see <system/graphics.h>)
+        const int mFormat;
+        // Gralloc usage mask : GRALLOC_USAGE_* (see <hardware/gralloc.h>)
+        uint32_t mUsage;
+        // Output rotation this stream should undergo
+        const int mRotation;
+        // Color space of image data.
+        android_dataspace_t mDataSpace;
+        // Max simultaneous in-flight buffers for this stream
+        uint32_t mMaxBuffers;
+        // Lock protecting the Stream object for modifications
+        android::Mutex mLock;
+};
+}  // namespace default_camera_hal
+
+#endif  // DEFAULT_CAMERA_HAL_STREAM_H_
diff --git a/modules/camera/3_4/stream_format.cpp b/modules/camera/3_4/stream_format.cpp
new file mode 100644
index 0000000..43f4cae
--- /dev/null
+++ b/modules/camera/3_4/stream_format.cpp
@@ -0,0 +1,120 @@
+/*
+ * 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 "common.h"
+#include "stream.h"
+
+namespace v4l2_camera_hal {
+
+StreamFormat::StreamFormat(const default_camera_hal::Stream& stream)
+    // TODO(b/30000211): multiplanar support.
+    : type_(V4L2_BUF_TYPE_VIDEO_CAPTURE),
+      v4l2_pixel_format_(
+          StreamFormat::HalToV4L2PixelFormat(stream.getFormat())),
+      width_(stream.getWidth()),
+      height_(stream.getHeight()),
+      bytes_per_line_(0),
+      min_buffer_size_(0) {
+  HAL_LOG_ENTER();
+}
+
+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) {
+  HAL_LOG_ENTER();
+}
+
+void StreamFormat::FillFormatRequest(v4l2_format* format) const {
+  HAL_LOG_ENTER();
+
+  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:
+      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;
+    default:
+      // Unrecognized 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_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.
+      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..9358d3d
--- /dev/null
+++ b/modules/camera/3_4/stream_format.h
@@ -0,0 +1,68 @@
+/*
+ * 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 <linux/videodev2.h>
+
+#include "common.h"
+#include "stream.h"
+
+namespace v4l2_camera_hal {
+
+enum FormatCategory {
+  kFormatCategoryRaw,
+  kFormatCategoryStalling,
+  kFormatCategoryNonStalling,
+  kFormatCategoryUnknown,
+};
+
+class StreamFormat {
+ public:
+  StreamFormat(const default_camera_hal::Stream& stream);
+  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..521f835
--- /dev/null
+++ b/modules/camera/3_4/v4l2_camera.cpp
@@ -0,0 +1,372 @@
+/*
+ * 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 "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}}) {
+  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::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();
+  // Nothing to do.
+  return 0;
+}
+
+int V4L2Camera::enqueueBuffer(const camera3_stream_buffer_t* camera_buffer) {
+  HAL_LOG_ENTER();
+
+  int res = device_->EnqueueBuffer(camera_buffer);
+  if (res) {
+    HAL_LOGE("Device failed to enqueue buffer.");
+    return res;
+  }
+
+  // Turn on the stream.
+  // TODO(b/29334616): Lock around stream on/off access, only start stream
+  // if not already on. (For now, since it's synchronous, it will always be
+  // turned off before another call to this function).
+  res = device_->StreamOn();
+  if (res) {
+    HAL_LOGE("Device failed to turn on stream.");
+    return res;
+  }
+
+  // TODO(b/29334616): Enqueueing and dequeueing should be separate worker
+  // threads, not in the same function.
+
+  // Dequeue the buffer.
+  v4l2_buffer result_buffer;
+  res = device_->DequeueBuffer(&result_buffer);
+  if (res) {
+    HAL_LOGE("Device failed to dequeue buffer.");
+    return res;
+  }
+
+  // All done, cleanup.
+  // TODO(b/29334616): Lock around stream on/off access, only stop stream if
+  // buffer queue is empty (synchronously, there's only ever 1 buffer in the
+  // queue at a time, so this is safe).
+  res = device_->StreamOff();
+  if (res) {
+    HAL_LOGE("Device failed to turn off stream.");
+    return res;
+  }
+
+  return 0;
+}
+
+int V4L2Camera::getResultSettings(android::CameraMetadata* metadata,
+                                  uint64_t* timestamp) {
+  HAL_LOG_ENTER();
+
+  // Get the results.
+  int res = metadata_->FillResultMetadata(metadata);
+  if (res) {
+    HAL_LOGE("Failed to fill result metadata.");
+    return res;
+  }
+
+  // Extract the timestamp.
+  int64_t frame_time = 0;
+  res = SingleTagValue(*metadata, ANDROID_SENSOR_TIMESTAMP, &frame_time);
+  if (res) {
+    HAL_LOGE("Failed to extract timestamp from result metadata");
+    return res;
+  }
+  *timestamp = static_cast<uint64_t>(frame_time);
+
+  return 0;
+}
+
+bool V4L2Camera::isSupportedStreamSet(default_camera_hal::Stream** streams,
+                                      int count,
+                                      uint32_t mode) {
+  HAL_LOG_ENTER();
+
+  if (mode != CAMERA3_STREAM_CONFIGURATION_NORMAL_MODE) {
+    HAL_LOGE("Unsupported stream configuration mode: %d", mode);
+    return false;
+  }
+
+  // This should be checked by the caller, but put here as a sanity check.
+  if (count < 1) {
+    HAL_LOGE("Must request at least 1 stream");
+    return false;
+  }
+
+  // Count the number of streams of each type.
+  int32_t num_input = 0;
+  int32_t num_raw = 0;
+  int32_t num_stalling = 0;
+  int32_t num_non_stalling = 0;
+  for (int i = 0; i < count; ++i) {
+    default_camera_hal::Stream* stream = streams[i];
+
+    if (stream->isInputType()) {
+      ++num_input;
+    }
+
+    if (stream->isOutputType()) {
+      StreamFormat format(*stream);
+      switch (format.Category()) {
+        case kFormatCategoryRaw:
+          ++num_raw;
+        case kFormatCategoryStalling:
+          ++num_stalling;
+          break;
+        case kFormatCategoryNonStalling:
+          ++num_non_stalling;
+          break;
+        case kFormatCategoryUnknown:  // Fall through.
+        default:
+          HAL_LOGE(
+              "Unsupported format for stream %d: %d", i, stream->getFormat());
+          return false;
+      }
+    }
+  }
+
+  if (num_input > max_input_streams_ || num_raw > max_output_streams_[0] ||
+      num_non_stalling > max_output_streams_[1] ||
+      num_stalling > max_output_streams_[2]) {
+    HAL_LOGE(
+        "Invalid stream configuration: %d input, %d RAW, %d non-stalling, "
+        "%d stalling (max supported: %d input, %d RAW, %d non-stalling, "
+        "%d stalling)",
+        max_input_streams_,
+        max_output_streams_[0],
+        max_output_streams_[1],
+        max_output_streams_[2],
+        num_input,
+        num_raw,
+        num_non_stalling,
+        num_stalling);
+    return false;
+  }
+
+  // TODO(b/29939583): The above logic should be all that's necessary,
+  // but V4L2 doesn't actually support more than 1 stream at a time. So for now,
+  // if not all streams are the same format and size, error. Note that this
+  // means the HAL is not spec-compliant; the requested streams are technically
+  // valid and it is not technically allowed to error once it has reached this
+  // point.
+  int format = streams[0]->getFormat();
+  uint32_t width = streams[0]->getWidth();
+  uint32_t height = streams[0]->getHeight();
+  for (int i = 1; i < count; ++i) {
+    const default_camera_hal::Stream* stream = streams[i];
+    if (stream->getFormat() != format || stream->getWidth() != width ||
+        stream->getHeight() != 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->getFormat(),
+          stream->getWidth(),
+          stream->getHeight());
+      return false;
+    }
+  }
+
+  return true;
+}
+
+int V4L2Camera::setupStream(default_camera_hal::Stream* stream,
+                            uint32_t* max_buffers) {
+  HAL_LOG_ENTER();
+
+  if (stream->getRotation() != CAMERA3_STREAM_ROTATION_0) {
+    HAL_LOGE("Rotation %d not supported", stream->getRotation());
+    return -EINVAL;
+  }
+
+  // Doesn't matter what was requested, we always use dataspace V0_JFIF.
+  // Note: according to camera3.h, this isn't allowed, but etalvala@google.com
+  // claims it's underdocumented; the implementation lets the HAL overwrite it.
+  stream->setDataSpace(HAL_DATASPACE_V0_JFIF);
+
+  int res = device_->SetFormat(*stream, max_buffers);
+  if (res) {
+    HAL_LOGE("Failed to set device to correct format for stream.");
+    return res;
+  }
+  // Sanity check.
+  if (*max_buffers < 1) {
+    HAL_LOGE("Setting format resulted in an invalid maximum of %u buffers.",
+             *max_buffers);
+    return -ENODEV;
+  }
+
+  return 0;
+}
+
+bool V4L2Camera::isValidCaptureSettings(
+    const android::CameraMetadata& settings) {
+  HAL_LOG_ENTER();
+
+  return metadata_->IsValidRequest(settings);
+}
+
+int V4L2Camera::setSettings(const android::CameraMetadata& new_settings) {
+  HAL_LOG_ENTER();
+
+  return metadata_->SetRequestSettings(new_settings);
+}
+
+}  // 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..804012b
--- /dev/null
+++ b/modules/camera/3_4/v4l2_camera.h
@@ -0,0 +1,97 @@
+/*
+ * 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 <string>
+
+#include <camera/CameraMetadata.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 is device-compatible.
+  bool isSupportedStreamSet(default_camera_hal::Stream** streams,
+                            int count,
+                            uint32_t mode) override;
+  // Set up the device for a stream, and get the maximum number of
+  // buffers that stream can handle (max_buffers is an output parameter).
+  int setupStream(default_camera_hal::Stream* stream,
+                  uint32_t* max_buffers) override;
+  // Verify settings are valid for a capture with this device.
+  bool isValidCaptureSettings(const android::CameraMetadata& settings) override;
+  // Set settings for a capture.
+  int setSettings(const android::CameraMetadata& new_settings) override;
+  // Enqueue a buffer to receive data from the camera.
+  int enqueueBuffer(const camera3_stream_buffer_t* camera_buffer) override;
+  // Get the shutter time and updated settings for the most recent frame.
+  // The metadata parameter is both an input and output; frame-specific
+  // result fields should be appended to what is passed in.
+  int getResultSettings(android::CameraMetadata* metadata, uint64_t* timestamp);
+
+  // V4L2 helper.
+  std::shared_ptr<V4L2Wrapper> device_;
+  std::unique_ptr<V4L2Wrapper::Connection> connection_;
+  std::unique_ptr<Metadata> metadata_;
+
+  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..5812649
--- /dev/null
+++ b/modules/camera/3_4/v4l2_gralloc.cpp
@@ -0,0 +1,287 @@
+/*
+ * 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"
+
+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() {
+  HAL_LOG_ENTER();
+
+  // 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) {
+  HAL_LOG_ENTER();
+}
+
+V4L2Gralloc::~V4L2Gralloc() {
+  HAL_LOG_ENTER();
+
+  // 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) {
+  HAL_LOG_ENTER();
+
+  // 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;
+  switch(stream->format) {
+    // TODO(b/30119452): support more YCbCr formats.
+    case HAL_PIXEL_FORMAT_YCbCr_420_888:
+      android_ycbcr yuv_data;
+      mModule->lock_ycbcr(mModule, buffer, stream->usage, 0, 0,
+                          stream->width, stream->height, &yuv_data);
+
+      // 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.
+        HAL_LOGV("V4L2 YUV matches gralloc YUV.");
+        data = yuv_data.y;
+      } else {
+        // If not, allocate a contiguous buffer of appropriate size
+        // (to be transformed back upon unlock).
+        HAL_LOGV("Need to transform V4L2 YUV to gralloc YUV.");
+        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 HAL_PIXEL_FORMAT_BLOB:
+      // Jpeg buffers are just contiguous blobs; lock length * 1.
+      mModule->lock(mModule, buffer, stream->usage, 0, 0, device_buffer->length, 1, &data);
+      break;
+    default:
+      return -EINVAL;
+  }
+
+  // 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) {
+  HAL_LOG_ENTER();
+
+  // 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;
+  }
+
+  // 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..7ece59d
--- /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..b9c64be
--- /dev/null
+++ b/modules/camera/3_4/v4l2_metadata_factory.cpp
@@ -0,0 +1,484 @@
+/*
+ * 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/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}));
+
+  // 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));
+  components.insert(std::unique_ptr<PartialMetadataInterface>(
+      new Property<camera_metadata_rational_t>(
+          ANDROID_CONTROL_AE_COMPENSATION_STEP, kAeCompensationUnit)));
+  // TODO(b/31021522): Autofocus subcomponent, AFTrigger.
+  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): AE & AF triggers.
+  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(
+          {{V4L2_EXPOSURE_AUTO, ANDROID_CONTROL_AE_MODE_ON},
+           {V4L2_EXPOSURE_MANUAL, ANDROID_CONTROL_AE_MODE_OFF}})),
+      ANDROID_CONTROL_AE_MODE_ON));
+  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));
+  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));
+  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));
+  }
+  // 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}}))));
+  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));
+  }
+  // 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(std::unique_ptr<PartialMetadataInterface>(
+      new Property<uint8_t>(ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
+                            ANDROID_CONTROL_AWB_LOCK_AVAILABLE_FALSE)));
+  // 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.
+  components.insert(
+      NoEffectMenuControl<uint8_t>(ANDROID_CONTROL_MODE,
+                                   ANDROID_CONTROL_AVAILABLE_MODES,
+                                   {ANDROID_CONTROL_MODE_AUTO}));
+
+  // 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.
+  components.insert(
+      NoEffectMenuControl<uint8_t>(ANDROID_EDGE_MODE,
+                                   ANDROID_EDGE_AVAILABLE_EDGE_MODES,
+                                   {ANDROID_EDGE_MODE_FAST}));
+
+  // TODO(b/31023454): subcomponents of flash.
+  // Missing android.flash.mode control, since it uses a different enum.
+  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));
+
+  // 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}));
+  // info.hyperfocalDistance not required for UNCALIBRATED.
+  // 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(NoEffectOptionlessControl<uint8_t>(
+      ANDROID_CONTROL_CAPTURE_INTENT,
+      ANDROID_CONTROL_CAPTURE_INTENT_STILL_CAPTURE));
+
+  // Unable to control noise reduction in V4L2 devices,
+  // but FAST is allowed to be the same as OFF.
+  components.insert(NoEffectMenuControl<uint8_t>(
+      ANDROID_NOISE_REDUCTION_MODE,
+      ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
+      {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)));
+  // "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.
+  components.insert(std::unique_ptr<PartialMetadataInterface>(
+      new Property<std::array<int32_t, 4>>(
+          ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
+          {{0, 0, pixel_array_size[0], pixel_array_size[1]}})));
+  // 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)));
+  // TODO(b/29457051): Actually get a timestamp here. For now spoofing as non-0.
+  components.insert(FixedState<int64_t>(ANDROID_SENSOR_TIMESTAMP, 1));
+  // 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): Should actually do something for this, and range should
+  // be dependent on the stream configuration being used.
+  components.insert(NoEffectOptionlessControl<int64_t>(
+      ANDROID_SENSOR_FRAME_DURATION, 33333333L));  // 1/30 s.
+
+  // TODO(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)));
+
+  // 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));
+
+  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..3d7ba85
--- /dev/null
+++ b/modules/camera/3_4/v4l2_wrapper.cpp
@@ -0,0 +1,603 @@
+/*
+ * 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 <vector>
+
+#include <fcntl.h>
+#include <linux/videodev2.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <mutex>
+
+#include <nativehelper/ScopedFd.h>
+
+#include "common.h"
+#include "stream.h"
+#include "stream_format.h"
+#include "v4l2_gralloc.h"
+
+namespace v4l2_camera_hal {
+
+const int32_t kStandardSizes[][2] = {
+    {1920, 1080}, {1280, 720}, {640, 480}, {320, 240}};
+
+V4L2Wrapper* V4L2Wrapper::NewV4L2Wrapper(const std::string device_path) {
+  HAL_LOG_ENTER();
+
+  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)),
+      max_buffers_(0),
+      connection_count_(0) {
+  HAL_LOG_ENTER();
+}
+
+V4L2Wrapper::~V4L2Wrapper() {
+  HAL_LOG_ENTER();
+}
+
+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;
+  }
+
+  int fd = TEMP_FAILURE_RETRY(open(device_path_.c_str(), O_RDWR));
+  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();  // Includes close().
+  format_.reset();
+  max_buffers_ = 0;
+  // 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() {
+  HAL_LOG_ENTER();
+
+  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;
+  }
+
+  return 0;
+}
+
+int V4L2Wrapper::StreamOff() {
+  HAL_LOG_ENTER();
+
+  if (!format_) {
+    HAL_LOGE("Stream format must be set to turn off stream.");
+    return -ENODEV;
+  }
+
+  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();
+  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;
+  }
+
+  return 0;
+}
+
+int V4L2Wrapper::QueryControl(uint32_t control_id,
+                              v4l2_query_ext_ctrl* result) {
+  HAL_LOG_ENTER();
+  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) {
+  HAL_LOG_ENTER();
+
+  // 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) {
+  HAL_LOG_ENTER();
+  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) {
+  HAL_LOG_ENTER();
+
+  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 plus largest possible.
+    sizes->insert({{{static_cast<int32_t>(size_query.stepwise.max_width),
+                     static_cast<int32_t>(size_query.stepwise.max_height)}}});
+    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 default_camera_hal::Stream& stream,
+                           uint32_t* result_max_buffers) {
+  HAL_LOG_ENTER();
+
+  // Should be checked earlier; sanity check.
+  if (stream.isInputType()) {
+    HAL_LOGE("Input streams not supported.");
+    return -EINVAL;
+  }
+
+  StreamFormat desired_format(stream);
+  if (format_ && desired_format == *format_) {
+    HAL_LOGV("Already in correct format, skipping format setting.");
+    return 0;
+  }
+
+  // Not in the correct format, set our 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, setup new buffers.
+  int res = SetupBuffers();
+  if (res) {
+    HAL_LOGE("Failed to set up buffers for new format.");
+    return res;
+  }
+  *result_max_buffers = max_buffers_;
+  return 0;
+}
+
+int V4L2Wrapper::SetupBuffers() {
+  HAL_LOG_ENTER();
+
+  if (!format_) {
+    HAL_LOGE("Stream format must be set before setting up buffers.");
+    return -ENODEV;
+  }
+
+  // "Request" a buffer (since we're using a userspace buffer, this just
+  // tells V4L2 to switch into userspace buffer mode).
+  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 = 1;
+
+  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.
+  max_buffers_ = req_buffers.count;
+  // Sanity check.
+  if (max_buffers_ < 1) {
+    HAL_LOGE("REQBUFS claims it can't handle any buffers.");
+    return -ENODEV;
+  }
+  return 0;
+}
+
+int V4L2Wrapper::EnqueueBuffer(const camera3_stream_buffer_t* camera_buffer) {
+  HAL_LOG_ENTER();
+
+  if (!format_) {
+    HAL_LOGE("Stream format must be set before enqueuing buffers.");
+    return -ENODEV;
+  }
+
+  // Set up a v4l2 buffer struct.
+  v4l2_buffer device_buffer;
+  memset(&device_buffer, 0, sizeof(device_buffer));
+  device_buffer.type = format_->type();
+  // TODO(b/29334616): when this is async, actually limit the number
+  // of buffers used to the known max, and set this according to the
+  // queue length.
+  device_buffer.index = 0;
+
+  // 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;
+  }
+
+  return 0;
+}
+
+int V4L2Wrapper::DequeueBuffer(v4l2_buffer* buffer) {
+  HAL_LOG_ENTER();
+
+  if (!format_) {
+    HAL_LOGE("Stream format must be set before dequeueing buffers.");
+    return -ENODEV;
+  }
+
+  memset(buffer, 0, sizeof(*buffer));
+  buffer->type = format_->type();
+  buffer->memory = V4L2_MEMORY_USERPTR;
+  if (IoctlLocked(VIDIOC_DQBUF, buffer) < 0) {
+    HAL_LOGE("DQBUF fails: %s", strerror(errno));
+    return -ENODEV;
+  }
+
+  // Now that we're done painting the buffer, we can unlock it.
+  int res = gralloc_->unlock(buffer);
+  if (res) {
+    HAL_LOGE("Gralloc failed to unlock buffer after dequeueing.");
+    return res;
+  }
+
+  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..3798c14
--- /dev/null
+++ b/modules/camera/3_4/v4l2_wrapper.h
@@ -0,0 +1,127 @@
+/*
+ * 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 <nativehelper/ScopedFd.h>
+
+#include "common.h"
+#include "stream.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 default_camera_hal::Stream& stream,
+                        uint32_t* result_max_buffers);
+  // Manage buffers.
+  virtual int EnqueueBuffer(const camera3_stream_buffer_t* camera_buffer);
+  virtual int DequeueBuffer(v4l2_buffer* buffer);
+
+ 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);
+  // Adjust buffers any time a device is connected/reformatted.
+  int SetupBuffers();
+
+  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.
+  ScopedFd 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_;
+  // The maximum number of buffers this device can handle in its current format.
+  uint32_t max_buffers_;
+  // 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..c717d32
--- /dev/null
+++ b/modules/camera/3_4/v4l2_wrapper_mock.h
@@ -0,0 +1,55 @@
+/*
+ * 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_METHOD2(SetFormat,
+               int(const default_camera_hal::Stream& stream,
+                   uint32_t* result_max_buffers));
+  MOCK_METHOD1(EnqueueBuffer,
+               int(const camera3_stream_buffer_t* camera_buffer));
+  MOCK_METHOD1(DequeueBuffer, int(v4l2_buffer* buffer));
+};
+
+}  // namespace v4l2_camera_hal
+
+#endif  // V4L2_CAMERA_HAL_V4L2_WRAPPER_MOCK_H_