Plug in metadata refactor.

Replaces metadata usage in Camera/V4L2Camera with the
new Metadata class.

Switches Camera from camera_metadata_t to android::CameraMetadata
where appropriate for ease of use/clarity of ownership.

Also cleaned up some "error" logs that were potentially expected,
leaving it to the caller to log if something goes wrong, since
they were numerous and cluttering the log feed.

BUG: https://b/30140438, https://b/29335262
TEST: unit tests pass, test picture program still works,
  tested setting a V4L2 control to a non-default value.
Change-Id: I24e50c9b71736dfc576debf8d09dbe36b9bbf23a
diff --git a/modules/camera/3_4/camera.cpp b/modules/camera/3_4/camera.cpp
index 755ed02..a1f7e86 100644
--- a/modules/camera/3_4/camera.cpp
+++ b/modules/camera/3_4/camera.cpp
@@ -25,6 +25,8 @@
 #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
@@ -52,12 +54,11 @@
 
 Camera::Camera(int id)
   : mId(id),
-    mStaticInfo(NULL),
+    mSettingsSet(false),
     mBusy(false),
     mCallbackOps(NULL),
     mStreams(NULL),
-    mNumStreams(0),
-    mSettings(NULL)
+    mNumStreams(0)
 {
     memset(&mTemplates, 0, sizeof(mTemplates));
     memset(&mDevice, 0, sizeof(mDevice));
@@ -70,17 +71,6 @@
 
 Camera::~Camera()
 {
-    if (mStaticInfo != NULL) {
-        free_camera_metadata(mStaticInfo);
-    }
-    if (mSettings != NULL) {
-        free_camera_metadata(mSettings);
-    }
-    for (camera_metadata_t* metadata : mTemplates) {
-        if (metadata != NULL) {
-            free_camera_metadata(metadata);
-        }
-    }
 }
 
 int Camera::openDevice(const hw_module_t *module, hw_device_t **device)
@@ -111,11 +101,52 @@
     info->device_version = mDevice.common.version;
     initDeviceInfo(info);
     if (mStaticInfo == NULL) {
-      if (initStaticInfo(&mStaticInfo)) {
-        return -ENODEV;
-      }
+        std::unique_ptr<android::CameraMetadata> static_info =
+            std::make_unique<android::CameraMetadata>();
+        if (initStaticInfo(static_info.get())) {
+            return -ENODEV;
+        }
+        mStaticInfo = std::move(static_info);
     }
-    info->static_camera_characteristics = mStaticInfo;
+    // 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;
 }
 
@@ -156,6 +187,9 @@
     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);
@@ -215,8 +249,6 @@
     mStreams = newStreams;
     mNumStreams = stream_config->num_streams;
 
-    // Clear out last seen settings metadata
-    setSettings(NULL);
     return 0;
 
 err_out:
@@ -332,9 +364,23 @@
         return NULL;
     }
 
-    // Will return NULL (indicating unsupported) if the template is not
-    // initialized.
-    return mTemplates[type];
+    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)
@@ -353,14 +399,10 @@
             request->frame_number, request->settings);
 
     // NULL indicates use last settings
-    if (request->settings == NULL) {
-        if (mSettings == NULL) {
-            ALOGE("%s:%d: NULL settings without previous set Frame:%d Req:%p",
-                    __func__, mId, request->frame_number, request);
-            return -EINVAL;
-        }
-    } else {
-        setSettings(request->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) {
@@ -375,19 +417,31 @@
     } else {
         ALOGV("%s:%d: Capturing new frame.", __func__, mId);
 
-        if (!isValidCaptureSettings(request->settings)) {
+        // 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(
@@ -404,33 +458,15 @@
     // one call to process_capture_request at a time, this call is guaranteed
     // to correspond with the most recently enqueued buffer.
 
-    // Not wrapped in a unique_ptr since control is immediately
-    // handed off to the device via getResultSettings.
-    // TODO(b/30035628): This shouldn't even be an issue actually -
-    // the result settings returned should be a completely new
-    // object allocated by the function, not passed in and back
-    // out (the cloning of the request metadata is just a shim
-    // to fill in all the control fields until the device actually
-    // checks their real values).
-    camera_metadata_t* result_metadata = clone_camera_metadata(
-        request->settings);
-    if (!result_metadata) {
-        return -ENODEV;
-    }
+    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);
-    // Upon getting result settings (for now from this function,
-    // eventually by callback, the Camera regains responsibility for
-    // the metadata, so immediately wrap it in a unique_ptr, before
-    // potentially returning due to failure.
-    std::unique_ptr<camera_metadata_t, void(*)(camera_metadata_t *)>
-        returned_metadata(result_metadata, free_camera_metadata);
     if (res) {
         return res;
     }
-    result.result = returned_metadata.get();
+    result.result = result_metadata.release();
 
     // Notify the framework with the shutter time.
     result.frame_number = request->frame_number;
@@ -444,17 +480,6 @@
     return 0;
 }
 
-void Camera::setSettings(const camera_metadata_t *new_settings)
-{
-    if (mSettings != NULL) {
-        free_camera_metadata(mSettings);
-        mSettings = NULL;
-    }
-
-    if (new_settings != NULL)
-        mSettings = clone_camera_metadata(new_settings);
-}
-
 bool Camera::isValidReprocessSettings(const camera_metadata_t* /*settings*/)
 {
     // TODO: reject settings that cannot be reprocessed
@@ -536,7 +561,6 @@
     dprintf(fd, "Camera ID: %d (Busy: %d)\n", mId, mBusy);
 
     // TODO: dump all settings
-    dprintf(fd, "Most Recent Settings: (%p)\n", mSettings);
 
     dprintf(fd, "Number of streams: %d\n", mNumStreams);
     for (int i = 0; i < mNumStreams; i++) {
@@ -563,31 +587,6 @@
     return "Invalid template type!";
 }
 
-int Camera::setTemplate(int type, const camera_metadata_t *settings)
-{
-    android::Mutex::Autolock al(mDeviceLock);
-
-    if (!isValidTemplateType(type)) {
-        ALOGE("%s:%d: Invalid template request type: %d", __func__, mId, type);
-        return -EINVAL;
-    }
-
-    if (mTemplates[type] != NULL) {
-        ALOGE("%s:%d: Setting already constructed template type %s(%d)",
-                __func__, mId, templateToString(type), type);
-        return -EINVAL;
-    }
-
-    // Make a durable copy of the underlying metadata
-    mTemplates[type] = clone_camera_metadata(settings);
-    if (mTemplates[type] == NULL) {
-        ALOGE("%s:%d: Failed to clone metadata %p for template type %s(%d)",
-                __func__, mId, settings, templateToString(type), type);
-        return -EINVAL;
-    }
-    return 0;
-}
-
 extern "C" {
 // Get handle to camera from device priv data
 static Camera *camdev_to_camera(const camera3_device_t *dev)
diff --git a/modules/camera/3_4/camera.h b/modules/camera/3_4/camera.h
index a3a5d92..158c36d 100644
--- a/modules/camera/3_4/camera.h
+++ b/modules/camera/3_4/camera.h
@@ -19,9 +19,12 @@
 #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 {
@@ -58,9 +61,11 @@
         // Disconnect from the device: close dev nodes, etc.
         virtual void disconnect() = 0;
         // Initialize static camera characteristics for individual device
-        virtual int initStaticInfo(camera_metadata_t **out) = 0;
-        // Initialize device info: facing, orientation, resource cost,
-        // and conflicting devices (/conflicting devices length)
+        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,
@@ -70,7 +75,10 @@
         virtual int setupStream(Stream* stream, uint32_t* max_buffers) = 0;
         // Verify settings are valid for a capture
         virtual bool isValidCaptureSettings(
-            const camera_metadata_t *settings) = 0;
+            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
@@ -79,10 +87,8 @@
         // 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(camera_metadata_t **metadata,
+        virtual int getResultSettings(android::CameraMetadata* metadata,
                                       uint64_t *timestamp) = 0;
-        // Accessor used by initDevice() to set the templates' metadata
-        int setTemplate(int type, const camera_metadata_t *static_info);
         // Prettyprint template names
         const char* templateToString(int type);
 
@@ -97,8 +103,6 @@
         bool isValidStreamSet(Stream **array, int count, uint32_t mode);
         // Calculate usage and max_bufs of each stream
         int setupStreams(Stream **array, int count);
-        // Copy new settings for re-use and clean up old settings.
-        void setSettings(const camera_metadata_t *new_settings);
         // Verify settings are valid for reprocessing an input buffer
         bool isValidReprocessSettings(const camera_metadata_t *settings);
         // Process an output buffer
@@ -111,8 +115,11 @@
 
         // Identifier used by framework to distinguish cameras
         const int mId;
-        // camera_metadata structure containing static characteristics
-        camera_metadata_t *mStaticInfo;
+        // 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
@@ -128,10 +135,8 @@
         Stream **mStreams;
         // Number of streams in mStreams
         int mNumStreams;
-        // Static array of standard camera settings templates
-        camera_metadata_t *mTemplates[CAMERA3_TEMPLATE_COUNT];
-        // Most recent request settings seen, memoized to be reused
-        camera_metadata_t *mSettings;
+        // Standard camera settings templates
+        std::unique_ptr<const android::CameraMetadata> mTemplates[CAMERA3_TEMPLATE_COUNT];
 };
 }  // namespace default_camera_hal
 
diff --git a/modules/camera/3_4/metadata/enum_converter.cpp b/modules/camera/3_4/metadata/enum_converter.cpp
index 3165804..f4c9819 100644
--- a/modules/camera/3_4/metadata/enum_converter.cpp
+++ b/modules/camera/3_4/metadata/enum_converter.cpp
@@ -49,7 +49,7 @@
   }
 
   if (count == 0) {
-    HAL_LOGE("Couldn't find V4L2 conversion of metadata value %d.", value);
+    HAL_LOGV("Couldn't find V4L2 conversion of metadata value %d.", value);
     return -EINVAL;
   } else if (count > 1) {
     HAL_LOGV(
@@ -64,7 +64,7 @@
 
   auto element_range = v4l2_to_metadata_.equal_range(value);
   if (element_range.first == element_range.second) {
-    HAL_LOGE("Couldn't find metadata conversion of V4L2 value %d.", value);
+    HAL_LOGV("Couldn't find metadata conversion of V4L2 value %d.", value);
     return -EINVAL;
   }
 
diff --git a/modules/camera/3_4/metadata/metadata.cpp b/modules/camera/3_4/metadata/metadata.cpp
index ddd73c1..efc9959 100644
--- a/modules/camera/3_4/metadata/metadata.cpp
+++ b/modules/camera/3_4/metadata/metadata.cpp
@@ -35,6 +35,10 @@
 
 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;
@@ -123,6 +127,10 @@
 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) {
@@ -172,6 +180,12 @@
 }
 
 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;
diff --git a/modules/camera/3_4/metadata/metadata_test.cpp b/modules/camera/3_4/metadata/metadata_test.cpp
index e97e60b..508884c 100644
--- a/modules/camera/3_4/metadata/metadata_test.cpp
+++ b/modules/camera/3_4/metadata/metadata_test.cpp
@@ -162,6 +162,11 @@
   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));
@@ -231,6 +236,11 @@
   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.
 
@@ -304,4 +314,9 @@
   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/partial_metadata_factory.h b/modules/camera/3_4/metadata/partial_metadata_factory.h
index a554ee7..27ea6e8 100644
--- a/modules/camera/3_4/metadata/partial_metadata_factory.h
+++ b/modules/camera/3_4/metadata/partial_metadata_factory.h
@@ -223,6 +223,9 @@
           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.
@@ -269,8 +272,9 @@
           new SliderControlOptions<T>(metadata_min, metadata_max));
       break;
     default:
-      HAL_LOGE("Control %d is of unsupported type %d",
+      HAL_LOGE("Control %d (%s) is of unsupported type %d",
                control_id,
+               control_query.name,
                control_query.type);
       return nullptr;
   }
diff --git a/modules/camera/3_4/v4l2_camera.cpp b/modules/camera/3_4/v4l2_camera.cpp
index 87a7a31..9ebefeb 100644
--- a/modules/camera/3_4/v4l2_camera.cpp
+++ b/modules/camera/3_4/v4l2_camera.cpp
@@ -25,11 +25,11 @@
 
 #include <camera/CameraMetadata.h>
 #include <hardware/camera3.h>
-#include <nativehelper/ScopedFd.h>
 
 #include "common.h"
+#include "metadata/metadata_common.h"
 #include "stream_format.h"
-#include "v4l2_gralloc.h"
+#include "v4l2_metadata_factory.h"
 
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
 
@@ -57,14 +57,23 @@
     return nullptr;
   }
 
-  return new V4L2Camera(id, std::move(v4l2_wrapper));
+  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)
+V4L2Camera::V4L2Camera(int id, std::shared_ptr<V4L2Wrapper> v4l2_wrapper,
+                       std::unique_ptr<Metadata> metadata)
     : default_camera_hal::Camera(id),
-      mV4L2Device(std::move(v4l2_wrapper)),
-      mTemplatesInitialized(false),
-      mCharacteristicsInitialized(false) {
+      device_(std::move(v4l2_wrapper)),
+      metadata_(std::move(metadata)),
+      max_input_streams_(0),
+      max_output_streams_({{0, 0, 0}}) {
   HAL_LOG_ENTER();
 }
 
@@ -75,19 +84,19 @@
 int V4L2Camera::connect() {
   HAL_LOG_ENTER();
 
-  if (mConnection) {
+  if (connection_) {
     HAL_LOGE("Already connected. Please disconnect and try again.");
     return -EIO;
   }
 
-  mConnection.reset(new V4L2Wrapper::Connection(mV4L2Device));
-  if (mConnection->status()) {
+  connection_.reset(new V4L2Wrapper::Connection(device_));
+  if (connection_->status()) {
     HAL_LOGE("Failed to connect to device.");
-    return mConnection->status();
+    return connection_->status();
   }
 
   // TODO(b/29185945): confirm this is a supported device.
-  // This is checked by the HAL, but the device at mDevicePath may
+  // 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
@@ -101,603 +110,49 @@
 void V4L2Camera::disconnect() {
   HAL_LOG_ENTER();
 
-  mConnection.reset();
+  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(camera_metadata_t** out) {
+int V4L2Camera::initStaticInfo(android::CameraMetadata* out) {
   HAL_LOG_ENTER();
 
-  android::status_t res;
-  // Device characteristics need to be queried prior
-  // to static info setup.
-  if (!mCharacteristicsInitialized) {
-    res = initCharacteristics();
-    if (res) {
-      return res;
-    }
-  }
-
-  android::CameraMetadata info;
-
-  // Static metadata characteristics from /system/media/camera/docs/docs.html.
-
-  /* android.colorCorrection. */
-
-  // No easy way to turn chromatic aberration correction OFF in v4l2,
-  // though this may be supportable via a collection of other user controls.
-  uint8_t avail_aberration_modes[] = {
-    ANDROID_COLOR_CORRECTION_ABERRATION_MODE_FAST,
-    ANDROID_COLOR_CORRECTION_ABERRATION_MODE_HIGH_QUALITY};
-  res = info.update(ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
-                    avail_aberration_modes, ARRAY_SIZE(avail_aberration_modes));
-  if (res != android::OK) {
+  int res = metadata_->FillStaticMetadata(out);
+  if (res) {
+    HAL_LOGE("Failed to get static metadata.");
     return res;
   }
 
-  /* android.control. */
-
-  /*   3As */
-
-  res = info.update(ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,
-                    mAeAntibandingModes.data(), mAeAntibandingModes.size());
-  if (res != android::OK) {
+  // 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;
   }
 
-  res = info.update(ANDROID_CONTROL_AE_AVAILABLE_MODES,
-                    mAeModes.data(), mAeModes.size());
-  if (res != android::OK) {
-    return res;
-  }
-
-  // Flatten mFpsRanges.
-  res = info.update(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
-                    mFpsRanges.data(), mFpsRanges.total_num_elements());
-  if (res != android::OK) {
-    return res;
-  }
-
-  res = info.update(ANDROID_CONTROL_AE_COMPENSATION_RANGE,
-                    mAeCompensationRange.data(), mAeCompensationRange.size());
-  if (res != android::OK) {
-    return res;
-  }
-
-  res = info.update(ANDROID_CONTROL_AE_COMPENSATION_STEP,
-                    &mAeCompensationStep, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  res = info.update(ANDROID_CONTROL_AF_AVAILABLE_MODES,
-                    mAfModes.data(), mAfModes.size());
-  if (res != android::OK) {
-    return res;
-  }
-
-  res = info.update(ANDROID_CONTROL_AWB_AVAILABLE_MODES,
-                    mAwbModes.data(), mAwbModes.size());
-  if (res != android::OK) {
-    return res;
-  }
-
-  // Couldn't find any V4L2 support for regions, though maybe it's out there.
-  int32_t max_regions[] = {/*AE*/ 0,/*AWB*/ 0,/*AF*/ 0};
-  res = info.update(ANDROID_CONTROL_MAX_REGIONS,
-                    max_regions, ARRAY_SIZE(max_regions));
-  if (res != android::OK) {
-    return res;
-  }
-
-  res = info.update(ANDROID_CONTROL_AE_LOCK_AVAILABLE,
-                    &mAeLockAvailable, 1);
-  if (res != android::OK) {
-    return res;
-  }
-  res = info.update(ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
-                    &mAwbLockAvailable, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  /*   Scene modes. */
-
-  res = info.update(ANDROID_CONTROL_AVAILABLE_SCENE_MODES,
-                    mSceneModes.data(), mSceneModes.size());
-  if (res != android::OK) {
-    return res;
-  }
-
-  // A 3-tuple of AE, AWB, AF overrides for each scene mode.
-  // Ignored for DISABLED, FACE_PRIORITY and FACE_PRIORITY_LOW_LIGHT.
-  uint8_t scene_mode_overrides[] = {
-    /*SCENE_MODE_DISABLED*/ /*AE*/0, /*AW*/0, /*AF*/0};
-  res = info.update(ANDROID_CONTROL_SCENE_MODE_OVERRIDES,
-                    scene_mode_overrides, ARRAY_SIZE(scene_mode_overrides));
-  if (res != android::OK) {
-    return res;
-  }
-
-  /*   Top level 3A/Scenes switch. */
-
-  res = info.update(ANDROID_CONTROL_AVAILABLE_MODES,
-                    mControlModes.data(), mControlModes.size());
-  if (res != android::OK) {
-    return res;
-  }
-
-  /*   Other android.control configuration. */
-
-  res = info.update(ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
-                    mVideoStabilizationModes.data(),
-                    mVideoStabilizationModes.size());
-  if (res != android::OK) {
-    return res;
-  }
-
-  res = info.update(ANDROID_CONTROL_AVAILABLE_EFFECTS,
-                    mEffects.data(), mEffects.size());
-  if (res != android::OK) {
-    return res;
-  }
-
-  // AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS only necessary
-  // for devices supporting CONSTRAINED_HIGH_SPEED_VIDEO,
-  // which this HAL doesn't support.
-
-  // POST_RAW_SENSITIVITY_BOOST_RANGE only necessary
-  // for devices supporting RAW format outputs.
-
-  /* android.edge. */
-
-  // 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.
-  uint8_t avail_edge_modes[] = {
-    ANDROID_EDGE_MODE_FAST};
-  res = info.update(ANDROID_EDGE_AVAILABLE_EDGE_MODES,
-                    avail_edge_modes, ARRAY_SIZE(avail_edge_modes));
-  if (res != android::OK) {
-    return res;
-  }
-
-  /* android.flash. */
-
-  res = info.update(ANDROID_FLASH_INFO_AVAILABLE,
-                    &mFlashAvailable, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  // info.chargeDuration, color.Temperature, maxEnergy marked FUTURE.
-
-  /* android.hotPixel. */
-
-  // No known V4L2 hot pixel correction. But it might be happening,
-  // so we report FAST/HIGH_QUALITY.
-  uint8_t avail_hot_pixel_modes[] = {
-    ANDROID_HOT_PIXEL_MODE_FAST,
-    ANDROID_HOT_PIXEL_MODE_HIGH_QUALITY};
-  res = info.update(ANDROID_HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES,
-                    avail_hot_pixel_modes, ARRAY_SIZE(avail_hot_pixel_modes));
-  if (res != android::OK) {
-    return res;
-  }
-
-  /* android.jpeg. */
-
-  // 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.
-  int32_t thumbnail_sizes[] = {
-    0, 0};
-  res = info.update(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,
-                    thumbnail_sizes, ARRAY_SIZE(thumbnail_sizes));
-  if (res != android::OK) {
-    return res;
-  }
-
-  // V4L2 can query this with VIDIOC_TRY_FMT (or VIDIOC_S_FMT if TRY
-  // isn't supported), reading the fmt.pix.sizeimage for the largest
-  // jpeg size. For now use a constant (defined in V4L2Gralloc.h).
-  int32_t max_jpeg_size = V4L2_MAX_JPEG_SIZE;
-  res = info.update(ANDROID_JPEG_MAX_SIZE,
-                    &max_jpeg_size, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  /* android.lens. */
-
-  /*   Misc. lens control. */
-
-  res = info.update(ANDROID_LENS_INFO_AVAILABLE_APERTURES,
-                    &mAperture, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  res = info.update(ANDROID_LENS_INFO_AVAILABLE_FILTER_DENSITIES,
-                    &mFilterDensity, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  res = info.update(ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
-                    mOpticalStabilizationModes.data(),
-                    mOpticalStabilizationModes.size());
-  if (res != android::OK) {
-    return res;
-  }
-
-  // lens.info.shadingMapSize not required for non-full devices.
-
-  // All V4L2 devices are considered to be external facing.
-  uint8_t facing = ANDROID_LENS_FACING_EXTERNAL;
-  res = info.update(ANDROID_LENS_FACING, &facing, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  /*   Zoom/Focus. */
-
-  // No way to actually get the focal length in V4L2, but it's a required key,
-  // so we just fake it.
-  res = info.update(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS,
-                    &mFocalLength, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  // V4L2 focal units do not correspond to a particular physical unit.
-  uint8_t focus_calibration =
-      ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION_UNCALIBRATED;
-  res = info.update(ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION,
-                    &focus_calibration, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  // info.hyperfocalDistance not required for UNCALIBRATED.
-
-  res = info.update(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
-                    &mFocusDistance, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  /*   Depth. */
-
-  // DEPTH capability not supported by this HAL. Not implemented:
-  // poseRotation
-  // poseTranslation
-  // intrinsicCalibration
-  // radialDistortion
-
-  /* anroid.noise. */
-
-  // Unable to control noise reduction in V4L2 devices,
-  // but FAST is allowed to be the same as OFF.
-  uint8_t avail_noise_reduction_modes[] = {ANDROID_NOISE_REDUCTION_MODE_FAST};
-  res = info.update(ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
-                    avail_noise_reduction_modes,
-                    ARRAY_SIZE(avail_noise_reduction_modes));
-  if (res != android::OK) {
-    return res;
-  }
-
-  /* android.request. */
-
-  int32_t max_num_output_streams[] = {mMaxRawOutputStreams,
-                                      mMaxNonStallingOutputStreams,
-                                      mMaxStallingOutputStreams};
-  res = info.update(ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,
-                    max_num_output_streams, ARRAY_SIZE(max_num_output_streams));
-  if (res != android::OK) {
-    return res;
-  }
-
-  res = info.update(ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,
-                    &mMaxInputStreams, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  // No way to know for V4L2, so fake with max allowable latency.
-  // Doesn't mean much without per-frame controls.
-  uint8_t pipeline_max_depth = 4;
-  res = info.update(ANDROID_REQUEST_PIPELINE_MAX_DEPTH,
-                    &pipeline_max_depth, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  // Partial results not supported; partialResultCount defaults to 1.
-
-  // Available capabilities & keys queried at very end of this method.
-
-  /* android.scaler. */
-
-  /*   Cropping. */
-
-  res = info.update(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
-                    &mMaxZoom, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  res = info.update(ANDROID_SCALER_CROPPING_TYPE, &mCropType, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  /*   Streams. */
-
-  // availableInputOutputFormatsMap only required for reprocessing capability.
-
-  // Flatten mStreamConfigs.
-  res = info.update(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
-                    mStreamConfigs.data(),
-                    mStreamConfigs.total_num_elements());
-  if (res != android::OK) {
-    return res;
-  }
-
-  // Flatten mMinFrameDurations.
-  res = info.update(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
-                    mMinFrameDurations.data(),
-                    mMinFrameDurations.total_num_elements());
-  if (res != android::OK) {
-    return res;
-  }
-
-  // Flatten mStallDurations.
-  res = info.update(ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,
-                    mStallDurations.data(),
-                    mStallDurations.total_num_elements());
-  if (res != android::OK) {
-    return res;
-  }
-
-  /* android.sensor. */
-
-  /*   Sizes. */
-
-  res = info.update(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
-                    mPixelArraySize.data(), mPixelArraySize.size());
-  if (res != android::OK) {
-    return res;
-  }
-  // No V4L2 way to differentiate active vs. inactive parts of the rectangle.
-  res = info.update(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
-                    mPixelArraySize.data(), mPixelArraySize.size());
-  if (res != android::OK) {
-    return res;
-  }
-
-  res = info.update(ANDROID_SENSOR_INFO_PHYSICAL_SIZE,
-                    mPhysicalSize.data(), mPhysicalSize.size());
-  if (res != android::OK) {
-    return res;
-  }
-
-  /*   Misc sensor information. */
-
-  res = info.update(ANDROID_SENSOR_INFO_MAX_FRAME_DURATION,
-                    &mMaxFrameDuration, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  // HAL uses BOOTTIME timestamps.
-  // TODO(b/29457051): make sure timestamps are consistent throughout the HAL.
-  uint8_t timestamp_source = ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN;
-  res = info.update(ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE,
-                    &timestamp_source, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  // As in initDeviceInfo, no way to actually get orientation.
-  res = info.update(ANDROID_SENSOR_ORIENTATION, &mOrientation, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  // availableTestPatternModes just defaults to OFF, which is fine.
-
-  // info.exposureTimeRange, info.sensitivityRange:
-  // exposure/sensitivity manual control not supported.
-  // Could query V4L2_CID_ISO_SENSITIVITY to support sensitivity if desired.
-
-  // info.whiteLevel, info.lensShadingApplied,
-  // info.preCorrectionPixelArraySize, referenceIlluminant1/2,
-  // calibrationTransform1/2, colorTransform1/2, forwardMatrix1/2,
-  // blackLevelPattern, profileHueSatMapDimensions
-  // all only necessary for RAW.
-
-  // baseGainFactor marked FUTURE.
-
-  // maxAnalogSensitivity optional for LIMITED device.
-
-  // opticalBlackRegions: No known way to get in V4L2, but not necessary.
-
-  // opaqueRawSize not necessary since RAW_OPAQUE format not supported.
-
-  /* android.shading */
-
-  // No known V4L2 lens shading. But it might be happening,
-  // so we report FAST/HIGH_QUALITY.
-  uint8_t avail_shading_modes[] = {
-    ANDROID_SHADING_MODE_FAST,
-    ANDROID_SHADING_MODE_HIGH_QUALITY};
-  res = info.update(ANDROID_SHADING_AVAILABLE_MODES,
-                    avail_shading_modes, ARRAY_SIZE(avail_shading_modes));
-  if (res != android::OK) {
-    return res;
-  }
-
-  /* android.statistics */
-
-  // Face detection not supported.
-  uint8_t avail_face_detect_modes[] = {
-    ANDROID_STATISTICS_FACE_DETECT_MODE_OFF};
-  res = info.update(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
-                    avail_face_detect_modes,
-                    ARRAY_SIZE(avail_face_detect_modes));
-  if (res != android::OK) {
-    return res;
-  }
-
-  int32_t max_face_count = 0;
-  res = info.update(ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
-                    &max_face_count, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  // info.histogramBucketCount, info.maxHistogramCount,
-  // info.maxSharpnessMapValue, info.sharpnessMapSizemarked FUTURE.
-
-  // ON only needs to be supported for RAW capable devices.
-  uint8_t avail_hot_pixel_map_modes[] = {
-    ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE_OFF};
-  res = info.update(ANDROID_STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES,
-                    avail_hot_pixel_map_modes,
-                    ARRAY_SIZE(avail_hot_pixel_map_modes));
-  if (res != android::OK) {
-    return res;
-  }
-
-  // ON only needs to be supported for RAW capable devices.
-  uint8_t avail_lens_shading_map_modes[] = {
-    ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF};
-  res = info.update(ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES,
-                    avail_lens_shading_map_modes,
-                    ARRAY_SIZE(avail_lens_shading_map_modes));
-  if (res != android::OK) {
-    return res;
-  }
-
-  /* android.tonemap. */
-
-  // tonemapping only required for MANUAL_POST_PROCESSING capability.
-
-  /* android.led. */
-
-  // May or may not have LEDs available.
-  if (!mLeds.empty()) {
-    res = info.update(ANDROID_LED_AVAILABLE_LEDS, mLeds.data(), mLeds.size());
-    if (res != android::OK) {
-      return res;
-    }
-  }
-
-  /* android.sync. */
-
-  // "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.
-  int32_t max_latency = ANDROID_SYNC_MAX_LATENCY_UNKNOWN;
-  res = info.update(ANDROID_SYNC_MAX_LATENCY,
-                    &max_latency, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  /* android.reprocess. */
-
-  // REPROCESSING not supported by this HAL.
-
-  /* android.depth. */
-
-  // DEPTH not supported by this HAL.
-
-  /* Capabilities and android.info. */
-
-  uint8_t hw_level = ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
-  res = info.update(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
-                    &hw_level, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  uint8_t capabilities[] = {
-    ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE};
-  res = info.update(ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
-                    capabilities, ARRAY_SIZE(capabilities));
-  if (res != android::OK) {
-    return res;
-  }
-
-  // Scan a default request template for included request keys.
-  if (!mTemplatesInitialized) {
-    res = initTemplates();
-    if (res) {
-      return res;
-    }
-  }
-  const camera_metadata_t* preview_request = nullptr;
-  // Search templates from the beginning for a supported one.
-  for (uint8_t template_id = 1; template_id < CAMERA3_TEMPLATE_COUNT;
-       ++template_id) {
-    preview_request = constructDefaultRequestSettings(template_id);
-    if (preview_request != nullptr) {
-      break;
-    }
-  }
-  if (preview_request == nullptr) {
-    HAL_LOGE("No valid templates, can't get request keys.");
-    return -ENODEV;
-  }
-  std::vector<int32_t> avail_request_keys = getMetadataKeys(preview_request);
-  res = info.update(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS,
-                    avail_request_keys.data(), avail_request_keys.size());
-  if (res != android::OK) {
-    return res;
-  }
-
-  // Result keys will be duplicated from the request, plus a few extras.
-  // TODO(b/30035628): additonal available result keys.
-  std::vector<int32_t> avail_result_keys(avail_request_keys);
-  res = info.update(ANDROID_REQUEST_AVAILABLE_RESULT_KEYS,
-                    avail_result_keys.data(), avail_result_keys.size());
-  if (res != android::OK) {
-    return res;
-  }
-
-  // Last thing, once all the available characteristics have been added.
-  const camera_metadata_t* static_characteristics = info.getAndLock();
-  std::vector<int32_t> avail_characteristics_keys =
-      getMetadataKeys(static_characteristics);
-  res = info.unlock(static_characteristics);
-  if (res != android::OK) {
-    return res;
-  }
-  avail_characteristics_keys.push_back(
-      ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS);
-  res = info.update(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS,
-                    avail_characteristics_keys.data(),
-                    avail_characteristics_keys.size());
-  if (res != android::OK) {
-    return res;
-  }
-
-  *out = info.release();
   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->facing = CAMERA_FACING_EXTERNAL;
-  info->orientation = mOrientation;
   info->resource_cost = 100;
   info->conflicting_devices = nullptr;
   info->conflicting_devices_length = 0;
@@ -705,22 +160,14 @@
 
 int V4L2Camera::initDevice() {
   HAL_LOG_ENTER();
-
-  // Templates should be set up if they haven't already been.
-  if (!mTemplatesInitialized) {
-    int res = initTemplates();
-    if (res) {
-      return res;
-    }
-  }
-
+  // Nothing to do.
   return 0;
 }
 
 int V4L2Camera::enqueueBuffer(const camera3_stream_buffer_t* camera_buffer) {
   HAL_LOG_ENTER();
 
-  int res = mV4L2Device->EnqueueBuffer(camera_buffer);
+  int res = device_->EnqueueBuffer(camera_buffer);
   if (res) {
     HAL_LOGE("Device failed to enqueue buffer.");
     return res;
@@ -730,7 +177,7 @@
   // 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 = mV4L2Device->StreamOn();
+  res = device_->StreamOn();
   if (res) {
     HAL_LOGE("Device failed to turn on stream.");
     return res;
@@ -741,7 +188,7 @@
 
   // Dequeue the buffer.
   v4l2_buffer result_buffer;
-  res = mV4L2Device->DequeueBuffer(&result_buffer);
+  res = device_->DequeueBuffer(&result_buffer);
   if (res) {
     HAL_LOGE("Device failed to dequeue buffer.");
     return res;
@@ -751,7 +198,7 @@
   // 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 = mV4L2Device->StreamOff();
+  res = device_->StreamOff();
   if (res) {
     HAL_LOGE("Device failed to turn off stream.");
     return res;
@@ -760,489 +207,26 @@
   return 0;
 }
 
-int V4L2Camera::getResultSettings(camera_metadata** metadata,
+int V4L2Camera::getResultSettings(android::CameraMetadata* metadata,
                                   uint64_t* timestamp) {
   HAL_LOG_ENTER();
 
-  int res = 0;
-  android::CameraMetadata frame_metadata(*metadata);
-
-  // TODO(b/30035628): fill in.
-  // For now just spoof the timestamp to a non-0 value and send it back.
-  int64_t frame_time = 1;
-  res = frame_metadata.update(ANDROID_SENSOR_TIMESTAMP, &frame_time, 1);
-  if (res != android::OK) {
+  // Get the results.
+  int res = metadata_->FillResultMetadata(metadata);
+  if (res) {
+    HAL_LOGE("Failed to fill result metadata.");
     return res;
   }
 
-  *metadata = frame_metadata.release();
-  *timestamp = frame_time;
-
-  return 0;
-}
-
-int V4L2Camera::initTemplates() {
-  HAL_LOG_ENTER();
-  int res = 0;
-
-  // Device characteristics need to be queried prior
-  // to template setup.
-  if (!mCharacteristicsInitialized) {
-    res = initCharacteristics();
-    if (res) {
-      return res;
-    }
-  }
-
-  // Note: static metadata expects all templates/requests
-  // to provide values for all supported keys.
-
-  android::CameraMetadata base_metadata;
-
-  // Start with defaults for all templates.
-
-  /* android.colorCorrection. */
-
-  uint8_t aberration_mode = ANDROID_COLOR_CORRECTION_ABERRATION_MODE_FAST;
-  res = base_metadata.update(ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
-                             &aberration_mode, 1);
-  if (res != android::OK) {
+  // 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;
   }
-
-  uint8_t color_correction_mode = ANDROID_COLOR_CORRECTION_MODE_FAST;
-  res = base_metadata.update(ANDROID_COLOR_CORRECTION_MODE,
-                             &color_correction_mode, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  // transform and gains are for the unsupported MANUAL_POST_PROCESSING only.
-
-  /* android.control. */
-
-  /*   AE. */
-  uint8_t ae_antibanding_mode = ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO;
-  res = base_metadata.update(ANDROID_CONTROL_AE_ANTIBANDING_MODE,
-                             &ae_antibanding_mode, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  // Only matters if AE_MODE = OFF
-  int32_t ae_exposure_compensation = 0;
-  res = base_metadata.update(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
-                             &ae_exposure_compensation, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  uint8_t ae_lock = ANDROID_CONTROL_AE_LOCK_OFF;
-  res = base_metadata.update(ANDROID_CONTROL_AE_LOCK, &ae_lock, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  uint8_t ae_mode = ANDROID_CONTROL_AE_MODE_ON;
-  res = base_metadata.update(ANDROID_CONTROL_AE_MODE, &ae_mode, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  // AE regions not supported.
-
-  // FPS set per-template.
-
-  uint8_t ae_precapture_trigger = ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_IDLE;
-  res = base_metadata.update(ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
-                             &ae_precapture_trigger, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  /*   AF. */
-
-  // AF mode set per-template.
-
-  // AF regions not supported.
-
-  uint8_t af_trigger = ANDROID_CONTROL_AF_TRIGGER_IDLE;
-  res = base_metadata.update(ANDROID_CONTROL_AF_TRIGGER, &af_trigger, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  /*   AWB. */
-
-  // Priority: auto > off > Whatever is available.
-  uint8_t default_awb_mode = mAwbModes[0];
-  if (std::count(mAwbModes.begin(), mAwbModes.end(),
-                ANDROID_CONTROL_AWB_MODE_AUTO)) {
-    default_awb_mode = ANDROID_CONTROL_AWB_MODE_AUTO;
-  } else if (std::count(mAwbModes.begin(), mAwbModes.end(),
-                       ANDROID_CONTROL_AWB_MODE_OFF)) {
-    default_awb_mode = ANDROID_CONTROL_AWB_MODE_OFF;
-  }
-  res = base_metadata.update(ANDROID_CONTROL_AWB_MODE, &default_awb_mode, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  // AWB regions not supported.
-
-  /*   Other controls. */
-
-  uint8_t effect_mode = ANDROID_CONTROL_EFFECT_MODE_OFF;
-  res = base_metadata.update(ANDROID_CONTROL_EFFECT_MODE, &effect_mode, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  uint8_t control_mode = ANDROID_CONTROL_MODE_AUTO;
-  res = base_metadata.update(ANDROID_CONTROL_MODE, &control_mode, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  uint8_t scene_mode = ANDROID_CONTROL_SCENE_MODE_DISABLED;
-  res = base_metadata.update(ANDROID_CONTROL_SCENE_MODE,
-                             &scene_mode, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  uint8_t video_stabilization = ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF;
-  res = base_metadata.update(ANDROID_CONTROL_VIDEO_STABILIZATION_MODE,
-                             &video_stabilization, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  // postRawSensitivityBoost: RAW not supported, leave null.
-
-  /* android.demosaic. */
-
-  // mode marked FUTURE.
-
-  /* android.edge. */
-
-  uint8_t edge_mode = ANDROID_EDGE_MODE_FAST;
-  res = base_metadata.update(ANDROID_EDGE_MODE, &edge_mode, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  // strength marked FUTURE.
-
-  /* android.flash. */
-
-  // firingPower, firingTime marked FUTURE.
-
-  uint8_t flash_mode = ANDROID_FLASH_MODE_OFF;
-  res = base_metadata.update(ANDROID_FLASH_MODE, &flash_mode, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  /* android.hotPixel. */
-
-  uint8_t hp_mode = ANDROID_HOT_PIXEL_MODE_FAST;
-  res = base_metadata.update(ANDROID_HOT_PIXEL_MODE, &hp_mode, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  /* android.jpeg. */
-
-  double gps_coords[] = {/*latitude*/0, /*longitude*/0, /*altitude*/0};
-  res = base_metadata.update(ANDROID_JPEG_GPS_COORDINATES, gps_coords, 3);
-  if (res != android::OK) {
-    return res;
-  }
-
-  uint8_t gps_processing_method[] = "none";
-  res = base_metadata.update(ANDROID_JPEG_GPS_PROCESSING_METHOD,
-                             gps_processing_method,
-                             ARRAY_SIZE(gps_processing_method));
-  if (res != android::OK) {
-    return res;
-  }
-
-  int64_t gps_timestamp = 0;
-  res = base_metadata.update(ANDROID_JPEG_GPS_TIMESTAMP, &gps_timestamp, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  // JPEG orientation is relative to sensor orientation (mOrientation).
-  int32_t jpeg_orientation = 0;
-  res = base_metadata.update(ANDROID_JPEG_ORIENTATION, &jpeg_orientation, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  // 1-100, larger is higher quality.
-  uint8_t jpeg_quality = 80;
-  res = base_metadata.update(ANDROID_JPEG_QUALITY, &jpeg_quality, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  // TODO(b/29580107): If thumbnail quality actually matters/can be adjusted,
-  // adjust this.
-  uint8_t thumbnail_quality = 80;
-  res = base_metadata.update(ANDROID_JPEG_THUMBNAIL_QUALITY,
-                             &thumbnail_quality, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  // TODO(b/29580107): Choose a size matching the resolution.
-  int32_t thumbnail_size[] = {0, 0};
-  res = base_metadata.update(ANDROID_JPEG_THUMBNAIL_SIZE, thumbnail_size, 2);
-  if (res != android::OK) {
-    return res;
-  }
-
-  /* android.lens. */
-
-  // Fixed values.
-  res = base_metadata.update(ANDROID_LENS_APERTURE, &mAperture, 1);
-  if (res != android::OK) {
-    return res;
-  }
-  res = base_metadata.update(ANDROID_LENS_FILTER_DENSITY, &mFilterDensity, 1);
-  if (res != android::OK) {
-    return res;
-  }
-  res = base_metadata.update(ANDROID_LENS_FOCAL_LENGTH, &mFocalLength, 1);
-  if (res != android::OK) {
-    return res;
-  }
-  res = base_metadata.update(ANDROID_LENS_FOCUS_DISTANCE, &mFocusDistance, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  uint8_t optical_stabilization = ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF;
-  res = base_metadata.update(ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
-                    &optical_stabilization, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  /* android.noiseReduction. */
-
-  uint8_t noise_reduction_mode = ANDROID_NOISE_REDUCTION_MODE_FAST;
-  res = base_metadata.update(ANDROID_NOISE_REDUCTION_MODE,
-                             &noise_reduction_mode, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  // strength marked FUTURE.
-
-  /* android.request. */
-
-  // Request Id unused by the HAL for now, and these are just
-  // templates, so just fill it in with a dummy.
-  int32_t id = 0;
-  res = base_metadata.update(ANDROID_REQUEST_ID, &id, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  // metadataMode marked FUTURE.
-
-  /* android.scaler. */
-
-  // No cropping by default; use the full active array.
-  res = base_metadata.update(ANDROID_SCALER_CROP_REGION, mPixelArraySize.data(),
-                             mPixelArraySize.size());
-  if (res != android::OK) {
-    return res;
-  }
-
-  /* android.sensor. */
-
-  // exposureTime, sensitivity, testPattern[Data,Mode] not supported.
-
-  // Ignored when AE is OFF.
-  int64_t frame_duration = 33333333L; // 1/30 s.
-  res = base_metadata.update(ANDROID_SENSOR_FRAME_DURATION, &frame_duration, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  /* android.shading. */
-
-  uint8_t shading_mode = ANDROID_SHADING_MODE_FAST;
-  res = base_metadata.update(ANDROID_SHADING_MODE, &shading_mode, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  /* android.statistics. */
-
-  uint8_t face_detect_mode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
-  res = base_metadata.update(ANDROID_STATISTICS_FACE_DETECT_MODE,
-                             &face_detect_mode, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  // histogramMode, sharpnessMapMode marked FUTURE.
-
-  uint8_t hp_map_mode = ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE_OFF;
-  res = base_metadata.update(ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE,
-                             &hp_map_mode, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  uint8_t lens_shading_map_mode = ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF;
-  res = base_metadata.update(ANDROID_STATISTICS_LENS_SHADING_MAP_MODE,
-                             &lens_shading_map_mode, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  /* android.tonemap. */
-
-  // Tonemap only required for MANUAL_POST_PROCESSING capability.
-
-  /* android.led. */
-
-  uint8_t transmit = ANDROID_LED_TRANSMIT_ON;
-  res = base_metadata.update(ANDROID_LED_TRANSMIT, &transmit, 1);
-  if (res != android::OK) {
-    return res;
-  }
-
-  /* android.reprocess */
-
-  // Only needed for REPROCESS capability.
-
-
-  /* Template variable values. */
-
-  // Find the FPS ranges "closest" to a desired range
-  // (minimum abs distance from min to min and max to max).
-  // Find both a fixed rate and a variable rate, for different purposes.
-  std::array<int32_t, 2> desired_flat_fps_range = {{30, 30}};
-  std::array<int32_t, 2> desired_variable_fps_range = {{5, 30}};
-  std::array<int32_t, 2> flat_fps_range;
-  std::array<int32_t, 2> variable_fps_range;
-  int32_t best_flat_distance = std::numeric_limits<int32_t>::max();
-  int32_t best_variable_distance = std::numeric_limits<int32_t>::max();
-  size_t num_fps_ranges = mFpsRanges.num_arrays();
-  for (size_t i = 0; i < num_fps_ranges; ++i) {
-    const int32_t* range = mFpsRanges[i];
-    // Variable fps.
-    int32_t distance = std::abs(range[0] - desired_variable_fps_range[0]) +
-        std::abs(range[1] - desired_variable_fps_range[1]);
-    if (distance < best_variable_distance) {
-      variable_fps_range[0] = range[0];
-      variable_fps_range[1] = range[1];
-      best_variable_distance = distance;
-    }
-    // Flat fps. Only do if range is actually flat.
-    // Note at least one flat range is required,
-    // so something will always be filled in.
-    if (range[0] == range[1]) {
-      distance = std::abs(range[0] - desired_flat_fps_range[0]) +
-          std::abs(range[1] - desired_flat_fps_range[1]);
-      if (distance < best_flat_distance) {
-        flat_fps_range[0] = range[0];
-        flat_fps_range[1] = range[1];
-        best_flat_distance = distance;
-      }
-    }
-  }
-
-  // Priority: continuous > auto > off > whatever is available.
-  bool continuous_still_avail = std::count(
-      mAfModes.begin(), mAfModes.end(),
-      ANDROID_CONTROL_AF_MODE_CONTINUOUS_PICTURE);
-  bool continuous_video_avail = std::count(
-      mAfModes.begin(), mAfModes.end(),
-      ANDROID_CONTROL_AF_MODE_CONTINUOUS_VIDEO);
-  uint8_t non_continuous_af_mode = mAfModes[0];
-  if (std::count(mAfModes.begin(), mAfModes.end(),
-                       ANDROID_CONTROL_AF_MODE_AUTO)) {
-    non_continuous_af_mode = ANDROID_CONTROL_AF_MODE_AUTO;
-  } else if (std::count(mAfModes.begin(), mAfModes.end(),
-                       ANDROID_CONTROL_AF_MODE_OFF)) {
-    non_continuous_af_mode = ANDROID_CONTROL_AF_MODE_OFF;
-  }
-  uint8_t still_af_mode = continuous_still_avail ?
-      ANDROID_CONTROL_AF_MODE_CONTINUOUS_PICTURE : non_continuous_af_mode;
-  uint8_t video_af_mode =  continuous_video_avail ?
-      ANDROID_CONTROL_AF_MODE_CONTINUOUS_VIDEO : non_continuous_af_mode;
-
-  for (uint8_t template_id = 1; template_id < CAMERA3_TEMPLATE_COUNT;
-       ++template_id) {
-    // General differences/support.
-    uint8_t intent;
-    uint8_t af_mode;
-    std::array<int32_t, 2> fps_range;
-    switch(template_id) {
-      case CAMERA3_TEMPLATE_PREVIEW:
-        intent = ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW;
-        af_mode = still_af_mode;
-        fps_range = flat_fps_range;
-        break;
-      case CAMERA3_TEMPLATE_STILL_CAPTURE:
-        intent = ANDROID_CONTROL_CAPTURE_INTENT_STILL_CAPTURE;
-        af_mode = still_af_mode;
-        fps_range = variable_fps_range;
-        break;
-      case CAMERA3_TEMPLATE_VIDEO_RECORD:
-        intent = ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_RECORD;
-        af_mode = video_af_mode;
-        fps_range = flat_fps_range;
-        break;
-      case CAMERA3_TEMPLATE_VIDEO_SNAPSHOT:
-        intent = ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT;
-        af_mode = video_af_mode;
-        fps_range = flat_fps_range;
-        break;
-      case CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG:  // Fall through.
-      case CAMERA3_TEMPLATE_MANUAL:  // Fall though.
-      default:
-        // Unsupported/unrecognized. Don't add this template; skip it.
-        continue;
-    }
-
-    // Copy our base metadata and add the new items.
-    android::CameraMetadata template_metadata(base_metadata);
-    res = template_metadata.update(ANDROID_CONTROL_CAPTURE_INTENT, &intent, 1);
-    if (res != android::OK) {
-      return res;
-    }
-    res = template_metadata.update(ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
-                                   fps_range.data(), fps_range.size());
-    if (res != android::OK) {
-      return res;
-    }
-    res = template_metadata.update(ANDROID_CONTROL_AF_MODE, &af_mode, 1);
-    if (res != android::OK) {
-      return res;
-    }
-
-    const camera_metadata_t* template_raw_metadata =
-        template_metadata.getAndLock();
-    res = setTemplate(template_id, template_raw_metadata);
-    if (res != android::OK) {
-      return res;
-    }
-    res = template_metadata.unlock(template_raw_metadata);
-    if (res != android::OK) {
-      return res;
-    }
-  }
+  *timestamp = static_cast<uint64_t>(frame_time);
 
-  mTemplatesInitialized = true;
   return 0;
 }
 
@@ -1292,15 +276,15 @@
     }
   }
 
-  if (num_input > mMaxInputStreams ||
-      num_raw > mMaxRawOutputStreams ||
-      num_stalling > mMaxStallingOutputStreams ||
-      num_non_stalling > mMaxNonStallingOutputStreams) {
-    HAL_LOGE("Invalid stream configuration: %d input, %d RAW, %d stalling, "
-             "%d non-stalling (max supported: %d input, %d RAW, %d stalling, "
-             "%d non-stalling)", mMaxInputStreams, mMaxRawOutputStreams,
-             mMaxStallingOutputStreams, mMaxNonStallingOutputStreams, num_input,
-             num_raw, num_stalling, num_non_stalling);
+  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;
   }
 
@@ -1343,7 +327,7 @@
   // claims it's underdocumented; the implementation lets the HAL overwrite it.
   stream->setDataSpace(HAL_DATASPACE_V0_JFIF);
 
-  int res = mV4L2Device->SetFormat(*stream, max_buffers);
+  int res = device_->SetFormat(*stream, max_buffers);
   if (res) {
     HAL_LOGE("Failed to set device to correct format for stream.");
     return res;
@@ -1358,230 +342,16 @@
   return 0;
 }
 
-bool V4L2Camera::isValidCaptureSettings(const camera_metadata_t* settings) {
+bool V4L2Camera::isValidCaptureSettings(const android::CameraMetadata& settings) {
   HAL_LOG_ENTER();
 
-  // TODO(b/29335262): reject capture settings this camera isn't capable of.
-  return true;
+  return metadata_->IsValidRequest(settings);
 }
 
-int V4L2Camera::initCharacteristics() {
+int V4L2Camera::setSettings(const android::CameraMetadata& new_settings) {
   HAL_LOG_ENTER();
 
-  /* Physical characteristics. */
-  // No way to get these in V4L2, so faked.
-  // Note: While many of these are primarily informative for post-processing
-  // calculations by the app and will potentially cause bad results there,
-  // focal length and physical size are actually used in framework
-  // calculations (field of view, pixel pitch, etc), so faking them may
-  // have unexpected results.
-  mAperture = 2.0;  // RPi camera v2 is f/2.0.
-  mFilterDensity = 0.0;
-  mFocalLength = 3.04;  // RPi camera v2 is 3.04mm.
-  mOrientation = 0;
-  mPhysicalSize = {{3.674, 2.760}};  // RPi camera v2 is 3.674 x 2.760 mm.
-
-  /* Fixed features. */
-
-  // TODO(b/29394024): query VIDIOC_CROPCAP to get pixel rectangle.
-  // Spoofing as 640 x 480 for now.
-  mPixelArraySize = {{/*xmin*/0, /*ymin*/0, /*width*/640, /*height*/480}};
-
-  // 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.
-  mCropType = ANDROID_SCALER_CROPPING_TYPE_FREEFORM;
-
-  // TODO(b/29394024): query VIDIOC_CROPCAP to get cropping ranges,
-  // and VIDIOC_G_CROP to determine if cropping is supported.
-  // If the ioctl isn't available (or cropping has non-square pixelaspect),
-  // assume no cropping/scaling.
-  // May need to try setting some crops to determine what the driver actually
-  // supports (including testing center vs freeform).
-  mMaxZoom = 1;
-
-  // TODO(b/29394024): query V4L2_CID_EXPOSURE_BIAS.
-  mAeCompensationRange = {{0, 0}};
-  mAeCompensationStep = {1, 1};
-
-  // TODO(b/29394024): query V4L2_CID_3A_LOCK.
-  mAeLockAvailable = ANDROID_CONTROL_AE_LOCK_AVAILABLE_FALSE;
-  mAwbLockAvailable = ANDROID_CONTROL_AWB_LOCK_AVAILABLE_FALSE;
-
-  // TODO(b/29394024): query V4L2_CID_FLASH_LED_MODE.
-  mFlashAvailable = 0;
-
-  // TODO(b/29394024): query V4L2_CID_FOCUS_ABSOLUTE for focus range.
-  mFocusDistance = 0;  // Fixed focus.
-
-  // TODO(b/29939583): V4L2 can only support 1 stream at a time.
-  // For now, just reporting minimum allowable for LIMITED devices.
-  mMaxRawOutputStreams = 0;
-  mMaxStallingOutputStreams = 1;
-  mMaxNonStallingOutputStreams = 2;
-  // Reprocessing not supported.
-  mMaxInputStreams = 0;
-
-  /* Features with (potentially) multiple options. */
-
-  // TODO(b/29394024): query V4L2_CID_EXPOSURE_AUTO for ae modes.
-  mAeModes.push_back(ANDROID_CONTROL_AE_MODE_ON);
-
-  // TODO(b/29394024): query V4L2_CID_POWER_LINE_FREQUENCY.
-  // Auto as the default, since it could mean anything, while OFF would
-  // require guaranteeing no antibanding happens.
-  mAeAntibandingModes.push_back(ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO);
-
-  // TODO(b/29394024): query V4L2_CID_FOCUS_AUTO for
-  // CONTINUOUS_VIDEO/CONTINUOUS_PICTURE. V4L2_CID_AUTO_FOCUS_START
-  // supports what Android thinks of as auto focus (single auto focus).
-  // V4L2_CID_AUTO_FOCUS_RANGE allows MACRO.
-  mAfModes.push_back(ANDROID_CONTROL_AF_MODE_OFF);
-
-  // TODO(b/29394024): query V4L2_CID_AUTO_WHITE_BALANCE, or
-  // V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE if available.
-  mAwbModes.push_back(ANDROID_CONTROL_AWB_MODE_AUTO);
-
-  // TODO(b/29394024): query V4L2_CID_SCENE_MODE.
-  mSceneModes.push_back(ANDROID_CONTROL_SCENE_MODE_DISABLED);
-
-  mControlModes.push_back(ANDROID_CONTROL_MODE_AUTO);
-  if (mSceneModes.size() > 1) {
-    // We have some mode other than just DISABLED available.
-    mControlModes.push_back(ANDROID_CONTROL_MODE_USE_SCENE_MODE);
-  }
-
-  // TODO(b/29394024): query V4L2_CID_COLORFX.
-  mEffects.push_back(ANDROID_CONTROL_EFFECT_MODE_OFF);
-
-  // TODO(b/29394024): query V4L2_CID_FLASH_INDICATOR_INTENSITY.
-  // For now, no indicator LED available; nothing to push back.
-  // When there is, push back ANDROID_LED_AVAILABLE_LEDS_TRANSMIT.
-
-  // TODO(b/29394024): query V4L2_CID_IMAGE_STABILIZATION.
-  mOpticalStabilizationModes.push_back(
-      ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF);
-  mVideoStabilizationModes.push_back(
-      ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF);
-
-  // Need to be connected to query the device.
-  V4L2Wrapper::Connection temp_connection(mV4L2Device);
-  if (temp_connection.status()) {
-    HAL_LOGE("Failed to connect to device.");
-    return temp_connection.status();
-  }
-
-  // Get all supported formats.
-  std::set<uint32_t> v4l2_formats;
-  int res = mV4L2Device->GetFormats(&v4l2_formats);
-  if (res) {
-    HAL_LOGE("Failed to get device formats.");
-    return res;
-  }
-  std::set<int32_t> hal_formats;
-  for (auto v4l2_format : v4l2_formats) {
-    int32_t hal_format = StreamFormat::V4L2ToHalPixelFormat(v4l2_format);
-    if (hal_format < 0) {
-      // Unrecognized/unused format. Skip it.
-      continue;
-    }
-    hal_formats.insert(hal_format);
-  }
-  // In addition to well-defined formats, must support "Implementation Defined"
-  // (in this case what that means is managed by the StreamFormat class).
-  hal_formats.insert(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED);
-
-  // Requirements check: need to support YCbCr_420_888 and JPEG.
-  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;
-  }
-
-  // Find sizes and frame durations for all formats.
-  // We also want to find the smallest max frame duration amongst all formats.
-  mMaxFrameDuration = std::numeric_limits<int64_t>::max();
-  int64_t min_yuv_frame_duration = std::numeric_limits<int64_t>::max();
-  for (auto hal_format : hal_formats) {
-    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;
-    }
-
-    std::set<std::array<int32_t, 2>> frame_sizes;
-    res = mV4L2Device->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) {
-      mStreamConfigs.push_back({{hal_format, frame_size[0], frame_size[1],
-                ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT}});
-
-      std::array<int64_t, 2> duration_range;
-      res = mV4L2Device->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 min_frame_duration = duration_range[0];
-      int64_t max_frame_duration = duration_range[1];
-
-      mMinFrameDurations.push_back(
-          {{hal_format, frame_size[0], frame_size[1], min_frame_duration}});
-
-      // 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 (max_frame_duration < mMaxFrameDuration) {
-        mMaxFrameDuration = max_frame_duration;
-      }
-
-      // We only care about min frame duration (max frame rate) for YUV.
-      if (hal_format == HAL_PIXEL_FORMAT_YCbCr_420_888 &&
-          min_frame_duration < min_yuv_frame_duration) {
-        min_yuv_frame_duration = min_frame_duration;
-      }
-
-      // 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;
-      }
-      mStallDurations.push_back(
-          {{hal_format, frame_size[0], frame_size[1], stall_duration}});
-    }
-  }
-
-  // This 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.
-  // Convert from frame durations measured in ns.
-  int32_t min_yuv_fps = 1000000000 / mMaxFrameDuration;
-  if (min_yuv_fps > 15) {
-    return -ENODEV;
-  }
-  int32_t max_yuv_fps = 1000000000 / min_yuv_frame_duration;
-  mFpsRanges.push_back({{min_yuv_fps, max_yuv_fps}});
-  mFpsRanges.push_back({{max_yuv_fps, max_yuv_fps}});
-  // Always advertise {30, 30} if max is even higher,
-  // since this is what the default video requests use.
-  if (max_yuv_fps > 30) {
-    mFpsRanges.push_back({{30, 30}});
-  }
-
-  mCharacteristicsInitialized = true;
-  return 0;
+  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
index a4621f8..d9af15d 100644
--- a/modules/camera/3_4/v4l2_camera.h
+++ b/modules/camera/3_4/v4l2_camera.h
@@ -21,14 +21,12 @@
 
 #include <array>
 #include <string>
-#include <vector>
 
-#include <system/camera_metadata.h>
+#include <camera/CameraMetadata.h>
 
 #include "camera.h"
 #include "common.h"
-#include "metadata/array_vector.h"
-#include "v4l2_gralloc.h"
+#include "metadata/metadata.h"
 #include "v4l2_wrapper.h"
 
 namespace v4l2_camera_hal {
@@ -46,7 +44,8 @@
 private:
   // Constructor private to allow failing on bad input.
   // Use NewV4L2Camera instead.
-  V4L2Camera(int id, std::shared_ptr<V4L2Wrapper> v4l2_wrapper);
+  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.
@@ -54,11 +53,13 @@
   // Disconnect from the device: close dev nodes, etc.
   void disconnect() override;
   // Initialize static camera characteristics for individual device.
-  int initStaticInfo(camera_metadata_t** out) override;
-  // Initialize device info: facing, orientation, resource cost,
-  // and conflicting devices (/conflicting devices length).
+  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;
-  // Initialize whole device (templates/etc) when opened.
+  // Extra initialization of device when opened.
   int initDevice() override;
   // Verify stream configuration is device-compatible.
   bool isSupportedStreamSet(default_camera_hal::Stream** streams,
@@ -68,66 +69,24 @@
   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 camera_metadata_t* settings) override;
+  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(camera_metadata_t** metadata, uint64_t* timestamp);
+  int getResultSettings(android::CameraMetadata* metadata, uint64_t* timestamp);
 
   // V4L2 helper.
-  std::shared_ptr<V4L2Wrapper> mV4L2Device;
-  std::unique_ptr<V4L2Wrapper::Connection> mConnection;
+  std::shared_ptr<V4L2Wrapper> device_;
+  std::unique_ptr<V4L2Wrapper::Connection> connection_;
+  std::unique_ptr<Metadata> metadata_;
 
-  bool mTemplatesInitialized;
-  int initTemplates();
-
-  // Camera characteristics.
-  bool mCharacteristicsInitialized;  // If false, characteristics are invalid.
-  // Fixed characteristics.
-  float mAperture;
-  float mFilterDensity;
-  float mFocalLength;
-  int32_t mOrientation;
-  std::array<float, 2> mPhysicalSize;  // {width, height}, in mm.
-  std::array<int32_t, 4> mPixelArraySize;  // {xmin, ymin, width, height}.
-  uint8_t mCropType;
-  float mMaxZoom;
-  std::array<int32_t, 2> mAeCompensationRange;  // {min, max}.
-  camera_metadata_rational mAeCompensationStep;
-  uint8_t mAeLockAvailable;
-  uint8_t mAwbLockAvailable;
-  uint8_t mFlashAvailable;
-  float mFocusDistance;
-  int32_t mMaxRawOutputStreams;
-  int32_t mMaxStallingOutputStreams;
-  int32_t mMaxNonStallingOutputStreams;
-  int32_t mMaxInputStreams;
-  std::vector<int32_t> mSupportedFormats;
-  // Variable characteristics available options.
-  std::vector<uint8_t> mAeModes;
-  std::vector<uint8_t> mAeAntibandingModes;
-  std::vector<uint8_t> mAfModes;
-  std::vector<uint8_t> mAwbModes;
-  std::vector<uint8_t> mSceneModes;
-  std::vector<uint8_t> mControlModes;
-  std::vector<uint8_t> mEffects;
-  std::vector<uint8_t> mLeds;
-  std::vector<uint8_t> mOpticalStabilizationModes;
-  std::vector<uint8_t> mVideoStabilizationModes;
-  // {format, width, height, direction} (input or output).
-  ArrayVector<int32_t, 4> mStreamConfigs;
-  // {format, width, height, duration} (duration in ns).
-  ArrayVector<int64_t, 4> mMinFrameDurations;
-  int64_t mMaxFrameDuration;
-  // {format, width, height, duration} (duration in ns).
-  ArrayVector<int64_t, 4> mStallDurations;
-  // {min, max} (in fps).
-  ArrayVector<int32_t, 2> mFpsRanges;
-
-  // Initialize characteristics and set mCharacteristicsInitialized to True.
-  int initCharacteristics();
+  int32_t max_input_streams_;
+  std::array<int, 3> max_output_streams_;  // {raw, non-stalling, stalling}.
 
   DISALLOW_COPY_AND_ASSIGN(V4L2Camera);
 };
diff --git a/modules/camera/3_4/v4l2_metadata_factory.cpp b/modules/camera/3_4/v4l2_metadata_factory.cpp
index 412e6d4..b9c64be 100644
--- a/modules/camera/3_4/v4l2_metadata_factory.cpp
+++ b/modules/camera/3_4/v4l2_metadata_factory.cpp
@@ -41,13 +41,16 @@
                     std::unique_ptr<Metadata>* result) {
   HAL_LOG_ENTER();
 
-  // TODO: Temporarily connect to the device so that V4L2-specific components
-  // can make any necessary queries.
+  // 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/30140438): Add all metadata components used by V4L2Camera here.
-  // Currently these are all the fixed properties, ignored controls, and
-  // V4L2 enum controls. Will add the other properties as more PartialMetadata
-  // subclasses get implemented.
+  // TODO(b/30035628): Add states.
 
   PartialMetadataSet components;
 
diff --git a/modules/camera/3_4/v4l2_wrapper.cpp b/modules/camera/3_4/v4l2_wrapper.cpp
index b88faaf..3d7ba85 100644
--- a/modules/camera/3_4/v4l2_wrapper.cpp
+++ b/modules/camera/3_4/v4l2_wrapper.cpp
@@ -17,6 +17,7 @@
 #include "v4l2_wrapper.h"
 
 #include <algorithm>
+#include <array>
 #include <limits>
 #include <vector>
 
@@ -36,8 +37,8 @@
 
 namespace v4l2_camera_hal {
 
-const std::vector<std::array<int32_t, 2>> kStandardSizes(
-    {{{1920, 1080}}, {{1280, 720}}, {{640, 480}}, {{320, 240}}});
+const int32_t kStandardSizes[][2] = {
+    {1920, 1080}, {1280, 720}, {640, 480}, {320, 240}};
 
 V4L2Wrapper* V4L2Wrapper::NewV4L2Wrapper(const std::string device_path) {
   HAL_LOG_ENTER();
@@ -103,16 +104,14 @@
   if (connection_count_ == 0) {
     // Not connected.
     HAL_LOGE("Camera device %s is not connected, cannot disconnect.",
-             device_path_.c_str(),
-             connection_count_);
+             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(),
-             connection_count_);
+             device_path_.c_str());
     return;
   }
 
@@ -368,7 +367,7 @@
     // 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) {
+    for (const auto size : kStandardSizes) {
       // Find the closest size, rounding up.
       uint32_t desired_width = size[0];
       uint32_t desired_height = size[1];