Merge "Create fake CarPropertyValue for an error event" into qt-dev
diff --git a/car-lib/src/android/car/hardware/property/CarPropertyEvent.java b/car-lib/src/android/car/hardware/property/CarPropertyEvent.java
index 260d5fa..4cb7e92 100644
--- a/car-lib/src/android/car/hardware/property/CarPropertyEvent.java
+++ b/car-lib/src/android/car/hardware/property/CarPropertyEvent.java
@@ -32,7 +32,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
@@ -69,11 +70,24 @@
     /**
      * Constructor for {@link CarPropertyEvent}.
      */
-    public CarPropertyEvent(@NonNull int eventType, @NonNull CarPropertyValue<?> carPropertyValue) {
+    public CarPropertyEvent(int eventType, @NonNull CarPropertyValue<?> carPropertyValue) {
         mEventType  = eventType;
         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/car-lib/src/android/car/hardware/property/CarPropertyManager.java b/car-lib/src/android/car/hardware/property/CarPropertyManager.java
index 85ffd34..3f7da1d 100644
--- a/car-lib/src/android/car/hardware/property/CarPropertyManager.java
+++ b/car-lib/src/android/car/hardware/property/CarPropertyManager.java
@@ -562,6 +562,12 @@
             listeners.forEach(new Consumer<CarPropertyEventCallback>() {
                 @Override
                 public void accept(CarPropertyEventCallback listener) {
+                    if (DBG) {
+                        Log.d(TAG, new StringBuilder().append("onErrorEvent for ")
+                                        .append("property: ").append(value.getPropertyId())
+                                        .append(" areaId: ").append(value.getAreaId())
+                                        .toString());
+                    }
                     listener.onErrorEvent(value.getPropertyId(), value.getAreaId());
                 }
             });
diff --git a/service/src/com/android/car/CarPropertyService.java b/service/src/com/android/car/CarPropertyService.java
index ed24251..3b4e388 100644
--- a/service/src/com/android/car/CarPropertyService.java
+++ b/service/src/com/android/car/CarPropertyService.java
@@ -395,7 +395,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);
@@ -410,9 +410,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 31627b9..27bc0ae 100644
--- a/service/src/com/android/car/ICarImpl.java
+++ b/service/src/com/android/car/ICarImpl.java
@@ -504,6 +504,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";
@@ -532,6 +533,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("\tenable-uxr true|false");
             pw.println("\t  Enable/Disable UX restrictions and App blocking.");
             pw.println("\tgarage-mode [on|off|query]");
@@ -584,7 +587,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) {
@@ -722,11 +735,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");
@@ -734,7 +748,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 d7faa2b..374ae7b 100644
--- a/service/src/com/android/car/hal/VehicleHal.java
+++ b/service/src/com/android/car/hal/VehicleHal.java
@@ -628,6 +628,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;