Move Vehicle HAL under automotive package

Test: verified unit tests and VTS are passing

Bug: b/33200203
Change-Id: I69cc4bc0d4cbc1a6bf2f77564a8f70412fe06947
diff --git a/automotive/vehicle/2.0/default/tests/AccessControlConfigParser_test.cpp b/automotive/vehicle/2.0/default/tests/AccessControlConfigParser_test.cpp
new file mode 100644
index 0000000..7c9a4d9
--- /dev/null
+++ b/automotive/vehicle/2.0/default/tests/AccessControlConfigParser_test.cpp
@@ -0,0 +1,152 @@
+/*
+ * 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 <gtest/gtest.h>
+#include <memory>
+#include <fstream>
+#include <unordered_set>
+
+#include "vehicle_hal_manager/AccessControlConfigParser.h"
+#include <vehicle_hal_manager/VehicleUtils.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace {
+
+class AccessControlConfigParserTest : public ::testing::Test {
+protected:
+    void SetUp() override {
+        std::vector<int32_t> supportedProperties {
+            toInt(VehicleProperty::HVAC_FAN_SPEED),
+            toInt(VehicleProperty::HVAC_FAN_DIRECTION),
+        };
+        parser.reset(new AccessControlConfigParser(supportedProperties));
+    }
+public:
+    PropertyAclMap aclMap;
+    std::unique_ptr<AccessControlConfigParser> parser;
+};
+
+TEST_F(AccessControlConfigParserTest, basicParsing) {
+    std::stringstream file;
+    file << "S:0x0500 1000 RW" << std::endl;
+
+    ASSERT_TRUE(parser->parseFromStream(&file, &aclMap));
+
+    ASSERT_EQ(1, aclMap.size());
+    auto it = aclMap.find(toInt(VehicleProperty::HVAC_FAN_SPEED));
+    ASSERT_NE(aclMap.end(), it);
+    ASSERT_EQ(VehiclePropertyAccess::READ_WRITE, it->second.access);
+    ASSERT_EQ(toInt(VehicleProperty::HVAC_FAN_SPEED), it->second.propId);
+    ASSERT_EQ(1000u, it->second.uid);
+}
+
+TEST_F(AccessControlConfigParserTest, multipleUids) {
+    std::stringstream file;
+    file << "Set AID_AUDIO 1004" << std::endl
+            << "Set AID_SYSTEM 1000" << std::endl
+            << "S:0x0500 AID_SYSTEM RW" << std::endl
+            << "S:0x0500 AID_AUDIO RW" << std::endl
+            << "S:0x0500 0xbeef R" << std::endl;  // Read-only.
+
+    std::unordered_set<unsigned> expectedUids {1000, 1004, 0xbeef};
+
+    ASSERT_TRUE(parser->parseFromStream(&file, &aclMap));
+
+    auto range = aclMap.equal_range(toInt(VehicleProperty::HVAC_FAN_SPEED));
+    for (auto it = range.first; it != range.second; ++it) {
+        auto& acl = it->second;
+
+        ASSERT_EQ(1, expectedUids.count(acl.uid))
+                << " uid: " << std::hex << acl.uid;
+
+        if (acl.uid == 0xbeef) {
+            ASSERT_EQ(VehiclePropertyAccess::READ, acl.access);
+        } else {
+            ASSERT_EQ(VehiclePropertyAccess::READ_WRITE, acl.access);
+        }
+    }
+}
+
+TEST_F(AccessControlConfigParserTest, fileContainsJunk) {
+    std::stringstream file;
+    file << "This string will be ignored with warning in the log" << std::endl
+         << "# However comments are quit legitimate" << std::endl
+         << "S:0x0500 0xbeef R # YAY" << std::endl;
+
+    ASSERT_FALSE(parser->parseFromStream(&file, &aclMap));
+
+    ASSERT_EQ(1, aclMap.size());
+    auto it = aclMap.find(toInt(VehicleProperty::HVAC_FAN_SPEED));
+    ASSERT_NE(aclMap.end(), it);
+    ASSERT_EQ(VehiclePropertyAccess::READ, it->second.access);
+    ASSERT_EQ(toInt(VehicleProperty::HVAC_FAN_SPEED), it->second.propId);
+    ASSERT_EQ(0xbeef, it->second.uid);
+}
+
+TEST_F(AccessControlConfigParserTest, badIntegerFormat) {
+    std::stringstream file;
+    file << "S:0x0500 A12 RW " << std::endl;
+
+    ASSERT_FALSE(parser->parseFromStream(&file, &aclMap));
+    ASSERT_EQ(0, aclMap.size());
+}
+
+TEST_F(AccessControlConfigParserTest, ignoreNotSupportedProperties) {
+    std::stringstream file;
+    file << "S:0x0666 1000 RW " << std::endl;
+
+    ASSERT_FALSE(parser->parseFromStream(&file, &aclMap));
+    ASSERT_EQ(0, aclMap.size());
+}
+
+TEST_F(AccessControlConfigParserTest, multipleCalls) {
+    std::stringstream configFile;
+    configFile << "S:0x0500 1000 RW" << std::endl;
+
+    ASSERT_TRUE(parser->parseFromStream(&configFile, &aclMap));
+    ASSERT_EQ(1, aclMap.size());
+
+    std::stringstream configFile2;
+    configFile2 << "S:0x0501 1004 RW" << std::endl;
+    ASSERT_TRUE(parser->parseFromStream(&configFile2, &aclMap));
+    ASSERT_EQ(2, aclMap.size());
+
+    auto it = aclMap.find(toInt(VehicleProperty::HVAC_FAN_SPEED));
+    ASSERT_NE(aclMap.end(), it);
+    ASSERT_EQ(VehiclePropertyAccess::READ_WRITE, it->second.access);
+    ASSERT_EQ(toInt(VehicleProperty::HVAC_FAN_SPEED), it->second.propId);
+    ASSERT_EQ(1000u, it->second.uid);
+
+    it = aclMap.find(toInt(VehicleProperty::HVAC_FAN_DIRECTION));
+    ASSERT_NE(aclMap.end(), it);
+    ASSERT_EQ(VehiclePropertyAccess::READ_WRITE, it->second.access);
+    ASSERT_EQ(toInt(VehicleProperty::HVAC_FAN_DIRECTION), it->second.propId);
+    ASSERT_EQ(1004u, it->second.uid);
+}
+
+
+}  // namespace anonymous
+
+}  // namespace V2_0
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/automotive/vehicle/2.0/default/tests/Obd2SensorStore_test.cpp b/automotive/vehicle/2.0/default/tests/Obd2SensorStore_test.cpp
new file mode 100644
index 0000000..3ebebbd
--- /dev/null
+++ b/automotive/vehicle/2.0/default/tests/Obd2SensorStore_test.cpp
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2017 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 <gtest/gtest.h>
+
+#include "vehicle_hal_manager/Obd2SensorStore.h"
+#include "vehicle_hal_manager/VehicleUtils.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace {
+
+static constexpr size_t getNumVendorIntegerSensors() {
+    return 5;
+}
+static constexpr size_t getNumVendorFloatSensors() {
+    return 3;
+}
+
+// this struct holds information necessary for a test to be able to validate
+// that the sensor bitmask contains the right data:
+//   - the index of the byte at which the bit for a given sensor lives
+//   - the expected value of that byte given that a certain sensor is present
+class BitmaskIndexingInfo {
+public:
+    size_t mByteIndex;
+    uint8_t mExpectedByteValue;
+
+    // Returns the information required to validate the bitmask for an
+    // integer-valued sensor.
+    static BitmaskIndexingInfo getForIntegerSensor(size_t index) {
+        const size_t indexInBitstream = index;
+        return getForBitstreamIndex(indexInBitstream);
+    }
+
+    // Returns the information required to validate the bitmask for a
+    // float-valued sensor.
+    static BitmaskIndexingInfo getForFloatSensor(size_t index) {
+        const size_t indexInBitstream = toInt(Obd2IntegerSensorIndex::LAST_SYSTEM_INDEX) +
+                                        1 + getNumVendorIntegerSensors() + index;
+        return getForBitstreamIndex(indexInBitstream);
+    }
+
+private:
+    static BitmaskIndexingInfo getForBitstreamIndex(size_t indexInBitstream) {
+        BitmaskIndexingInfo indexingInfo;
+        indexingInfo.mByteIndex = indexInBitstream / 8;
+        indexingInfo.mExpectedByteValue = 1 << (indexInBitstream % 8);
+        return indexingInfo;
+    }
+};
+
+static Obd2SensorStore getSensorStore() {
+    return Obd2SensorStore(getNumVendorIntegerSensors(),
+                           getNumVendorFloatSensors());
+}
+
+// Test that one can set and retrieve a value for the first integer sensor.
+TEST(Obd2SensorStoreTest, setFirstIntegerSensor) {
+    Obd2SensorStore sensorStore(getSensorStore());
+    sensorStore.setIntegerSensor(
+        Obd2IntegerSensorIndex::FUEL_SYSTEM_STATUS,
+        toInt(FuelSystemStatus::CLOSED_LOOP));
+    const auto& integerSensors(sensorStore.getIntegerSensors());
+    const auto& sensorBitmask(sensorStore.getSensorsBitmask());
+    ASSERT_EQ(
+        toInt(FuelSystemStatus::CLOSED_LOOP),
+        integerSensors[toInt(Obd2IntegerSensorIndex::FUEL_SYSTEM_STATUS)]);
+    const BitmaskIndexingInfo indexingInfo(BitmaskIndexingInfo::getForIntegerSensor(
+        toInt(Obd2IntegerSensorIndex::FUEL_SYSTEM_STATUS)));
+    ASSERT_EQ(
+        indexingInfo.mExpectedByteValue,
+        sensorBitmask[indexingInfo.mByteIndex]);
+}
+
+// Test that one can set and retrieve a value for the first float sensor.
+TEST(Obd2SensorStoreTest, setFirstFloatSensor) {
+    Obd2SensorStore sensorStore(getSensorStore());
+    sensorStore.setFloatSensor(
+        Obd2FloatSensorIndex::CALCULATED_ENGINE_LOAD,
+        1.25f);
+    const auto& floatSensors(sensorStore.getFloatSensors());
+    const auto& sensorBitmask(sensorStore.getSensorsBitmask());
+    ASSERT_EQ(
+        1.25f,
+        floatSensors[toInt(Obd2FloatSensorIndex::CALCULATED_ENGINE_LOAD)]);
+    const BitmaskIndexingInfo indexingInfo(BitmaskIndexingInfo::getForFloatSensor(
+        toInt(Obd2FloatSensorIndex::CALCULATED_ENGINE_LOAD)));
+    ASSERT_EQ(
+        indexingInfo.mExpectedByteValue,
+        sensorBitmask[indexingInfo.mByteIndex]);
+}
+
+// Test that one can set and retrieve a value for an integer sensor.
+TEST(Obd2SensorStoreTest, setAnyIntegerSensor) {
+    Obd2SensorStore sensorStore(getSensorStore());
+    sensorStore.setIntegerSensor(
+        Obd2IntegerSensorIndex::ABSOLUTE_BAROMETRIC_PRESSURE,
+        4000);
+    const auto& integerSensors(sensorStore.getIntegerSensors());
+    const auto& sensorBitmask(sensorStore.getSensorsBitmask());
+    ASSERT_EQ(4000,
+        integerSensors[toInt(Obd2IntegerSensorIndex::ABSOLUTE_BAROMETRIC_PRESSURE)]);
+    const BitmaskIndexingInfo indexingInfo(BitmaskIndexingInfo::getForIntegerSensor(
+        toInt(Obd2IntegerSensorIndex::ABSOLUTE_BAROMETRIC_PRESSURE)));
+    ASSERT_EQ(
+        indexingInfo.mExpectedByteValue,
+        sensorBitmask[indexingInfo.mByteIndex]);
+}
+
+// Test that one can set and retrieve a value for a float sensor.
+TEST(Obd2SensorStoreTest, setAnyFloatSensor) {
+    Obd2SensorStore sensorStore(getSensorStore());
+    sensorStore.setFloatSensor(
+        Obd2FloatSensorIndex::OXYGEN_SENSOR3_VOLTAGE,
+        2.5f);
+    const auto& floatSensors(sensorStore.getFloatSensors());
+    const auto& sensorBitmask(sensorStore.getSensorsBitmask());
+    ASSERT_EQ(2.5f,
+        floatSensors[toInt(Obd2FloatSensorIndex::OXYGEN_SENSOR3_VOLTAGE)]);
+    const BitmaskIndexingInfo indexingInfo(BitmaskIndexingInfo::getForFloatSensor(
+        toInt(Obd2FloatSensorIndex::OXYGEN_SENSOR3_VOLTAGE)));
+    ASSERT_EQ(
+        indexingInfo.mExpectedByteValue,
+        sensorBitmask[indexingInfo.mByteIndex]);
+}
+
+// Test that one can set and retrieve a value for the last system integer sensor.
+TEST(Obd2SensorStoreTest, setLastSystemIntegerSensor) {
+    Obd2SensorStore sensorStore(getSensorStore());
+    sensorStore.setIntegerSensor(
+        Obd2IntegerSensorIndex::LAST_SYSTEM_INDEX,
+        30);
+    const auto& integerSensors(sensorStore.getIntegerSensors());
+    const auto& sensorBitmask(sensorStore.getSensorsBitmask());
+    ASSERT_EQ(30,
+        integerSensors[toInt(Obd2IntegerSensorIndex::LAST_SYSTEM_INDEX)]);
+    const BitmaskIndexingInfo indexingInfo(BitmaskIndexingInfo::getForIntegerSensor(
+        toInt(Obd2IntegerSensorIndex::LAST_SYSTEM_INDEX)));
+    ASSERT_EQ(
+        indexingInfo.mExpectedByteValue,
+        sensorBitmask[indexingInfo.mByteIndex]);
+}
+
+// Test that one can set and retrieve a value for the last system float sensor.
+TEST(Obd2SensorStoreTest, setLastSystemFloatSensor) {
+    Obd2SensorStore sensorStore(getSensorStore());
+    sensorStore.setFloatSensor(
+        Obd2FloatSensorIndex::LAST_SYSTEM_INDEX,
+        2.5f);
+    const auto& floatSensors(sensorStore.getFloatSensors());
+    const auto& sensorBitmask(sensorStore.getSensorsBitmask());
+    ASSERT_EQ(2.5f,
+        floatSensors[toInt(Obd2FloatSensorIndex::LAST_SYSTEM_INDEX)]);
+    const BitmaskIndexingInfo indexingInfo(BitmaskIndexingInfo::getForFloatSensor(
+        toInt(Obd2FloatSensorIndex::LAST_SYSTEM_INDEX)));
+    ASSERT_EQ(
+        indexingInfo.mExpectedByteValue,
+        sensorBitmask[indexingInfo.mByteIndex]);
+}
+
+// Test that one can set and retrieve a value for two integer sensors at once.
+TEST(Obd2SensorStoreTest, setTwoIntegerSensors) {
+    Obd2SensorStore sensorStore(getSensorStore());
+    sensorStore.setIntegerSensor(
+        Obd2IntegerSensorIndex::CONTROL_MODULE_VOLTAGE,
+        6);
+    sensorStore.setIntegerSensor(
+        Obd2IntegerSensorIndex::TIME_SINCE_TROUBLE_CODES_CLEARED,
+        1245);
+    const auto& integerSensors(sensorStore.getIntegerSensors());
+    const auto& sensorBitmask(sensorStore.getSensorsBitmask());
+    ASSERT_EQ(6,
+        integerSensors[toInt(Obd2IntegerSensorIndex::CONTROL_MODULE_VOLTAGE)]);
+    ASSERT_EQ(1245,
+        integerSensors[toInt(Obd2IntegerSensorIndex::TIME_SINCE_TROUBLE_CODES_CLEARED)]);
+    const BitmaskIndexingInfo voltageIndexingInfo(BitmaskIndexingInfo::getForIntegerSensor(
+        toInt(Obd2IntegerSensorIndex::CONTROL_MODULE_VOLTAGE)));
+    const BitmaskIndexingInfo timeIndexingInfo(BitmaskIndexingInfo::getForIntegerSensor(
+        toInt(Obd2IntegerSensorIndex::TIME_SINCE_TROUBLE_CODES_CLEARED)));
+    if (voltageIndexingInfo.mByteIndex == timeIndexingInfo.mByteIndex) {
+        ASSERT_EQ(
+            voltageIndexingInfo.mExpectedByteValue |
+            timeIndexingInfo.mExpectedByteValue,
+            sensorBitmask[timeIndexingInfo.mByteIndex]);
+    }
+    else {
+        ASSERT_EQ(
+            timeIndexingInfo.mExpectedByteValue,
+            sensorBitmask[timeIndexingInfo.mByteIndex]);
+        ASSERT_EQ(
+            voltageIndexingInfo.mExpectedByteValue,
+            sensorBitmask[voltageIndexingInfo.mByteIndex]);
+    }
+}
+
+// Test that one can set and retrieve a value for two float sensors at once.
+TEST(Obd2SensorStoreTest, setTwoFloatSensors) {
+    Obd2SensorStore sensorStore(getSensorStore());
+    sensorStore.setFloatSensor(
+        Obd2FloatSensorIndex::VEHICLE_SPEED,
+        1.25f);
+    sensorStore.setFloatSensor(
+        Obd2FloatSensorIndex::MAF_AIR_FLOW_RATE,
+        2.5f);
+    const auto& floatSensors(sensorStore.getFloatSensors());
+    const auto& sensorBitmask(sensorStore.getSensorsBitmask());
+    ASSERT_EQ(1.25f,
+        floatSensors[toInt(Obd2FloatSensorIndex::VEHICLE_SPEED)]);
+    ASSERT_EQ(2.5f,
+        floatSensors[toInt(Obd2FloatSensorIndex::MAF_AIR_FLOW_RATE)]);
+    const BitmaskIndexingInfo speedIndexingInfo(BitmaskIndexingInfo::getForFloatSensor(
+        toInt(Obd2FloatSensorIndex::VEHICLE_SPEED)));
+    const BitmaskIndexingInfo airflowIndexingInfo(BitmaskIndexingInfo::getForFloatSensor(
+        toInt(Obd2FloatSensorIndex::MAF_AIR_FLOW_RATE)));
+    if (speedIndexingInfo.mByteIndex == airflowIndexingInfo.mByteIndex) {
+        ASSERT_EQ(
+            speedIndexingInfo.mExpectedByteValue |
+            airflowIndexingInfo.mExpectedByteValue,
+            sensorBitmask[airflowIndexingInfo.mByteIndex]);
+    }
+    else {
+        ASSERT_EQ(
+            speedIndexingInfo.mExpectedByteValue,
+            sensorBitmask[speedIndexingInfo.mByteIndex]);
+        ASSERT_EQ(
+            airflowIndexingInfo.mExpectedByteValue,
+            sensorBitmask[airflowIndexingInfo.mByteIndex]);
+    }
+}
+
+// Test that one can set and retrieve a value for a vendor integer sensor.
+TEST(Obd2SensorStoreTest, setVendorIntegerSensor) {
+    const size_t sensorIndex = toInt(Obd2IntegerSensorIndex::LAST_SYSTEM_INDEX) + 2;
+    Obd2SensorStore sensorStore(getSensorStore());
+    sensorStore.setIntegerSensor(sensorIndex, 22);
+    const auto& integerSensors(sensorStore.getIntegerSensors());
+    const auto& sensorBitmask(sensorStore.getSensorsBitmask());
+    ASSERT_EQ(22, integerSensors[sensorIndex]);
+    const BitmaskIndexingInfo indexingInfo(BitmaskIndexingInfo::getForIntegerSensor(
+        sensorIndex));
+    ASSERT_EQ(
+        indexingInfo.mExpectedByteValue,
+        sensorBitmask[indexingInfo.mByteIndex]);
+}
+
+// Test that one can set and retrieve a value for a vendor float sensor.
+TEST(Obd2SensorStoreTest, setVendorFloatSensor) {
+    const size_t sensorIndex = toInt(Obd2FloatSensorIndex::LAST_SYSTEM_INDEX) + 2;
+    Obd2SensorStore sensorStore(getSensorStore());
+    sensorStore.setFloatSensor(sensorIndex, 1.25f);
+    const auto& floatSensors(sensorStore.getFloatSensors());
+    const auto& sensorBitmask(sensorStore.getSensorsBitmask());
+    ASSERT_EQ(1.25f, floatSensors[sensorIndex]);
+    const BitmaskIndexingInfo indexingInfo(BitmaskIndexingInfo::getForFloatSensor(
+        sensorIndex));
+    ASSERT_EQ(
+        indexingInfo.mExpectedByteValue,
+        sensorBitmask[indexingInfo.mByteIndex]);
+}
+
+}  // namespace anonymous
+
+}  // namespace V2_0
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/automotive/vehicle/2.0/default/tests/SubscriptionManager_test.cpp b/automotive/vehicle/2.0/default/tests/SubscriptionManager_test.cpp
new file mode 100644
index 0000000..c6c6add
--- /dev/null
+++ b/automotive/vehicle/2.0/default/tests/SubscriptionManager_test.cpp
@@ -0,0 +1,198 @@
+/*
+ * 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 <unordered_map>
+#include <iostream>
+
+#include <gtest/gtest.h>
+
+#include "vehicle_hal_manager/SubscriptionManager.h"
+
+#include "VehicleHalTestUtils.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace {
+
+using namespace std::placeholders;
+
+class SubscriptionManagerTest : public ::testing::Test {
+public:
+    SubscriptionManager manager;
+
+    static constexpr int32_t PROP1 = toInt(VehicleProperty::HVAC_FAN_SPEED);
+    static constexpr int32_t PROP2 = toInt(VehicleProperty::DISPLAY_BRIGHTNESS);
+
+    sp<IVehicleCallback> cb1 = new MockedVehicleCallback();
+    sp<IVehicleCallback> cb2 = new MockedVehicleCallback();
+    sp<IVehicleCallback> cb3 = new MockedVehicleCallback();
+
+    hidl_vec<SubscribeOptions> subscrToProp1 = {
+        SubscribeOptions {
+            .propId = PROP1,
+            .vehicleAreas = toInt(VehicleAreaZone::ROW_1_LEFT),
+            .flags = SubscribeFlags::HAL_EVENT
+        },
+    };
+
+    hidl_vec<SubscribeOptions> subscrToProp2 = {
+        SubscribeOptions {
+            .propId = PROP2,
+            .flags = SubscribeFlags::HAL_EVENT
+        },
+    };
+
+    hidl_vec<SubscribeOptions> subscrToProp1and2 = {
+        SubscribeOptions {
+            .propId = PROP1,
+            .vehicleAreas = toInt(VehicleAreaZone::ROW_1_LEFT),
+            .flags = SubscribeFlags::HAL_EVENT
+        },
+        SubscribeOptions {
+            .propId = PROP2,
+            .flags = SubscribeFlags::HAL_EVENT
+        },
+    };
+
+    static std::list<sp<IVehicleCallback>> extractCallbacks(
+            const std::list<sp<HalClient>>& clients) {
+        std::list<sp<IVehicleCallback>> callbacks;
+        for (auto c : clients) {
+            callbacks.push_back(c->getCallback());
+        }
+        return callbacks;
+    }
+
+    std::list<sp<HalClient>> clientsToProp1() {
+        return manager.getSubscribedClients(PROP1,
+                                            toInt(VehicleAreaZone::ROW_1_LEFT),
+                                            SubscribeFlags::DEFAULT);
+    }
+
+    std::list<sp<HalClient>> clientsToProp2() {
+        return manager.getSubscribedClients(PROP2, 0,
+                                            SubscribeFlags::DEFAULT);
+    }
+};
+
+
+TEST_F(SubscriptionManagerTest, multipleClients) {
+    manager.addOrUpdateSubscription(cb1, subscrToProp1);
+    manager.addOrUpdateSubscription(cb2, subscrToProp1);
+
+    auto clients = manager.getSubscribedClients(
+            PROP1,
+            toInt(VehicleAreaZone::ROW_1_LEFT),
+            SubscribeFlags::HAL_EVENT);
+
+    ASSERT_ALL_EXISTS({cb1, cb2}, extractCallbacks(clients));
+}
+
+TEST_F(SubscriptionManagerTest, negativeCases) {
+    manager.addOrUpdateSubscription(cb1, subscrToProp1);
+
+    // Wrong zone
+    auto clients = manager.getSubscribedClients(
+            PROP1,
+            toInt(VehicleAreaZone::ROW_2_LEFT),
+            SubscribeFlags::HAL_EVENT);
+    ASSERT_TRUE(clients.empty());
+
+    // Wrong prop
+    clients = manager.getSubscribedClients(
+            toInt(VehicleProperty::AP_POWER_BOOTUP_REASON),
+            toInt(VehicleAreaZone::ROW_1_LEFT),
+            SubscribeFlags::HAL_EVENT);
+    ASSERT_TRUE(clients.empty());
+
+    // Wrong flag
+    clients = manager.getSubscribedClients(
+            PROP1,
+            toInt(VehicleAreaZone::ROW_1_LEFT),
+            SubscribeFlags::SET_CALL);
+    ASSERT_TRUE(clients.empty());
+}
+
+TEST_F(SubscriptionManagerTest, mulipleSubscriptions) {
+    manager.addOrUpdateSubscription(cb1, subscrToProp1);
+
+    auto clients = manager.getSubscribedClients(
+            PROP1,
+            toInt(VehicleAreaZone::ROW_1_LEFT),
+            SubscribeFlags::DEFAULT);
+    ASSERT_EQ((size_t) 1, clients.size());
+    ASSERT_EQ(cb1, clients.front()->getCallback());
+
+    // Same property, but different zone, to make sure we didn't unsubscribe
+    // from previous zone.
+    manager.addOrUpdateSubscription(cb1, {
+        SubscribeOptions {
+                .propId = PROP1,
+                .vehicleAreas = toInt(VehicleAreaZone::ROW_2),
+                .flags = SubscribeFlags::DEFAULT
+            }
+        });
+
+    clients = manager.getSubscribedClients(PROP1,
+                                           toInt(VehicleAreaZone::ROW_1_LEFT),
+                                           SubscribeFlags::DEFAULT);
+    ASSERT_ALL_EXISTS({cb1}, extractCallbacks(clients));
+
+    clients = manager.getSubscribedClients(PROP1,
+                                           toInt(VehicleAreaZone::ROW_2),
+                                           SubscribeFlags::DEFAULT);
+    ASSERT_ALL_EXISTS({cb1}, extractCallbacks(clients));
+}
+
+TEST_F(SubscriptionManagerTest, unsubscribe) {
+    manager.addOrUpdateSubscription(cb1, subscrToProp1);
+    manager.addOrUpdateSubscription(cb2, subscrToProp2);
+    manager.addOrUpdateSubscription(cb3, subscrToProp1and2);
+
+    ASSERT_ALL_EXISTS({cb1, cb3}, extractCallbacks(clientsToProp1()));
+    ASSERT_ALL_EXISTS({cb2, cb3}, extractCallbacks(clientsToProp2()));
+
+    ASSERT_FALSE(manager.unsubscribe(cb1, PROP1));
+    ASSERT_ALL_EXISTS({cb3}, extractCallbacks(clientsToProp1()));
+
+    // Make sure nothing changed in PROP2 so far.
+    ASSERT_ALL_EXISTS({cb2, cb3}, extractCallbacks(clientsToProp2()));
+
+    // No one subscribed to PROP1, subscription for PROP2 is not affected.
+    ASSERT_TRUE(manager.unsubscribe(cb3, PROP1));
+    ASSERT_ALL_EXISTS({cb2, cb3}, extractCallbacks(clientsToProp2()));
+
+    ASSERT_FALSE(manager.unsubscribe(cb3, PROP2));
+    ASSERT_ALL_EXISTS({cb2}, extractCallbacks(clientsToProp2()));
+
+    // The last client unsubscribed from this property.
+    ASSERT_TRUE(manager.unsubscribe(cb2, PROP2));
+
+    // No one was subscribed, return false.
+    ASSERT_FALSE(manager.unsubscribe(cb1, PROP1));
+}
+
+}  // namespace anonymous
+
+}  // namespace V2_0
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/automotive/vehicle/2.0/default/tests/VehicleHalManager_test.cpp b/automotive/vehicle/2.0/default/tests/VehicleHalManager_test.cpp
new file mode 100644
index 0000000..1ca5824
--- /dev/null
+++ b/automotive/vehicle/2.0/default/tests/VehicleHalManager_test.cpp
@@ -0,0 +1,477 @@
+/*
+ * 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 <unordered_map>
+#include <iostream>
+
+#include <utils/SystemClock.h>
+
+#include <gtest/gtest.h>
+
+#include "vehicle_hal_manager/VehicleHalManager.h"
+
+#include "VehicleHalTestUtils.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace {
+
+using namespace std::placeholders;
+
+constexpr char kCarMake[] = "Default Car";
+constexpr int kRetriablePropMockedAttempts = 3;
+
+class MockedVehicleHal : public VehicleHal {
+public:
+    MockedVehicleHal() {
+        mConfigs.assign(std::begin(kVehicleProperties),
+                        std::end(kVehicleProperties));
+    }
+
+    std::vector<VehiclePropConfig> listProperties() override {
+        return mConfigs;
+    }
+
+    VehiclePropValuePtr get(const VehiclePropValue& requestedPropValue,
+             StatusCode* outStatus) override {
+        *outStatus = StatusCode::OK;
+        VehiclePropValuePtr pValue;
+        auto property = static_cast<VehicleProperty>(requestedPropValue.prop);
+        int32_t areaId = requestedPropValue.areaId;
+
+        switch (property) {
+            case VehicleProperty::INFO_MAKE:
+                pValue = getValuePool()->obtainString(kCarMake);
+                break;
+            case VehicleProperty::INFO_FUEL_CAPACITY:
+                if (fuelCapacityAttemptsLeft-- > 0) {
+                    // Emulate property not ready yet.
+                    *outStatus = StatusCode::TRY_AGAIN;
+                } else {
+                    pValue = getValuePool()->obtainFloat(42.42);
+                }
+                break;
+            case VehicleProperty::VEHICLE_MAP_SERVICE:
+                pValue = getValuePool()->obtainComplex();
+                pValue->value.int32Values = hidl_vec<int32_t> { 10, 20 };
+                pValue->value.int64Values = hidl_vec<int64_t> { 30, 40 };
+                pValue->value.floatValues = hidl_vec<float_t> { 1.1, 2.2 };
+                pValue->value.bytes = hidl_vec<uint8_t> { 1, 2, 3 };
+                pValue->value.stringValue = kCarMake;
+                break;
+            default:
+                auto key = makeKey(toInt(property), areaId);
+                if (mValues.count(key) == 0) {
+                    ALOGW("");
+                }
+                pValue = getValuePool()->obtain(mValues[key]);
+        }
+
+        if (*outStatus == StatusCode::OK && pValue.get() != nullptr) {
+            pValue->prop = toInt(property);
+            pValue->areaId = areaId;
+            pValue->timestamp = elapsedRealtimeNano();
+        }
+
+        return pValue;
+    }
+
+    StatusCode set(const VehiclePropValue& propValue) override {
+        if (toInt(VehicleProperty::MIRROR_FOLD) == propValue.prop
+                && mirrorFoldAttemptsLeft-- > 0) {
+            return StatusCode::TRY_AGAIN;
+        }
+
+        mValues[makeKey(propValue)] = propValue;
+        return StatusCode::OK;
+    }
+
+    StatusCode subscribe(int32_t property,
+                         int32_t areas,
+                         float sampleRate) override {
+        return StatusCode::OK;
+    }
+
+    StatusCode unsubscribe(int32_t property) override {
+        return StatusCode::OK;
+    }
+
+    void sendPropEvent(recyclable_ptr<VehiclePropValue> value) {
+        doHalEvent(std::move(value));
+    }
+
+    void sendHalError(StatusCode error, int32_t property, int32_t areaId) {
+        doHalPropertySetError(error, property, areaId);
+    }
+
+public:
+    int fuelCapacityAttemptsLeft = kRetriablePropMockedAttempts;
+    int mirrorFoldAttemptsLeft = kRetriablePropMockedAttempts;
+
+private:
+    int64_t makeKey(const VehiclePropValue& v) const {
+        return makeKey(v.prop, v.areaId);
+    }
+
+    int64_t makeKey(int32_t prop, int32_t area) const {
+        return (static_cast<int64_t>(prop) << 32) | area;
+    }
+
+private:
+    std::vector<VehiclePropConfig> mConfigs;
+    std::unordered_map<int64_t, VehiclePropValue> mValues;
+};
+
+class VehicleHalManagerTest : public ::testing::Test {
+protected:
+    void SetUp() override {
+        hal.reset(new MockedVehicleHal);
+        manager.reset(new VehicleHalManager(hal.get()));
+
+        objectPool = hal->getValuePool();
+    }
+
+    void TearDown() override {
+        manager.reset(nullptr);
+        hal.reset(nullptr);
+    }
+public:
+    void invokeGet(int32_t property, int32_t areaId) {
+        VehiclePropValue requestedValue {};
+        requestedValue.prop = property;
+        requestedValue.areaId = areaId;
+
+        invokeGet(requestedValue);
+    }
+
+    void invokeGet(const VehiclePropValue& requestedPropValue) {
+        actualValue = VehiclePropValue {};  // reset previous values
+
+        StatusCode refStatus;
+        VehiclePropValue refValue;
+        bool called = false;
+        manager->get(requestedPropValue, [&refStatus, &refValue, &called]
+            (StatusCode status, const VehiclePropValue& value) {
+            refStatus = status;
+            refValue = value;
+            called = true;
+        });
+        ASSERT_TRUE(called) << "callback wasn't called for prop: "
+                            << hexString(requestedPropValue.prop);
+
+        actualValue = refValue;
+        actualStatusCode = refStatus;
+    }
+
+public:
+    VehiclePropValue actualValue;
+    StatusCode actualStatusCode;
+
+    VehiclePropValuePool* objectPool;
+    std::unique_ptr<MockedVehicleHal> hal;
+    std::unique_ptr<VehicleHalManager> manager;
+};
+
+TEST_F(VehicleHalManagerTest, getPropConfigs) {
+    hidl_vec<int32_t> properties =
+        { toInt(VehicleProperty::HVAC_FAN_SPEED),
+          toInt(VehicleProperty::INFO_MAKE) };
+    bool called = false;
+
+    manager->getPropConfigs(properties,
+            [&called] (StatusCode status,
+                       const hidl_vec<VehiclePropConfig>& c) {
+        ASSERT_EQ(StatusCode::OK, status);
+        ASSERT_EQ(2u, c.size());
+        called = true;
+    });
+
+    ASSERT_TRUE(called);  // Verify callback received.
+
+    called = false;
+    manager->getPropConfigs({ toInt(VehicleProperty::HVAC_FAN_SPEED) },
+            [&called] (StatusCode status,
+                       const hidl_vec<VehiclePropConfig>& c) {
+        ASSERT_EQ(StatusCode::OK, status);
+        ASSERT_EQ(1u, c.size());
+        ASSERT_EQ(toString(kVehicleProperties[1]), toString(c[0]));
+        called = true;
+    });
+    ASSERT_TRUE(called);  // Verify callback received.
+
+    // TODO(pavelm): add case case when property was not declared.
+}
+
+TEST_F(VehicleHalManagerTest, getAllPropConfigs) {
+    bool called = false;
+    manager->getAllPropConfigs(
+            [&called] (const hidl_vec<VehiclePropConfig>& propConfigs) {
+        ASSERT_EQ(arraysize(kVehicleProperties), propConfigs.size());
+
+        for (size_t i = 0; i < propConfigs.size(); i++) {
+            ASSERT_EQ(toString(kVehicleProperties[i]),
+                      toString(propConfigs[i]));
+        }
+        called = true;
+    });
+    ASSERT_TRUE(called);  // Verify callback received.
+}
+
+TEST_F(VehicleHalManagerTest, halErrorEvent) {
+    const auto PROP = toInt(VehicleProperty::DISPLAY_BRIGHTNESS);
+
+    sp<MockedVehicleCallback> cb = new MockedVehicleCallback();
+
+    hidl_vec<SubscribeOptions> options = {
+        SubscribeOptions {
+            .propId = PROP,
+            .flags = SubscribeFlags::DEFAULT
+        },
+    };
+
+    StatusCode res = manager->subscribe(cb, options);
+    ASSERT_EQ(StatusCode::OK, res);
+
+    hal->sendHalError(StatusCode::TRY_AGAIN, PROP, 0 /* area id*/);
+}
+
+TEST_F(VehicleHalManagerTest, subscribe) {
+    const auto PROP = toInt(VehicleProperty::DISPLAY_BRIGHTNESS);
+
+    sp<MockedVehicleCallback> cb = new MockedVehicleCallback();
+
+    hidl_vec<SubscribeOptions> options = {
+        SubscribeOptions {
+            .propId = PROP,
+            .flags = SubscribeFlags::DEFAULT
+        }
+    };
+
+    StatusCode res = manager->subscribe(cb, options);
+    ASSERT_EQ(StatusCode::OK, res);
+
+    auto unsubscribedValue = objectPool->obtain(VehiclePropertyType::INT32);
+    unsubscribedValue->prop = toInt(VehicleProperty::HVAC_FAN_SPEED);
+
+    hal->sendPropEvent(std::move(unsubscribedValue));
+    auto& receivedEnvents = cb->getReceivedEvents();
+
+    ASSERT_TRUE(cb->waitForExpectedEvents(0)) << " Unexpected events received: "
+                                              << receivedEnvents.size()
+                                              << (receivedEnvents.size() > 0
+                                                  ? toString(receivedEnvents.front()[0]) : "");
+
+    auto subscribedValue = objectPool->obtain(VehiclePropertyType::INT32);
+    subscribedValue->prop = PROP;
+    subscribedValue->value.int32Values[0] = 42;
+
+    cb->reset();
+    VehiclePropValue actualValue(*subscribedValue.get());
+    hal->sendPropEvent(std::move(subscribedValue));
+
+    ASSERT_TRUE(cb->waitForExpectedEvents(1)) << "Events received: "
+                                              << receivedEnvents.size();
+
+    ASSERT_EQ(toString(actualValue),
+              toString(cb->getReceivedEvents().front()[0]));
+}
+
+TEST_F(VehicleHalManagerTest, subscribe_WriteOnly) {
+    const auto PROP = toInt(VehicleProperty::HVAC_SEAT_TEMPERATURE);
+
+    sp<MockedVehicleCallback> cb = new MockedVehicleCallback();
+
+    hidl_vec<SubscribeOptions> options = {
+        SubscribeOptions {
+            .propId = PROP,
+            .flags = SubscribeFlags::HAL_EVENT
+        },
+    };
+
+    StatusCode res = manager->subscribe(cb, options);
+    // Unable to subscribe on Hal Events for write-only properties.
+    ASSERT_EQ(StatusCode::INVALID_ARG, res);
+
+
+    options[0].flags = SubscribeFlags::SET_CALL;
+
+    res = manager->subscribe(cb, options);
+    // OK to subscribe on SET method call for write-only properties.
+    ASSERT_EQ(StatusCode::OK, res);
+}
+
+TEST_F(VehicleHalManagerTest, get_Complex) {
+    invokeGet(toInt(VehicleProperty::VEHICLE_MAP_SERVICE), 0);
+
+    ASSERT_EQ(StatusCode::OK, actualStatusCode);
+    ASSERT_EQ(toInt(VehicleProperty::VEHICLE_MAP_SERVICE), actualValue.prop);
+
+    ASSERT_EQ(3, actualValue.value.bytes.size());
+    ASSERT_EQ(1, actualValue.value.bytes[0]);
+    ASSERT_EQ(2, actualValue.value.bytes[1]);
+    ASSERT_EQ(3, actualValue.value.bytes[2]);
+
+    ASSERT_EQ(2, actualValue.value.int32Values.size());
+    ASSERT_EQ(10, actualValue.value.int32Values[0]);
+    ASSERT_EQ(20, actualValue.value.int32Values[1]);
+
+    ASSERT_EQ(2, actualValue.value.floatValues.size());
+    ASSERT_FLOAT_EQ(1.1, actualValue.value.floatValues[0]);
+    ASSERT_FLOAT_EQ(2.2, actualValue.value.floatValues[1]);
+
+    ASSERT_EQ(2, actualValue.value.int64Values.size());
+    ASSERT_FLOAT_EQ(30, actualValue.value.int64Values[0]);
+    ASSERT_FLOAT_EQ(40, actualValue.value.int64Values[1]);
+
+    ASSERT_STREQ(kCarMake, actualValue.value.stringValue.c_str());
+}
+
+TEST_F(VehicleHalManagerTest, get_StaticString) {
+    invokeGet(toInt(VehicleProperty::INFO_MAKE), 0);
+
+    ASSERT_EQ(StatusCode::OK, actualStatusCode);
+    ASSERT_EQ(toInt(VehicleProperty::INFO_MAKE), actualValue.prop);
+    ASSERT_STREQ(kCarMake, actualValue.value.stringValue.c_str());
+}
+
+TEST_F(VehicleHalManagerTest, get_NegativeCases) {
+    // Write-only property must fail.
+    invokeGet(toInt(VehicleProperty::HVAC_SEAT_TEMPERATURE), 0);
+    ASSERT_EQ(StatusCode::ACCESS_DENIED, actualStatusCode);
+
+    // Unknown property must fail.
+    invokeGet(toInt(VehicleProperty::MIRROR_Z_MOVE), 0);
+    ASSERT_EQ(StatusCode::INVALID_ARG, actualStatusCode);
+}
+
+TEST_F(VehicleHalManagerTest, get_Retriable) {
+    actualStatusCode = StatusCode::TRY_AGAIN;
+    int attempts = 0;
+    while (StatusCode::TRY_AGAIN == actualStatusCode && ++attempts < 10) {
+        invokeGet(toInt(VehicleProperty::INFO_FUEL_CAPACITY), 0);
+
+    }
+    ASSERT_EQ(StatusCode::OK, actualStatusCode);
+    ASSERT_EQ(kRetriablePropMockedAttempts + 1, attempts);
+    ASSERT_FLOAT_EQ(42.42, actualValue.value.floatValues[0]);
+}
+
+TEST_F(VehicleHalManagerTest, set_Basic) {
+    const auto PROP = toInt(VehicleProperty::DISPLAY_BRIGHTNESS);
+    const auto VAL = 7;
+
+    auto expectedValue = hal->getValuePool()->obtainInt32(VAL);
+    expectedValue->prop = PROP;
+    expectedValue->areaId = 0;
+
+    actualStatusCode = manager->set(*expectedValue.get());
+    ASSERT_EQ(StatusCode::OK, actualStatusCode);
+
+    invokeGet(PROP, 0);
+    ASSERT_EQ(StatusCode::OK, actualStatusCode);
+    ASSERT_EQ(PROP, actualValue.prop);
+    ASSERT_EQ(VAL, actualValue.value.int32Values[0]);
+}
+
+TEST_F(VehicleHalManagerTest, set_DifferentAreas) {
+    const auto PROP = toInt(VehicleProperty::HVAC_FAN_SPEED);
+    const auto VAL1 = 1;
+    const auto VAL2 = 2;
+    const auto AREA1 = toInt(VehicleAreaZone::ROW_1_LEFT);
+    const auto AREA2 = toInt(VehicleAreaZone::ROW_1_RIGHT);
+
+    {
+        auto expectedValue1 = hal->getValuePool()->obtainInt32(VAL1);
+        expectedValue1->prop = PROP;
+        expectedValue1->areaId = AREA1;
+        actualStatusCode = manager->set(*expectedValue1.get());
+        ASSERT_EQ(StatusCode::OK, actualStatusCode);
+
+        auto expectedValue2 = hal->getValuePool()->obtainInt32(VAL2);
+        expectedValue2->prop = PROP;
+        expectedValue2->areaId = AREA2;
+        actualStatusCode = manager->set(*expectedValue2.get());
+        ASSERT_EQ(StatusCode::OK, actualStatusCode);
+    }
+
+    {
+        invokeGet(PROP, AREA1);
+        ASSERT_EQ(StatusCode::OK, actualStatusCode);
+        ASSERT_EQ(PROP, actualValue.prop);
+        ASSERT_EQ(AREA1, actualValue.areaId);
+        ASSERT_EQ(VAL1, actualValue.value.int32Values[0]);
+
+        invokeGet(PROP, AREA2);
+        ASSERT_EQ(StatusCode::OK, actualStatusCode);
+        ASSERT_EQ(PROP, actualValue.prop);
+        ASSERT_EQ(AREA2, actualValue.areaId);
+        ASSERT_EQ(VAL2, actualValue.value.int32Values[0]);
+    }
+}
+
+TEST_F(VehicleHalManagerTest, set_Retriable) {
+    const auto PROP = toInt(VehicleProperty::MIRROR_FOLD);
+
+    auto v = hal->getValuePool()->obtainBoolean(true);
+    v->prop = PROP;
+    v->areaId = 0;
+
+    actualStatusCode = StatusCode::TRY_AGAIN;
+    int attempts = 0;
+    while (StatusCode::TRY_AGAIN == actualStatusCode && ++attempts < 10) {
+        actualStatusCode = manager->set(*v.get());
+    }
+
+    ASSERT_EQ(StatusCode::OK, actualStatusCode);
+    ASSERT_EQ(kRetriablePropMockedAttempts + 1, attempts);
+
+    invokeGet(PROP, 0);
+    ASSERT_EQ(StatusCode::OK, actualStatusCode);
+    ASSERT_TRUE(actualValue.value.int32Values[0]);
+}
+
+TEST(HalClientVectorTest, basic) {
+    HalClientVector clients;
+    sp<IVehicleCallback> callback1 = new MockedVehicleCallback();
+
+    sp<HalClient> c1 = new HalClient(callback1, 10, 20);
+    sp<HalClient> c2 = new HalClient(callback1, 10, 20);
+
+    clients.addOrUpdate(c1);
+    clients.addOrUpdate(c1);
+    clients.addOrUpdate(c2);
+    ASSERT_EQ(2u, clients.size());
+    ASSERT_FALSE(clients.isEmpty());
+    ASSERT_LE(0, clients.indexOf(c1));
+    ASSERT_LE(0, clients.remove(c1));
+    ASSERT_GT(0, clients.indexOf(c1));  // c1 was already removed
+    ASSERT_GT(0, clients.remove(c1));   // attempt to remove c1 again
+    ASSERT_LE(0, clients.remove(c2));
+
+    ASSERT_TRUE(clients.isEmpty());
+}
+
+}  // namespace anonymous
+
+}  // namespace V2_0
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/automotive/vehicle/2.0/default/tests/VehicleHalTestUtils.h b/automotive/vehicle/2.0/default/tests/VehicleHalTestUtils.h
new file mode 100644
index 0000000..a512fcf
--- /dev/null
+++ b/automotive/vehicle/2.0/default/tests/VehicleHalTestUtils.h
@@ -0,0 +1,274 @@
+/*
+ * 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 android_hardware_automotive_vehicle_V2_0_VehicleDebugUtils_H_
+#define android_hardware_automotive_vehicle_V2_0_VehicleDebugUtils_H_
+
+#include <android/hardware/automotive/vehicle/2.0/types.h>
+#include <vehicle_hal_manager/VehicleUtils.h>
+#include <ios>
+#include <sstream>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+const VehiclePropConfig kVehicleProperties[] = {
+    {
+        .prop = toInt(VehicleProperty::INFO_MAKE),
+        .access = VehiclePropertyAccess::READ,
+        .changeMode = VehiclePropertyChangeMode::STATIC,
+        .configString = "Some=config,options=if,you=have_any",
+    },
+
+    {
+        .prop = toInt(VehicleProperty::HVAC_FAN_SPEED),
+        .access = VehiclePropertyAccess::READ_WRITE,
+        .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
+        .supportedAreas = static_cast<int32_t>(
+            VehicleAreaZone::ROW_1_LEFT | VehicleAreaZone::ROW_1_RIGHT),
+        .areaConfigs = {
+            VehicleAreaConfig {
+                .areaId = toInt(VehicleAreaZone::ROW_1_LEFT),
+                .minInt32Value = 1,
+                .maxInt32Value = 7},
+            VehicleAreaConfig {
+                .areaId = toInt(VehicleAreaZone::ROW_1_RIGHT),
+                .minInt32Value = 1,
+                .maxInt32Value = 5,
+            }
+        }
+    },
+
+    // Write-only property
+    {
+        .prop = toInt(VehicleProperty::HVAC_SEAT_TEMPERATURE),
+        .access = VehiclePropertyAccess::WRITE,
+        .changeMode = VehiclePropertyChangeMode::ON_SET,
+        .supportedAreas = static_cast<int32_t>(
+            VehicleAreaZone::ROW_1_LEFT | VehicleAreaZone::ROW_1_RIGHT),
+        .areaConfigs = {
+            VehicleAreaConfig {
+                .areaId = toInt(VehicleAreaZone::ROW_1_LEFT),
+                .minInt32Value = 64,
+                .maxInt32Value = 80},
+            VehicleAreaConfig {
+                .areaId = toInt(VehicleAreaZone::ROW_1_RIGHT),
+                .minInt32Value = 64,
+                .maxInt32Value = 80,
+            }
+        }
+    },
+
+    {
+        .prop = toInt(VehicleProperty::INFO_FUEL_CAPACITY),
+        .access = VehiclePropertyAccess::READ,
+        .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
+        .areaConfigs = {
+            VehicleAreaConfig {
+                .minFloatValue = 0,
+                .maxFloatValue = 1.0
+            }
+        }
+    },
+
+    {
+        .prop = toInt(VehicleProperty::DISPLAY_BRIGHTNESS),
+        .access = VehiclePropertyAccess::READ_WRITE,
+        .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
+        .areaConfigs = {
+            VehicleAreaConfig {
+                .minInt32Value = 0,
+                .maxInt32Value = 10
+            }
+        }
+    },
+
+    {
+        .prop = toInt(VehicleProperty::MIRROR_FOLD),
+        .access = VehiclePropertyAccess::READ_WRITE,
+        .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
+
+    },
+
+    // Complex data type.
+    {
+        .prop = toInt(VehicleProperty::VEHICLE_MAP_SERVICE),
+        .access = VehiclePropertyAccess::READ_WRITE,
+        .changeMode = VehiclePropertyChangeMode::ON_CHANGE
+    }
+};
+
+constexpr auto kTimeout = std::chrono::milliseconds(500);
+
+class MockedVehicleCallback : public IVehicleCallback {
+private:
+    using MuxGuard = std::lock_guard<std::mutex>;
+    using HidlVecOfValues = hidl_vec<VehiclePropValue>;
+public:
+    // Methods from ::android::hardware::automotive::vehicle::V2_0::IVehicleCallback follow.
+    Return<void> onPropertyEvent(
+            const hidl_vec<VehiclePropValue>& values) override {
+        {
+            MuxGuard  g(mLock);
+            mReceivedEvents.push_back(values);
+        }
+        mEventCond.notify_one();
+        return Return<void>();
+    }
+    Return<void> onPropertySet(const VehiclePropValue& value) override {
+        return Return<void>();
+    }
+    Return<void> onPropertySetError(StatusCode errorCode,
+                                    int32_t propId,
+                                    int32_t areaId) override {
+        return Return<void>();
+    }
+
+    bool waitForExpectedEvents(size_t expectedEvents) {
+        std::unique_lock<std::mutex> g(mLock);
+
+        if (expectedEvents == 0 && mReceivedEvents.size() == 0) {
+            // No events expected, let's sleep a little bit to make sure
+            // nothing will show up.
+            return mEventCond.wait_for(g, kTimeout) == std::cv_status::timeout;
+        }
+
+        while (expectedEvents != mReceivedEvents.size()) {
+            if (mEventCond.wait_for(g, kTimeout) == std::cv_status::timeout) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    void reset() {
+        mReceivedEvents.clear();
+    }
+
+    const std::vector<HidlVecOfValues>& getReceivedEvents() {
+        return mReceivedEvents;
+    }
+
+private:
+    std::mutex mLock;
+    std::condition_variable mEventCond;
+    std::vector<HidlVecOfValues> mReceivedEvents;
+};
+
+template<typename T>
+inline std::string hexString(T value) {
+    std::stringstream ss;
+    ss << std::showbase << std::hex << value;
+    return ss.str();
+}
+
+template <typename T, typename Collection>
+inline void assertAllExistsAnyOrder(
+        std::initializer_list<T> expected,
+        const Collection& actual,
+        const char* msg) {
+    std::set<T> expectedSet = expected;
+
+    for (auto a: actual) {
+        ASSERT_EQ(1u, expectedSet.erase(a))
+                << msg << "\nContains not unexpected value.\n";
+    }
+
+    ASSERT_EQ(0u, expectedSet.size())
+            << msg
+            << "\nDoesn't contain expected value.";
+}
+
+#define ASSERT_ALL_EXISTS(...) \
+    assertAllExistsAnyOrder(__VA_ARGS__, (std::string("Called from: ") + \
+            std::string(__FILE__) + std::string(":") + \
+            std::to_string(__LINE__)).c_str()); \
+
+template<typename T>
+inline std::string enumToHexString(T value) {
+    return hexString(toInt(value));
+}
+
+template <typename T>
+inline std::string vecToString(const hidl_vec<T>& vec) {
+    std::stringstream ss("[");
+    for (size_t i = 0; i < vec.size(); i++) {
+        if (i != 0) ss << ",";
+        ss << vec[i];
+    }
+    ss << "]";
+    return ss.str();
+}
+
+inline std::string toString(const VehiclePropValue &v) {
+    std::stringstream ss;
+    ss << "VehiclePropValue {n"
+       << "  prop: " << hexString(v.prop) << ",\n"
+       << "  areaId: " << hexString(v.areaId) << ",\n"
+       << "  timestamp: " << v.timestamp << ",\n"
+       << "  value {\n"
+       << "    int32Values: " << vecToString(v.value.int32Values) << ",\n"
+       << "    floatValues: " << vecToString(v.value.floatValues) << ",\n"
+       << "    int64Values: " << vecToString(v.value.int64Values) << ",\n"
+       << "    bytes: " << vecToString(v.value.bytes) << ",\n"
+       << "    string: " << v.value.stringValue.c_str() << ",\n"
+       << "  }\n"
+       << "}\n";
+
+    return ss.str();
+}
+
+inline std::string toString(const VehiclePropConfig &config) {
+    std::stringstream ss;
+    ss << "VehiclePropConfig {\n"
+       << "  prop: " << hexString(config.prop) << ",\n"
+       << "  supportedAreas: " << hexString(config.supportedAreas) << ",\n"
+       << "  access: " << enumToHexString(config.access) << ",\n"
+       << "  changeMode: " << enumToHexString(config.changeMode) << ",\n"
+       << "  configFlags: " << hexString(config.configFlags) << ",\n"
+       << "  minSampleRate: " << config.minSampleRate << ",\n"
+       << "  maxSampleRate: " << config.maxSampleRate << ",\n"
+       << "  configString: " << config.configString.c_str() << ",\n";
+
+    ss << "  areaConfigs {\n";
+    for (size_t i = 0; i < config.areaConfigs.size(); i++) {
+        const auto &area = config.areaConfigs[i];
+        ss << "    areaId: " << hexString(area.areaId) << ",\n"
+           << "    minFloatValue: " << area.minFloatValue << ",\n"
+           << "    minFloatValue: " << area.maxFloatValue << ",\n"
+           << "    minInt32Value: " << area.minInt32Value << ",\n"
+           << "    minInt32Value: " << area.maxInt32Value << ",\n"
+           << "    minInt64Value: " << area.minInt64Value << ",\n"
+           << "    minInt64Value: " << area.maxInt64Value << ",\n";
+    }
+    ss << "  }\n"
+       << "}\n";
+
+    return ss.str();
+}
+
+
+}  // namespace V2_0
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
+
+
+#endif //android_hardware_automotive_vehicle_V2_0_VehicleDebugUtils_H_
diff --git a/automotive/vehicle/2.0/default/tests/VehicleObjectPool_test.cpp b/automotive/vehicle/2.0/default/tests/VehicleObjectPool_test.cpp
new file mode 100644
index 0000000..0d57284
--- /dev/null
+++ b/automotive/vehicle/2.0/default/tests/VehicleObjectPool_test.cpp
@@ -0,0 +1,131 @@
+/*
+ * 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 <thread>
+
+#include <gtest/gtest.h>
+
+#include <utils/SystemClock.h>
+
+#include "vehicle_hal_manager/VehicleObjectPool.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace {
+
+class VehicleObjectPoolTest : public ::testing::Test {
+protected:
+    void SetUp() override {
+        stats = PoolStats::instance();
+        resetStats();
+        valuePool.reset(new VehiclePropValuePool);
+    }
+
+    void TearDown() override {
+        // At the end, all created objects should be either recycled or deleted.
+        // Some objects could be recycled multiple times, that's why it's <=
+        ASSERT_EQ(stats->Obtained, stats->Recycled);
+        ASSERT_LE(stats->Created, stats->Recycled);
+    }
+private:
+    void resetStats() {
+        stats->Obtained = 0;
+        stats->Created = 0;
+        stats->Recycled = 0;
+    }
+
+public:
+    PoolStats* stats;
+    std::unique_ptr<VehiclePropValuePool> valuePool;
+};
+
+TEST_F(VehicleObjectPoolTest, valuePoolBasicCorrectness) {
+    void* raw = valuePool->obtain(VehiclePropertyType::INT32).get();
+    // At this point, v1 should be recycled and the only object in the pool.
+    ASSERT_EQ(raw, valuePool->obtain(VehiclePropertyType::INT32).get());
+    // Obtaining value of another type - should return a new object
+    ASSERT_NE(raw, valuePool->obtain(VehiclePropertyType::FLOAT).get());
+
+    ASSERT_EQ(3u, stats->Obtained);
+    ASSERT_EQ(2u, stats->Created);
+}
+
+TEST_F(VehicleObjectPoolTest, valuePoolStrings) {
+    valuePool->obtain(VehiclePropertyType::STRING);
+    auto vs = valuePool->obtain(VehiclePropertyType::STRING);
+    vs->value.stringValue = "Hello";
+    void* raw = vs.get();
+    vs.reset();  // delete the pointer
+
+    auto vs2 = valuePool->obtain(VehiclePropertyType::STRING);
+    ASSERT_EQ(0u, vs2->value.stringValue.size());
+    ASSERT_NE(raw, valuePool->obtain(VehiclePropertyType::STRING).get());
+
+    ASSERT_EQ(0u, stats->Obtained);
+}
+
+TEST_F(VehicleObjectPoolTest, valuePoolMultithreadedBenchmark) {
+    // In this test we have T threads that concurrently in C cycles
+    // obtain and release O VehiclePropValue objects of FLOAT / INT32 types.
+
+    const auto T = 2;
+    const auto C  = 500;
+    const auto O = 100;
+
+    auto poolPtr = valuePool.get();
+
+    std::vector<std::thread> threads;
+    auto start = elapsedRealtimeNano();
+    for (int i = 0; i < T; i++) {
+        threads.push_back(std::thread([&poolPtr] () {
+            for (int j = 0; j < C; j++) {
+                std::vector<recyclable_ptr<VehiclePropValue>> vec;
+                for (int k = 0; k < O; k++) {
+                    vec.push_back(
+                        poolPtr->obtain(k % 2 == 0
+                                        ? VehiclePropertyType::FLOAT
+                                        : VehiclePropertyType::INT32));
+                }
+            }
+        }));
+    }
+
+    for (auto& t : threads) {
+        t.join();
+    }
+    auto finish = elapsedRealtimeNano();
+
+    ASSERT_EQ(T * C * O, stats->Obtained);
+    ASSERT_EQ(T * C * O, stats->Recycled);
+    // Created less than obtained.
+    ASSERT_GE(T * O, stats->Created);
+
+    auto elapsedMs = (finish - start) / 1000000;
+    ASSERT_GE(1000, elapsedMs);  // Less a second to access 100K objects.
+                                 // Typically it takes about 0.1s on Nexus6P.
+}
+
+}  // namespace anonymous
+
+}  // namespace V2_0
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/automotive/vehicle/2.0/default/tests/VehiclePropConfigIndex_test.cpp b/automotive/vehicle/2.0/default/tests/VehiclePropConfigIndex_test.cpp
new file mode 100644
index 0000000..fad4ab3
--- /dev/null
+++ b/automotive/vehicle/2.0/default/tests/VehiclePropConfigIndex_test.cpp
@@ -0,0 +1,77 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include "vehicle_hal_manager/VehiclePropConfigIndex.h"
+
+#include "VehicleHalTestUtils.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace {
+
+class PropConfigTest : public ::testing::Test {
+protected:
+    void SetUp() override {
+        configs.assign(std::begin(kVehicleProperties),
+                       std::end(kVehicleProperties));
+    }
+
+    void TearDown() override {}
+
+public:
+    std::vector<VehiclePropConfig> configs;
+};
+
+TEST_F(PropConfigTest, hasConfig) {
+    VehiclePropConfigIndex index(configs);
+
+    ASSERT_TRUE(index.hasConfig(toInt(VehicleProperty::HVAC_FAN_SPEED)));
+    ASSERT_TRUE(index.hasConfig(toInt(VehicleProperty::INFO_MAKE)));
+    ASSERT_TRUE(index.hasConfig(toInt(VehicleProperty::INFO_FUEL_CAPACITY)));
+
+    ASSERT_FALSE(index.hasConfig(toInt(VehicleProperty::INVALID)));
+}
+
+TEST_F(PropConfigTest, getAllConfig) {
+    VehiclePropConfigIndex index(configs);
+
+    std::vector<VehiclePropConfig> actualConfigs = index.getAllConfigs();
+    ASSERT_EQ(configs.size(), actualConfigs.size());
+
+    for (size_t i = 0; i < actualConfigs.size(); i++) {
+        ASSERT_EQ(toString(configs[i]), toString(actualConfigs[i]));
+    }
+}
+
+TEST_F(PropConfigTest, getConfigs) {
+    VehiclePropConfigIndex index(configs);
+    auto actualConfig = index.getConfig(toInt(VehicleProperty::HVAC_FAN_SPEED));
+    ASSERT_EQ(toString(configs[1]), toString(actualConfig));
+}
+
+}  // namespace anonymous
+
+}  // namespace V2_0
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android