Vehicle HAL reference impl Part I

Implemented:
  - defined VehicleHal
  - object pool for VehiclePropValue objects
  - batching of vehicle HAL events
  - subscription management

Test: unit tests provided

Bug: b/31971746
Change-Id: Idd2d0aee7b32a975c3db54812be235e13f52905a
diff --git a/vehicle/2.0/default/tests/SubscriptionManager_test.cpp b/vehicle/2.0/default/tests/SubscriptionManager_test.cpp
new file mode 100644
index 0000000..c3db993
--- /dev/null
+++ b/vehicle/2.0/default/tests/SubscriptionManager_test.cpp
@@ -0,0 +1,203 @@
+/*
+ * 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/VehiclePropConfigIndex.h>
+#include <VehicleHal.h>
+#include <vehicle_hal_manager/VehicleHalManager.h>
+#include "vehicle_hal_manager/SubscriptionManager.h"
+
+#include "VehicleHalTestUtils.h"
+
+namespace android {
+namespace hardware {
+namespace vehicle {
+namespace V2_0 {
+
+namespace {
+
+using namespace std::placeholders;
+
+class SubscriptionManagerTest : public ::testing::Test {
+public:
+    SubscriptionManager manager;
+
+    const VehicleProperty PROP1 = VehicleProperty::HVAC_FAN_SPEED;
+    const VehicleProperty PROP2 = VehicleProperty::DISPLAY_BRIGHTNESS;
+
+    sp<IVehicleCallback> cb1 = new MockedVehicleCallback();
+    sp<IVehicleCallback> cb2 = new MockedVehicleCallback();
+    sp<IVehicleCallback> cb3 = new MockedVehicleCallback();
+
+    hidl_vec<SubscribeOptions> subscrToProp1 = init_hidl_vec(
+        {
+            SubscribeOptions {
+                .propId = PROP1,
+                .vehicleAreas = val(VehicleAreaZone::ROW_1_LEFT),
+                .flags = SubscribeFlags::HAL_EVENT
+            },
+        });
+
+    hidl_vec<SubscribeOptions> subscrToProp2 = init_hidl_vec(
+        {
+            SubscribeOptions {
+                .propId = PROP2,
+                .flags = SubscribeFlags::HAL_EVENT
+            },
+        });
+
+    hidl_vec<SubscribeOptions> subscrToProp1and2 = init_hidl_vec(
+        {
+            SubscribeOptions {
+                .propId = PROP1,
+                .vehicleAreas = val(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,
+                                            val(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,
+            val(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,
+            val(VehicleAreaZone::ROW_2_LEFT),
+            SubscribeFlags::HAL_EVENT);
+    ASSERT_TRUE(clients.empty());
+
+    // Wrong prop
+    clients = manager.getSubscribedClients(
+            VehicleProperty::AP_POWER_BOOTUP_REASON,
+            val(VehicleAreaZone::ROW_1_LEFT),
+            SubscribeFlags::HAL_EVENT);
+    ASSERT_TRUE(clients.empty());
+
+    // Wrong flag
+    clients = manager.getSubscribedClients(
+            PROP1,
+            val(VehicleAreaZone::ROW_1_LEFT),
+            SubscribeFlags::SET_CALL);
+    ASSERT_TRUE(clients.empty());
+}
+
+TEST_F(SubscriptionManagerTest, mulipleSubscriptions) {
+    manager.addOrUpdateSubscription(cb1, subscrToProp1);
+
+    auto clients = manager.getSubscribedClients(
+            PROP1,
+            val(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, init_hidl_vec(
+        {
+            SubscribeOptions {
+                .propId = PROP1,
+                .vehicleAreas = val(VehicleAreaZone::ROW_2),
+                .flags = SubscribeFlags::DEFAULT
+            }
+        }));
+
+    clients = manager.getSubscribedClients(PROP1,
+                                           val(VehicleAreaZone::ROW_1_LEFT),
+                                           SubscribeFlags::DEFAULT);
+    ASSERT_ALL_EXISTS({cb1}, extractCallbacks(clients));
+
+    clients = manager.getSubscribedClients(PROP1,
+                                           val(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 hardware
+}  // namespace android
diff --git a/vehicle/2.0/default/tests/VehicleHalManager_test.cpp b/vehicle/2.0/default/tests/VehicleHalManager_test.cpp
new file mode 100644
index 0000000..1410ddf
--- /dev/null
+++ b/vehicle/2.0/default/tests/VehicleHalManager_test.cpp
@@ -0,0 +1,208 @@
+/*
+ * 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/VehiclePropConfigIndex.h>
+#include <VehicleHal.h>
+#include <vehicle_hal_manager/VehicleHalManager.h>
+#include "vehicle_hal_manager/SubscriptionManager.h"
+
+#include "VehicleHalTestUtils.h"
+
+namespace android {
+namespace hardware {
+namespace vehicle {
+namespace V2_0 {
+
+namespace {
+
+using namespace std::placeholders;
+
+class MockedVehicleHal : public VehicleHal {
+public:
+    MockedVehicleHal() {
+        mConfigs.assign(std::begin(kVehicleProperties),
+                        std::end(kVehicleProperties));
+    }
+
+    std::vector<VehiclePropConfig> listProperties() override {
+        return mConfigs;
+    }
+
+    VehiclePropValuePtr get(VehicleProperty property,
+                 int32_t areaId,
+                 status_t* outStatus) override {
+        *outStatus = OK;
+        return getValuePool()->obtain(mValues[property]);
+    }
+
+    status_t set(const VehiclePropValue& propValue) override {
+        mValues[propValue.prop] = propValue;
+        return OK;
+    }
+
+    status_t subscribe(VehicleProperty property,
+                       int32_t areas,
+                       float sampleRate) override {
+        return OK;
+    }
+
+    status_t unsubscribe(VehicleProperty property) override {
+        return OK;
+    }
+
+    void sendPropEvent(recyclable_ptr<VehiclePropValue> value) {
+        doHalEvent(std::move(value));
+    }
+
+private:
+    std::vector<VehiclePropConfig> mConfigs;
+    std::unordered_map<VehicleProperty, 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:
+    VehiclePropValuePool* objectPool;
+    std::unique_ptr<MockedVehicleHal> hal;
+    std::unique_ptr<VehicleHalManager> manager;
+};
+
+class HalClientVectorTest : public ::testing::Test {
+public:
+    HalClientVector clients;
+};
+
+TEST_F(VehicleHalManagerTest, getPropConfigs) {
+    hidl_vec<VehicleProperty> properties = init_hidl_vec(
+        { VehicleProperty::HVAC_FAN_SPEED,VehicleProperty::INFO_MAKE} );
+    bool called = false;
+    manager->getPropConfigs(properties,
+                            [&called] (const hidl_vec<VehiclePropConfig>& c) {
+        ASSERT_EQ(2u, c.size());
+        called = true;
+    });
+    ASSERT_TRUE(called);  // Verify callback received.
+
+    called = false;
+    manager->getPropConfigs(init_hidl_vec({VehicleProperty::HVAC_FAN_SPEED}),
+                            [&called] (const hidl_vec<VehiclePropConfig>& c) {
+        ASSERT_EQ(1u, c.size());
+        ASSERT_EQ(toString(kVehicleProperties[1]), toString(c[0]));
+        called = true;
+    });
+    ASSERT_TRUE(called);  // Verify callback received.
+}
+
+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, subscribe) {
+    const VehicleProperty PROP = VehicleProperty::DISPLAY_BRIGHTNESS;
+
+    sp<MockedVehicleCallback> cb = new MockedVehicleCallback();
+
+    hidl_vec<SubscribeOptions> options = init_hidl_vec(
+        {
+            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 = 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(HalClientVectorTest, basic) {
+    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_GE(0, clients.indexOf(c1));
+    ASSERT_GE(0, clients.remove(c1));
+    ASSERT_GE(0, clients.indexOf(c1));
+    ASSERT_GE(0, clients.remove(c1));
+    ASSERT_GE(0, clients.remove(c2));
+
+    ASSERT_TRUE(clients.isEmpty());
+}
+
+}  // namespace anonymous
+
+}  // namespace V2_0
+}  // namespace vehicle
+}  // namespace hardware
+}  // namespace android
diff --git a/vehicle/2.0/default/tests/VehicleHalTestUtils.h b/vehicle/2.0/default/tests/VehicleHalTestUtils.h
new file mode 100644
index 0000000..b3b3ffa
--- /dev/null
+++ b/vehicle/2.0/default/tests/VehicleHalTestUtils.h
@@ -0,0 +1,244 @@
+/*
+ * 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_vehicle_V2_0_VehicleDebugUtils_H_
+#define android_hardware_vehicle_V2_0_VehicleDebugUtils_H_
+
+#include <android/hardware/vehicle/2.0/types.h>
+#include <vehicle_hal_manager/VehicleUtils.h>
+#include <ios>
+#include <sstream>
+
+namespace android {
+namespace hardware {
+namespace vehicle {
+namespace V2_0 {
+
+const VehiclePropConfig kVehicleProperties[] = {
+    {
+        .prop = VehicleProperty::INFO_MAKE,
+        .access = VehiclePropertyAccess::READ,
+        .changeMode = VehiclePropertyChangeMode::STATIC,
+        .permissionModel = VehiclePermissionModel::OEM_ONLY,
+        .configString = "Some=config,options=if,you=have_any",
+    },
+
+    {
+        .prop = VehicleProperty::HVAC_FAN_SPEED,
+        .access = VehiclePropertyAccess::READ_WRITE,
+        .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
+        .permissionModel = VehiclePermissionModel::NO_RESTRICTION,
+        .supportedAreas = static_cast<int32_t>(
+            VehicleAreaZone::ROW_1_LEFT | VehicleAreaZone::ROW_1_RIGHT),
+        .areaConfigs = init_hidl_vec({
+                                         VehicleAreaConfig {
+                                             .areaId = val(
+                                                 VehicleAreaZone::ROW_2_LEFT),
+                                             .minInt32Value = 1,
+                                             .maxInt32Value = 7},
+                                         VehicleAreaConfig {
+                                             .areaId = val(
+                                                 VehicleAreaZone::ROW_1_RIGHT),
+                                             .minInt32Value = 1,
+                                             .maxInt32Value = 5,
+                                         }
+                                     }),
+    },
+
+    {
+        .prop = VehicleProperty::INFO_FUEL_CAPACITY,
+        .access = VehiclePropertyAccess::READ,
+        .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
+        .permissionModel = VehiclePermissionModel::OEM_ONLY,
+        .areaConfigs = init_hidl_vec({
+                                         VehicleAreaConfig {
+                                             .minFloatValue = 0,
+                                             .maxFloatValue = 1.0
+                                         }
+                                     })
+    },
+
+    {
+        .prop = VehicleProperty::DISPLAY_BRIGHTNESS,
+        .access = VehiclePropertyAccess::READ_WRITE,
+        .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
+        .permissionModel = VehiclePermissionModel::OEM_ONLY,
+        .areaConfigs = init_hidl_vec({
+                                         VehicleAreaConfig {
+                                             .minInt32Value = 0,
+                                             .maxInt32Value = 10
+                                         }
+                                     })
+    }
+};
+
+constexpr auto kTimeout = std::chrono::milliseconds(500);
+
+class MockedVehicleCallback : public IVehicleCallback {
+public:
+    // Methods from ::android::hardware::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> onError(StatusCode errorCode,
+                         VehicleProperty propId,
+                         VehiclePropertyOperation operation) 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<hidl_vec<VehiclePropValue>>& getReceivedEvents() {
+        return mReceivedEvents;
+    }
+
+private:
+    using MuxGuard = std::lock_guard<std::mutex>;
+
+    std::mutex mLock;
+    std::condition_variable mEventCond;
+    std::vector<hidl_vec<VehiclePropValue>> 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(val(value));
+}
+
+template <typename T>
+inline std::string toString(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: " << enumToHexString(v.prop) << ",\n"
+       << "  areaId: " << hexString(v.areaId) << ",\n"
+       << "  timestamp: " << v.timestamp << ",\n"
+       << "  value {\n"
+       << "    int32Values: " << toString(v.value.int32Values) << ",\n"
+       << "    floatValues: " << toString(v.value.floatValues) << ",\n"
+       << "    int64Values: " << toString(v.value.int64Values) << ",\n"
+       << "    bytes: " << toString(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: " << enumToHexString(config.prop) << ",\n"
+       << "  supportedAreas: " << hexString(config.supportedAreas) << ",\n"
+       << "  access: " << enumToHexString(config.access) << ",\n"
+       << "  permissionModel: " << enumToHexString(config.permissionModel) << ",\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 hardware
+}  // namespace android
+
+
+#endif //VEHICLEHALDEBUGUTILS_H
diff --git a/vehicle/2.0/default/tests/VehicleObjectPool_test.cpp b/vehicle/2.0/default/tests/VehicleObjectPool_test.cpp
new file mode 100644
index 0000000..88b1be0
--- /dev/null
+++ b/vehicle/2.0/default/tests/VehicleObjectPool_test.cpp
@@ -0,0 +1,128 @@
+/*
+ * 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 <vehicle_hal_manager/VehicleObjectPool.h>
+#include <utils/SystemClock.h>
+
+namespace android {
+namespace hardware {
+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 hardware
+}  // namespace android
diff --git a/vehicle/2.0/default/tests/VehiclePropConfigIndex_test.cpp b/vehicle/2.0/default/tests/VehiclePropConfigIndex_test.cpp
new file mode 100644
index 0000000..aae7e62
--- /dev/null
+++ b/vehicle/2.0/default/tests/VehiclePropConfigIndex_test.cpp
@@ -0,0 +1,75 @@
+/*
+ * 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 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(VehicleProperty::HVAC_FAN_SPEED));
+    ASSERT_TRUE(index.hasConfig(VehicleProperty::INFO_MAKE));
+    ASSERT_TRUE(index.hasConfig(VehicleProperty::INFO_FUEL_CAPACITY));
+
+    ASSERT_FALSE(index.hasConfig(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(VehicleProperty::HVAC_FAN_SPEED);
+    ASSERT_EQ(toString(configs[1]), toString(actualConfig));
+}
+
+}  // namespace anonymous
+
+}  // namespace V2_0
+}  // namespace vehicle
+}  // namespace hardware
+}  // namespace android
\ No newline at end of file