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