Create fake CarPropertyValue for an error event

Instead of create a CarPropertyValue with NULL value, we create
it with a default fake value. CarPropertyManager drops the value
of CarPropertyValue, when it has the ERROR flag as eventType.
Add a adb command to inject an error event. Use it to test.
For exmaple:
dumpsys activity service com.android.car inject-error-event 354419977 12 3

Bug: 134441058
Test: inject an error event for VHAL
Change-Id: I6d2f0523a3c57ee252b777e6d79c8d7e6de322a4
Merged-In: I6d2f0523a3c57ee252b777e6d79c8d7e6de322a4
(cherry picked from commit 4b098cf1b9e347805a6fc4308549104491f1dbcb)
diff --git a/car-lib/src/android/car/hardware/property/CarPropertyEvent.java b/car-lib/src/android/car/hardware/property/CarPropertyEvent.java
index 638fbf2..258403a 100644
--- a/car-lib/src/android/car/hardware/property/CarPropertyEvent.java
+++ b/car-lib/src/android/car/hardware/property/CarPropertyEvent.java
@@ -31,7 +31,8 @@
     private final int mEventType;
     private final CarPropertyValue<?> mCarPropertyValue;
 
-    // Getters.
+    // Use it as default value for error events.
+    private static final int ERROR_EVENT_VALUE = -1;
 
     /**
      * @return EventType field
@@ -73,6 +74,19 @@
         mCarPropertyValue = carPropertyValue;
     }
 
+    /**
+     * Constructor for {@link CarPropertyEvent} when it is an error event.
+     *
+     * The status of {@link CarPropertyValue} should be {@link CarPropertyValue#STATUS_ERROR}.
+     * In {@link CarPropertyManager}, the value of {@link CarPropertyValue} will be dropped.
+     */
+    public static CarPropertyEvent createErrorEvent(int propertyId, int areaId) {
+        // valueWithErrorCode will not be propagated to listeners
+        CarPropertyValue<Integer> valueWithErrorCode = new CarPropertyValue<>(propertyId, areaId,
+                    CarPropertyValue.STATUS_ERROR, 0, ERROR_EVENT_VALUE);
+        return new CarPropertyEvent(PROPERTY_EVENT_ERROR, valueWithErrorCode);
+    }
+
     private CarPropertyEvent(Parcel in) {
         mEventType  = in.readInt();
         mCarPropertyValue = in.readParcelable(CarPropertyValue.class.getClassLoader());
diff --git a/service/src/com/android/car/CarPropertyService.java b/service/src/com/android/car/CarPropertyService.java
index a01d4b8..fa08688 100644
--- a/service/src/com/android/car/CarPropertyService.java
+++ b/service/src/com/android/car/CarPropertyService.java
@@ -372,7 +372,7 @@
         List<Client> clients = mPropIdClientMap.get(property);
         if (clients != null) {
             List<CarPropertyEvent> eventList = new LinkedList<>();
-            eventList.add(createErrorEvent(property, area));
+            eventList.add(CarPropertyEvent.createErrorEvent(property, area));
             for (Client c : clients) {
                 try {
                     c.getListener().onEvent(eventList);
@@ -387,9 +387,4 @@
                     + toHexString(property));
         }
     }
-
-    private static CarPropertyEvent createErrorEvent(int property, int area) {
-        return new CarPropertyEvent(CarPropertyEvent.PROPERTY_EVENT_ERROR,
-                new CarPropertyValue<>(property, area, null));
-    }
 }
diff --git a/service/src/com/android/car/ICarImpl.java b/service/src/com/android/car/ICarImpl.java
index 67c50a7..8d4212a 100644
--- a/service/src/com/android/car/ICarImpl.java
+++ b/service/src/com/android/car/ICarImpl.java
@@ -427,6 +427,7 @@
         private static final String COMMAND_HELP = "-h";
         private static final String COMMAND_DAY_NIGHT_MODE = "day-night-mode";
         private static final String COMMAND_INJECT_VHAL_EVENT = "inject-vhal-event";
+        private static final String COMMAND_INJECT_ERROR_EVENT = "inject-error-event";
         private static final String COMMAND_ENABLE_UXR = "enable-uxr";
         private static final String COMMAND_GARAGE_MODE = "garage-mode";
         private static final String COMMAND_GET_DO_ACTIVITIES = "get-do-activities";
@@ -448,6 +449,8 @@
             pw.println("\t  Force into day/night mode or restore to auto.");
             pw.println("\tinject-vhal-event property [zone] data(can be comma separated list)");
             pw.println("\t  Inject a vehicle property for testing");
+            pw.println("\tinject-error-event property zone errorCode");
+            pw.println("\t  Inject an error event from VHAL for testing.");
             pw.println("\tdisable-uxr true|false");
             pw.println("\t  Disable UX restrictions and App blocking.");
             pw.println("\tgarage-mode [on|off|query]");
@@ -487,7 +490,17 @@
                         // Global
                         data = args[2];
                     }
-                    injectVhalEvent(args[1], zone, data, writer);
+                    injectVhalEvent(args[1], zone, data, false, writer);
+                    break;
+                case COMMAND_INJECT_ERROR_EVENT:
+                    if (args.length != 4) {
+                        writer.println("Incorrect number of arguments");
+                        dumpHelp(writer);
+                        break;
+                    }
+                    String errorAreaId = args[2];
+                    String errorCode = args[3];
+                    injectVhalEvent(args[1], errorAreaId, errorCode, true, writer);
                     break;
                 case COMMAND_ENABLE_UXR:
                     if (args.length < 2) {
@@ -584,11 +597,12 @@
          *
          * @param property the Vehicle property Id as defined in the HAL
          * @param zone     Zone that this event services
+         * @param isErrorEvent indicates the type of event
          * @param value    Data value of the event
          * @param writer   PrintWriter
          */
         private void injectVhalEvent(String property, String zone, String value,
-                PrintWriter writer) {
+                boolean isErrorEvent, PrintWriter writer) {
             if (zone != null && (zone.equalsIgnoreCase(PARAM_VEHICLE_PROPERTY_AREA_GLOBAL))) {
                 if (!isPropertyAreaTypeGlobal(property)) {
                     writer.println("Property area type inconsistent with given zone");
@@ -596,7 +610,11 @@
                 }
             }
             try {
-                mHal.injectVhalEvent(property, zone, value);
+                if (isErrorEvent) {
+                    mHal.injectOnPropertySetError(property, zone, value);
+                } else {
+                    mHal.injectVhalEvent(property, zone, value);
+                }
             } catch (NumberFormatException e) {
                 writer.println("Invalid property Id zone Id or value" + e);
                 dumpHelp(writer);
diff --git a/service/src/com/android/car/hal/VehicleHal.java b/service/src/com/android/car/hal/VehicleHal.java
index f73d07c..70953b6 100644
--- a/service/src/com/android/car/hal/VehicleHal.java
+++ b/service/src/com/android/car/hal/VehicleHal.java
@@ -552,6 +552,23 @@
         onPropertyEvent(Lists.newArrayList(v));
     }
 
+    /**
+     * Inject an error event.
+     *
+     * @param property the Vehicle property Id as defined in the HAL
+     * @param zone Zone for the event to inject
+     * @param errorCode Error code return from HAL
+     */
+    public void injectOnPropertySetError(String property, String zone, String errorCode) {
+        if (zone == null || property == null || errorCode == null) {
+            return;
+        }
+        int propId = Integer.decode(property);
+        int zoneId = Integer.decode(zone);
+        int errorId = Integer.decode(errorCode);
+        onPropertySetError(errorId, propId, zoneId);
+    }
+
     private static class VehiclePropertyEventInfo {
         private int eventCount;
         private VehiclePropValue lastEvent;