Add CTS test for setPropertiesAsync.

Test: Presubmit
Bug: 264719384

Change-Id: Ice9c72ffe49f040044502b8c353041283c06e692
diff --git a/tests/tests/car/src/android/car/cts/CarPropertyManagerTest.java b/tests/tests/car/src/android/car/cts/CarPropertyManagerTest.java
index a212f15..a71d148 100644
--- a/tests/tests/car/src/android/car/cts/CarPropertyManagerTest.java
+++ b/tests/tests/car/src/android/car/cts/CarPropertyManagerTest.java
@@ -18,10 +18,12 @@
 
 import static android.car.cts.utils.ShellPermissionUtils.runWithShellPermissionIdentity;
 import static android.car.hardware.property.CarPropertyManager.GetPropertyResult;
+import static android.car.hardware.property.CarPropertyManager.SetPropertyResult;
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import static org.junit.Assert.assertThrows;
 import static org.junit.Assume.assumeTrue;
 
 import android.car.Car;
@@ -4862,8 +4864,8 @@
 
             int expectedResultCount = pendingRequests.size();
 
-            TestGetPropertyAsyncCallback testGetPropertyAsyncCallback =
-                    new TestGetPropertyAsyncCallback(pendingRequests);
+            TestPropertyAsyncCallback testGetPropertyAsyncCallback =
+                    new TestPropertyAsyncCallback(pendingRequests);
             mCarPropertyManager.getPropertiesAsync(
                     getPropertyRequests,
                     /* cancellationSignal= */ null,
@@ -4922,8 +4924,9 @@
         }
     }
 
-    private static final class TestGetPropertyAsyncCallback implements
-            CarPropertyManager.GetPropertyCallback {
+    private static final class TestPropertyAsyncCallback implements
+            CarPropertyManager.GetPropertyCallback,
+            CarPropertyManager.SetPropertyCallback {
         private final CountDownLatch mCountDownLatch;
         private final Set<Integer> mPendingRequests;
         private final int mNumberOfRequests;
@@ -4935,7 +4938,7 @@
         @GuardedBy("mLock")
         private final List<PropIdAreaId> mReceivedPropIdAreaIds = new ArrayList();
 
-        TestGetPropertyAsyncCallback(Set<Integer> pendingRequests) {
+        TestPropertyAsyncCallback(Set<Integer> pendingRequests) {
             mNumberOfRequests = pendingRequests.size();
             mCountDownLatch = new CountDownLatch(mNumberOfRequests);
             mPendingRequests = pendingRequests;
@@ -4971,10 +4974,15 @@
         }
 
         @Override
-        public void onFailure(@NonNull CarPropertyManager.GetPropertyError getPropertyError) {
-            int requestId = getPropertyError.getRequestId();
-            int propId = getPropertyError.getPropertyId();
-            int areaId = getPropertyError.getAreaId();
+        public void onSuccess(@NonNull SetPropertyResult setPropertyResult) {
+            // TODO(b/264719384): Implement this.
+        }
+
+        @Override
+        public void onFailure(@NonNull CarPropertyManager.PropertyAsyncError error) {
+            int requestId = error.getRequestId();
+            int propId = error.getPropertyId();
+            int areaId = error.getAreaId();
             synchronized (mLock) {
                 if (!mPendingRequests.contains(requestId)) {
                     mErrorList.add(toMsg(requestId, propId, areaId) + " not present");
@@ -5966,6 +5974,87 @@
                 Car.PERMISSION_CONTROL_ADAS_STATES);
     }
 
+    /**
+     * Test for {@link CarPropertyManager#setPropertiesAsync}
+     *
+     * Generates SetPropertyRequest objects for supported writable properties and verifies if there
+     * are no exceptions or request timeouts.
+     */
+    @Test
+    @ApiTest(apis = {"android.car.hardware.property.CarPropertyManager#setPropertiesAsync(List, "
+            + "long, CancellationSignal, Executor, SetPropertyCallback)",
+            "android.car.hardware.property.CarPropertyManager#generateSetPropertyRequest"})
+    public void testSetAllSupportedReadablePropertiesAsync() throws Exception {
+        runWithShellPermissionIdentity(() -> {
+            Executor executor = Executors.newFixedThreadPool(1);
+            Set<Integer> pendingRequests = new ArraySet<>();
+            List<CarPropertyManager.SetPropertyRequest<?>> setPropertyRequests =
+                    new ArrayList<>();
+            Set<PropIdAreaId> requestPropIdAreaIds = new ArraySet<>();
+            List<CarPropertyConfig> configs = mCarPropertyManager.getPropertyList();
+            for (CarPropertyConfig cfg : configs) {
+                if (cfg.getAccess() != CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_WRITE
+                        && cfg.getAccess()
+                                != CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE) {
+                    continue;
+                }
+                int[] areaIds = cfg.getAreaIds();
+                int propId = cfg.getPropertyId();
+                for (int areaId : areaIds) {
+                    Object defaultValue = VehiclePropertyVerifier.getDefaultValue(
+                            cfg.getPropertyType());
+                    if (defaultValue == null) {
+                        // Skip setting MIXED type property since we do not have know what should
+                        // be the correct format for it.
+                        continue;
+                    }
+                    CarPropertyManager.SetPropertyRequest spr =
+                            mCarPropertyManager.generateSetPropertyRequest(propId, areaId,
+                                    defaultValue);
+                    setPropertyRequests.add(spr);
+                    pendingRequests.add(spr.getRequestId());
+                    requestPropIdAreaIds.add(new PropIdAreaId(propId, areaId));
+                }
+            }
+
+            int expectedResultCount = pendingRequests.size();
+
+            TestPropertyAsyncCallback callback = new TestPropertyAsyncCallback(pendingRequests);
+            mCarPropertyManager.setPropertiesAsync(setPropertyRequests, /* timeoutInMs= */ 1000,
+                    /* cancellationSignal= */ null, executor, callback);
+
+            // TODO(b/264719384): Add validation for the results.
+        });
+    }
+
+    @Test
+    @ApiTest(apis = {"android.car.hardware.property.CarPropertyManager#generateSetPropertyRequest"})
+    public void testGenerateSetPropertyRequest() throws Exception {
+        assertThrows(NullPointerException.class, () -> {
+            mCarPropertyManager.generateSetPropertyRequest(VehiclePropertyIds.FUEL_LEVEL,
+                    /* areaId= */ 1, /* value= */ null);
+        });
+
+        CarPropertyManager.SetPropertyRequest request;
+        request = mCarPropertyManager.generateSetPropertyRequest(VehiclePropertyIds.FUEL_LEVEL,
+                /* areaId= */ 1, /* value= */ Integer.valueOf(1));
+
+        int requestId1 = request.getRequestId();
+        assertThat(request.getPropertyId()).isEqualTo(VehiclePropertyIds.FUEL_LEVEL);
+        assertThat(request.getAreaId()).isEqualTo(1);
+        assertThat(request.getValue()).isEqualTo(1);
+
+        request = mCarPropertyManager.generateSetPropertyRequest(VehiclePropertyIds.INFO_VIN,
+                /* areaId= */ 2, /* value= */ new String("1234"));
+
+        int requestId2 = request.getRequestId();
+        assertThat(request.getPropertyId()).isEqualTo(VehiclePropertyIds.INFO_VIN);
+        assertThat(request.getAreaId()).isEqualTo(2);
+        assertThat(request.getValue()).isEqualTo(new String("1234"));
+        assertWithMessage("generateSetPropertyRequest must generate unique IDs").that(requestId1)
+                .isNotEqualTo(requestId2);
+    }
+
     private int getCounterBySampleRate(float maxSampleRateHz) {
         if (Float.compare(maxSampleRateHz, (float) FAST_OR_FASTEST_EVENT_COUNTER) > 0) {
             return FAST_OR_FASTEST_EVENT_COUNTER;
diff --git a/tests/tests/car/src/android/car/cts/utils/VehiclePropertyVerifier.java b/tests/tests/car/src/android/car/cts/utils/VehiclePropertyVerifier.java
index 5fd5c4b..c0bb570 100644
--- a/tests/tests/car/src/android/car/cts/utils/VehiclePropertyVerifier.java
+++ b/tests/tests/car/src/android/car/cts/utils/VehiclePropertyVerifier.java
@@ -35,9 +35,9 @@
 import android.car.hardware.CarPropertyValue;
 import android.car.hardware.property.CarPropertyManager;
 import android.car.hardware.property.CarPropertyManager.GetPropertyCallback;
-import android.car.hardware.property.CarPropertyManager.GetPropertyError;
 import android.car.hardware.property.CarPropertyManager.GetPropertyRequest;
 import android.car.hardware.property.CarPropertyManager.GetPropertyResult;
+import android.car.hardware.property.CarPropertyManager.PropertyAsyncError;
 import android.car.hardware.property.PropertyNotAvailableException;
 import android.os.SystemClock;
 import android.util.SparseArray;
@@ -172,6 +172,38 @@
         return new Builder<>(propertyId, access, areaType, changeMode, propertyType);
     }
 
+    @Nullable
+    public static <U> U getDefaultValue(Class<?> clazz) {
+        if (clazz == Boolean.class) {
+            return (U) Boolean.TRUE;
+        }
+        if (clazz == Integer.class) {
+            return (U) (Integer) 2;
+        }
+        if (clazz == Float.class) {
+            return (U) (Float) 2.f;
+        }
+        if (clazz == Long.class) {
+            return (U) (Long) 2L;
+        }
+        if (clazz == Integer[].class) {
+            return (U) new Integer[]{2};
+        }
+        if (clazz == Float[].class) {
+            return (U) new Float[]{2.f};
+        }
+        if (clazz == Long[].class) {
+            return (U) new Long[]{2L};
+        }
+        if (clazz == String.class) {
+            return (U) new String("test");
+        }
+        if (clazz == byte[].class) {
+            return (U) new byte[]{(byte) 0xbe, (byte) 0xef};
+        }
+        return null;
+    }
+
     private static String accessToString(int access) {
         switch (access) {
             case CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_NONE:
@@ -255,6 +287,7 @@
                     verifyCarPropertyValueCallback(carPropertyConfig, carPropertyManager);
                     verifyCarPropertyValueSetter(carPropertyConfig, carPropertyManager);
                     verifyGetPropertiesAsync(carPropertyConfig, carPropertyManager);
+                    // TODO(b/266000988): verifySetProeprtiesAsync(...)
 
                     if (hvacPowerStateByAreaId != null) {
                         // TODO(b/265483050): Reenable once the bug is fixed.
@@ -430,34 +463,6 @@
                 CAR_PROPERTY_VALUE_SOURCE_CALLBACK);
     }
 
-    @Nullable
-    private static <U> U getDefaultValue(Class<?> clazz) {
-        if (clazz == Integer.class) {
-            return (U) (Integer) 2;
-        }
-        if (clazz == Float.class) {
-            return (U) (Float) 2.f;
-        }
-        if (clazz == Long.class) {
-            return (U) (Long) 2L;
-        }
-        if (clazz == Integer[].class) {
-            return (U) new Integer[]{2};
-        }
-        if (clazz == Float[].class) {
-            return (U) new Float[]{2.f};
-        }
-        if (clazz == Long[].class) {
-            return (U) new Long[]{2L};
-        }
-        if (clazz == String.class) {
-            return (U) new String("test");
-        }
-        if (clazz == byte[].class) {
-            return (U) new byte[]{(byte) 0xbe, (byte) 0xef};
-        }
-        return null;
-    }
 
     private void verifySetNotAvailable(CarPropertyConfig<T> carPropertyConfig,
             CarPropertyManager carPropertyManager) {
@@ -1310,8 +1315,8 @@
         }
 
         @Override
-        public void onFailure(GetPropertyError getPropertyError) {
-            assertWithMessage("GetPropertyError with requestId "
+        public void onFailure(PropertyAsyncError getPropertyError) {
+            assertWithMessage("PropertyAsyncError with requestId "
                     + getPropertyError.getRequestId() + " returned with error code: "
                     + getPropertyError.getErrorCode()).fail();
         }