Adding VmsUtils for parsing Vms Vehicle Properties

Test: Tested unit test app on mojave.
Bug: 68704508
Change-Id: I4833e4940710752a2f7943323c5e7ff2489f1c07
diff --git a/automotive/vehicle/2.0/default/Android.bp b/automotive/vehicle/2.0/default/Android.bp
index 1690163..6a254a5 100644
--- a/automotive/vehicle/2.0/default/Android.bp
+++ b/automotive/vehicle/2.0/default/Android.bp
@@ -46,6 +46,7 @@
         "common/src/VehicleObjectPool.cpp",
         "common/src/VehiclePropertyStore.cpp",
         "common/src/VehicleUtils.cpp",
+        "common/src/VmsUtils.cpp",
     ],
     local_include_dirs: ["common/include/vhal_v2_0"],
     export_include_dirs: ["common/include"],
@@ -93,6 +94,7 @@
         "tests/VehicleHalManager_test.cpp",
         "tests/VehicleObjectPool_test.cpp",
         "tests/VehiclePropConfigIndex_test.cpp",
+        "tests/VmsUtils_test.cpp",
     ],
     header_libs: ["libbase_headers"],
 }
diff --git a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VmsUtils.h b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VmsUtils.h
new file mode 100644
index 0000000..9e32bb5
--- /dev/null
+++ b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VmsUtils.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2018 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 android_hardware_automotive_vehicle_V2_0_VmsUtils_H_
+#define android_hardware_automotive_vehicle_V2_0_VmsUtils_H_
+
+#include <memory>
+#include <string>
+
+#include <android/hardware/automotive/vehicle/2.0/types.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+namespace vms {
+
+// VmsUtils are a set of abstractions for creating and parsing Vehicle Property
+// updates to VehicleProperty::VEHICLE_MAP_SERVICE. The format for parsing a
+// VehiclePropValue update with a VMS message is specified in the Vehicle HIDL.
+//
+// This interface is meant for use by HAL clients of VMS; corresponding
+// functionality is also provided by VMS in the embedded car service.
+
+// A VmsLayer is comprised of a type, subtype, and version.
+struct VmsLayer {
+    VmsLayer(int type, int subtype, int version) : type(type), subtype(subtype), version(version) {}
+    int type;
+    int subtype;
+    int version;
+};
+
+struct VmsLayerAndPublisher {
+    VmsLayer layer;
+    int publisher_id;
+};
+
+// A VmsAssociatedLayer is used by subscribers to specify which publisher IDs
+// are acceptable for a given layer.
+struct VmsAssociatedLayer {
+    VmsLayer layer;
+    std::vector<int> publisher_ids;
+};
+
+// A VmsLayerOffering refers to a single layer that can be published, along with
+// its dependencies. Dependencies can be empty.
+struct VmsLayerOffering {
+    VmsLayerOffering(VmsLayer layer, std::vector<VmsLayer> dependencies)
+        : layer(layer), dependencies(dependencies) {}
+    VmsLayerOffering(VmsLayer layer) : layer(layer), dependencies() {}
+    VmsLayer layer;
+    std::vector<VmsLayer> dependencies;
+};
+
+// A VmsSubscriptionsState is delivered in response to a
+// VmsMessageType.SUBSCRIPTIONS_REQUEST or on the first SUBSCRIBE or last
+// UNSUBSCRIBE for a layer. It indicates which layers or associated_layers are
+// currently being subscribed to in the system.
+struct VmsSubscriptionsState {
+    int sequence_number;
+    std::vector<VmsLayer> layers;
+    std::vector<VmsAssociatedLayer> associated_layers;
+};
+
+struct VmsAvailabilityState {
+    int sequence_number;
+    std::vector<VmsAssociatedLayer> associated_layers;
+};
+
+// Creates a VehiclePropValue containing a message of type
+// VmsMessageType.SUBSCRIBE, specifying to the VMS service
+// which layer to subscribe to.
+std::unique_ptr<VehiclePropValue> createSubscribeMessage(const VmsLayer& layer);
+
+// Creates a VehiclePropValue containing a message of type
+// VmsMessageType.SUBSCRIBE_TO_PUBLISHER, specifying to the VMS service
+// which layer and publisher_id to subscribe to.
+std::unique_ptr<VehiclePropValue> createSubscribeToPublisherMessage(
+    const VmsLayerAndPublisher& layer);
+
+// Creates a VehiclePropValue containing a message of type
+// VmsMessageType.UNSUBSCRIBE, specifying to the VMS service
+// which layer to unsubscribe from.
+std::unique_ptr<VehiclePropValue> createUnsubscribeMessage(const VmsLayer& layer);
+
+// Creates a VehiclePropValue containing a message of type
+// VmsMessageType.UNSUBSCRIBE_TO_PUBLISHER, specifying to the VMS service
+// which layer and publisher_id to unsubscribe from.
+std::unique_ptr<VehiclePropValue> createUnsubscribeToPublisherMessage(
+    const VmsLayerAndPublisher& layer);
+
+// Creates a VehiclePropValue containing a message of type
+// VmsMessageType.OFFERING, specifying to the VMS service which layers are being
+// offered and their dependencies, if any.
+std::unique_ptr<VehiclePropValue> createOfferingMessage(
+    const std::vector<VmsLayerOffering>& offering);
+
+// Creates a VehiclePropValue containing a message of type
+// VmsMessageType.AVAILABILITY_REQUEST.
+std::unique_ptr<VehiclePropValue> createAvailabilityRequest();
+
+// Creates a VehiclePropValue containing a message of type
+// VmsMessageType.AVAILABILITY_REQUEST.
+std::unique_ptr<VehiclePropValue> createSubscriptionsRequest();
+
+// Creates a VehiclePropValue containing a message of type VmsMessageType.DATA.
+// Returns a nullptr if the byte string in bytes is empty.
+//
+// For example, to build a VehiclePropMessage containing a proto, the caller
+// should convert the proto to a byte string using the SerializeToString proto
+// API, then use this inteface to build the VehicleProperty.
+std::unique_ptr<VehiclePropValue> createDataMessage(const std::string& bytes);
+
+// Returns true if the VehiclePropValue pointed to by value contains a valid Vms
+// message, i.e. the VehicleProperty, VehicleArea, and VmsMessageType are all
+// valid. Note: If the VmsMessageType enum is extended, this function will
+// return false for any new message types added.
+bool isValidVmsMessage(const VehiclePropValue& value);
+
+// Returns the message type. Expects that the VehiclePropValue contains a valid
+// Vms message, as verified by isValidVmsMessage.
+VmsMessageType parseMessageType(const VehiclePropValue& value);
+
+// Constructs a string byte array from a message of type VmsMessageType.DATA.
+// Returns an empty string if the message type doesn't match or if the
+// VehiclePropValue does not contain a byte array.
+//
+// A proto message can then be constructed by passing the result of this
+// function to ParseFromString.
+std::string parseData(const VehiclePropValue& value);
+
+// TODO(aditin): Need to implement additional parsing functions per message
+// type.
+
+}  // namespace vms
+}  // namespace V2_0
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
+
+#endif  // android_hardware_automotive_vehicle_V2_0_VmsUtils_H_
diff --git a/automotive/vehicle/2.0/default/common/src/VmsUtils.cpp b/automotive/vehicle/2.0/default/common/src/VmsUtils.cpp
new file mode 100644
index 0000000..abf425f
--- /dev/null
+++ b/automotive/vehicle/2.0/default/common/src/VmsUtils.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2018 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 "VmsUtils.h"
+
+#include <common/include/vhal_v2_0/VehicleUtils.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+namespace vms {
+
+static constexpr int kMessageIndex = toInt(VmsBaseMessageIntegerValuesIndex::MESSAGE_TYPE);
+static constexpr int kMessageTypeSize = 1;
+static constexpr int kLayerNumberSize = 1;
+static constexpr int kLayerSize = 3;
+static constexpr int kLayerAndPublisherSize = 4;
+
+// TODO(aditin): We should extend the VmsMessageType enum to include a first and
+// last, which would prevent breakages in this API. However, for all of the
+// functions in this module, we only need to guarantee that the message type is
+// between SUBSCRIBE and DATA.
+static constexpr int kFirstMessageType = toInt(VmsMessageType::SUBSCRIBE);
+static constexpr int kLastMessageType = toInt(VmsMessageType::DATA);
+
+std::unique_ptr<VehiclePropValue> createBaseVmsMessage(size_t message_size) {
+    auto result = createVehiclePropValue(VehiclePropertyType::INT32, message_size);
+    result->prop = toInt(VehicleProperty::VEHICLE_MAP_SERVICE);
+    result->areaId = toInt(VehicleArea::GLOBAL);
+    return result;
+}
+
+std::unique_ptr<VehiclePropValue> createSubscribeMessage(const VmsLayer& layer) {
+    auto result = createBaseVmsMessage(kMessageTypeSize + kLayerSize);
+    result->value.int32Values = hidl_vec<int32_t>{toInt(VmsMessageType::SUBSCRIBE), layer.type,
+                                                  layer.subtype, layer.version};
+    return result;
+}
+
+std::unique_ptr<VehiclePropValue> createSubscribeToPublisherMessage(
+    const VmsLayerAndPublisher& layer_publisher) {
+    auto result = createBaseVmsMessage(kMessageTypeSize + kLayerAndPublisherSize);
+    result->value.int32Values = hidl_vec<int32_t>{
+        toInt(VmsMessageType::SUBSCRIBE_TO_PUBLISHER), layer_publisher.layer.type,
+        layer_publisher.layer.subtype, layer_publisher.layer.version, layer_publisher.publisher_id};
+    return result;
+}
+
+std::unique_ptr<VehiclePropValue> createUnsubscribeMessage(const VmsLayer& layer) {
+    auto result = createBaseVmsMessage(kMessageTypeSize + kLayerSize);
+    result->value.int32Values = hidl_vec<int32_t>{toInt(VmsMessageType::UNSUBSCRIBE), layer.type,
+                                                  layer.subtype, layer.version};
+    return result;
+}
+
+std::unique_ptr<VehiclePropValue> createUnsubscribeToPublisherMessage(
+    const VmsLayerAndPublisher& layer_publisher) {
+    auto result = createBaseVmsMessage(kMessageTypeSize + kLayerAndPublisherSize);
+    result->value.int32Values = hidl_vec<int32_t>{
+        toInt(VmsMessageType::UNSUBSCRIBE_TO_PUBLISHER), layer_publisher.layer.type,
+        layer_publisher.layer.subtype, layer_publisher.layer.version, layer_publisher.publisher_id};
+    return result;
+}
+
+std::unique_ptr<VehiclePropValue> createOfferingMessage(
+    const std::vector<VmsLayerOffering>& offering) {
+    int message_size = kMessageTypeSize + kLayerNumberSize;
+    for (const auto& offer : offering) {
+        message_size += kLayerNumberSize + (1 + offer.dependencies.size()) * kLayerSize;
+    }
+    auto result = createBaseVmsMessage(message_size);
+
+    std::vector<int32_t> offers = {toInt(VmsMessageType::OFFERING),
+                                   static_cast<int>(offering.size())};
+    for (const auto& offer : offering) {
+        std::vector<int32_t> layer_vector = {offer.layer.type, offer.layer.subtype,
+                                             offer.layer.version,
+                                             static_cast<int32_t>(offer.dependencies.size())};
+        for (const auto& dependency : offer.dependencies) {
+            std::vector<int32_t> dependency_layer = {dependency.type, dependency.subtype,
+                                                     dependency.version};
+            layer_vector.insert(layer_vector.end(), dependency_layer.begin(),
+                                dependency_layer.end());
+        }
+        offers.insert(offers.end(), layer_vector.begin(), layer_vector.end());
+    }
+    result->value.int32Values = offers;
+    return result;
+}
+
+std::unique_ptr<VehiclePropValue> createAvailabilityRequest() {
+    auto result = createBaseVmsMessage(kMessageTypeSize);
+    result->value.int32Values = hidl_vec<int32_t>{
+        toInt(VmsMessageType::AVAILABILITY_REQUEST),
+    };
+    return result;
+}
+
+std::unique_ptr<VehiclePropValue> createSubscriptionsRequest() {
+    auto result = createBaseVmsMessage(kMessageTypeSize);
+    result->value.int32Values = hidl_vec<int32_t>{
+        toInt(VmsMessageType::SUBSCRIPTIONS_REQUEST),
+    };
+    return result;
+}
+
+std::unique_ptr<VehiclePropValue> createDataMessage(const std::string& bytes) {
+    auto result = createBaseVmsMessage(kMessageTypeSize);
+    result->value.int32Values = hidl_vec<int32_t>{toInt(VmsMessageType::DATA)};
+    result->value.bytes = std::vector<uint8_t>(bytes.begin(), bytes.end());
+    return result;
+}
+
+bool verifyPropertyAndArea(const VehiclePropValue& value) {
+    return (value.prop == toInt(VehicleProperty::VEHICLE_MAP_SERVICE) &&
+            value.areaId == toInt(VehicleArea::GLOBAL));
+}
+
+bool verifyMessageType(const VehiclePropValue& value) {
+    return (value.value.int32Values.size() > 0 &&
+            value.value.int32Values[kMessageIndex] >= kFirstMessageType &&
+            value.value.int32Values[kMessageIndex] <= kLastMessageType);
+}
+
+bool isValidVmsMessage(const VehiclePropValue& value) {
+    return (verifyPropertyAndArea(value) && verifyMessageType(value));
+}
+
+VmsMessageType parseMessageType(const VehiclePropValue& value) {
+    return static_cast<VmsMessageType>(value.value.int32Values[kMessageIndex]);
+}
+
+std::string parseData(const VehiclePropValue& value) {
+    if (isValidVmsMessage(value) && parseMessageType(value) == VmsMessageType::DATA &&
+        value.value.bytes.size() > 0) {
+        return std::string(value.value.bytes.begin(), value.value.bytes.end());
+    } else {
+        return std::string();
+    }
+}
+
+}  // namespace vms
+}  // namespace V2_0
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/automotive/vehicle/2.0/default/tests/VmsUtils_test.cpp b/automotive/vehicle/2.0/default/tests/VmsUtils_test.cpp
new file mode 100644
index 0000000..c102ce8
--- /dev/null
+++ b/automotive/vehicle/2.0/default/tests/VmsUtils_test.cpp
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2018 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 <android/hardware/automotive/vehicle/2.0/IVehicle.h>
+#include <gtest/gtest.h>
+
+#include "VehicleHalTestUtils.h"
+#include "vhal_v2_0/VmsUtils.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+namespace vms {
+
+namespace {
+
+TEST(VmsUtilsTest, subscribeMessage) {
+    VmsLayer layer(1, 0, 2);
+    auto message = createSubscribeMessage(layer);
+    ASSERT_NE(message, nullptr);
+    EXPECT_TRUE(isValidVmsMessage(*message));
+    EXPECT_EQ(message->prop, toInt(VehicleProperty::VEHICLE_MAP_SERVICE));
+    EXPECT_EQ(message->areaId, toInt(VehicleArea::GLOBAL));
+    EXPECT_EQ(message->value.int32Values.size(), 0x4ul);
+    EXPECT_EQ(parseMessageType(*message), VmsMessageType::SUBSCRIBE);
+
+    // Layer
+    EXPECT_EQ(message->value.int32Values[1], 1);
+    EXPECT_EQ(message->value.int32Values[2], 0);
+    EXPECT_EQ(message->value.int32Values[3], 2);
+}
+
+TEST(VmsUtilsTest, unsubscribeMessage) {
+    VmsLayer layer(1, 0, 2);
+    auto message = createUnsubscribeMessage(layer);
+    ASSERT_NE(message, nullptr);
+    EXPECT_TRUE(isValidVmsMessage(*message));
+    EXPECT_EQ(message->prop, toInt(VehicleProperty::VEHICLE_MAP_SERVICE));
+    EXPECT_EQ(message->areaId, toInt(VehicleArea::GLOBAL));
+    EXPECT_EQ(message->value.int32Values.size(), 0x4ul);
+    EXPECT_EQ(parseMessageType(*message), VmsMessageType::UNSUBSCRIBE);
+
+    // Layer
+    EXPECT_EQ(message->value.int32Values[1], 1);
+    EXPECT_EQ(message->value.int32Values[2], 0);
+    EXPECT_EQ(message->value.int32Values[3], 2);
+}
+
+TEST(VmsUtilsTest, singleOfferingMessage) {
+    std::vector<VmsLayerOffering> offering = {VmsLayerOffering(VmsLayer(1, 0, 2))};
+    auto message = createOfferingMessage(offering);
+    ASSERT_NE(message, nullptr);
+    EXPECT_TRUE(isValidVmsMessage(*message));
+    EXPECT_EQ(message->prop, toInt(VehicleProperty::VEHICLE_MAP_SERVICE));
+    EXPECT_EQ(message->areaId, toInt(VehicleArea::GLOBAL));
+    EXPECT_EQ(message->value.int32Values.size(), 0x6ul);
+    EXPECT_EQ(parseMessageType(*message), VmsMessageType::OFFERING);
+
+    // Number of layer offerings
+    EXPECT_EQ(message->value.int32Values[1], 1);
+
+    // Layer
+    EXPECT_EQ(message->value.int32Values[2], 1);
+    EXPECT_EQ(message->value.int32Values[3], 0);
+    EXPECT_EQ(message->value.int32Values[4], 2);
+
+    // Number of dependencies
+    EXPECT_EQ(message->value.int32Values[5], 0);
+}
+
+TEST(VmsUtilsTest, offeringWithDependencies) {
+    VmsLayer layer(1, 0, 2);
+    std::vector<VmsLayer> dependencies = {VmsLayer(2, 0, 2)};
+    std::vector<VmsLayerOffering> offering = {VmsLayerOffering(layer, dependencies)};
+    auto message = createOfferingMessage(offering);
+    ASSERT_NE(message, nullptr);
+    EXPECT_TRUE(isValidVmsMessage(*message));
+    EXPECT_EQ(message->prop, toInt(VehicleProperty::VEHICLE_MAP_SERVICE));
+    EXPECT_EQ(message->areaId, toInt(VehicleArea::GLOBAL));
+    EXPECT_EQ(message->value.int32Values.size(), 0x9ul);
+    EXPECT_EQ(parseMessageType(*message), VmsMessageType::OFFERING);
+
+    // Number of layer offerings
+    EXPECT_EQ(message->value.int32Values[1], 1);
+
+    // Layer
+    EXPECT_EQ(message->value.int32Values[2], 1);
+    EXPECT_EQ(message->value.int32Values[3], 0);
+    EXPECT_EQ(message->value.int32Values[4], 2);
+
+    // Number of dependencies
+    EXPECT_EQ(message->value.int32Values[5], 1);
+
+    // Dependency 1
+    EXPECT_EQ(message->value.int32Values[6], 2);
+    EXPECT_EQ(message->value.int32Values[7], 0);
+    EXPECT_EQ(message->value.int32Values[8], 2);
+}
+
+TEST(VmsUtilsTest, availabilityMessage) {
+    auto message = createAvailabilityRequest();
+    ASSERT_NE(message, nullptr);
+    EXPECT_TRUE(isValidVmsMessage(*message));
+    EXPECT_EQ(message->prop, toInt(VehicleProperty::VEHICLE_MAP_SERVICE));
+    EXPECT_EQ(message->areaId, toInt(VehicleArea::GLOBAL));
+    EXPECT_EQ(message->value.int32Values.size(), 0x1ul);
+    EXPECT_EQ(parseMessageType(*message), VmsMessageType::AVAILABILITY_REQUEST);
+}
+
+TEST(VmsUtilsTest, subscriptionsMessage) {
+    auto message = createSubscriptionsRequest();
+    ASSERT_NE(message, nullptr);
+    EXPECT_TRUE(isValidVmsMessage(*message));
+    EXPECT_EQ(message->prop, toInt(VehicleProperty::VEHICLE_MAP_SERVICE));
+    EXPECT_EQ(message->areaId, toInt(VehicleArea::GLOBAL));
+    EXPECT_EQ(message->value.int32Values.size(), 0x1ul);
+    EXPECT_EQ(parseMessageType(*message), VmsMessageType::SUBSCRIPTIONS_REQUEST);
+}
+
+TEST(VmsUtilsTest, dataMessage) {
+    std::string bytes = "aaa";
+    auto message = createDataMessage(bytes);
+    ASSERT_NE(message, nullptr);
+    EXPECT_TRUE(isValidVmsMessage(*message));
+    EXPECT_EQ(message->prop, toInt(VehicleProperty::VEHICLE_MAP_SERVICE));
+    EXPECT_EQ(message->areaId, toInt(VehicleArea::GLOBAL));
+    EXPECT_EQ(message->value.int32Values.size(), 0x1ul);
+    EXPECT_EQ(parseMessageType(*message), VmsMessageType::DATA);
+    EXPECT_EQ(message->value.bytes.size(), bytes.size());
+    EXPECT_EQ(memcmp(message->value.bytes.data(), bytes.data(), bytes.size()), 0);
+}
+
+TEST(VmsUtilsTest, emptyMessageInvalid) {
+    VehiclePropValue empty_prop;
+    EXPECT_FALSE(isValidVmsMessage(empty_prop));
+}
+
+TEST(VmsUtilsTest, invalidMessageType) {
+    VmsLayer layer(1, 0, 2);
+    auto message = createSubscribeMessage(layer);
+    message->value.int32Values[0] = 0;
+
+    EXPECT_FALSE(isValidVmsMessage(*message));
+}
+
+TEST(VmsUtilsTest, parseDataMessage) {
+    std::string bytes = "aaa";
+    auto message = createDataMessage(bytes);
+    auto data_str = parseData(*message);
+    ASSERT_FALSE(data_str.empty());
+    EXPECT_EQ(data_str, bytes);
+}
+
+TEST(VmsUtilsTest, parseInvalidDataMessage) {
+    VmsLayer layer(1, 0, 2);
+    auto message = createSubscribeMessage(layer);
+    auto data_str = parseData(*message);
+    EXPECT_TRUE(data_str.empty());
+}
+
+}  // namespace
+
+}  // namespace vms
+}  // namespace V2_0
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android