Add v4l2 enum controls.

Some V4L2 controls are simple enum menus, and are directly
analagous to metadata controls. The new V4L2EnumControl class
is a generalized converter between the two.

BUG: https://b/30140438, https://b/29394024
TEST: unit tests pass

Change-Id: I9f8bed200e21cb7e3f3336608fe210f094b7aa42
diff --git a/modules/camera/3_4/Android.mk b/modules/camera/3_4/Android.mk
index 7b644c3..278a351 100644
--- a/modules/camera/3_4/Android.mk
+++ b/modules/camera/3_4/Android.mk
@@ -40,6 +40,7 @@
 v4l2_src_files := \
   camera.cpp \
   metadata/metadata.cpp \
+  metadata/v4l2_enum_control.cpp \
   stream.cpp \
   stream_format.cpp \
   v4l2_camera.cpp \
@@ -54,6 +55,7 @@
   metadata/ignored_control_test.cpp \
   metadata/metadata_test.cpp \
   metadata/optioned_control_test.cpp \
+  metadata/v4l2_enum_control_test.cpp \
 
 # V4L2 Camera HAL.
 # ==============================================================================
diff --git a/modules/camera/3_4/metadata/control.h b/modules/camera/3_4/metadata/control.h
index 4b4adcf..eff8c2f 100644
--- a/modules/camera/3_4/metadata/control.h
+++ b/modules/camera/3_4/metadata/control.h
@@ -46,6 +46,11 @@
   inline int32_t ControlTag() const { return ControlTags()[0]; }
 
   // Get/Set the control value. Return non-0 on failure.
+  // Controls are allowed to be unreliable, so SetValue is best-effort;
+  // GetValue immediately after may not match (SetValue may, for example,
+  // automatically replace invalid values with valid ones,
+  // or have a delay before setting the requested value).
+  // GetValue should always indicate the actual current value of the control.
   virtual int GetValue(T* value) const = 0;
   virtual int SetValue(const T& value) = 0;
   // Helper to check if val is supported by this control.
diff --git a/modules/camera/3_4/metadata/v4l2_enum_control.cpp b/modules/camera/3_4/metadata/v4l2_enum_control.cpp
new file mode 100644
index 0000000..21ca22b
--- /dev/null
+++ b/modules/camera/3_4/metadata/v4l2_enum_control.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "v4l2_enum_control.h"
+
+#include "../common.h"
+
+namespace v4l2_camera_hal {
+
+V4L2EnumControl* V4L2EnumControl::NewV4L2EnumControl(
+    std::shared_ptr<V4L2Wrapper> device, int v4l2_control, int32_t control_tag,
+    int32_t options_tag, const std::map<int32_t, uint8_t>& v4l2_to_metadata) {
+  HAL_LOG_ENTER();
+
+  // Query the device.
+  v4l2_query_ext_ctrl control_query;
+  int res = device->QueryControl(v4l2_control, &control_query);
+  if (res) {
+    HAL_LOGE("Failed to query control %d.", v4l2_control);
+    return nullptr;
+  }
+  if (control_query.type != V4L2_CTRL_TYPE_MENU) {
+    HAL_LOGE(
+        "Enum controls can only be constructed from V4L2 menu controls (%d is "
+        "of type %d)",
+        v4l2_control, control_query.type);
+    return nullptr;
+  }
+
+  // Convert device options to metadata options.
+  std::vector<uint8_t> options;
+  int32_t control_max = static_cast<int32_t>(control_query.maximum);
+  // Query maximum is inclusive.
+  for (int32_t i = static_cast<int32_t>(control_query.minimum);
+       i <= control_max; i += control_query.step) {
+    auto map_entry = v4l2_to_metadata.find(i);
+    if (map_entry == v4l2_to_metadata.end()) {
+      HAL_LOGW("Control %d has unknown option %d.", v4l2_control, i);
+    } else {
+      options.push_back(map_entry->second);
+    }
+  }
+  if (options.empty()) {
+    HAL_LOGE("No supported options for control %d.", v4l2_control);
+    return nullptr;
+  }
+
+  // Construct the device.
+  return new V4L2EnumControl(device, v4l2_control, control_tag, options_tag,
+                             std::move(v4l2_to_metadata), std::move(options));
+}
+
+V4L2EnumControl::V4L2EnumControl(
+    std::shared_ptr<V4L2Wrapper> device, int v4l2_control, int32_t control_tag,
+    int32_t options_tag, const std::map<int32_t, uint8_t> v4l2_to_metadata,
+    std::vector<uint8_t> options)
+    : OptionedControl<uint8_t>(control_tag, options_tag, std::move(options)),
+      device_(device),
+      v4l2_control_(v4l2_control),
+      v4l2_to_metadata_(std::move(v4l2_to_metadata)) {
+  HAL_LOG_ENTER();
+}
+
+int V4L2EnumControl::GetValue(uint8_t* value) const {
+  HAL_LOG_ENTER();
+
+  // Query the device for V4L2 value.
+  int32_t v4l2_value = 0;
+  int res = device_->GetControl(v4l2_control_, &v4l2_value);
+  if (res) {
+    HAL_LOGE("Failed to get value for control %d from device.", v4l2_control_);
+    return res;
+  }
+
+  // Convert to metadata value.
+  auto metadata_element = v4l2_to_metadata_.find(v4l2_value);
+  if (metadata_element == v4l2_to_metadata_.end()) {
+    HAL_LOGE("Unknown value %d for control %d.", v4l2_value, v4l2_control_);
+    return -ENODEV;
+  }
+  *value = metadata_element->second;
+  return 0;
+}
+
+int V4L2EnumControl::SetValue(const uint8_t& value) {
+  HAL_LOG_ENTER();
+
+  if (!IsSupported(value)) {
+    HAL_LOGE("Invalid control value %d.", value);
+    return -EINVAL;
+  }
+
+  // Convert to V4L2 value by doing an inverse lookup in the map.
+  bool found = false;
+  int32_t v4l2_value = -1;
+  for (auto kv : v4l2_to_metadata_) {
+    if (kv.second == value) {
+      v4l2_value = kv.first;
+      found = true;
+      break;
+    }
+  }
+  if (!found) {
+    HAL_LOGE("Couldn't find V4L2 conversion of valid control value %d.", value);
+    return -ENODEV;
+  }
+
+  return device_->SetControl(v4l2_control_, v4l2_value);
+}
+
+}  // namespace v4l2_camera_hal
diff --git a/modules/camera/3_4/metadata/v4l2_enum_control.h b/modules/camera/3_4/metadata/v4l2_enum_control.h
new file mode 100644
index 0000000..93fc0e2
--- /dev/null
+++ b/modules/camera/3_4/metadata/v4l2_enum_control.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef V4L2_CAMERA_HAL_METADATA_V4L2_ENUM_CONTROL_H_
+#define V4L2_CAMERA_HAL_METADATA_V4L2_ENUM_CONTROL_H_
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include <gtest/gtest_prod.h>
+
+#include "../v4l2_wrapper.h"
+#include "optioned_control.h"
+
+namespace v4l2_camera_hal {
+
+// A V4L2EnumControl is a direct mapping between a V4L2 control
+// and an Android metadata control.
+class V4L2EnumControl : public OptionedControl<uint8_t> {
+ public:
+  // Use this method to create V4L2EnumControl objects.
+  // Functionally equivalent to "new V4L2EnumControl"
+  // except that it may return nullptr in case of failure.
+  static V4L2EnumControl* NewV4L2EnumControl(
+      std::shared_ptr<V4L2Wrapper> device, int v4l2_control,
+      int32_t control_tag, int32_t options_tag,
+      const std::map<int32_t, uint8_t>& v4l2_to_metadata);
+
+ private:
+  // Constructor private to allow failing on bad input.
+  // Use NewV4L2EnumControl instead.
+  // The values in |v4l2_to_metadata| must be a superset of |options|.
+  V4L2EnumControl(std::shared_ptr<V4L2Wrapper> device, int v4l2_control,
+                  int32_t control_tag, int32_t options_tag,
+                  const std::map<int32_t, uint8_t> v4l2_to_metadata,
+                  std::vector<uint8_t> options);
+
+  virtual int GetValue(uint8_t* value) const override;
+  virtual int SetValue(const uint8_t& value) override;
+
+  std::shared_ptr<V4L2Wrapper> device_;
+  const int v4l2_control_;
+  const std::map<int32_t, uint8_t> v4l2_to_metadata_;
+
+  FRIEND_TEST(V4L2EnumControlTest, SetValue);
+  FRIEND_TEST(V4L2EnumControlTest, SetValueFail);
+  FRIEND_TEST(V4L2EnumControlTest, SetInvalidValue);
+  FRIEND_TEST(V4L2EnumControlTest, SetUnmapped);
+  FRIEND_TEST(V4L2EnumControlTest, GetValue);
+  FRIEND_TEST(V4L2EnumControlTest, GetValueFail);
+  FRIEND_TEST(V4L2EnumControlTest, GetUnmapped);
+  friend class V4L2EnumControlTest;  // Access to private constructor.
+
+  DISALLOW_COPY_AND_ASSIGN(V4L2EnumControl);
+};
+
+}  // namespace v4l2_camera_hal
+
+#endif  // V4L2_CAMERA_HAL_METADATA_V4L2_ENUM_CONTROL_H_
diff --git a/modules/camera/3_4/metadata/v4l2_enum_control_test.cpp b/modules/camera/3_4/metadata/v4l2_enum_control_test.cpp
new file mode 100644
index 0000000..6df7321
--- /dev/null
+++ b/modules/camera/3_4/metadata/v4l2_enum_control_test.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "v4l2_enum_control.h"
+
+#include <array>
+
+#include <camera/CameraMetadata.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "../v4l2_wrapper_mock.h"
+#include "array_vector.h"
+#include "test_common.h"
+
+using testing::AtMost;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::Test;
+using testing::_;
+
+namespace v4l2_camera_hal {
+
+class V4L2EnumControlTest : public Test {
+ protected:
+  V4L2EnumControlTest()
+      : options_({10, 20, 30, 50}),  // Subset of the full map.
+        options_map_(
+            {{0, 0}, {1, 10}, {2, 20}, {3, 30}, {4, 40}, {5, 50}, {6, 60}}) {}
+
+  virtual void SetUp() {
+    device_.reset(new V4L2WrapperMock());
+    control_.reset(new V4L2EnumControl(device_, v4l2_control_, control_tag_,
+                                       options_tag_, options_map_, options_));
+  }
+
+  virtual uint8_t V4L2ToMetadata(int32_t value) {
+    return options_map_.at(value);
+  }
+
+  virtual int32_t MetadataToV4L2(uint8_t value) {
+    for (auto kv : options_map_) {
+      if (kv.second == value) {
+        return kv.first;
+      }
+    }
+    return -1;
+  }
+
+  std::unique_ptr<V4L2EnumControl> control_;
+  std::shared_ptr<V4L2WrapperMock> device_;
+
+  static constexpr int v4l2_control_ = 123;
+  // Need tags that match the data type (uint8_t) being passed.
+  static constexpr int32_t options_tag_ = ANDROID_CONTROL_AVAILABLE_SCENE_MODES;
+  static constexpr int32_t control_tag_ = ANDROID_CONTROL_SCENE_MODE;
+
+  const std::vector<uint8_t> options_;
+  const std::map<int32_t, uint8_t> options_map_;
+};
+
+TEST_F(V4L2EnumControlTest, NewV4L2EnumSuccess) {
+  // Should query the device.
+  v4l2_query_ext_ctrl query_result;
+  query_result.type = V4L2_CTRL_TYPE_MENU;
+  query_result.minimum = 1;
+  query_result.maximum = 5;
+  query_result.step = 2;
+  EXPECT_CALL(*device_, QueryControl(v4l2_control_, _))
+      .WillOnce(DoAll(SetArgPointee<1>(query_result), Return(0)));
+
+  std::unique_ptr<V4L2EnumControl> test_control(
+      V4L2EnumControl::NewV4L2EnumControl(device_, v4l2_control_, control_tag_,
+                                          options_tag_, options_map_));
+  // Shouldn't be null.
+  ASSERT_NE(test_control.get(), nullptr);
+
+  // Should pass through tags.
+  // The macro doesn't like the static variables
+  // being passed directly to assertions.
+  int32_t expected_tag = options_tag_;
+  ASSERT_EQ(test_control->StaticTags().size(), 1);
+  EXPECT_EQ(test_control->StaticTags()[0], expected_tag);
+  // Controls use the same tag for getting and setting.
+  expected_tag = control_tag_;
+  ASSERT_EQ(test_control->ControlTags().size(), 1);
+  EXPECT_EQ(test_control->ControlTags()[0], expected_tag);
+  ASSERT_EQ(test_control->DynamicTags().size(), 1);
+  EXPECT_EQ(test_control->DynamicTags()[0], expected_tag);
+
+  // Should populate the options according to capabilities returned.
+  android::CameraMetadata metadata;
+  ASSERT_EQ(test_control->PopulateStaticFields(&metadata), 0);
+  // Min 1, max 5, step 2 means {1,3,5} converted to metadata values.
+  std::vector<uint8_t> expected_options;
+  expected_options.push_back(V4L2ToMetadata(1));
+  expected_options.push_back(V4L2ToMetadata(3));
+  expected_options.push_back(V4L2ToMetadata(5));
+  ExpectMetadataEq(metadata, options_tag_, expected_options);
+}
+
+TEST_F(V4L2EnumControlTest, NewV4L2EnumFailed) {
+  // Querying the device fails.
+  int err = -99;
+  EXPECT_CALL(*device_, QueryControl(v4l2_control_, _)).WillOnce(Return(err));
+
+  std::unique_ptr<V4L2EnumControl> test_control(
+      V4L2EnumControl::NewV4L2EnumControl(device_, v4l2_control_, control_tag_,
+                                          options_tag_, options_map_));
+  // Should return null to indicate error.
+  ASSERT_EQ(test_control.get(), nullptr);
+}
+
+TEST_F(V4L2EnumControlTest, NewV4L2EnumInvalid) {
+  // Control type is not supported.
+  v4l2_query_ext_ctrl query_result;
+  query_result.type = V4L2_CTRL_TYPE_INTEGER;
+  query_result.minimum = 1;
+  query_result.maximum = 5;
+  query_result.step = 2;
+  EXPECT_CALL(*device_, QueryControl(v4l2_control_, _))
+      .WillOnce(DoAll(SetArgPointee<1>(query_result), Return(0)));
+
+  std::unique_ptr<V4L2EnumControl> test_control(
+      V4L2EnumControl::NewV4L2EnumControl(device_, v4l2_control_, control_tag_,
+                                          options_tag_, options_map_));
+  // Should return null to indicate error.
+  ASSERT_FALSE(test_control);
+}
+
+TEST_F(V4L2EnumControlTest, SetValue) {
+  // Should go through the device.
+  uint8_t val = options_[1];
+  EXPECT_CALL(*device_, SetControl(v4l2_control_, MetadataToV4L2(val), _))
+      .WillOnce(Return(0));
+  EXPECT_EQ(control_->SetValue(val), 0);
+}
+
+TEST_F(V4L2EnumControlTest, SetValueFail) {
+  // Should go through the device but fail.
+  uint8_t val = options_[1];
+  int err = -99;
+  EXPECT_CALL(*device_, SetControl(v4l2_control_, MetadataToV4L2(val), _))
+      .WillOnce(Return(err));
+  EXPECT_EQ(control_->SetValue(val), err);
+}
+
+TEST_F(V4L2EnumControlTest, SetInvalidValue) {
+  uint8_t val = 99;  // Not one of the supported values.
+  EXPECT_EQ(control_->SetValue(val), -EINVAL);
+}
+
+TEST_F(V4L2EnumControlTest, SetUnmapped) {
+  // If the enum control is validly constructed, this should never happen.
+  // Purposefully misconstruct a device for this test (empty map).
+  V4L2EnumControl test_control(device_, v4l2_control_, control_tag_,
+                               options_tag_, {}, options_);
+  EXPECT_EQ(test_control.SetValue(options_[0]), -ENODEV);
+}
+
+TEST_F(V4L2EnumControlTest, GetValue) {
+  // Should go through the device.
+  uint8_t expected = options_[0];
+  EXPECT_CALL(*device_, GetControl(v4l2_control_, _))
+      .WillOnce(DoAll(SetArgPointee<1>(MetadataToV4L2(expected)), Return(0)));
+  uint8_t actual =
+      expected + 1;  // Initialize to something other than expected.
+  EXPECT_EQ(control_->GetValue(&actual), 0);
+  EXPECT_EQ(actual, expected);
+}
+
+TEST_F(V4L2EnumControlTest, GetValueFail) {
+  // Should go through the device but fail.
+  int err = -99;
+  EXPECT_CALL(*device_, GetControl(v4l2_control_, _)).WillOnce(Return(err));
+  uint8_t unused;
+  EXPECT_EQ(control_->GetValue(&unused), err);
+}
+
+TEST_F(V4L2EnumControlTest, GetUnmapped) {
+  int32_t invalid = -99;  // Not in our map.
+  EXPECT_CALL(*device_, GetControl(v4l2_control_, _))
+      .WillOnce(DoAll(SetArgPointee<1>(invalid), Return(0)));
+  uint8_t unused;
+  EXPECT_EQ(control_->GetValue(&unused), -ENODEV);
+}
+
+}  // namespace v4l2_camera_hal
diff --git a/modules/camera/3_4/v4l2_wrapper.h b/modules/camera/3_4/v4l2_wrapper.h
index da38e5a..f2f08b3 100644
--- a/modules/camera/3_4/v4l2_wrapper.h
+++ b/modules/camera/3_4/v4l2_wrapper.h
@@ -43,7 +43,9 @@
    public:
     Connection(std::shared_ptr<V4L2Wrapper> device)
         : device_(std::move(device)), connect_result_(device_->Connect()) {}
-    ~Connection() { if (connect_result_ == 0) device_->Disconnect(); }
+    ~Connection() {
+      if (connect_result_ == 0) device_->Disconnect();
+    }
     // Check whether the connection succeeded or not.
     inline int status() const { return connect_result_; }
 
@@ -53,26 +55,26 @@
   };
 
   // Turn the stream on or off.
-  int StreamOn();
-  int StreamOff();
+  virtual int StreamOn();
+  virtual int StreamOff();
   // Manage controls.
-  int QueryControl(uint32_t control_id, v4l2_query_ext_ctrl* result);
-  int GetControl(uint32_t control_id, int32_t* value);
-  int SetControl(uint32_t control_id, int32_t desired,
-                 int32_t* result = nullptr);
+  virtual int QueryControl(uint32_t control_id, v4l2_query_ext_ctrl* result);
+  virtual int GetControl(uint32_t control_id, int32_t* value);
+  virtual int SetControl(uint32_t control_id, int32_t desired,
+                         int32_t* result = nullptr);
   // Manage format.
-  int GetFormats(std::set<uint32_t>* v4l2_formats);
-  int GetFormatFrameSizes(uint32_t v4l2_format,
-                          std::set<std::array<int32_t, 2>>* sizes);
+  virtual int GetFormats(std::set<uint32_t>* v4l2_formats);
+  virtual int GetFormatFrameSizes(uint32_t v4l2_format,
+                                  std::set<std::array<int32_t, 2>>* sizes);
   // Durations are returned in ns.
-  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);
+  virtual int GetFormatFrameDurationRange(
+      uint32_t v4l2_format, const std::array<int32_t, 2>& size,
+      std::array<int64_t, 2>* duration_range);
+  virtual int SetFormat(const default_camera_hal::Stream& stream,
+                        uint32_t* result_max_buffers);
   // Manage buffers.
-  int EnqueueBuffer(const camera3_stream_buffer_t* camera_buffer);
-  int DequeueBuffer(v4l2_buffer* buffer);
+  virtual int EnqueueBuffer(const camera3_stream_buffer_t* camera_buffer);
+  virtual int DequeueBuffer(v4l2_buffer* buffer);
 
  private:
   // Constructor is private to allow failing on bad input.
diff --git a/modules/camera/3_4/v4l2_wrapper_mock.h b/modules/camera/3_4/v4l2_wrapper_mock.h
index d82ed33..1c3d27b 100644
--- a/modules/camera/3_4/v4l2_wrapper_mock.h
+++ b/modules/camera/3_4/v4l2_wrapper_mock.h
@@ -28,8 +28,6 @@
 class V4L2WrapperMock : public V4L2Wrapper {
  public:
   V4L2WrapperMock() : V4L2Wrapper("", nullptr){};
-  MOCK_METHOD0(Connect, int());
-  MOCK_METHOD0(Disconnect, void());
   MOCK_METHOD0(StreamOn, int());
   MOCK_METHOD0(StreamOff, int());
   MOCK_METHOD2(QueryControl,
@@ -37,6 +35,12 @@
   MOCK_METHOD2(GetControl, int(uint32_t control_id, int32_t* value));
   MOCK_METHOD3(SetControl,
                int(uint32_t control_id, int32_t desired, int32_t* result));
+  MOCK_METHOD1(GetFormats, int(std::set<uint32_t>*));
+  MOCK_METHOD2(GetFormatFrameSizes,
+               int(uint32_t, std::set<std::array<int32_t, 2>>*));
+  MOCK_METHOD3(GetFormatFrameDurationRange,
+               int(uint32_t, const std::array<int32_t, 2>&,
+                   std::array<int64_t, 2>*));
   MOCK_METHOD2(SetFormat, int(const default_camera_hal::Stream& stream,
                               uint32_t* result_max_buffers));
   MOCK_METHOD1(EnqueueBuffer,