Add V4L2 Control Factory

Also moves all control factory methods into a separate file.

BUG: 30900438
TEST: unit tests pass
Change-Id: I885903d8e23a548b63fd20006568145a233c0316
diff --git a/modules/camera/3_4/metadata/control.h b/modules/camera/3_4/metadata/control.h
index 199ecd0..7c8c6bf 100644
--- a/modules/camera/3_4/metadata/control.h
+++ b/modules/camera/3_4/metadata/control.h
@@ -22,9 +22,7 @@
 #include <system/camera_metadata.h>
 
 #include "../common.h"
-#include "menu_control_options.h"
 #include "metadata_common.h"
-#include "no_effect_control_delegate.h"
 #include "partial_metadata_interface.h"
 #include "tagged_control_delegate.h"
 #include "tagged_control_options.h"
@@ -52,13 +50,7 @@
   virtual int SetRequestValues(
       const android::CameraMetadata& metadata) override;
 
-  // Factory methods for some common combinations of delegates & options.
-  // NoEffectMenuControl: Some menu options, but they have no effect.
-  // The default value will be the first element of |options|.
-  static std::unique_ptr<Control<T>> NoEffectMenuControl(
-      int32_t delegate_tag, int32_t options_tag, const std::vector<T>& options);
-
- protected:
+ private:
   std::unique_ptr<TaggedControlDelegate<T>> delegate_;
   std::unique_ptr<TaggedControlOptions<T>> options_;
 
@@ -178,24 +170,6 @@
   return delegate_->SetValue(requested);
 }
 
-template <typename T>
-std::unique_ptr<Control<T>> Control<T>::NoEffectMenuControl(
-    int32_t delegate_tag, int32_t options_tag, const std::vector<T>& options) {
-  HAL_LOG_ENTER();
-
-  if (options.empty()) {
-    HAL_LOGE("At least one option must be provided.");
-    return nullptr;
-  }
-
-  return std::make_unique<Control<T>>(
-      std::make_unique<TaggedControlDelegate<T>>(
-          delegate_tag,
-          std::make_unique<NoEffectControlDelegate<T>>(options[0])),
-      std::make_unique<TaggedControlOptions<T>>(
-          options_tag, std::make_unique<MenuControlOptions<T>>(options)));
-}
-
 }  // namespace v4l2_camera_hal
 
 #endif  // V4L2_CAMERA_HAL_METADATA_CONTROL_H_
diff --git a/modules/camera/3_4/metadata/control_factory.h b/modules/camera/3_4/metadata/control_factory.h
new file mode 100644
index 0000000..911b67f
--- /dev/null
+++ b/modules/camera/3_4/metadata/control_factory.h
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef V4L2_CAMERA_HAL_METADATA_CONTROL_FACTORY_H_
+#define V4L2_CAMERA_HAL_METADATA_CONTROL_FACTORY_H_
+
+#include "../common.h"
+#include "control.h"
+#include "menu_control_options.h"
+#include "no_effect_control_delegate.h"
+#include "ranged_converter.h"
+#include "slider_control_options.h"
+#include "tagged_control_delegate.h"
+#include "tagged_control_options.h"
+#include "v4l2_control_delegate.h"
+
+namespace v4l2_camera_hal {
+
+enum class ControlType { kMenu, kSlider };
+
+// Static functions to create controls. Nullptr is returned on failures.
+
+// NoEffectMenuControl: Some menu options, but they have no effect.
+// The default value will be the first element of |options|.
+template <typename T>
+static std::unique_ptr<Control<T>> NoEffectMenuControl(
+    int32_t delegate_tag, int32_t options_tag, const std::vector<T>& options);
+
+// NoEffectSliderControl: A slider of options, but they have no effect.
+// The default value will be |min|.
+template <typename T>
+static std::unique_ptr<Control<T>> NoEffectSliderControl(int32_t delegate_tag,
+                                                         int32_t options_tag,
+                                                         T min,
+                                                         T max);
+
+// NoEffectControl: A control with no effect and only a single allowable
+// value. Chooses an appropriate ControlOptionsInterface depending on type.
+template <typename T>
+static std::unique_ptr<Control<T>> NoEffectControl(ControlType type,
+                                                   int32_t delegate_tag,
+                                                   int32_t options_tag,
+                                                   T value);
+
+// V4L2Control: A control corresponding to a V4L2 control.
+template <typename T>
+static std::unique_ptr<Control<T>> V4L2Control(
+    ControlType type,
+    int32_t delegate_tag,
+    int32_t options_tag,
+    std::shared_ptr<V4L2Wrapper> device,
+    int control_id,
+    std::shared_ptr<ConverterInterface<T, int32_t>> converter);
+
+// V4L2ControlOrDefault: Like V4L2Control, but if the V4L2Control fails to
+// initialize for some reason, this method will fall back to NoEffectControl.
+template <typename T>
+static std::unique_ptr<Control<T>> V4L2ControlOrDefault(
+    ControlType type,
+    int32_t delegate_tag,
+    int32_t options_tag,
+    std::shared_ptr<V4L2Wrapper> device,
+    int control_id,
+    std::shared_ptr<ConverterInterface<T, int32_t>> converter,
+    const T& default_value);
+
+// -----------------------------------------------------------------------------
+
+template <typename T>
+std::unique_ptr<Control<T>> NoEffectMenuControl(int32_t delegate_tag,
+                                                int32_t options_tag,
+                                                const std::vector<T>& options) {
+  HAL_LOG_ENTER();
+
+  if (options.empty()) {
+    HAL_LOGE("At least one option must be provided.");
+    return nullptr;
+  }
+
+  return std::make_unique<Control<T>>(
+      std::make_unique<TaggedControlDelegate<T>>(
+          delegate_tag,
+          std::make_unique<NoEffectControlDelegate<T>>(options[0])),
+      std::make_unique<TaggedControlOptions<T>>(
+          options_tag, std::make_unique<MenuControlOptions<T>>(options)));
+}
+
+template <typename T>
+std::unique_ptr<Control<T>> NoEffectSliderControl(int32_t delegate_tag,
+                                                  int32_t options_tag,
+                                                  T min,
+                                                  T max) {
+  HAL_LOG_ENTER();
+
+  return std::make_unique<Control<T>>(
+      std::make_unique<TaggedControlDelegate<T>>(
+          delegate_tag, std::make_unique<NoEffectControlDelegate<T>>(min)),
+      std::make_unique<TaggedControlOptions<T>>(
+          options_tag, std::make_unique<SliderControlOptions<T>>(min, max)));
+}
+
+template <typename T>
+std::unique_ptr<Control<T>> NoEffectControl(ControlType type,
+                                            int32_t delegate_tag,
+                                            int32_t options_tag,
+                                            T value) {
+  HAL_LOG_ENTER();
+
+  switch (type) {
+    case ControlType::kMenu:
+      return NoEffectMenuControl<T>(delegate_tag, options_tag, {value});
+    case ControlType::kSlider:
+      return NoEffectSliderControl(delegate_tag, options_tag, value, value);
+  }
+}
+
+template <typename T>
+std::unique_ptr<Control<T>> V4L2Control(
+    ControlType type,
+    int32_t delegate_tag,
+    int32_t options_tag,
+    std::shared_ptr<V4L2Wrapper> device,
+    int control_id,
+    std::shared_ptr<ConverterInterface<T, int32_t>> converter) {
+  HAL_LOG_ENTER();
+
+  // Query the device.
+  v4l2_query_ext_ctrl control_query;
+  int res = device->QueryControl(control_id, &control_query);
+  if (res) {
+    HAL_LOGE("Failed to query control %d.", control_id);
+    return nullptr;
+  }
+
+  int32_t control_min = static_cast<int32_t>(control_query.minimum);
+  int32_t control_max = static_cast<int32_t>(control_query.maximum);
+  int32_t control_step = static_cast<int32_t>(control_query.step);
+  if (control_min > control_max) {
+    HAL_LOGE("No acceptable values (min %d is greater than max %d).",
+             control_min,
+             control_max);
+    return nullptr;
+  }
+
+  // Variables needed by the various switch statements.
+  std::vector<T> options;
+  T metadata_val;
+  T metadata_min;
+  T metadata_max;
+  // Set up the result converter and result options based on type.
+  std::shared_ptr<ConverterInterface<T, int32_t>> result_converter(converter);
+  std::unique_ptr<ControlOptionsInterface<T>> result_options;
+  switch (control_query.type) {
+    case V4L2_CTRL_TYPE_BOOLEAN:  // Fall-through.
+    case V4L2_CTRL_TYPE_MENU:
+      if (type != ControlType::kMenu) {
+        HAL_LOGE(
+            "V4L2 control %d is of type %d, which isn't compatible with "
+            "desired metadata control type %d",
+            control_id,
+            control_query.type,
+            type);
+        return nullptr;
+      }
+
+      // Convert each available option,
+      // ignoring ones without a known conversion.
+      for (int32_t i = control_min; i <= control_max; i += control_step) {
+        res = converter->V4L2ToMetadata(i, &metadata_val);
+        if (res == -EINVAL) {
+          HAL_LOGV("V4L2 value %d for control %d has no metadata equivalent.",
+                   i,
+                   control_id);
+          continue;
+        } else if (res) {
+          HAL_LOGE("Error converting value %d for control %d.", i, control_id);
+          return nullptr;
+        }
+        options.push_back(metadata_val);
+      }
+      // Check to make sure there's at least one option.
+      if (options.empty()) {
+        HAL_LOGE("No valid options for control %d.", control_id);
+        return nullptr;
+      }
+      result_options.reset(new MenuControlOptions<T>(options));
+      // No converter changes necessary.
+      break;
+    case V4L2_CTRL_TYPE_INTEGER:
+      if (type != ControlType::kSlider) {
+        HAL_LOGE(
+            "V4L2 control %d is of type %d, which isn't compatible with "
+            "desired metadata control type %d",
+            control_id,
+            control_query.type,
+            type);
+        return nullptr;
+      }
+
+      // Upgrade to a range/step-clamping converter.
+      result_converter.reset(new RangedConverter<T, int32_t>(
+          converter, control_min, control_max, control_step));
+
+      // Convert the min and max.
+      res = result_converter->V4L2ToMetadata(control_min, &metadata_min);
+      if (res) {
+        HAL_LOGE(
+            "Failed to convert V4L2 min value %d for control %d to metadata.",
+            control_min,
+            control_id);
+        return nullptr;
+      }
+      res = result_converter->V4L2ToMetadata(control_max, &metadata_max);
+      if (res) {
+        HAL_LOGE(
+            "Failed to convert V4L2 max value %d for control %d to metadata.",
+            control_max,
+            control_id);
+        return nullptr;
+      }
+      result_options.reset(
+          new SliderControlOptions<T>(metadata_min, metadata_max));
+      break;
+    default:
+      HAL_LOGE("Control %d is of unsupported type %d",
+               control_id,
+               control_query.type);
+      return nullptr;
+  }
+
+  // Construct the control.
+  return std::make_unique<Control<T>>(
+      std::make_unique<TaggedControlDelegate<T>>(
+          delegate_tag,
+          std::make_unique<V4L2ControlDelegate<T>>(
+              device, control_id, result_converter)),
+      std::make_unique<TaggedControlOptions<T>>(options_tag,
+                                                std::move(result_options)));
+}
+
+template <typename T>
+std::unique_ptr<Control<T>> V4L2ControlOrDefault(
+    ControlType type,
+    int32_t delegate_tag,
+    int32_t options_tag,
+    std::shared_ptr<V4L2Wrapper> device,
+    int control_id,
+    std::shared_ptr<ConverterInterface<T, int32_t>> converter,
+    const T& default_value) {
+  HAL_LOG_ENTER();
+
+  std::unique_ptr<Control<T>> result = V4L2Control(
+      type, delegate_tag, options_tag, device, control_id, converter);
+  if (!result) {
+    result = NoEffectControl(type, delegate_tag, options_tag, default_value);
+  }
+  return result;
+}
+
+}  // namespace v4l2_camera_hal
+
+#endif  // V4L2_CAMERA_HAL_METADATA_CONTROL_FACTORY_H_
diff --git a/modules/camera/3_4/metadata/control_factory_test.cpp b/modules/camera/3_4/metadata/control_factory_test.cpp
new file mode 100644
index 0000000..9010879
--- /dev/null
+++ b/modules/camera/3_4/metadata/control_factory_test.cpp
@@ -0,0 +1,433 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <camera/CameraMetadata.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "../v4l2_wrapper_mock.h"
+#include "control_factory.h"
+#include "converter_interface_mock.h"
+#include "metadata_common.h"
+#include "test_common.h"
+
+using testing::AtMost;
+using testing::Expectation;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::Test;
+using testing::_;
+
+namespace v4l2_camera_hal {
+
+class ControlFactoryTest : public Test {
+ protected:
+  virtual void SetUp() {
+    mock_device_.reset(new V4L2WrapperMock());
+    mock_converter_.reset(new ConverterInterfaceMock<uint8_t, int32_t>());
+    // Nullify control so an error will be thrown
+    // if a test doesn't construct it.
+    control_.reset();
+  }
+
+  virtual void ExpectTags() {
+    ASSERT_EQ(control_->StaticTags().size(), 1);
+    EXPECT_EQ(control_->StaticTags()[0], options_tag_);
+    ASSERT_EQ(control_->ControlTags().size(), 1);
+    EXPECT_EQ(control_->ControlTags()[0], delegate_tag_);
+    ASSERT_EQ(control_->DynamicTags().size(), 1);
+    EXPECT_EQ(control_->DynamicTags()[0], delegate_tag_);
+  }
+
+  virtual void ExpectOptions(const std::vector<uint8_t>& options) {
+    // Options should be available.
+    android::CameraMetadata metadata;
+    ASSERT_EQ(control_->PopulateStaticFields(&metadata), 0);
+    EXPECT_EQ(metadata.entryCount(), 1);
+    ExpectMetadataEq(metadata, options_tag_, options);
+  }
+
+  virtual void ExpectValue(uint8_t value) {
+    android::CameraMetadata metadata;
+    ASSERT_EQ(control_->PopulateDynamicFields(&metadata), 0);
+    EXPECT_EQ(metadata.entryCount(), 1);
+    ExpectMetadataEq(metadata, delegate_tag_, value);
+  }
+
+  std::unique_ptr<Control<uint8_t>> control_;
+  std::shared_ptr<ConverterInterfaceMock<uint8_t, int32_t>> mock_converter_;
+  std::shared_ptr<V4L2WrapperMock> mock_device_;
+
+  // Need tags that match the data type (uint8_t) being passed.
+  const int32_t delegate_tag_ = ANDROID_COLOR_CORRECTION_ABERRATION_MODE;
+  const int32_t options_tag_ =
+      ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES;
+};
+
+TEST_F(ControlFactoryTest, NoEffectMenu) {
+  std::vector<uint8_t> test_options = {9, 8, 12};
+  control_ =
+      NoEffectMenuControl<uint8_t>(delegate_tag_, options_tag_, test_options);
+  ASSERT_NE(control_, nullptr);
+
+  ExpectTags();
+
+  // Options should be available.
+  ExpectOptions(test_options);
+  // Default value should be test_options[0].
+  ExpectValue(test_options[0]);
+}
+
+TEST_F(ControlFactoryTest, NoEffectGenericMenu) {
+  uint8_t default_val = 9;
+  control_ = NoEffectControl<uint8_t>(
+      ControlType::kMenu, delegate_tag_, options_tag_, default_val);
+  ASSERT_NE(control_, nullptr);
+
+  ExpectTags();
+
+  // Options should be available.
+  ExpectOptions({default_val});
+  // |default_val| should be default option.
+  ExpectValue(default_val);
+}
+
+TEST_F(ControlFactoryTest, NoEffectSlider) {
+  std::vector<uint8_t> test_range = {9, 12};
+  control_ = NoEffectSliderControl<uint8_t>(
+      delegate_tag_, options_tag_, test_range[0], test_range[1]);
+  ASSERT_NE(control_, nullptr);
+
+  ExpectTags();
+
+  // Single option should be available.
+  ExpectOptions(test_range);
+  // Default value should be the minimum (test_range[0]).
+  ExpectValue(test_range[0]);
+}
+
+TEST_F(ControlFactoryTest, NoEffectGenericSlider) {
+  uint8_t default_val = 9;
+  control_ = NoEffectControl<uint8_t>(
+      ControlType::kSlider, delegate_tag_, options_tag_, default_val);
+  ASSERT_NE(control_, nullptr);
+
+  ExpectTags();
+
+  // Range containing only |default_val| should be available.
+  ExpectOptions({default_val, default_val});
+  // |default_val| should be default option.
+  ExpectValue(default_val);
+}
+
+TEST_F(ControlFactoryTest, V4L2FactoryQueryFail) {
+  int control_id = 55;
+  // Should query the device.
+  EXPECT_CALL(*mock_device_, QueryControl(control_id, _)).WillOnce(Return(-1));
+  control_ = V4L2Control<uint8_t>(ControlType::kMenu,
+                                  delegate_tag_,
+                                  options_tag_,
+                                  mock_device_,
+                                  control_id,
+                                  mock_converter_);
+  // Failure, should return null.
+  ASSERT_EQ(control_, nullptr);
+}
+
+TEST_F(ControlFactoryTest, V4L2FactoryQueryBadType) {
+  int control_id = 55;
+  v4l2_query_ext_ctrl query_result;
+  query_result.type = V4L2_CTRL_TYPE_CTRL_CLASS;
+  // Should query the device.
+  EXPECT_CALL(*mock_device_, QueryControl(control_id, _))
+      .WillOnce(DoAll(SetArgPointee<1>(query_result), Return(0)));
+  control_ = V4L2Control<uint8_t>(ControlType::kMenu,
+                                  delegate_tag_,
+                                  options_tag_,
+                                  mock_device_,
+                                  control_id,
+                                  mock_converter_);
+  // Failure, should return null.
+  ASSERT_EQ(control_, nullptr);
+}
+
+TEST_F(ControlFactoryTest, V4L2FactoryQueryBadRange) {
+  int control_id = 55;
+  v4l2_query_ext_ctrl query_result;
+  query_result.type = V4L2_CTRL_TYPE_MENU;
+  query_result.minimum = 10;
+  query_result.maximum = 1;  // Less than minimum.
+  // Should query the device.
+  EXPECT_CALL(*mock_device_, QueryControl(control_id, _))
+      .WillOnce(DoAll(SetArgPointee<1>(query_result), Return(0)));
+  control_ = V4L2Control<uint8_t>(ControlType::kMenu,
+                                  delegate_tag_,
+                                  options_tag_,
+                                  mock_device_,
+                                  control_id,
+                                  mock_converter_);
+  // Failure, should return null.
+  ASSERT_EQ(control_, nullptr);
+}
+
+TEST_F(ControlFactoryTest, V4L2FactoryTypeRequestMenuMismatch) {
+  int control_id = 55;
+  v4l2_query_ext_ctrl query_result;
+  query_result.type = V4L2_CTRL_TYPE_INTEGER;
+  query_result.minimum = 1;
+  query_result.maximum = 7;
+  query_result.step = 2;
+  // Have conversions for values 1-5, by step size 2.
+  std::map<int32_t, uint8_t> conversion_map = {{1, 10}, {3, 30}, {5, 50}};
+
+  // Should query the device.
+  EXPECT_CALL(*mock_device_, QueryControl(control_id, _))
+      .WillOnce(DoAll(SetArgPointee<1>(query_result), Return(0)));
+
+  // If you ask for a Menu, but the V4L2 control is a slider type, that's bad.
+  control_ = V4L2Control<uint8_t>(ControlType::kMenu,
+                                  delegate_tag_,
+                                  options_tag_,
+                                  mock_device_,
+                                  control_id,
+                                  mock_converter_);
+  ASSERT_EQ(control_, nullptr);
+}
+
+TEST_F(ControlFactoryTest, V4L2FactoryTypeRequestSliderMismatch) {
+  int control_id = 55;
+  v4l2_query_ext_ctrl query_result;
+  query_result.type = V4L2_CTRL_TYPE_MENU;
+  query_result.minimum = 1;
+  query_result.maximum = 7;
+  query_result.step = 2;
+  // Have conversions for values 1-5, by step size 2.
+  std::map<int32_t, uint8_t> conversion_map = {{1, 10}, {3, 30}, {5, 50}};
+
+  // Should query the device.
+  EXPECT_CALL(*mock_device_, QueryControl(control_id, _))
+      .WillOnce(DoAll(SetArgPointee<1>(query_result), Return(0)));
+
+  // If you ask for a Slider and get a Menu, that's bad.
+  control_ = V4L2Control<uint8_t>(ControlType::kSlider,
+                                  delegate_tag_,
+                                  options_tag_,
+                                  mock_device_,
+                                  control_id,
+                                  mock_converter_);
+  ASSERT_EQ(control_, nullptr);
+}
+
+TEST_F(ControlFactoryTest, V4L2FactoryMenu) {
+  int control_id = 55;
+  v4l2_query_ext_ctrl query_result;
+  query_result.type = V4L2_CTRL_TYPE_MENU;
+  query_result.minimum = 1;
+  query_result.maximum = 7;
+  query_result.step = 2;
+  // Have conversions for values 1-5, by step size 2.
+  std::map<int32_t, uint8_t> conversion_map = {{1, 10}, {3, 30}, {5, 50}};
+
+  // Should query the device.
+  EXPECT_CALL(*mock_device_, QueryControl(control_id, _))
+      .WillOnce(DoAll(SetArgPointee<1>(query_result), Return(0)));
+  // Should convert values.
+  std::vector<uint8_t> expected_options;
+  for (auto kv : conversion_map) {
+    EXPECT_CALL(*mock_converter_, V4L2ToMetadata(kv.first, _))
+        .WillOnce(DoAll(SetArgPointee<1>(kv.second), Return(0)));
+    expected_options.push_back(kv.second);
+  }
+  // Will fail to convert 7 with -EINVAL, shouldn't matter.
+  EXPECT_CALL(*mock_converter_, V4L2ToMetadata(7, _)).WillOnce(Return(-EINVAL));
+
+  control_ = V4L2Control<uint8_t>(ControlType::kMenu,
+                                  delegate_tag_,
+                                  options_tag_,
+                                  mock_device_,
+                                  control_id,
+                                  mock_converter_);
+  ASSERT_NE(control_, nullptr);
+
+  ExpectTags();
+  ExpectOptions(expected_options);
+}
+
+TEST_F(ControlFactoryTest, V4L2FactoryMenuConversionFail) {
+  int control_id = 55;
+  v4l2_query_ext_ctrl query_result;
+  query_result.type = V4L2_CTRL_TYPE_MENU;
+  query_result.minimum = 1;
+  query_result.maximum = 7;
+  query_result.step = 2;
+
+  // Should query the device.
+  EXPECT_CALL(*mock_device_, QueryControl(control_id, _))
+      .WillOnce(DoAll(SetArgPointee<1>(query_result), Return(0)));
+  // Conversion fails with non-EINVAL error.
+  EXPECT_CALL(*mock_converter_, V4L2ToMetadata(_, _)).WillOnce(Return(-1));
+
+  control_ = V4L2Control<uint8_t>(ControlType::kMenu,
+                                  delegate_tag_,
+                                  options_tag_,
+                                  mock_device_,
+                                  control_id,
+                                  mock_converter_);
+  ASSERT_EQ(control_, nullptr);
+}
+
+TEST_F(ControlFactoryTest, V4L2FactoryMenuNoConversions) {
+  int control_id = 55;
+  v4l2_query_ext_ctrl query_result;
+  query_result.type = V4L2_CTRL_TYPE_MENU;
+  query_result.minimum = 1;
+  query_result.maximum = 1;
+  query_result.step = 1;
+
+  // Should query the device.
+  EXPECT_CALL(*mock_device_, QueryControl(control_id, _))
+      .WillOnce(DoAll(SetArgPointee<1>(query_result), Return(0)));
+  // Conversion fails with -EINVAL error.
+  EXPECT_CALL(*mock_converter_, V4L2ToMetadata(1, _)).WillOnce(Return(-EINVAL));
+
+  control_ = V4L2Control<uint8_t>(ControlType::kMenu,
+                                  delegate_tag_,
+                                  options_tag_,
+                                  mock_device_,
+                                  control_id,
+                                  mock_converter_);
+  // Since there were no convertable options, should fail.
+  ASSERT_EQ(control_, nullptr);
+}
+
+TEST_F(ControlFactoryTest, V4L2FactoryInteger) {
+  int control_id = 55;
+  v4l2_query_ext_ctrl query_result;
+  query_result.type = V4L2_CTRL_TYPE_INTEGER;
+  query_result.minimum = 1;
+  query_result.maximum = 7;
+  query_result.step = 2;
+  // Have conversions for values 1 & 7.
+  std::map<int32_t, uint8_t> conversion_map = {{1, 10}, {7, 70}};
+
+  // Should query the device.
+  EXPECT_CALL(*mock_device_, QueryControl(control_id, _))
+      .WillOnce(DoAll(SetArgPointee<1>(query_result), Return(0)));
+  // Should convert values.
+  std::vector<uint8_t> expected_options;
+  for (auto kv : conversion_map) {
+    EXPECT_CALL(*mock_converter_, V4L2ToMetadata(kv.first, _))
+        .WillOnce(DoAll(SetArgPointee<1>(kv.second), Return(0)));
+    expected_options.push_back(kv.second);
+  }
+
+  control_ = V4L2Control<uint8_t>(ControlType::kSlider,
+                                  delegate_tag_,
+                                  options_tag_,
+                                  mock_device_,
+                                  control_id,
+                                  mock_converter_);
+  ASSERT_NE(control_, nullptr);
+
+  ExpectTags();
+  ExpectOptions(expected_options);
+
+  // Should be fitting converted values to steps.
+  uint8_t set_val = 10;
+  android::CameraMetadata metadata;
+  EXPECT_EQ(UpdateMetadata(&metadata, delegate_tag_, set_val), 0);
+  EXPECT_CALL(*mock_converter_, MetadataToV4L2(set_val, _))
+      .WillOnce(DoAll(SetArgPointee<1>(4), Return(0)));
+  // When it calls into the device, the 4 returned above should be
+  // rounded down to the step value of 3.
+  EXPECT_CALL(*mock_device_, SetControl(control_id, 3, _)).WillOnce(Return(0));
+  EXPECT_EQ(control_->SetRequestValues(metadata), 0);
+}
+
+TEST_F(ControlFactoryTest, V4L2FactoryIntegerFailedConversion) {
+  int control_id = 55;
+  v4l2_query_ext_ctrl query_result;
+  query_result.type = V4L2_CTRL_TYPE_INTEGER;
+  query_result.minimum = 1;
+  query_result.maximum = 7;
+  query_result.step = 2;
+
+  // Should query the device.
+  EXPECT_CALL(*mock_device_, QueryControl(control_id, _))
+      .WillOnce(DoAll(SetArgPointee<1>(query_result), Return(0)));
+  // Fail to convert a value. Even -EINVAL is bad in this case.
+  EXPECT_CALL(*mock_converter_, V4L2ToMetadata(1, _)).WillOnce(Return(-EINVAL));
+
+  control_ = V4L2Control<uint8_t>(ControlType::kSlider,
+                                  delegate_tag_,
+                                  options_tag_,
+                                  mock_device_,
+                                  control_id,
+                                  mock_converter_);
+  ASSERT_EQ(control_, nullptr);
+}
+
+TEST_F(ControlFactoryTest, V4L2FallbackMenu) {
+  uint8_t default_val = 9;
+  int control_id = 55;
+
+  // Should query the device.
+  EXPECT_CALL(*mock_device_, QueryControl(control_id, _)).WillOnce(Return(-1));
+
+  // Shouldn't fail, should fall back to menu control.
+  control_ = V4L2ControlOrDefault<uint8_t>(ControlType::kMenu,
+                                           delegate_tag_,
+                                           options_tag_,
+                                           mock_device_,
+                                           control_id,
+                                           mock_converter_,
+                                           default_val);
+  ASSERT_NE(control_, nullptr);
+
+  ExpectTags();
+
+  // Options should be available.
+  ExpectOptions({default_val});
+  // |default_val| should be default option.
+  ExpectValue(default_val);
+}
+
+TEST_F(ControlFactoryTest, V4L2FallbackSlider) {
+  uint8_t default_val = 9;
+  int control_id = 55;
+
+  // Should query the device.
+  EXPECT_CALL(*mock_device_, QueryControl(control_id, _)).WillOnce(Return(-1));
+
+  // Shouldn't fail, should fall back to slider control.
+  control_ = V4L2ControlOrDefault<uint8_t>(ControlType::kSlider,
+                                           delegate_tag_,
+                                           options_tag_,
+                                           mock_device_,
+                                           control_id,
+                                           mock_converter_,
+                                           default_val);
+  ASSERT_NE(control_, nullptr);
+
+  ExpectTags();
+
+  // Range containing only |default_val| should be available.
+  ExpectOptions({default_val, default_val});
+  // |default_val| should be default option.
+  ExpectValue(default_val);
+}
+
+}  // namespace v4l2_camera_hal
diff --git a/modules/camera/3_4/metadata/control_test.cpp b/modules/camera/3_4/metadata/control_test.cpp
index b414ad3..5297a01 100644
--- a/modules/camera/3_4/metadata/control_test.cpp
+++ b/modules/camera/3_4/metadata/control_test.cpp
@@ -28,7 +28,6 @@
 using testing::AtMost;
 using testing::Expectation;
 using testing::Return;
-using testing::ReturnRef;
 using testing::SetArgPointee;
 using testing::Test;
 using testing::_;
@@ -62,6 +61,35 @@
     }
   }
 
+  virtual void ExpectTags(bool with_options = true) {
+    if (with_options) {
+      ASSERT_EQ(control_->StaticTags().size(), 1);
+      EXPECT_EQ(control_->StaticTags()[0], options_tag_);
+    } else {
+      EXPECT_TRUE(control_->StaticTags().empty());
+    }
+    // Controls use the same delgate, and thus tag, for getting and setting.
+    ASSERT_EQ(control_->ControlTags().size(), 1);
+    EXPECT_EQ(control_->ControlTags()[0], delegate_tag_);
+    ASSERT_EQ(control_->DynamicTags().size(), 1);
+    EXPECT_EQ(control_->DynamicTags()[0], delegate_tag_);
+  }
+
+  virtual void ExpectOptions(const std::vector<uint8_t>& options) {
+    // Options should be available.
+    android::CameraMetadata metadata;
+    ASSERT_EQ(control_->PopulateStaticFields(&metadata), 0);
+    EXPECT_EQ(metadata.entryCount(), 1);
+    ExpectMetadataEq(metadata, options_tag_, options);
+  }
+
+  virtual void ExpectValue(uint8_t value) {
+    android::CameraMetadata metadata;
+    ASSERT_EQ(control_->PopulateDynamicFields(&metadata), 0);
+    EXPECT_EQ(metadata.entryCount(), 1);
+    ExpectMetadataEq(metadata, delegate_tag_, value);
+  }
+
   std::unique_ptr<Control<uint8_t>> control_;
   std::unique_ptr<ControlDelegateInterfaceMock<uint8_t>> mock_delegate_;
   std::unique_ptr<ControlOptionsInterfaceMock<uint8_t>> mock_options_;
@@ -74,24 +102,12 @@
 
 TEST_F(ControlTest, Tags) {
   PrepareControl();
-  ASSERT_EQ(control_->StaticTags().size(), 1);
-  EXPECT_EQ(control_->StaticTags()[0], options_tag_);
-  // Controls use the same delgate, and thus tag, for getting and setting.
-  ASSERT_EQ(control_->ControlTags().size(), 1);
-  EXPECT_EQ(control_->ControlTags()[0], delegate_tag_);
-  ASSERT_EQ(control_->DynamicTags().size(), 1);
-  EXPECT_EQ(control_->DynamicTags()[0], delegate_tag_);
+  ExpectTags();
 }
 
 TEST_F(ControlTest, TagsNoOptions) {
   PrepareControl(false);
-  // No options, so no options tag.
-  ASSERT_EQ(control_->StaticTags().size(), 0);
-  // Controls use the same delgate, and thus tag, for getting and setting.
-  ASSERT_EQ(control_->ControlTags().size(), 1);
-  EXPECT_EQ(control_->ControlTags()[0], delegate_tag_);
-  ASSERT_EQ(control_->DynamicTags().size(), 1);
-  EXPECT_EQ(control_->DynamicTags()[0], delegate_tag_);
+  ExpectTags(false);
 }
 
 TEST_F(ControlTest, PopulateStatic) {
@@ -99,13 +115,7 @@
   EXPECT_CALL(*mock_options_, MetadataRepresentation())
       .WillOnce(Return(expected));
   PrepareControl();
-
-  android::CameraMetadata metadata;
-  ASSERT_EQ(control_->PopulateStaticFields(&metadata), 0);
-  // Should only have added 1 entry.
-  EXPECT_EQ(metadata.entryCount(), 1);
-  // Should have added the right entry.
-  ExpectMetadataEq(metadata, options_tag_, expected);
+  ExpectOptions(expected);
 }
 
 TEST_F(ControlTest, PopulateStaticNoOptions) {
@@ -121,14 +131,7 @@
   EXPECT_CALL(*mock_delegate_, GetValue(_))
       .WillOnce(DoAll(SetArgPointee<0>(test_option), Return(0)));
   PrepareControl();
-
-  android::CameraMetadata metadata;
-  ASSERT_EQ(control_->PopulateDynamicFields(&metadata), 0);
-
-  // Should only have added 1 entry.
-  EXPECT_EQ(metadata.entryCount(), 1);
-  // Should have added the right entry.
-  ExpectMetadataEq(metadata, delegate_tag_, test_option);
+  ExpectValue(test_option);
 }
 
 TEST_F(ControlTest, PopulateDynamicNoOptions) {
@@ -137,14 +140,7 @@
   EXPECT_CALL(*mock_delegate_, GetValue(_))
       .WillOnce(DoAll(SetArgPointee<0>(test_option), Return(0)));
   PrepareControl(false);
-
-  android::CameraMetadata metadata;
-  EXPECT_EQ(control_->PopulateDynamicFields(&metadata), 0);
-
-  // Should only have added 1 entry.
-  EXPECT_EQ(metadata.entryCount(), 1);
-  // Should have added the right entry.
-  ExpectMetadataEq(metadata, delegate_tag_, test_option);
+  ExpectValue(test_option);
 }
 
 TEST_F(ControlTest, PopulateDynamicFail) {
@@ -303,31 +299,4 @@
   EXPECT_EQ(control_->SetRequestValues(metadata), 0);
 }
 
-TEST_F(ControlTest, NoEffectMenuFactory) {
-  std::vector<uint8_t> test_options = {9, 8, 12};
-  std::unique_ptr<Control<uint8_t>> dut = Control<uint8_t>::NoEffectMenuControl(
-      delegate_tag_, options_tag_, test_options);
-  ASSERT_NE(dut, nullptr);
-
-  ASSERT_EQ(dut->StaticTags().size(), 1);
-  EXPECT_EQ(dut->StaticTags()[0], options_tag_);
-  // Controls use the same delgate, and thus tag, for getting and setting.
-  ASSERT_EQ(dut->ControlTags().size(), 1);
-  EXPECT_EQ(dut->ControlTags()[0], delegate_tag_);
-  ASSERT_EQ(dut->DynamicTags().size(), 1);
-  EXPECT_EQ(dut->DynamicTags()[0], delegate_tag_);
-
-  // Options should be available.
-  android::CameraMetadata metadata;
-  ASSERT_EQ(dut->PopulateStaticFields(&metadata), 0);
-  EXPECT_EQ(metadata.entryCount(), 1);
-  ExpectMetadataEq(metadata, options_tag_, test_options);
-
-  // Default value should be test_options[0].
-  metadata.clear();
-  ASSERT_EQ(dut->PopulateDynamicFields(&metadata), 0);
-  EXPECT_EQ(metadata.entryCount(), 1);
-  ExpectMetadataEq(metadata, delegate_tag_, test_options[0]);
-}
-
 }  // namespace v4l2_camera_hal