Vehicle HAL reference impl Part II

Implemented:
  - IVehicle::get <-- changed signature
  - IVehicle::set
  - status_t replaced with StatusCode
  - changed error handling to handle errors on SET

Test: unit tests provided
Bug: b/31971746
Change-Id: I9ea3feab7539adf588f1278fb905c0a458fa1627
diff --git a/vehicle/2.0/default/tests/SubscriptionManager_test.cpp b/vehicle/2.0/default/tests/SubscriptionManager_test.cpp
index c3db993..19b11e6 100644
--- a/vehicle/2.0/default/tests/SubscriptionManager_test.cpp
+++ b/vehicle/2.0/default/tests/SubscriptionManager_test.cpp
@@ -50,7 +50,7 @@
         {
             SubscribeOptions {
                 .propId = PROP1,
-                .vehicleAreas = val(VehicleAreaZone::ROW_1_LEFT),
+                .vehicleAreas = toInt(VehicleAreaZone::ROW_1_LEFT),
                 .flags = SubscribeFlags::HAL_EVENT
             },
         });
@@ -67,7 +67,7 @@
         {
             SubscribeOptions {
                 .propId = PROP1,
-                .vehicleAreas = val(VehicleAreaZone::ROW_1_LEFT),
+                .vehicleAreas = toInt(VehicleAreaZone::ROW_1_LEFT),
                 .flags = SubscribeFlags::HAL_EVENT
             },
             SubscribeOptions {
@@ -87,7 +87,7 @@
 
     std::list<sp<HalClient>> clientsToProp1() {
         return manager.getSubscribedClients(PROP1,
-                                            val(VehicleAreaZone::ROW_1_LEFT),
+                                            toInt(VehicleAreaZone::ROW_1_LEFT),
                                             SubscribeFlags::DEFAULT);
     }
 
@@ -104,7 +104,7 @@
 
     auto clients = manager.getSubscribedClients(
             PROP1,
-            val(VehicleAreaZone::ROW_1_LEFT),
+            toInt(VehicleAreaZone::ROW_1_LEFT),
             SubscribeFlags::HAL_EVENT);
 
     ASSERT_ALL_EXISTS({cb1, cb2}, extractCallbacks(clients));
@@ -116,21 +116,21 @@
     // Wrong zone
     auto clients = manager.getSubscribedClients(
             PROP1,
-            val(VehicleAreaZone::ROW_2_LEFT),
+            toInt(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),
+            toInt(VehicleAreaZone::ROW_1_LEFT),
             SubscribeFlags::HAL_EVENT);
     ASSERT_TRUE(clients.empty());
 
     // Wrong flag
     clients = manager.getSubscribedClients(
             PROP1,
-            val(VehicleAreaZone::ROW_1_LEFT),
+            toInt(VehicleAreaZone::ROW_1_LEFT),
             SubscribeFlags::SET_CALL);
     ASSERT_TRUE(clients.empty());
 }
@@ -140,7 +140,7 @@
 
     auto clients = manager.getSubscribedClients(
             PROP1,
-            val(VehicleAreaZone::ROW_1_LEFT),
+            toInt(VehicleAreaZone::ROW_1_LEFT),
             SubscribeFlags::DEFAULT);
     ASSERT_EQ((size_t) 1, clients.size());
     ASSERT_EQ(cb1, clients.front()->getCallback());
@@ -151,18 +151,18 @@
         {
             SubscribeOptions {
                 .propId = PROP1,
-                .vehicleAreas = val(VehicleAreaZone::ROW_2),
+                .vehicleAreas = toInt(VehicleAreaZone::ROW_2),
                 .flags = SubscribeFlags::DEFAULT
             }
         }));
 
     clients = manager.getSubscribedClients(PROP1,
-                                           val(VehicleAreaZone::ROW_1_LEFT),
+                                           toInt(VehicleAreaZone::ROW_1_LEFT),
                                            SubscribeFlags::DEFAULT);
     ASSERT_ALL_EXISTS({cb1}, extractCallbacks(clients));
 
     clients = manager.getSubscribedClients(PROP1,
-                                           val(VehicleAreaZone::ROW_2),
+                                           toInt(VehicleAreaZone::ROW_2),
                                            SubscribeFlags::DEFAULT);
     ASSERT_ALL_EXISTS({cb1}, extractCallbacks(clients));
 }
diff --git a/vehicle/2.0/default/tests/VehicleHalManager_test.cpp b/vehicle/2.0/default/tests/VehicleHalManager_test.cpp
index 1410ddf..6ef1205 100644
--- a/vehicle/2.0/default/tests/VehicleHalManager_test.cpp
+++ b/vehicle/2.0/default/tests/VehicleHalManager_test.cpp
@@ -22,6 +22,7 @@
 #include <vehicle_hal_manager/VehiclePropConfigIndex.h>
 #include <VehicleHal.h>
 #include <vehicle_hal_manager/VehicleHalManager.h>
+#include <utils/SystemClock.h>
 #include "vehicle_hal_manager/SubscriptionManager.h"
 
 #include "VehicleHalTestUtils.h"
@@ -35,6 +36,9 @@
 
 using namespace std::placeholders;
 
+constexpr char kCarMake[] = "Default Car";
+constexpr int kRetriablePropMockedAttempts = 3;
+
 class MockedVehicleHal : public VehicleHal {
 public:
     MockedVehicleHal() {
@@ -46,35 +50,87 @@
         return mConfigs;
     }
 
-    VehiclePropValuePtr get(VehicleProperty property,
-                 int32_t areaId,
-                 status_t* outStatus) override {
-        *outStatus = OK;
-        return getValuePool()->obtain(mValues[property]);
+    VehiclePropValuePtr get(const VehiclePropValue& requestedPropValue,
+             StatusCode* outStatus) override {
+        *outStatus = StatusCode::OK;
+        VehiclePropValuePtr pValue;
+        VehicleProperty property = 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;
+            default:
+                auto key = makeKey(property, areaId);
+                if (mValues.count(key) == 0) {
+                    ALOGW("");
+                }
+                pValue = getValuePool()->obtain(mValues[key]);
+        }
+
+        if (*outStatus == StatusCode::OK && pValue.get() != nullptr) {
+            pValue->prop = property;
+            pValue->areaId = areaId;
+            pValue->timestamp = elapsedRealtimeNano();
+        }
+
+        return pValue;
     }
 
-    status_t set(const VehiclePropValue& propValue) override {
-        mValues[propValue.prop] = propValue;
-        return OK;
+    StatusCode set(const VehiclePropValue& propValue) override {
+        if (VehicleProperty::MIRROR_FOLD == propValue.prop
+                && mirrorFoldAttemptsLeft-- > 0) {
+            return StatusCode::TRY_AGAIN;
+        }
+
+        mValues[makeKey(propValue)] = propValue;
+        return StatusCode::OK;
     }
 
-    status_t subscribe(VehicleProperty property,
+    StatusCode subscribe(VehicleProperty property,
                        int32_t areas,
                        float sampleRate) override {
-        return OK;
+        return StatusCode::OK;
     }
 
-    status_t unsubscribe(VehicleProperty property) override {
-        return OK;
+    StatusCode unsubscribe(VehicleProperty property) override {
+        return StatusCode::OK;
     }
 
     void sendPropEvent(recyclable_ptr<VehiclePropValue> value) {
         doHalEvent(std::move(value));
     }
 
+    void sendHalError(StatusCode error, VehicleProperty 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(VehicleProperty prop, int32_t area) const {
+        return (static_cast<int64_t>(prop) << 32) | area;
+    }
+
 private:
     std::vector<VehiclePropConfig> mConfigs;
-    std::unordered_map<VehicleProperty, VehiclePropValue> mValues;
+    std::unordered_map<int64_t, VehiclePropValue> mValues;
 };
 
 class VehicleHalManagerTest : public ::testing::Test {
@@ -90,37 +146,70 @@
         manager.reset(nullptr);
         hal.reset(nullptr);
     }
+public:
+    void invokeGet(VehicleProperty 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: "
+                            << enumToHexString(requestedPropValue.prop);
+
+        actualValue = refValue;
+        actualStatusCode = refStatus;
+    }
 
 public:
+    VehiclePropValue actualValue;
+    StatusCode actualStatusCode;
+
     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) {
+            [&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(init_hidl_vec({VehicleProperty::HVAC_FAN_SPEED}),
-                            [&called] (const hidl_vec<VehiclePropConfig>& c) {
+            [&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) {
@@ -138,6 +227,25 @@
     ASSERT_TRUE(called);  // Verify callback received.
 }
 
+TEST_F(VehicleHalManagerTest, halErrorEvent) {
+    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);
+
+    hal->sendHalError(StatusCode::TRY_AGAIN, PROP, 0 /* area id*/);
+}
+
 TEST_F(VehicleHalManagerTest, subscribe) {
     const VehicleProperty PROP = VehicleProperty::DISPLAY_BRIGHTNESS;
 
@@ -161,9 +269,9 @@
     auto& receivedEnvents = cb->getReceivedEvents();
 
     ASSERT_TRUE(cb->waitForExpectedEvents(0)) << " Unexpected events received: "
-              << receivedEnvents.size()
-              << (receivedEnvents.size() > 0
-                      ? toString(receivedEnvents.front()[0]) : "");
+                                              << receivedEnvents.size()
+                                              << (receivedEnvents.size() > 0
+                                                  ? toString(receivedEnvents.front()[0]) : "");
 
     auto subscribedValue = objectPool->obtain(VehiclePropertyType::INT32);
     subscribedValue->prop = PROP;
@@ -174,13 +282,143 @@
     hal->sendPropEvent(std::move(subscribedValue));
 
     ASSERT_TRUE(cb->waitForExpectedEvents(1)) << "Events received: "
-            << receivedEnvents.size();
+                                              << receivedEnvents.size();
 
     ASSERT_EQ(toString(actualValue),
               toString(cb->getReceivedEvents().front()[0]));
 }
 
-TEST_F(HalClientVectorTest, basic) {
+TEST_F(VehicleHalManagerTest, subscribe_WriteOnly) {
+    const VehicleProperty PROP = VehicleProperty::HVAC_SEAT_TEMPERATURE;
+
+    sp<MockedVehicleCallback> cb = new MockedVehicleCallback();
+
+    hidl_vec<SubscribeOptions> options = init_hidl_vec(
+        {
+            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_StaticString) {
+    invokeGet(VehicleProperty::INFO_MAKE, 0);
+
+    ASSERT_EQ(StatusCode::OK, actualStatusCode);
+    ASSERT_EQ(VehicleProperty::INFO_MAKE, actualValue.prop);
+    ASSERT_STREQ(kCarMake, actualValue.value.stringValue.c_str());
+}
+
+TEST_F(VehicleHalManagerTest, get_NegativeCases) {
+    // Write-only property must fail.
+    invokeGet(VehicleProperty::HVAC_SEAT_TEMPERATURE, 0);
+    ASSERT_EQ(StatusCode::INVALID_ARG, actualStatusCode);
+
+    // Unknown property must fail.
+    invokeGet(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(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 = 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 = 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 = 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);
diff --git a/vehicle/2.0/default/tests/VehicleHalTestUtils.h b/vehicle/2.0/default/tests/VehicleHalTestUtils.h
index b3b3ffa..16d0be9 100644
--- a/vehicle/2.0/default/tests/VehicleHalTestUtils.h
+++ b/vehicle/2.0/default/tests/VehicleHalTestUtils.h
@@ -44,18 +44,37 @@
         .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,
-                                         }
-                                     }),
+             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 = VehicleProperty::HVAC_SEAT_TEMPERATURE,
+        .access = VehiclePropertyAccess::WRITE,
+        .changeMode = VehiclePropertyChangeMode::ON_SET,
+        .permissionModel = VehiclePermissionModel::NO_RESTRICTION,
+        .supportedAreas = static_cast<int32_t>(
+            VehicleAreaZone::ROW_1_LEFT | VehicleAreaZone::ROW_1_RIGHT),
+        .areaConfigs = init_hidl_vec({
+             VehicleAreaConfig {
+                 .areaId = toInt(VehicleAreaZone::ROW_1_LEFT),
+                 .minInt32Value = 64,
+                 .maxInt32Value = 80},
+             VehicleAreaConfig {
+                 .areaId = toInt(VehicleAreaZone::ROW_1_RIGHT),
+                 .minInt32Value = 64,
+                 .maxInt32Value = 80,
+             }
+         }),
     },
 
     {
@@ -82,12 +101,23 @@
                                              .maxInt32Value = 10
                                          }
                                      })
+    },
+
+    {
+        .prop = VehicleProperty::MIRROR_FOLD,
+        .access = VehiclePropertyAccess::READ_WRITE,
+        .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
+        .permissionModel = VehiclePermissionModel::OEM_ONLY,
+
     }
 };
 
 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::vehicle::V2_0::IVehicleCallback follow.
     Return<void> onPropertyEvent(
@@ -102,9 +132,9 @@
     Return<void> onPropertySet(const VehiclePropValue& value) override {
         return Return<void>();
     }
-    Return<void> onError(StatusCode errorCode,
-                         VehicleProperty propId,
-                         VehiclePropertyOperation operation) override {
+    Return<void> onPropertySetError(StatusCode errorCode,
+                                    VehicleProperty propId,
+                                    int32_t areaId) override {
         return Return<void>();
     }
 
@@ -129,16 +159,14 @@
         mReceivedEvents.clear();
     }
 
-    const std::vector<hidl_vec<VehiclePropValue>>& getReceivedEvents() {
+    const std::vector<HidlVecOfValues>& getReceivedEvents() {
         return mReceivedEvents;
     }
 
 private:
-    using MuxGuard = std::lock_guard<std::mutex>;
-
     std::mutex mLock;
     std::condition_variable mEventCond;
-    std::vector<hidl_vec<VehiclePropValue>> mReceivedEvents;
+    std::vector<HidlVecOfValues> mReceivedEvents;
 };
 
 template<typename T>
@@ -172,7 +200,7 @@
 
 template<typename T>
 inline std::string enumToHexString(T value) {
-    return hexString(val(value));
+    return hexString(toInt(value));
 }
 
 template <typename T>