Add on-property-set listener to VNS

Change-Id: Id0b0fb15f42e1af0ca4899ea625bcc77f1320db7
Fix: b/31656523
diff --git a/tests/libvehiclenetwork-java-test/src/com/android/car/vehiclenetwork/libtest/VehicleNetworkMockedTest.java b/tests/libvehiclenetwork-java-test/src/com/android/car/vehiclenetwork/libtest/VehicleNetworkMockedTest.java
index 7059f7d..531b3d4 100644
--- a/tests/libvehiclenetwork-java-test/src/com/android/car/vehiclenetwork/libtest/VehicleNetworkMockedTest.java
+++ b/tests/libvehiclenetwork-java-test/src/com/android/car/vehiclenetwork/libtest/VehicleNetworkMockedTest.java
@@ -21,6 +21,7 @@
 import android.util.Log;
 
 import com.android.car.vehiclenetwork.VehicleNetwork;
+import com.android.car.vehiclenetwork.VehicleNetwork.SubscribeFlags;
 import com.android.car.vehiclenetwork.VehicleNetwork.VehicleNetworkHalMock;
 import com.android.car.vehiclenetwork.VehicleNetwork.VehicleNetworkListener;
 import com.android.car.vehiclenetwork.VehicleNetworkConsts;
@@ -343,6 +344,48 @@
         mVehicleNetwork.unsubscribe(PROPERTY);
     }
 
+    public void testSubscribe_OnPropertySet() throws Exception {
+        final int PROPERTY = CUSTOM_PROPERTY_ZONED_INT32_VEC3;
+        final int ZONE = VehicleZone.VEHICLE_ZONE_ROW_1_LEFT;
+        final int[] VALUES = new int[] {10, 20, 30};
+        mVehicleNetwork.startMocking(mVehicleHalMock);
+        mVehicleNetwork.subscribe(PROPERTY, 0, ZONE, SubscribeFlags.SET_CALL);
+
+        mVehicleNetwork.setZonedIntVectorProperty(PROPERTY, ZONE, VALUES);
+
+        VehiclePropValue expectedValue = VehiclePropValueUtil.createZonedIntVectorValue(
+                PROPERTY, ZONE, VALUES, 0);
+
+        assertTrue(mListener.waitForOnPropertySet(TIMEOUT_MS, expectedValue));
+
+        mVehicleNetwork.injectEvent(expectedValue);
+        // Not subscribed to HAL events. No notifications expected here.
+        assertFalse(mListener.waitForEvent(TIMEOUT_MS, expectedValue));
+
+        mVehicleNetwork.unsubscribe(PROPERTY);
+    }
+
+    public void testSubscribe_HalEventAndOnPropertySet() throws Exception {
+        final int PROPERTY = CUSTOM_PROPERTY_ZONED_INT32_VEC3;
+        final int ZONE = VehicleZone.VEHICLE_ZONE_ROW_1_LEFT;
+        final int[] VALUES = new int[] {10, 20, 30};
+        mVehicleNetwork.startMocking(mVehicleHalMock);
+        mVehicleNetwork.subscribe(PROPERTY, 0, ZONE,
+                SubscribeFlags.SET_CALL | SubscribeFlags.HAL_EVENT);
+
+        mVehicleNetwork.setZonedIntVectorProperty(PROPERTY, ZONE, VALUES);
+
+        VehiclePropValue expectedValue = VehiclePropValueUtil.createZonedIntVectorValue(
+                PROPERTY, ZONE, VALUES, 0);
+
+        assertTrue(mListener.waitForOnPropertySet(TIMEOUT_MS, expectedValue));
+
+        mVehicleNetwork.injectEvent(expectedValue);
+        assertTrue(mListener.waitForEvent(TIMEOUT_MS, expectedValue));
+
+        mVehicleNetwork.unsubscribe(PROPERTY);
+    }
+
     public void testGetPropertyFailsForCustom() {
         try {
             mVehicleNetwork.getProperty(CUSTOM_PROPERTY_INT32);
@@ -366,10 +409,12 @@
         int mErrorProperty;
         int mErrorOperation;
         VehiclePropValues mValuesReceived;
+        VehiclePropValue mPropertySetValue;
 
         private final Semaphore mRestartWait = new Semaphore(0);
         private final Semaphore mErrorWait = new Semaphore(0);
         private final Semaphore mEventWait = new Semaphore(0);
+        private final Semaphore mOnPropertySetWait = new Semaphore(0);
 
         @Override
         public void onVehicleNetworkEvents(VehiclePropValues values) {
@@ -403,6 +448,12 @@
             mRestartWait.release();
         }
 
+        @Override
+        public void onPropertySet(VehiclePropValue value) {
+            mPropertySetValue = value;
+            mOnPropertySetWait.release();
+        }
+
         public boolean waitForHalRestartAndAssert(long timeoutMs, boolean expectedInMocking)
                 throws Exception {
             if (!mRestartWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
@@ -423,6 +474,17 @@
                     VehiclePropValueUtil.toString(mValuesReceived.getValues(0)));
             return true;
         }
+
+        public boolean waitForOnPropertySet(long timeoutMs, VehiclePropValue expected)
+                throws InterruptedException {
+            if (!mOnPropertySetWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
+                Log.w(TAG, "Timed out waiting for onPropertySet.");
+                return false;
+            }
+            assertEquals(VehiclePropValueUtil.toString(expected),
+                    VehiclePropValueUtil.toString(mPropertySetValue));
+            return true;
+        }
     }
 
     private interface VehiclePropertyHandler {
diff --git a/tests/libvehiclenetwork-java-test/src/com/android/car/vehiclenetwork/libtest/VehicleNetworkTest.java b/tests/libvehiclenetwork-java-test/src/com/android/car/vehiclenetwork/libtest/VehicleNetworkTest.java
index 6788bd5..7a10c3f 100644
--- a/tests/libvehiclenetwork-java-test/src/com/android/car/vehiclenetwork/libtest/VehicleNetworkTest.java
+++ b/tests/libvehiclenetwork-java-test/src/com/android/car/vehiclenetwork/libtest/VehicleNetworkTest.java
@@ -221,6 +221,9 @@
             // TODO Auto-generated method stub
         }
 
+        @Override
+        public void onPropertySet(VehiclePropValue value) { }
+
         private synchronized boolean waitForEvent(Integer prop, long timeoutMs)
                 throws InterruptedException {
             long now = SystemClock.elapsedRealtime();
diff --git a/tests/libvehiclenetwork-native-test/IVehicleNetworkMockedTest.cpp b/tests/libvehiclenetwork-native-test/IVehicleNetworkMockedTest.cpp
index 82a3765..4bd5449 100644
--- a/tests/libvehiclenetwork-native-test/IVehicleNetworkMockedTest.cpp
+++ b/tests/libvehiclenetwork-native-test/IVehicleNetworkMockedTest.cpp
@@ -105,13 +105,81 @@
 TEST_F(IVehicleNetworkMockedTest, halPropertyError) {
     sp<IVehicleNetworkTestListener> listener(new IVehicleNetworkTestListener());
     ASSERT_EQ(NO_ERROR, mVN->startMocking(mHalMock));
-    ASSERT_EQ(NO_ERROR, mVN->subscribe(listener, TEST_PROPERTY_INT32, 0, 0));
+    ASSERT_EQ(NO_ERROR, mVN->subscribe(listener, TEST_PROPERTY_ZONED_INT32, 0, 0, 0));
     const int ERROR_CODE = -123;
     const int OPERATION_CODE = 4567;
-    ASSERT_EQ(NO_ERROR, mVN->injectHalError(ERROR_CODE, TEST_PROPERTY_INT32, OPERATION_CODE));
+    ASSERT_EQ(NO_ERROR, mVN->injectHalError(
+            ERROR_CODE, TEST_PROPERTY_ZONED_INT32, OPERATION_CODE));
     listener->waitForHalError(WAIT_TIMEOUT_NS);
-    ASSERT_TRUE(listener->isErrorMatching(ERROR_CODE, TEST_PROPERTY_INT32, OPERATION_CODE));
-    mVN->unsubscribe(listener, TEST_PROPERTY_INT32);
+    ASSERT_TRUE(listener->isErrorMatching(
+            ERROR_CODE, TEST_PROPERTY_ZONED_INT32, OPERATION_CODE));
+    mVN->unsubscribe(listener, TEST_PROPERTY_ZONED_INT32);
+}
+
+TEST_F(IVehicleNetworkMockedTest, subscribeToSetProperty) {
+    sp<IVehicleNetworkTestListener> listener(new IVehicleNetworkTestListener());
+    ASSERT_EQ(NO_ERROR, mVN->startMocking(mHalMock));
+    ASSERT_EQ(NO_ERROR, mVN->subscribe(listener,
+                                       TEST_PROPERTY_BOOLEAN,
+                                       0 /* rate */,
+                                       0 /* zones */,
+                                       SubscribeFlags::SET_CALL));
+    vehicle_prop_value_t v = {
+            .prop = TEST_PROPERTY_BOOLEAN,
+            .value_type = VEHICLE_VALUE_TYPE_BOOLEAN,
+            .timestamp = elapsedRealtimeNano(),
+            .value = {
+                    .boolean_value = VEHICLE_TRUE
+            }
+    };
+
+    mVN->setProperty(v);
+
+    std::unique_ptr<ScopedVehiclePropValue> actualValue(new ScopedVehiclePropValue());
+    ASSERT_EQ(NO_ERROR, listener->waitForOnPropertySet(WAIT_TIMEOUT_NS, &actualValue));
+
+    ASSERT_EQ(v.prop, actualValue->value.prop);
+    ASSERT_EQ(v.value.boolean_value, actualValue->value.value.boolean_value);
+
+    mVN->unsubscribe(listener, TEST_PROPERTY_BOOLEAN);
+}
+
+TEST_F(IVehicleNetworkMockedTest, subscribeToSetPropertyZoned) {
+    const int PROP = TEST_PROPERTY_ZONED_INT32;
+    const int SUBSCRIBED_ZONE = VEHICLE_ZONE_ROW_1_RIGHT;
+    const int NON_SUBSCRIBED_ZONE = VEHICLE_ZONE_ROW_1_LEFT;
+
+    sp<IVehicleNetworkTestListener> listener(new IVehicleNetworkTestListener());
+    ASSERT_EQ(NO_ERROR, mVN->startMocking(mHalMock));
+    ASSERT_EQ(NO_ERROR, mVN->subscribe(listener,
+                                       PROP,
+                                       0 /* rate */,
+                                       SUBSCRIBED_ZONE,
+                                       SubscribeFlags::SET_CALL));
+    vehicle_prop_value_t v = {
+            .prop = PROP,
+            .value_type = VEHICLE_VALUE_TYPE_ZONED_INT32,
+            .timestamp = elapsedRealtimeNano(),
+            .zone = NON_SUBSCRIBED_ZONE,  // We do not expect notifications for this zone.
+            .value = {
+                    .int32_value = 42
+            }
+    };
+
+    mVN->setProperty(v);
+
+    std::unique_ptr<ScopedVehiclePropValue> actualValue(new ScopedVehiclePropValue());
+
+    ASSERT_EQ(-ETIMEDOUT, listener->waitForOnPropertySet(WAIT_TIMEOUT_NS, &actualValue));
+
+    v.zone = SUBSCRIBED_ZONE;
+    mVN->setProperty(v);
+    ASSERT_EQ(NO_ERROR, listener->waitForOnPropertySet(WAIT_TIMEOUT_NS, &actualValue));
+
+    ASSERT_EQ(v.prop, actualValue->value.prop);
+    ASSERT_EQ(v.value.int32_value, actualValue->value.value.int32_value);
+
+    mVN->unsubscribe(listener, PROP);
 }
 
 }; // namespace android
diff --git a/tests/libvehiclenetwork-native-test/IVehicleNetworkTest.cpp b/tests/libvehiclenetwork-native-test/IVehicleNetworkTest.cpp
index eafa1d9..2d13e26 100644
--- a/tests/libvehiclenetwork-native-test/IVehicleNetworkTest.cpp
+++ b/tests/libvehiclenetwork-native-test/IVehicleNetworkTest.cpp
@@ -158,9 +158,11 @@
     for (auto& config : properties->getList()) {
         String8 msg = String8::format("subscribing property 0x%x\n", config->prop);
         std::cout<<msg.string();
-        status_t r = vn->subscribe(listener, config->prop, config->max_sample_rate, 0);
+        status_t r = vn->subscribe(listener, config->prop, config->max_sample_rate,
+                                   0,  /* zones */
+                                   SubscribeFlags::HAL_EVENT);
         if (((config->access & VEHICLE_PROP_ACCESS_READ) == 0) ||
-                (config->change_mode == VEHICLE_PROP_CHANGE_MODE_STATIC)) { // cannot subsctibe
+                (config->change_mode == VEHICLE_PROP_CHANGE_MODE_STATIC)) { // cannot subscribe
             ASSERT_TRUE(r != NO_ERROR);
         } else {
             if ((config->prop >= (int32_t)VEHICLE_PROPERTY_INTERNAL_START) &&
diff --git a/tests/libvehiclenetwork-native-test/IVehicleNetworkTestListener.h b/tests/libvehiclenetwork-native-test/IVehicleNetworkTestListener.h
index 47a3a28..a25b9da 100644
--- a/tests/libvehiclenetwork-native-test/IVehicleNetworkTestListener.h
+++ b/tests/libvehiclenetwork-native-test/IVehicleNetworkTestListener.h
@@ -17,6 +17,8 @@
 #ifndef ANDROID_IVEHICLENETWORK_TEST_LISTER_H
 #define ANDROID_IVEHICLENETWORK_TEST_LISTER_H
 
+#include <queue>
+
 #include <IVehicleNetworkListener.h>
 
 namespace android {
@@ -24,7 +26,9 @@
 class IVehicleNetworkTestListener : public BnVehicleNetworkListener {
 public:
     IVehicleNetworkTestListener() :
-        mHalRestartCount(0) {};
+        mHalRestartCount(0),
+        mOnPropertySetValues()
+    {};
 
     virtual void onEvents(sp<VehiclePropValueListHolder>& events) {
         String8 msg("events ");
@@ -60,6 +64,16 @@
         mHalRestartCondition.signal();
     }
 
+    virtual void onPropertySet(const vehicle_prop_value_t& value) {
+        Mutex::Autolock autolock(mOnPropertySetLock);
+
+        std::unique_ptr<ScopedVehiclePropValue> scopedValue(new ScopedVehiclePropValue);
+        VehiclePropValueUtil::copyVehicleProp(&scopedValue->value,
+                                              value);
+        mOnPropertySetValues.push(std::move(scopedValue));
+        mOnPropertySetCondition.signal();
+    }
+
     void waitForEvents(nsecs_t reltime) {
         Mutex::Autolock autolock(mLock);
         mCondition.waitRelative(mLock, reltime);
@@ -99,6 +113,21 @@
         mHalErrorCondition.waitRelative(mHalErrorLock, reltime);
     }
 
+    status_t waitForOnPropertySet(nsecs_t reltime, std::unique_ptr<ScopedVehiclePropValue>* out) {
+        Mutex::Autolock autolock(mOnPropertySetLock);
+
+        status_t r = mOnPropertySetValues.empty()
+                     ? mOnPropertySetCondition.waitRelative(mOnPropertySetLock, reltime)
+                     : NO_ERROR;
+
+        if (r == NO_ERROR) {
+            VehiclePropValueUtil::copyVehicleProp(&(*out)->value,  /* dest */
+                                                  mOnPropertySetValues.back()->value /* src */);
+            mOnPropertySetValues.pop();
+        }
+        return r;
+    }
+
     bool isErrorMatching(int32_t errorCode, int32_t property, int32_t operation) {
         Mutex::Autolock autolock(mHalErrorLock);
         return mErrorCode == errorCode && mProperty == property && mOperation == operation;
@@ -129,6 +158,10 @@
     int32_t mErrorCode;
     int32_t mProperty;
     int32_t mOperation;
+
+    Mutex mOnPropertySetLock;
+    Condition mOnPropertySetCondition;
+    std::queue<std::unique_ptr<ScopedVehiclePropValue>> mOnPropertySetValues;
 };
 
 }; // namespace android
diff --git a/tests/libvehiclenetwork-native-test/TestPropertyDef.c b/tests/libvehiclenetwork-native-test/TestPropertyDef.c
index 5eacd17..6fa3e9c 100644
--- a/tests/libvehiclenetwork-native-test/TestPropertyDef.c
+++ b/tests/libvehiclenetwork-native-test/TestPropertyDef.c
@@ -58,6 +58,7 @@
         .config_flags = 0x1234567, // just random
         .min_sample_rate = 0,
         .max_sample_rate = 0,
+        .vehicle_zone_flags = VEHICLE_ZONE_ROW_1_LEFT | VEHICLE_ZONE_ROW_1_RIGHT,
         .hal_data = NULL,
     },
     {
diff --git a/tests/libvehiclenetwork-native-test/VehicleNetworkTestListener.h b/tests/libvehiclenetwork-native-test/VehicleNetworkTestListener.h
index e661eff..637d9f2 100644
--- a/tests/libvehiclenetwork-native-test/VehicleNetworkTestListener.h
+++ b/tests/libvehiclenetwork-native-test/VehicleNetworkTestListener.h
@@ -69,6 +69,10 @@
         //TODO cannot test this in native world without plumbing mocking
     }
 
+    virtual void onPropertySet(const vehicle_prop_value_t& /* value */) {
+        // TODO
+    }
+
     void waitForEvents(nsecs_t reltime) {
         Mutex::Autolock autolock(mLock);
         mCondition.waitRelative(mLock, reltime);
diff --git a/tests/vehicle_hal_test/src/com/android/car/vehiclenetwork/haltest/RecordingVehicleNetworkListener.java b/tests/vehicle_hal_test/src/com/android/car/vehiclenetwork/haltest/RecordingVehicleNetworkListener.java
index c10dbd0..61f66f3 100644
--- a/tests/vehicle_hal_test/src/com/android/car/vehiclenetwork/haltest/RecordingVehicleNetworkListener.java
+++ b/tests/vehicle_hal_test/src/com/android/car/vehiclenetwork/haltest/RecordingVehicleNetworkListener.java
@@ -113,6 +113,11 @@
         // TODO Auto-generated method stub
     }
 
+    @Override
+    public void onPropertySet(VehiclePropValue value) {
+        // TODO
+    }
+
     // To be used to compare expected vs recorded values.
     private static class NormalizedValue {
         private final int propertyId;