Add stream capabilites to metadata reader

Read configs, stall durations, and reprocess formats.

BUG: 31044638
TEST: unit tests pass

Change-Id: I9089de0d51b7c4d8ef940a3737785177f257382f
diff --git a/modules/camera/3_4/metadata/metadata_common.h b/modules/camera/3_4/metadata/metadata_common.h
index 15f8084..34b7777 100644
--- a/modules/camera/3_4/metadata/metadata_common.h
+++ b/modules/camera/3_4/metadata/metadata_common.h
@@ -160,7 +160,7 @@
 //
 // Returns:
 //   -ENOENT: The tag couldn't be found or was empty.
-//   -EINVAL: The tag contained more than one item.
+//   -EINVAL: The tag contained more than one item, or |val| is null.
 //   -ENODEV: The tag claims to be non-empty, but the data pointer is null.
 //   0: Success. |*val| will contain the value for |tag|.
 
@@ -169,6 +169,10 @@
 static int SingleTagValue(const android::CameraMetadata& metadata,
                           int32_t tag,
                           T* val) {
+  if (!val) {
+    HAL_LOGE("Null pointer passed to SingleTagValue.");
+    return -EINVAL;
+  }
   camera_metadata_ro_entry_t entry = metadata.find(tag);
   if (entry.count == 0) {
     HAL_LOGE("Metadata tag %d is empty.", tag);
@@ -196,6 +200,10 @@
 static int SingleTagValue(const android::CameraMetadata& metadata,
                           int32_t tag,
                           std::array<T, N>* val) {
+  if (!val) {
+    HAL_LOGE("Null pointer passed to SingleTagValue.");
+    return -EINVAL;
+  }
   camera_metadata_ro_entry_t entry = metadata.find(tag);
   if (entry.count == 0) {
     HAL_LOGE("Metadata tag %d is empty.", tag);
@@ -222,6 +230,84 @@
   return 0;
 }
 
+// VectorTagValue(metadata, tag, val)
+//
+// Get the value of the |tag| entry in |metadata|.
+// |tag| is expected to refer to an entry with a vector
+// of the templated type. For arrays, an error will be
+// returned if it the wrong number of items are present.
+//
+// Returns:
+//   -ENOENT: The tag couldn't be found or was empty. While technically an
+//            empty vector may be valid, this error is returned for consistency
+//            with SingleTagValue.
+//   -EINVAL: The tag contained an invalid number of entries (e.g. 6 entries for
+//            a vector of length 4 arrays), or |val| is null.
+//   -ENODEV: The tag claims to be non-empty, but the data pointer is null.
+//   0: Success. |*val| will contain the values for |tag|.
+template <typename T>
+static int VectorTagValue(const android::CameraMetadata& metadata,
+                          int32_t tag,
+                          std::vector<T>* val) {
+  if (!val) {
+    HAL_LOGE("Null pointer passed to VectorTagValue.");
+    return -EINVAL;
+  }
+  camera_metadata_ro_entry_t entry = metadata.find(tag);
+  if (entry.count == 0) {
+    return -ENOENT;
+  }
+  const T* data = nullptr;
+  GetDataPointer(entry, &data);
+  if (data == nullptr) {
+    HAL_LOGE("Metadata tag %d claims to have elements but is empty.", tag);
+    return -ENODEV;
+  }
+  // Copy the data for |tag| into the output vector.
+  *val = std::vector<T>(data, data + entry.count);
+  return 0;
+}
+
+// Specialization for std::array.
+template <typename T, size_t N>
+static int VectorTagValue(const android::CameraMetadata& metadata,
+                          int32_t tag,
+                          std::vector<std::array<T, N>>* val) {
+  if (!val) {
+    HAL_LOGE("Null pointer passed to VectorTagValue.");
+    return -EINVAL;
+  }
+  camera_metadata_ro_entry_t entry = metadata.find(tag);
+  if (entry.count == 0) {
+    return -ENOENT;
+  }
+  if (entry.count % N != 0) {
+    HAL_LOGE(
+        "Error: expected metadata tag %d to contain a vector of arrays of "
+        "length %d (had %d entries, which is not divisible by %d).",
+        tag,
+        N,
+        entry.count,
+        N);
+    return -EINVAL;
+  }
+  const T* data = nullptr;
+  GetDataPointer(entry, &data);
+  if (data == nullptr) {
+    HAL_LOGE("Metadata tag %d claims to have elements but is empty.", tag);
+    return -ENODEV;
+  }
+  // Copy the data for |tag| into separate arrays for the output vector.
+  size_t num_arrays = entry.count / N;
+  *val = std::vector<std::array<T, N>>(num_arrays);
+  for (size_t i = 0; i < num_arrays; ++i) {
+    for (size_t j = 0; j < N; ++j) {
+      val->at(i)[j] = data[i * N + j];
+    }
+  }
+  return 0;
+}
+
 }  // namespace v4l2_camera_hal
 
 #endif  // V4L2_CAMERA_HAL_METADATA_METADATA_COMMON_H_
diff --git a/modules/camera/3_4/metadata/metadata_reader.cpp b/modules/camera/3_4/metadata/metadata_reader.cpp
index c570048..53c9535 100644
--- a/modules/camera/3_4/metadata/metadata_reader.cpp
+++ b/modules/camera/3_4/metadata/metadata_reader.cpp
@@ -83,4 +83,167 @@
   return 0;
 }
 
+int MetadataReader::MaxInputStreams(int32_t* max_input) const {
+  int res = v4l2_camera_hal::SingleTagValue(
+      *metadata_, ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS, max_input);
+  if (res == -ENOENT) {
+    // Not required; default to 0.
+    *max_input = 0;
+  } else if (res) {
+    ALOGE("%s: Failed to get max output streams from static metadata.",
+          __func__);
+    return res;
+  }
+  return 0;
+}
+
+int MetadataReader::MaxOutputStreams(int32_t* max_raw,
+                                     int32_t* max_non_stalling,
+                                     int32_t* max_stalling) const {
+  std::array<int32_t, 3> max_output_streams;
+  int res = v4l2_camera_hal::SingleTagValue(
+      *metadata_, ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS, &max_output_streams);
+  if (res) {
+    ALOGE("%s: Failed to get max output streams from static metadata.",
+          __func__);
+    return res;
+  }
+  *max_raw = max_output_streams[0];
+  *max_non_stalling = max_output_streams[1];
+  *max_stalling = max_output_streams[2];
+  return 0;
+}
+
+int MetadataReader::StreamConfigurations(
+    std::vector<StreamConfiguration>* configs) const {
+  std::vector<RawStreamConfiguration> raw_stream_configs;
+  int res = v4l2_camera_hal::VectorTagValue(
+      *metadata_,
+      ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
+      &raw_stream_configs);
+  if (res) {
+    ALOGE("%s: Failed to get stream configs from static metadata.", __func__);
+    return res;
+  }
+
+  // TODO(b/31384253): check for required configs.
+
+  // Convert from raw.
+  configs->insert(
+      configs->end(), raw_stream_configs.begin(), raw_stream_configs.end());
+
+  // Check that all configs are valid.
+  for (const auto& config : *configs) {
+    // Must have positive dimensions.
+    if (config.spec.width < 1 || config.spec.height < 1) {
+      ALOGE("%s: Invalid stream config: non-positive dimensions (%d, %d).",
+            __func__,
+            config.spec.width,
+            config.spec.height);
+      return -EINVAL;
+    }
+    // Must have a known direction enum.
+    switch (config.direction) {
+      case ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT:
+      case ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT:
+        break;
+      default:
+        ALOGE("%s: Invalid stream config direction: %d.",
+              __func__,
+              config.direction);
+        return -EINVAL;
+    }
+  }
+  return 0;
+}
+
+int MetadataReader::StreamStallDurations(
+    std::vector<StreamStallDuration>* stalls) const {
+  std::vector<RawStreamStallDuration> raw_stream_stall_durations;
+  int res =
+      v4l2_camera_hal::VectorTagValue(*metadata_,
+                                      ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,
+                                      &raw_stream_stall_durations);
+  if (res) {
+    ALOGE("%s: Failed to get stall durations from static metadata.", __func__);
+    return res;
+  }
+
+  // Convert from raw.
+  stalls->insert(stalls->end(),
+                 raw_stream_stall_durations.begin(),
+                 raw_stream_stall_durations.end());
+  // Check that all stalls are valid.
+  for (const auto& stall : *stalls) {
+    // Must have positive dimensions.
+    if (stall.spec.width < 1 || stall.spec.height < 1) {
+      ALOGE("%s: Invalid stall duration: non-positive dimensions (%d, %d).",
+            __func__,
+            stall.spec.width,
+            stall.spec.height);
+      return -EINVAL;
+    }
+    // Must have a non-negative stall.
+    if (stall.duration < 0) {
+      ALOGE("%s: Invalid stall duration: negative stall %d.",
+            __func__,
+            stall.duration);
+      return -EINVAL;
+    }
+    // TODO(b/31384253): YUV_420_888, RAW10, RAW12, RAW_OPAQUE,
+    // and IMPLEMENTATION_DEFINED must have 0 stall duration.
+  }
+
+  return 0;
+}
+
+int MetadataReader::ReprocessFormats(ReprocessFormatMap* reprocess_map) const {
+  std::vector<int32_t> input_output_formats;
+  int res = v4l2_camera_hal::VectorTagValue(
+      *metadata_,
+      ANDROID_SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP,
+      &input_output_formats);
+  if (res) {
+    ALOGE("%s: Failed to get input output format map from static metadata.",
+          __func__);
+    return res;
+  }
+
+  // Convert from the raw vector.
+  for (size_t i = 0; i < input_output_formats.size();) {
+    // The map is represented as variable-length entries of the format
+    // input, num_outputs, <outputs>.
+
+    // Get the input format.
+    int32_t input_format = input_output_formats[i++];
+
+    // Find the output begin and end for this format.
+    int32_t num_output_formats = input_output_formats[i++];
+    if (num_output_formats < 1) {
+      ALOGE(
+          "%s: No output formats for input format %d.", __func__, input_format);
+      return -EINVAL;
+    }
+    size_t outputs_end = i + num_output_formats;
+    if (outputs_end > input_output_formats.size()) {
+      ALOGE("%s: Input format %d requests more data than available.",
+            __func__,
+            input_format);
+      return -EINVAL;
+    }
+
+    // Copy all the output formats into the map.
+    (*reprocess_map)[input_format].insert(
+        input_output_formats.data() + i,
+        input_output_formats.data() + outputs_end);
+
+    // Move on to the next entry.
+    i = outputs_end;
+  }
+
+  // TODO(b/31384253): check for required mappings.
+
+  return 0;
+}
+
 }  // namespace default_camera_hal
diff --git a/modules/camera/3_4/metadata/metadata_reader.h b/modules/camera/3_4/metadata/metadata_reader.h
index 64bedd6..828e992 100644
--- a/modules/camera/3_4/metadata/metadata_reader.h
+++ b/modules/camera/3_4/metadata/metadata_reader.h
@@ -17,11 +17,13 @@
 #ifndef DEFAULT_CAMERA_HAL_METADATA_METADATA_READER_H_
 #define DEFAULT_CAMERA_HAL_METADATA_METADATA_READER_H_
 
+#include <map>
 #include <memory>
 
 #include <camera/CameraMetadata.h>
 
 #include "../common.h"
+#include "types.h"
 
 namespace default_camera_hal {
 
@@ -51,6 +53,15 @@
   // The |facing| returned will be one of the enum values from system/camera.h.
   virtual int Facing(int* facing) const;
   virtual int Orientation(int* orientation) const;
+  virtual int MaxInputStreams(int32_t* max_input_streams) const;
+  virtual int MaxOutputStreams(int32_t* max_raw_output_streams,
+                               int32_t* max_non_stalling_output_streams,
+                               int32_t* max_stalling_output_streams) const;
+  virtual int StreamConfigurations(
+      std::vector<StreamConfiguration>* configs) const;
+  virtual int StreamStallDurations(
+      std::vector<StreamStallDuration>* stalls) const;
+  virtual int ReprocessFormats(ReprocessFormatMap* reprocess_map) const;
 
  private:
   std::unique_ptr<const android::CameraMetadata> metadata_;
diff --git a/modules/camera/3_4/metadata/metadata_reader_test.cpp b/modules/camera/3_4/metadata/metadata_reader_test.cpp
index cdbb3f9..eadf51c 100644
--- a/modules/camera/3_4/metadata/metadata_reader_test.cpp
+++ b/modules/camera/3_4/metadata/metadata_reader_test.cpp
@@ -21,6 +21,7 @@
 #include <gtest/gtest.h>
 #include <system/camera.h>
 
+#include "array_vector.h"
 #include "metadata_common.h"
 
 using testing::AtMost;
@@ -52,7 +53,15 @@
 
   const int32_t facing_tag_ = ANDROID_LENS_FACING;
   const int32_t orientation_tag_ = ANDROID_SENSOR_ORIENTATION;
+  const int32_t max_inputs_tag_ = ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS;
+  const int32_t max_outputs_tag_ = ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS;
+  const int32_t configs_tag_ = ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS;
+  const int32_t stalls_tag_ = ANDROID_SCALER_AVAILABLE_STALL_DURATIONS;
+  const int32_t reprocess_formats_tag_ =
+      ANDROID_SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP;
+
   const std::vector<int32_t> valid_orientations_ = {0, 90, 180, 270};
+  // TODO(b/31384253): check for required configs/reprocess formats.
 };
 
 TEST_F(MetadataReaderTest, FacingTranslations) {
@@ -130,4 +139,235 @@
   EXPECT_EQ(dut_->Orientation(&actual), -ENOENT);
 }
 
+TEST_F(MetadataReaderTest, MaxInputs) {
+  int32_t expected = 12;
+  ASSERT_EQ(v4l2_camera_hal::UpdateMetadata(
+                metadata_.get(), max_inputs_tag_, expected),
+            0);
+  FillDUT();
+  int32_t actual = expected + 1;
+  ASSERT_EQ(dut_->MaxInputStreams(&actual), 0);
+  EXPECT_EQ(actual, expected);
+}
+
+TEST_F(MetadataReaderTest, EmptyMaxInputs) {
+  FillDUT();
+  // Max inputs is an optional key; if not present the default is 0.
+  int32_t expected = 0;
+  int32_t actual = expected + 1;
+  ASSERT_EQ(dut_->MaxInputStreams(&actual), 0);
+  EXPECT_EQ(actual, expected);
+}
+
+TEST_F(MetadataReaderTest, MaxOutputs) {
+  std::array<int32_t, 3> expected = {{12, 34, 56}};
+  ASSERT_EQ(v4l2_camera_hal::UpdateMetadata(
+                metadata_.get(), max_outputs_tag_, expected),
+            0);
+  FillDUT();
+  std::array<int32_t, 3> actual;
+  ASSERT_EQ(dut_->MaxOutputStreams(&actual[0], &actual[1], &actual[2]), 0);
+  EXPECT_EQ(actual, expected);
+}
+
+TEST_F(MetadataReaderTest, InvalidMaxOutputs) {
+  // Must be a 3-tuple to be valid.
+  std::array<int32_t, 4> invalid = {{12, 34, 56, 78}};
+  ASSERT_EQ(v4l2_camera_hal::UpdateMetadata(
+                metadata_.get(), max_outputs_tag_, invalid),
+            0);
+  FillDUT();
+  int32_t actual;
+  // Don't mind the aliasing since we don't care about the value.
+  ASSERT_EQ(dut_->MaxOutputStreams(&actual, &actual, &actual), -EINVAL);
+}
+
+TEST_F(MetadataReaderTest, EmptyMaxOutputs) {
+  FillDUT();
+  int32_t actual;
+  // Don't mind the aliasing since we don't care about the value.
+  ASSERT_EQ(dut_->MaxOutputStreams(&actual, &actual, &actual), -ENOENT);
+}
+
+TEST_F(MetadataReaderTest, StreamConfigurations) {
+  v4l2_camera_hal::ArrayVector<int32_t, 4> configs;
+  std::array<int32_t, 4> config1{
+      {1, 2, 3, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT}};
+  std::array<int32_t, 4> config2{
+      {5, 6, 7, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT}};
+  configs.push_back(config1);
+  configs.push_back(config2);
+  ASSERT_EQ(
+      v4l2_camera_hal::UpdateMetadata(metadata_.get(), configs_tag_, configs),
+      0);
+  FillDUT();
+  std::vector<StreamConfiguration> actual;
+  ASSERT_EQ(dut_->StreamConfigurations(&actual), 0);
+  ASSERT_EQ(actual.size(), configs.num_arrays());
+  EXPECT_EQ(actual[0].spec.format, config1[0]);
+  EXPECT_EQ(actual[0].spec.width, config1[1]);
+  EXPECT_EQ(actual[0].spec.height, config1[2]);
+  EXPECT_EQ(actual[0].direction, config1[3]);
+  EXPECT_EQ(actual[1].spec.format, config2[0]);
+  EXPECT_EQ(actual[1].spec.width, config2[1]);
+  EXPECT_EQ(actual[1].spec.height, config2[2]);
+  EXPECT_EQ(actual[1].direction, config2[3]);
+}
+
+TEST_F(MetadataReaderTest, InvalidStreamConfigurationDirection) {
+  // -1 is not a valid direction.
+  std::array<int32_t, 4> config{{1, 2, 3, -1}};
+  ASSERT_EQ(
+      v4l2_camera_hal::UpdateMetadata(metadata_.get(), configs_tag_, config),
+      0);
+  FillDUT();
+  std::vector<StreamConfiguration> actual;
+  ASSERT_EQ(dut_->StreamConfigurations(&actual), -EINVAL);
+}
+
+TEST_F(MetadataReaderTest, InvalidStreamConfigurationSize) {
+  // Both size dimensions must be > 0.
+  std::array<int32_t, 4> config{
+      {1, 2, 0, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT}};
+  ASSERT_EQ(
+      v4l2_camera_hal::UpdateMetadata(metadata_.get(), configs_tag_, config),
+      0);
+  FillDUT();
+  std::vector<StreamConfiguration> actual;
+  ASSERT_EQ(dut_->StreamConfigurations(&actual), -EINVAL);
+}
+
+TEST_F(MetadataReaderTest, InvalidStreamConfigurationNumElements) {
+  // Should be a multiple of 4.
+  std::array<int32_t, 5> config{
+      {1, 2, 3, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT, 5}};
+  ASSERT_EQ(
+      v4l2_camera_hal::UpdateMetadata(metadata_.get(), configs_tag_, config),
+      0);
+  FillDUT();
+  std::vector<StreamConfiguration> actual;
+  ASSERT_EQ(dut_->StreamConfigurations(&actual), -EINVAL);
+}
+
+// TODO(b/31384253): Test that failure occurs if
+// required configurations are not present.
+
+TEST_F(MetadataReaderTest, NoStreamConfigurations) {
+  FillDUT();
+  std::vector<StreamConfiguration> actual;
+  ASSERT_EQ(dut_->StreamConfigurations(&actual), -ENOENT);
+}
+
+TEST_F(MetadataReaderTest, StreamStallDurations) {
+  v4l2_camera_hal::ArrayVector<int64_t, 4> stalls;
+  std::array<int64_t, 4> stall1{{1, 2, 3, 4}};
+  std::array<int64_t, 4> stall2{{5, 6, 7, 8}};
+  stalls.push_back(stall1);
+  stalls.push_back(stall2);
+  ASSERT_EQ(
+      v4l2_camera_hal::UpdateMetadata(metadata_.get(), stalls_tag_, stalls), 0);
+  FillDUT();
+  std::vector<StreamStallDuration> actual;
+  ASSERT_EQ(dut_->StreamStallDurations(&actual), 0);
+  ASSERT_EQ(actual.size(), stalls.num_arrays());
+  EXPECT_EQ(actual[0].spec.format, stall1[0]);
+  EXPECT_EQ(actual[0].spec.width, stall1[1]);
+  EXPECT_EQ(actual[0].spec.height, stall1[2]);
+  EXPECT_EQ(actual[0].duration, stall1[3]);
+  EXPECT_EQ(actual[1].spec.format, stall2[0]);
+  EXPECT_EQ(actual[1].spec.width, stall2[1]);
+  EXPECT_EQ(actual[1].spec.height, stall2[2]);
+  EXPECT_EQ(actual[1].duration, stall2[3]);
+}
+
+TEST_F(MetadataReaderTest, InvalidStreamStallDurationDuration) {
+  // -1 is not a valid duration.
+  std::array<int64_t, 4> stall{{1, 2, 3, -1}};
+  ASSERT_EQ(
+      v4l2_camera_hal::UpdateMetadata(metadata_.get(), stalls_tag_, stall), 0);
+  FillDUT();
+  std::vector<StreamStallDuration> actual;
+  ASSERT_EQ(dut_->StreamStallDurations(&actual), -EINVAL);
+}
+
+TEST_F(MetadataReaderTest, InvalidStreamStallDurationSize) {
+  // Both size dimensions must be > 0.
+  std::array<int64_t, 4> stall{{1, 2, 0, 3}};
+  ASSERT_EQ(
+      v4l2_camera_hal::UpdateMetadata(metadata_.get(), stalls_tag_, stall), 0);
+  FillDUT();
+  std::vector<StreamStallDuration> actual;
+  ASSERT_EQ(dut_->StreamStallDurations(&actual), -EINVAL);
+}
+
+TEST_F(MetadataReaderTest, InvalidStreamStallDurationNumElements) {
+  // Should be a multiple of 4.
+  std::array<int64_t, 5> stall{{1, 2, 3, 4, 5}};
+  ASSERT_EQ(
+      v4l2_camera_hal::UpdateMetadata(metadata_.get(), stalls_tag_, stall), 0);
+  FillDUT();
+  std::vector<StreamStallDuration> actual;
+  ASSERT_EQ(dut_->StreamStallDurations(&actual), -EINVAL);
+}
+
+// TODO(b/31384253): Test that failure occurs if
+// YUV_420_888, RAW10, RAW12, RAW_OPAQUE, or IMPLEMENTATION_DEFINED
+// formats have stall durations > 0.
+
+TEST_F(MetadataReaderTest, NoStreamStallDurations) {
+  FillDUT();
+  std::vector<StreamStallDuration> actual;
+  ASSERT_EQ(dut_->StreamStallDurations(&actual), -ENOENT);
+}
+
+TEST_F(MetadataReaderTest, ReprocessFormats) {
+  ReprocessFormatMap expected{{1, {4}}, {2, {5, 6}}, {3, {7, 8, 9}}};
+  std::vector<int32_t> raw;
+  for (const auto& input_outputs : expected) {
+    raw.push_back(input_outputs.first);
+    raw.push_back(input_outputs.second.size());
+    raw.insert(
+        raw.end(), input_outputs.second.begin(), input_outputs.second.end());
+  }
+  ASSERT_EQ(v4l2_camera_hal::UpdateMetadata(
+                metadata_.get(), reprocess_formats_tag_, raw),
+            0);
+  FillDUT();
+  ReprocessFormatMap actual;
+  ASSERT_EQ(dut_->ReprocessFormats(&actual), 0);
+  EXPECT_EQ(actual, expected);
+}
+
+TEST_F(MetadataReaderTest, ReprocessFormatsNoOutputs) {
+  // 0 indicates that there are 0 output formats for input format 1,
+  // which is not ok.
+  std::vector<int32_t> raw{1, 0};
+  ASSERT_EQ(v4l2_camera_hal::UpdateMetadata(
+                metadata_.get(), reprocess_formats_tag_, raw),
+            0);
+  FillDUT();
+  ReprocessFormatMap actual;
+  ASSERT_EQ(dut_->ReprocessFormats(&actual), -EINVAL);
+}
+
+TEST_F(MetadataReaderTest, ReprocessFormatsPastEnd) {
+  // 3 indicates that there are 3 output formats for input format 1,
+  // which is not ok since there are only 2 here.
+  std::vector<int32_t> raw{1, 3, 0, 0};
+  ASSERT_EQ(v4l2_camera_hal::UpdateMetadata(
+                metadata_.get(), reprocess_formats_tag_, raw),
+            0);
+  FillDUT();
+  ReprocessFormatMap actual;
+  ASSERT_EQ(dut_->ReprocessFormats(&actual), -EINVAL);
+}
+
+TEST_F(MetadataReaderTest, EmptyReprocessFormats) {
+  // 3 indicates that there are 3 output formats for input format 1,
+  // which is not ok since there are only 2 here.
+  FillDUT();
+  ReprocessFormatMap actual;
+  ASSERT_EQ(dut_->ReprocessFormats(&actual), -ENOENT);
+}
+
 }  // namespace default_camera_hal
diff --git a/modules/camera/3_4/metadata/types.h b/modules/camera/3_4/metadata/types.h
new file mode 100644
index 0000000..093fe01
--- /dev/null
+++ b/modules/camera/3_4/metadata/types.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DEFAULT_CAMERA_HAL_METADATA_TYPES_H_
+#define DEFAULT_CAMERA_HAL_METADATA_TYPES_H_
+
+#include <array>
+#include <map>
+#include <set>
+
+#include <hardware/camera3.h>
+
+namespace default_camera_hal {
+
+// A variety of Structs describing more complex metadata entries.
+
+// StreamSpec describe the attributes of a single stream.
+struct StreamSpec {
+  int32_t format;
+  int32_t width;
+  int32_t height;
+
+  StreamSpec(int32_t f, int32_t w, int32_t h)
+      : format(f), width(w), height(h) {}
+  StreamSpec(const camera3_stream_t* stream)
+      : format(stream->format), width(stream->width), height(stream->height) {}
+
+  struct Compare {
+    bool operator()(const StreamSpec& left, const StreamSpec& right) const {
+      // Base equality/comparison first on format, then on width, then height.
+      return left.format < right.format ||
+             (left.format == right.format &&
+              (left.width < right.width ||
+               (left.width == right.width && left.height < right.height)));
+    }
+  };
+};
+
+// StreamConfigurations indicate a possible direction configuration for
+// a given set of stream specifications.
+typedef std::array<int32_t, 4> RawStreamConfiguration;
+struct StreamConfiguration {
+  StreamSpec spec;
+  int32_t direction;
+
+  StreamConfiguration(const RawStreamConfiguration& raw)
+      : spec({raw[0], raw[1], raw[2]}), direction(raw[3]) {}
+};
+
+// StreamStallDurations indicate the stall duration (in ns) for
+// when a stream with a given set of specifications is used as output.
+typedef std::array<int64_t, 4> RawStreamStallDuration;
+struct StreamStallDuration {
+  StreamSpec spec;
+  int64_t duration;
+
+  StreamStallDuration(const RawStreamStallDuration& raw)
+      : spec({static_cast<int32_t>(raw[0]),
+              static_cast<int32_t>(raw[1]),
+              static_cast<int32_t>(raw[2])}),
+        duration(raw[3]) {}
+};
+
+// Map input formats to their supported reprocess output formats.
+typedef std::map<int32_t, std::set<int32_t>> ReprocessFormatMap;
+
+}  // namespace default_camera_hal
+
+#endif  // DEFAULT_CAMERA_HAL_METADATA_TYPES_H_