Add abstract control class.
Partial implementation of tagged_partial_metadata. A control
is a value that can be changed.
Implements supported/get/set of parent class, abstracting
into simpler supported/get/set methods for children to implement.
BUG: 30140438
Change-Id: Iaba6fc2f54f6a8786c5ca379972ee1da9604c7a3
TEST: unit tests pass
diff --git a/modules/camera/3_4/Android.mk b/modules/camera/3_4/Android.mk
index 2f343ac..63e7c1a 100644
--- a/modules/camera/3_4/Android.mk
+++ b/modules/camera/3_4/Android.mk
@@ -49,6 +49,7 @@
v4l2_wrapper.cpp \
v4l2_test_files := \
+ metadata/control_test.cpp \
metadata/fixed_property_test.cpp \
metadata/metadata_test.cpp \
diff --git a/modules/camera/3_4/metadata/control.h b/modules/camera/3_4/metadata/control.h
new file mode 100644
index 0000000..db19b1d
--- /dev/null
+++ b/modules/camera/3_4/metadata/control.h
@@ -0,0 +1,127 @@
+/*
+ * 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_H_
+#define V4L2_CAMERA_HAL_METADATA_CONTROL_H_
+
+#include <vector>
+
+#include <system/camera_metadata.h>
+
+#include "../common.h"
+#include "tagged_partial_metadata.h"
+
+namespace v4l2_camera_hal {
+
+// A Control is a PartialMetadata with values that can be gotten/set.
+template <typename T>
+class Control : public TaggedPartialMetadata {
+ public:
+ Control(int32_t control_tag, std::vector<int32_t> static_tags = {});
+
+ // Child classes still need to implement PopulateStaticFields.
+ virtual int PopulateDynamicFields(
+ android::CameraMetadata* metadata) const override;
+ virtual bool SupportsRequestValues(
+ const android::CameraMetadata& metadata) const override;
+ virtual int SetRequestValues(
+ const android::CameraMetadata& metadata) override;
+
+ protected:
+ // Simpler access to tag.
+ inline int32_t ControlTag() const { return ControlTags()[0]; }
+
+ // Get/Set the control value. Return non-0 on failure.
+ virtual int GetValue(T* value) const = 0;
+ virtual int SetValue(const T& value) = 0;
+ // Helper to check if val is supported by this control.
+ virtual bool IsSupported(const T& val) const = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(Control);
+};
+
+// -----------------------------------------------------------------------------
+
+template <typename T>
+Control<T>::Control(int32_t control_tag, std::vector<int32_t> static_tags)
+ // Controls use the same tag for setting the control
+ // and getting the dynamic value.
+ : TaggedPartialMetadata(static_tags, {control_tag}, {control_tag}) {
+ HAL_LOG_ENTER();
+}
+
+template <typename T>
+int Control<T>::PopulateDynamicFields(android::CameraMetadata* metadata) const {
+ HAL_LOG_ENTER();
+
+ // Populate the current setting.
+ T value;
+ int res = GetValue(&value);
+ if (res) {
+ return res;
+ }
+ return UpdateMetadata(metadata, ControlTag(), value);
+}
+
+template <typename T>
+bool Control<T>::SupportsRequestValues(
+ const android::CameraMetadata& metadata) const {
+ HAL_LOG_ENTER();
+ if (metadata.isEmpty()) {
+ // Implicitly supported.
+ return true;
+ }
+
+ // Check that the requested setting is in the supported options.
+ T requested;
+ int res = SingleTagValue(metadata, ControlTag(), &requested);
+ if (res == -ENOENT) {
+ // Nothing requested of this control, that's fine.
+ return true;
+ } else if (res) {
+ HAL_LOGE("Failure while searching for request value for tag %d",
+ ControlTag());
+ return false;
+ }
+ return IsSupported(requested);
+}
+
+template <typename T>
+int Control<T>::SetRequestValues(const android::CameraMetadata& metadata) {
+ HAL_LOG_ENTER();
+ if (metadata.isEmpty()) {
+ // No changes necessary.
+ return 0;
+ }
+
+ // Get the requested value.
+ T requested;
+ int res = SingleTagValue(metadata, ControlTag(), &requested);
+ if (res == -ENOENT) {
+ // Nothing requested of this control, nothing to do.
+ return 0;
+ } else if (res) {
+ HAL_LOGE("Failure while searching for request value for tag %d",
+ ControlTag());
+ return res;
+ }
+
+ return SetValue(requested);
+}
+
+} // namespace v4l2_camera_hal
+
+#endif // V4L2_CAMERA_HAL_METADATA_CONTROL_H_
diff --git a/modules/camera/3_4/metadata/control_test.cpp b/modules/camera/3_4/metadata/control_test.cpp
new file mode 100644
index 0000000..6e45d02
--- /dev/null
+++ b/modules/camera/3_4/metadata/control_test.cpp
@@ -0,0 +1,239 @@
+/*
+ * 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 "control.h"
+
+#include <array>
+
+#include <camera/CameraMetadata.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using testing::AtMost;
+using testing::Return;
+using testing::ReturnRef;
+using testing::SetArgPointee;
+using testing::Test;
+using testing::_;
+
+namespace v4l2_camera_hal {
+
+class ControlTest : public Test {
+ protected:
+ // A subclass of Control with the pure virtual methods mocked out.
+ template <typename T>
+ class MockControl : public Control<T> {
+ public:
+ MockControl(int32_t control_tag) : Control<T>(control_tag){};
+ MOCK_CONST_METHOD1_T(PopulateStaticFields,
+ int(android::CameraMetadata* metadata));
+ MOCK_CONST_METHOD1_T(GetValue, int(T* value));
+ MOCK_METHOD1_T(SetValue, int(const T& value));
+ MOCK_CONST_METHOD1_T(IsSupported, bool(const T& value));
+ };
+
+ virtual void SetUp() {
+ control_.reset(new MockControl<uint8_t>(control_tag_));
+ }
+ // Check that metadata of a given tag matches expectations.
+ virtual void ExpectMetadataEq(const android::CameraMetadata& metadata,
+ int32_t tag, const uint8_t* expected,
+ size_t size) {
+ camera_metadata_ro_entry_t entry = metadata.find(tag);
+ ASSERT_EQ(entry.count, size);
+ for (size_t i = 0; i < size; ++i) {
+ EXPECT_EQ(entry.data.u8[i], expected[i]);
+ }
+ }
+ virtual void ExpectMetadataEq(const android::CameraMetadata& metadata,
+ int32_t tag, const int32_t* expected,
+ size_t size) {
+ camera_metadata_ro_entry_t entry = metadata.find(tag);
+ ASSERT_EQ(entry.count, size);
+ for (size_t i = 0; i < size; ++i) {
+ EXPECT_EQ(entry.data.i32[i], expected[i]);
+ }
+ }
+ // Single item.
+ template <typename T>
+ void ExpectMetadataEq(const android::CameraMetadata& metadata, int32_t tag,
+ T expected) {
+ ExpectMetadataEq(metadata, tag, &expected, 1);
+ }
+ // Vector of items.
+ template <typename T>
+ void ExpectMetadataEq(const android::CameraMetadata& metadata, int32_t tag,
+ const std::vector<T>& expected) {
+ ExpectMetadataEq(metadata, tag, expected.data(), expected.size());
+ }
+
+ std::unique_ptr<MockControl<uint8_t>> control_;
+
+ // Need tags that match the data type (uint8_t) being passed.
+ static constexpr int32_t control_tag_ =
+ ANDROID_COLOR_CORRECTION_ABERRATION_MODE;
+};
+
+TEST_F(ControlTest, Tags) {
+ // Should have no static tags by default.
+ EXPECT_EQ(control_->StaticTags().size(), 0);
+ // Controls use the same tag for getting and setting.
+ // The macro doesn't like the static variables
+ // being passed directly to assertions.
+ int32_t expected_tag = control_tag_;
+ ASSERT_EQ(control_->ControlTags().size(), 1);
+ EXPECT_EQ(control_->ControlTags()[0], expected_tag);
+ ASSERT_EQ(control_->DynamicTags().size(), 1);
+ EXPECT_EQ(control_->DynamicTags()[0], expected_tag);
+}
+
+TEST_F(ControlTest, PopulateDynamic) {
+ uint8_t test_option = 99;
+ EXPECT_CALL(*control_, GetValue(_))
+ .WillOnce(DoAll(SetArgPointee<0>(test_option), Return(0)));
+
+ 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, control_tag_, test_option);
+}
+
+TEST_F(ControlTest, PopulateDynamicFail) {
+ int err = -99;
+ EXPECT_CALL(*control_, GetValue(_)).WillOnce(Return(err));
+
+ android::CameraMetadata metadata;
+ EXPECT_EQ(control_->PopulateDynamicFields(&metadata), err);
+
+ // Should not have added an entry.
+ EXPECT_TRUE(metadata.isEmpty());
+}
+
+TEST_F(ControlTest, SupportsRequest) {
+ android::CameraMetadata metadata;
+ uint8_t test_option = 123;
+ ASSERT_EQ(metadata.update(control_tag_, &test_option, 1), android::OK);
+
+ EXPECT_CALL(*control_, IsSupported(test_option)).WillOnce(Return(true));
+ EXPECT_EQ(control_->SupportsRequestValues(metadata), true);
+}
+
+TEST_F(ControlTest, ArraySupportsRequest) {
+ android::CameraMetadata metadata;
+ std::array<uint8_t, 2> test_option = {{12, 34}};
+ ASSERT_EQ(
+ metadata.update(control_tag_, test_option.data(), test_option.size()),
+ android::OK);
+
+ MockControl<std::array<uint8_t, 2>> test_control(control_tag_);
+ EXPECT_CALL(test_control, IsSupported(test_option)).WillOnce(Return(true));
+ EXPECT_EQ(test_control.SupportsRequestValues(metadata), true);
+}
+
+TEST_F(ControlTest, SupportsRequestFail) {
+ android::CameraMetadata metadata;
+ uint8_t test_option = 123;
+ ASSERT_EQ(metadata.update(control_tag_, &test_option, 1), android::OK);
+
+ EXPECT_CALL(*control_, IsSupported(test_option)).WillOnce(Return(false));
+ EXPECT_EQ(control_->SupportsRequestValues(metadata), false);
+}
+
+TEST_F(ControlTest, SupportsRequestInvalidNumber) {
+ // Start with a request for multiple values.
+ android::CameraMetadata metadata;
+ std::vector<uint8_t> test_data = {1, 2, 3};
+ ASSERT_EQ(metadata.update(control_tag_, test_data.data(), test_data.size()),
+ android::OK);
+ EXPECT_EQ(control_->SupportsRequestValues(metadata), false);
+}
+
+TEST_F(ControlTest, ArraySupportsRequestInvalidNumber) {
+ // Start with a request for a single (non-array) value.
+ android::CameraMetadata metadata;
+ uint8_t test_data = 1;
+ ASSERT_EQ(metadata.update(control_tag_, &test_data, 1), android::OK);
+
+ MockControl<std::array<uint8_t, 2>> test_control(control_tag_);
+ EXPECT_EQ(test_control.SupportsRequestValues(metadata), false);
+}
+
+TEST_F(ControlTest, SupportsRequestEmpty) {
+ android::CameraMetadata metadata;
+ EXPECT_EQ(control_->SupportsRequestValues(metadata), true);
+}
+
+TEST_F(ControlTest, SetRequest) {
+ android::CameraMetadata metadata(1);
+ uint8_t test_option = 123;
+ ASSERT_EQ(metadata.update(control_tag_, &test_option, 1), android::OK);
+
+ EXPECT_CALL(*control_, SetValue(test_option)).WillOnce(Return(0));
+ // Make the request.
+ ASSERT_EQ(control_->SetRequestValues(metadata), 0);
+}
+
+TEST_F(ControlTest, ArraySetRequest) {
+ android::CameraMetadata metadata;
+ std::array<uint8_t, 2> test_option = {{12, 34}};
+ ASSERT_EQ(
+ metadata.update(control_tag_, test_option.data(), test_option.size()),
+ android::OK);
+
+ MockControl<std::array<uint8_t, 2>> test_control(control_tag_);
+ EXPECT_CALL(test_control, SetValue(test_option)).WillOnce(Return(0));
+ EXPECT_EQ(test_control.SetRequestValues(metadata), 0);
+}
+
+TEST_F(ControlTest, SetRequestFail) {
+ android::CameraMetadata metadata(1);
+ uint8_t test_option = 123;
+ ASSERT_EQ(metadata.update(control_tag_, &test_option, 1), android::OK);
+
+ int err = -99;
+ EXPECT_CALL(*control_, SetValue(test_option)).WillOnce(Return(err));
+ EXPECT_EQ(control_->SetRequestValues(metadata), err);
+}
+
+TEST_F(ControlTest, SetRequestInvalidNumber) {
+ // Start with a request for multiple values.
+ android::CameraMetadata metadata;
+ std::vector<uint8_t> test_data = {1, 2, 3};
+ ASSERT_EQ(metadata.update(control_tag_, test_data.data(), test_data.size()),
+ android::OK);
+ EXPECT_EQ(control_->SetRequestValues(metadata), -EINVAL);
+}
+
+TEST_F(ControlTest, ArraySetRequestInvalidNumber) {
+ // Start with a request for a single (non-array) value.
+ android::CameraMetadata metadata;
+ uint8_t test_data = 1;
+ ASSERT_EQ(metadata.update(control_tag_, &test_data, 1), android::OK);
+
+ MockControl<std::array<uint8_t, 2>> test_control(control_tag_);
+ EXPECT_EQ(test_control.SetRequestValues(metadata), -EINVAL);
+}
+
+TEST_F(ControlTest, SetRequestEmpty) {
+ // Should do nothing.
+ android::CameraMetadata metadata;
+ EXPECT_EQ(control_->SetRequestValues(metadata), 0);
+}
+
+} // namespace v4l2_camera_hal
diff --git a/modules/camera/3_4/metadata/partial_metadata_interface.h b/modules/camera/3_4/metadata/partial_metadata_interface.h
index 21d198d..09bfebc 100644
--- a/modules/camera/3_4/metadata/partial_metadata_interface.h
+++ b/modules/camera/3_4/metadata/partial_metadata_interface.h
@@ -104,6 +104,111 @@
const ArrayVector<T, N>& val) {
return UpdateMetadata(metadata, tag, val.data(), val.total_num_elements());
}
+
+ // Specialization for vectors of arrays.
+ template <typename T, size_t N>
+ static int UpdateMetadata(android::CameraMetadata* metadata, int32_t tag,
+ const std::vector<std::array<T, N>>& val) {
+ // Convert to array vector so we know all the elements are contiguous.
+ ArrayVector<T, N> array_vector;
+ for (const auto& array : val) {
+ array_vector.push_back(array);
+ }
+ return UpdateMetadata(metadata, tag, array_vector);
+ }
+
+ // Get the data pointer of a given metadata entry. Enforces that |val| must
+ // be a type supported by camera_metadata.
+
+ static void GetDataPointer(camera_metadata_ro_entry_t& entry,
+ const uint8_t** val) {
+ *val = entry.data.u8;
+ }
+
+ static void GetDataPointer(camera_metadata_ro_entry_t& entry,
+ const int32_t** val) {
+ *val = entry.data.i32;
+ }
+
+ static void GetDataPointer(camera_metadata_ro_entry_t& entry,
+ const float** val) {
+ *val = entry.data.f;
+ }
+
+ static void GetDataPointer(camera_metadata_ro_entry_t& entry,
+ const int64_t** val) {
+ *val = entry.data.i64;
+ }
+
+ static void GetDataPointer(camera_metadata_ro_entry_t& entry,
+ const double** val) {
+ *val = entry.data.d;
+ }
+
+ static void GetDataPointer(camera_metadata_ro_entry_t& entry,
+ const camera_metadata_rational_t** val) {
+ *val = entry.data.r;
+ }
+
+ // Get a tag value that is expected to be a single item.
+ // Returns:
+ // -ENOENT: The tag couldn't be found or was empty.
+ // -EINVAL: The tag contained more than one item.
+ // -ENODEV: The tag claims to be non-empty, but the data pointer is null.
+ // 0: Success. |*val| will contain the value for |tag|.
+
+ // Generic (one of the types supported by TagValue above).
+ template <typename T>
+ static int SingleTagValue(const android::CameraMetadata& metadata,
+ int32_t tag, T* val) {
+ camera_metadata_ro_entry_t entry = metadata.find(tag);
+ if (entry.count == 0) {
+ HAL_LOGE("Metadata tag %d is empty.", tag);
+ return -ENOENT;
+ } else if (entry.count != 1) {
+ HAL_LOGE(
+ "Error: expected metadata tag %d to contain exactly 1 value "
+ "(had %d).",
+ tag, entry.count);
+ return -EINVAL;
+ }
+ const T* data = nullptr;
+ GetDataPointer(entry, &data);
+ if (data == nullptr) {
+ HAL_LOGE("Metadata tag %d is empty.", tag);
+ return -ENODEV;
+ }
+ *val = *data;
+ return 0;
+ }
+
+ // Specialization for std::array (of the types supported by TagValue above).
+ template <typename T, size_t N>
+ static int SingleTagValue(const android::CameraMetadata& metadata,
+ int32_t tag, std::array<T, N>* val) {
+ camera_metadata_ro_entry_t entry = metadata.find(tag);
+ if (entry.count == 0) {
+ HAL_LOGE("Metadata tag %d is empty.", tag);
+ return -ENOENT;
+ } else if (entry.count != N) {
+ HAL_LOGE(
+ "Error: expected metadata tag %d to contain a single array of "
+ "exactly %d values (had %d).",
+ tag, N, entry.count);
+ return -EINVAL;
+ }
+ const T* data = nullptr;
+ GetDataPointer(entry, &data);
+ if (data == nullptr) {
+ HAL_LOGE("Metadata tag %d is empty.", tag);
+ return -ENODEV;
+ }
+ // Fill in the array.
+ for (size_t i = 0; i < N; ++i) {
+ (*val)[i] = data[i];
+ }
+ return 0;
+ }
};
} // namespace v4l2_camera_hal