Add format querying.

Query formats, sizes, and frame rates.

BUG: https://b/29394024, https://b/30605346
TEST: Test java program shows a bunch of formats & rates,
can take pictures in different sizes now.

Change-Id: I203fd8f200dda79cf6946c58f130548b05269fbd
diff --git a/modules/camera/3_4/v4l2_camera.cpp b/modules/camera/3_4/v4l2_camera.cpp
index f8c1c61..87a7a31 100644
--- a/modules/camera/3_4/v4l2_camera.cpp
+++ b/modules/camera/3_4/v4l2_camera.cpp
@@ -51,7 +51,7 @@
 V4L2Camera* V4L2Camera::NewV4L2Camera(int id, const std::string path) {
   HAL_LOG_ENTER();
 
-  std::unique_ptr<V4L2Wrapper> v4l2_wrapper(V4L2Wrapper::NewV4L2Wrapper(path));
+  std::shared_ptr<V4L2Wrapper> v4l2_wrapper(V4L2Wrapper::NewV4L2Wrapper(path));
   if (!v4l2_wrapper) {
     HAL_LOGE("Failed to initialize V4L2 wrapper.");
     return nullptr;
@@ -60,7 +60,7 @@
   return new V4L2Camera(id, std::move(v4l2_wrapper));
 }
 
-V4L2Camera::V4L2Camera(int id, std::unique_ptr<V4L2Wrapper> v4l2_wrapper)
+V4L2Camera::V4L2Camera(int id, std::shared_ptr<V4L2Wrapper> v4l2_wrapper)
     : default_camera_hal::Camera(id),
       mV4L2Device(std::move(v4l2_wrapper)),
       mTemplatesInitialized(false),
@@ -75,10 +75,15 @@
 int V4L2Camera::connect() {
   HAL_LOG_ENTER();
 
-  int res = mV4L2Device->Connect();
-  if (res) {
+  if (mConnection) {
+    HAL_LOGE("Already connected. Please disconnect and try again.");
+    return -EIO;
+  }
+
+  mConnection.reset(new V4L2Wrapper::Connection(mV4L2Device));
+  if (mConnection->status()) {
     HAL_LOGE("Failed to connect to device.");
-    return res;
+    return mConnection->status();
   }
 
   // TODO(b/29185945): confirm this is a supported device.
@@ -96,7 +101,7 @@
 void V4L2Camera::disconnect() {
   HAL_LOG_ENTER();
 
-  mV4L2Device->Disconnect();
+  mConnection.reset();
 
   // TODO(b/29158098): Inform service of any flashes that are available again
   // because this camera is no longer in use.
@@ -1460,35 +1465,77 @@
   mVideoStabilizationModes.push_back(
       ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF);
 
-  // Need to support YUV_420_888 and JPEG.
-  // TODO(b/29394024): query VIDIOC_ENUM_FMT.
-  std::vector<uint32_t> formats = {{V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_YUV420}};
-  // We want to find the smallest maximum frame duration amongst formats.
-  mMaxFrameDuration = std::numeric_limits<int64_t>::max();
-  int64_t min_yuv_frame_duration = std::numeric_limits<int64_t>::max();
-  for (auto format : formats) {
-    int32_t hal_format = StreamFormat::V4L2ToHalPixelFormat(format);
+  // 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;
     }
-    // TODO(b/29394024): query VIDIOC_ENUM_FRAMESIZES.
-    // For now, just 640x480.
-    ArrayVector<int32_t, 2> frame_sizes;
-    frame_sizes.push_back({{640, 480}});
-    size_t num_frame_sizes = frame_sizes.num_arrays();
-    for (size_t i = 0; i < num_frame_sizes; ++i) {
-      const int32_t* frame_size = frame_sizes[i];
+    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}});
 
-      // TODO(b/29394024): query VIDIOC_ENUM_FRAMEINTERVALS.
-      // For now, using the emulator max value of .3 sec.
-      int64_t max_frame_duration = 300000000;
-      // For now, min value of 30 fps = 1/30 spf ~= 33333333 ns.
-      // For whatever reason the goldfish/qcom cameras report this as
-      // 33331760, so copying that.
-      int64_t min_frame_duration = 33331760;
+      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}});
diff --git a/modules/camera/3_4/v4l2_camera.h b/modules/camera/3_4/v4l2_camera.h
index 17770b6..a4621f8 100644
--- a/modules/camera/3_4/v4l2_camera.h
+++ b/modules/camera/3_4/v4l2_camera.h
@@ -46,7 +46,7 @@
 private:
   // Constructor private to allow failing on bad input.
   // Use NewV4L2Camera instead.
-  V4L2Camera(int id, std::unique_ptr<V4L2Wrapper> v4l2_wrapper);
+  V4L2Camera(int id, std::shared_ptr<V4L2Wrapper> v4l2_wrapper);
 
   // default_camera_hal::Camera virtual methods.
   // Connect to the device: open dev nodes, etc.
@@ -77,7 +77,8 @@
   int getResultSettings(camera_metadata_t** metadata, uint64_t* timestamp);
 
   // V4L2 helper.
-  std::unique_ptr<V4L2Wrapper> mV4L2Device;
+  std::shared_ptr<V4L2Wrapper> mV4L2Device;
+  std::unique_ptr<V4L2Wrapper::Connection> mConnection;
 
   bool mTemplatesInitialized;
   int initTemplates();
diff --git a/modules/camera/3_4/v4l2_gralloc.cpp b/modules/camera/3_4/v4l2_gralloc.cpp
index bb708c9..5812649 100644
--- a/modules/camera/3_4/v4l2_gralloc.cpp
+++ b/modules/camera/3_4/v4l2_gralloc.cpp
@@ -87,7 +87,7 @@
 
 int V4L2Gralloc::lock(const camera3_stream_buffer_t* camera_buffer,
                       uint32_t bytes_per_line,
-                      /*out*/ v4l2_buffer* device_buffer) {
+                      v4l2_buffer* device_buffer) {
   HAL_LOG_ENTER();
 
   // Lock the camera buffer (varies depending on if the buffer is YUV or not).
@@ -95,7 +95,6 @@
       camera_buffer, nullptr, bytes_per_line});
   buffer_handle_t buffer = *camera_buffer->buffer;
   void* data;
-  size_t size;
   camera3_stream_t* stream = camera_buffer->stream;
   switch(stream->format) {
     // TODO(b/30119452): support more YCbCr formats.
@@ -103,8 +102,6 @@
       android_ycbcr yuv_data;
       mModule->lock_ycbcr(mModule, buffer, stream->usage, 0, 0,
                           stream->width, stream->height, &yuv_data);
-      // YUV_420_888 = 1.5 planes (1 Y plane, 1/4 Cb, 1/4 Cr).
-      size = bytes_per_line * stream->height * 3 / 2;
 
       // Check if gralloc format matches v4l2 format
       // (same padding, not interleaved, contiguous).
@@ -124,16 +121,15 @@
         // 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[size];
+        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; always max_size * 1.
-      size = V4L2_MAX_JPEG_SIZE;
-      mModule->lock(mModule, buffer, stream->usage, 0, 0, size, 1, &data);
+      // 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;
@@ -144,7 +140,6 @@
                 "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);
-  device_buffer->length = size;
 
   // Note the mapping of data:buffer info for when unlock is called.
   mBufferMap.emplace(data, buffer_data.release());
diff --git a/modules/camera/3_4/v4l2_gralloc.h b/modules/camera/3_4/v4l2_gralloc.h
index 4a49b9b..7ece59d 100644
--- a/modules/camera/3_4/v4l2_gralloc.h
+++ b/modules/camera/3_4/v4l2_gralloc.h
@@ -27,8 +27,8 @@
 
 namespace v4l2_camera_hal {
 
-// Generously allow up to 3MB.
-static constexpr size_t V4L2_MAX_JPEG_SIZE = 3000000;
+// 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.
@@ -39,7 +39,7 @@
   static V4L2Gralloc* NewV4L2Gralloc();
   virtual ~V4L2Gralloc();
 
-  // Lock a camera buffer. Sets device buffer user pointer and length.
+  // 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);
diff --git a/modules/camera/3_4/v4l2_wrapper.cpp b/modules/camera/3_4/v4l2_wrapper.cpp
index d331a77..7d8e38c 100644
--- a/modules/camera/3_4/v4l2_wrapper.cpp
+++ b/modules/camera/3_4/v4l2_wrapper.cpp
@@ -16,6 +16,10 @@
 
 #include "v4l2_wrapper.h"
 
+#include <algorithm>
+#include <limits>
+#include <vector>
+
 #include <fcntl.h>
 #include <linux/videodev2.h>
 #include <sys/stat.h>
@@ -32,6 +36,9 @@
 
 namespace v4l2_camera_hal {
 
+const std::vector<std::array<int32_t, 2>> kStandardSizes(
+    {{{1920, 1080}}, {{1280, 720}}, {{640, 480}}, {{320, 240}}});
+
 V4L2Wrapper* V4L2Wrapper::NewV4L2Wrapper(const std::string device_path) {
   HAL_LOG_ENTER();
 
@@ -48,7 +55,8 @@
                          std::unique_ptr<V4L2Gralloc> gralloc)
     : device_path_(std::move(device_path)),
       gralloc_(std::move(gralloc)),
-      max_buffers_(0) {
+      max_buffers_(0),
+      connection_count_(0) {
   HAL_LOG_ENTER();
 }
 
@@ -56,28 +64,26 @@
 
 int V4L2Wrapper::Connect() {
   HAL_LOG_ENTER();
-  std::lock_guard<std::mutex> lock(device_lock_);
+  std::lock_guard<std::mutex> lock(connection_lock_);
 
   if (connected()) {
-    HAL_LOGE("Camera device %s is already connected. Close it first",
-             device_path_.c_str());
-    return -EIO;
+    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 -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;
-  // Already holding the lock, so don't call IoctlLocked.
-  int res = TEMP_FAILURE_RETRY(
-      ioctl(device_fd_.get(), VIDIOC_QUERY_EXT_CTRL, &query));
-  extended_query_supported_ = (res == 0);
+  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
@@ -90,7 +96,21 @@
 
 void V4L2Wrapper::Disconnect() {
   HAL_LOG_ENTER();
-  std::lock_guard<std::mutex> lock(device_lock_);
+  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(), connection_count_);
+    return;
+  }
+
+  --connection_count_;
+  if (connection_count_ > 0) {
+    HAL_LOGV("Disconnected from camera device %s. %d connections remain.",
+             device_path_.c_str(), connection_count_);
+    return;
+  }
 
   device_fd_.reset();  // Includes close().
   format_.reset();
@@ -102,7 +122,7 @@
 // Helper function. Should be used instead of ioctl throughout this class.
 template <typename T>
 int V4L2Wrapper::IoctlLocked(int request, T data) {
-  HAL_LOG_ENTER();
+  // Potentially called so many times logging entry is a bad idea.
   std::lock_guard<std::mutex> lock(device_lock_);
 
   if (!connected()) {
@@ -247,6 +267,136 @@
   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();
@@ -342,20 +492,19 @@
   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.
+  // 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;
   }
 
-  // Configure the device buffer based on the stream buffer.
-  device_buffer.memory = V4L2_MEMORY_USERPTR;
-  // 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;
-  // Lock the buffer for writing.
+  // 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) {
@@ -363,7 +512,7 @@
     return res;
   }
   if (IoctlLocked(VIDIOC_QBUF, &device_buffer) < 0) {
-    HAL_LOGE("QBUF (%d) fails: %s", 0, strerror(errno));
+    HAL_LOGE("QBUF fails: %s", strerror(errno));
     gralloc_->unlock(&device_buffer);
     return -ENODEV;
   }
diff --git a/modules/camera/3_4/v4l2_wrapper.h b/modules/camera/3_4/v4l2_wrapper.h
index 113499a..da38e5a 100644
--- a/modules/camera/3_4/v4l2_wrapper.h
+++ b/modules/camera/3_4/v4l2_wrapper.h
@@ -17,8 +17,10 @@
 #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>
@@ -36,9 +38,20 @@
   static V4L2Wrapper* NewV4L2Wrapper(const std::string device_path);
   virtual ~V4L2Wrapper();
 
-  // Connect or disconnect to the device.
-  int Connect();
-  void Disconnect();
+  // 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.
   int StreamOn();
   int StreamOff();
@@ -48,26 +61,37 @@
   int SetControl(uint32_t control_id, int32_t desired,
                  int32_t* result = nullptr);
   // Manage format.
+  int GetFormats(std::set<uint32_t>* v4l2_formats);
+  int GetFormatFrameSizes(uint32_t v4l2_format,
+                          std::set<std::array<int32_t, 2>>* sizes);
+  // Durations are returned in ns.
+  int GetFormatFrameDurationRange(uint32_t v4l2_format,
+                                  const std::array<int32_t, 2>& size,
+                                  std::array<int64_t, 2>* duration_range);
   int SetFormat(const default_camera_hal::Stream& stream,
                 uint32_t* result_max_buffers);
   // Manage buffers.
   int EnqueueBuffer(const camera3_stream_buffer_t* camera_buffer);
   int DequeueBuffer(v4l2_buffer* buffer);
 
-  inline bool connected() { return device_fd_.get() >= 0; }
-
  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.
@@ -82,7 +106,12 @@
   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);