Merge "Payload check in propertyHalService" into rvc-dev
diff --git a/service/src/com/android/car/hal/PropertyHalService.java b/service/src/com/android/car/hal/PropertyHalService.java
index 192b7ec..15038cd 100644
--- a/service/src/com/android/car/hal/PropertyHalService.java
+++ b/service/src/com/android/car/hal/PropertyHalService.java
@@ -32,6 +32,7 @@
import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
import android.hardware.automotive.vehicle.V2_0.VehiclePropertyType;
+import android.os.Build;
import android.util.Log;
import android.util.SparseArray;
@@ -374,6 +375,12 @@
Log.e(TAG, "Property is not supported: 0x" + toHexString(v.prop));
continue;
}
+ // Check payload if it is a userdebug build.
+ if (Build.IS_DEBUGGABLE && !mPropIds.checkPayload(v)) {
+ Log.e(TAG, "Drop event for property: " + v + " because it is failed "
+ + "in payload checking.");
+ continue;
+ }
int mgrPropId = halToManagerPropId(v.prop);
CarPropertyValue<?> propVal;
if (isMixedTypeProperty(v.prop)) {
diff --git a/service/src/com/android/car/hal/PropertyHalServiceIds.java b/service/src/com/android/car/hal/PropertyHalServiceIds.java
index b9f3134..3772843 100644
--- a/service/src/com/android/car/hal/PropertyHalServiceIds.java
+++ b/service/src/com/android/car/hal/PropertyHalServiceIds.java
@@ -21,15 +21,34 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.car.Car;
+import android.car.VehicleHvacFanDirection;
import android.car.hardware.property.VehicleVendorPermission;
+import android.hardware.automotive.vehicle.V2_0.EvConnectorType;
+import android.hardware.automotive.vehicle.V2_0.FuelType;
+import android.hardware.automotive.vehicle.V2_0.PortLocationType;
+import android.hardware.automotive.vehicle.V2_0.VehicleAreaSeat;
+import android.hardware.automotive.vehicle.V2_0.VehicleGear;
+import android.hardware.automotive.vehicle.V2_0.VehicleIgnitionState;
+import android.hardware.automotive.vehicle.V2_0.VehicleLightState;
+import android.hardware.automotive.vehicle.V2_0.VehicleLightSwitch;
+import android.hardware.automotive.vehicle.V2_0.VehicleOilLevel;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
import android.hardware.automotive.vehicle.V2_0.VehiclePropertyGroup;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropertyType;
+import android.hardware.automotive.vehicle.V2_0.VehicleSeatOccupancyState;
+import android.hardware.automotive.vehicle.V2_0.VehicleTurnSignal;
+import android.hardware.automotive.vehicle.V2_0.VehicleUnit;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/**
* Helper class to define which property IDs are used by PropertyHalService. This class binds the
@@ -45,7 +64,37 @@
*/
private final SparseArray<Pair<String, String>> mProps;
private final HashSet<Integer> mPropForUnits;
+ // Key: propId, Value: possible value for the property
+ private final HashMap<Integer, Set<Integer>> mPropToValidValue;
+ private final HashMap<Integer, Integer> mPropToValidBitFlag;
private static final String TAG = "PropertyHalServiceIds";
+ // Enums are used as return value in Vehicle HAL.
+ private static final Set<Integer> FUEL_TYPE =
+ new HashSet<>(getIntegersFromDataEnums(FuelType.class));
+ private static final Set<Integer> EV_CONNECTOR_TYPE =
+ new HashSet<>(getIntegersFromDataEnums(EvConnectorType.class));
+ private static final Set<Integer> PORT_LOCATION =
+ new HashSet<>(getIntegersFromDataEnums(PortLocationType.class));
+ private static final Set<Integer> VEHICLE_SEAT =
+ new HashSet<>(getIntegersFromDataEnums(VehicleAreaSeat.class));
+ private static final Set<Integer> OIL_LEVEL =
+ new HashSet<>(getIntegersFromDataEnums(VehicleOilLevel.class));
+ private static final Set<Integer> VEHICLE_GEAR =
+ new HashSet<>(getIntegersFromDataEnums(VehicleGear.class));
+ private static final Set<Integer> TURN_SIGNAL =
+ new HashSet<>(getIntegersFromDataEnums(VehicleTurnSignal.class));
+ private static final Set<Integer> IGNITION_STATE =
+ new HashSet<>(getIntegersFromDataEnums(VehicleIgnitionState.class));
+ private static final Set<Integer> VEHICLE_UNITS =
+ new HashSet<>(getIntegersFromDataEnums(VehicleUnit.class));
+ private static final Set<Integer> SEAT_OCCUPANCY_STATE =
+ new HashSet<>(getIntegersFromDataEnums(VehicleSeatOccupancyState.class));
+ private static final Set<Integer> VEHICLE_LIGHT_STATE =
+ new HashSet<>(getIntegersFromDataEnums(VehicleLightState.class));
+ private static final Set<Integer> VEHICLE_LIGHT_SWITCH =
+ new HashSet<>(getIntegersFromDataEnums(VehicleLightSwitch.class));
+ private static final int HVAC_FAN_DIRECTION_COMBINATIONS =
+ generateAllCombination(VehicleHvacFanDirection.class);
// default vendor permission
private static final int PERMISSION_CAR_VENDOR_DEFAULT = 0x00000000;
@@ -103,6 +152,8 @@
public PropertyHalServiceIds() {
mProps = new SparseArray<>();
mPropForUnits = new HashSet<>();
+ mPropToValidValue = new HashMap<>();
+ mPropToValidBitFlag = new HashMap<>();
// Add propertyId and read/write permissions
// Cabin Properties
mProps.put(VehicleProperty.DOOR_POS, new Pair<>(
@@ -485,6 +536,42 @@
mProps.put(VehicleProperty.SUPPORT_CUSTOMIZE_VENDOR_PERMISSION, new Pair<>(
Car.PERMISSION_READ_CAR_VENDOR_PERMISSION_INFO,
null));
+
+ // mPropToValidValue should contain all properties which has @data_enum in types.hal
+ mPropToValidValue.put(VehicleProperty.INFO_FUEL_TYPE, FUEL_TYPE);
+ mPropToValidValue.put(VehicleProperty.INFO_EV_CONNECTOR_TYPE, EV_CONNECTOR_TYPE);
+ mPropToValidValue.put(VehicleProperty.INFO_FUEL_DOOR_LOCATION, PORT_LOCATION);
+ mPropToValidValue.put(VehicleProperty.INFO_DRIVER_SEAT, VEHICLE_SEAT);
+ mPropToValidValue.put(VehicleProperty.INFO_MULTI_EV_PORT_LOCATIONS, PORT_LOCATION);
+ mPropToValidValue.put(VehicleProperty.ENGINE_OIL_LEVEL, OIL_LEVEL);
+ mPropToValidValue.put(VehicleProperty.GEAR_SELECTION, VEHICLE_GEAR);
+ mPropToValidValue.put(VehicleProperty.CURRENT_GEAR, VEHICLE_GEAR);
+ mPropToValidValue.put(VehicleProperty.TURN_SIGNAL_STATE, TURN_SIGNAL);
+ mPropToValidValue.put(VehicleProperty.IGNITION_STATE, IGNITION_STATE);
+ mPropToValidValue.put(VehicleProperty.HVAC_TEMPERATURE_DISPLAY_UNITS, VEHICLE_UNITS);
+ mPropToValidValue.put(VehicleProperty.DISTANCE_DISPLAY_UNITS, VEHICLE_UNITS);
+ mPropToValidValue.put(VehicleProperty.FUEL_VOLUME_DISPLAY_UNITS, VEHICLE_UNITS);
+ mPropToValidValue.put(VehicleProperty.TIRE_PRESSURE_DISPLAY_UNITS, VEHICLE_UNITS);
+ mPropToValidValue.put(VehicleProperty.EV_BATTERY_DISPLAY_UNITS, VEHICLE_UNITS);
+ mPropToValidValue.put(VehicleProperty.SEAT_OCCUPANCY, SEAT_OCCUPANCY_STATE);
+ mPropToValidValue.put(VehicleProperty.HIGH_BEAM_LIGHTS_STATE, VEHICLE_LIGHT_STATE);
+ mPropToValidValue.put(VehicleProperty.HEADLIGHTS_STATE, VEHICLE_LIGHT_STATE);
+ mPropToValidValue.put(VehicleProperty.FOG_LIGHTS_STATE, VEHICLE_LIGHT_STATE);
+ mPropToValidValue.put(VehicleProperty.HAZARD_LIGHTS_STATE, VEHICLE_LIGHT_STATE);
+ mPropToValidValue.put(VehicleProperty.CABIN_LIGHTS_STATE, VEHICLE_LIGHT_STATE);
+ mPropToValidValue.put(VehicleProperty.READING_LIGHTS_STATE, VEHICLE_LIGHT_STATE);
+ mPropToValidValue.put(VehicleProperty.HEADLIGHTS_SWITCH, VEHICLE_LIGHT_SWITCH);
+ mPropToValidValue.put(VehicleProperty.HIGH_BEAM_LIGHTS_SWITCH, VEHICLE_LIGHT_SWITCH);
+ mPropToValidValue.put(VehicleProperty.FOG_LIGHTS_SWITCH, VEHICLE_LIGHT_SWITCH);
+ mPropToValidValue.put(VehicleProperty.HAZARD_LIGHTS_SWITCH, VEHICLE_LIGHT_SWITCH);
+ mPropToValidValue.put(VehicleProperty.CABIN_LIGHTS_SWITCH, VEHICLE_LIGHT_SWITCH);
+ mPropToValidValue.put(VehicleProperty.READING_LIGHTS_SWITCH, VEHICLE_LIGHT_SWITCH);
+
+ // mPropToValidBitFlag contains all properties which return values are combinations of bits
+ mPropToValidBitFlag.put(VehicleProperty.HVAC_FAN_DIRECTION_AVAILABLE,
+ HVAC_FAN_DIRECTION_COMBINATIONS);
+ mPropToValidBitFlag.put(VehicleProperty.HVAC_FAN_DIRECTION,
+ HVAC_FAN_DIRECTION_COMBINATIONS);
}
/**
@@ -666,4 +753,108 @@
}
}
+ /**
+ * Checks property value's format for all properties. Checks property value range if property
+ * has @data_enum flag in types.hal.
+ * @return true if property value's payload is valid.
+ */
+ public boolean checkPayload(VehiclePropValue propValue) {
+ // Mixed property uses config array to indicate the data format. Checked it when convert it
+ // to CarPropertyValue.
+ if ((propValue.prop & VehiclePropertyType.MASK) == VehiclePropertyType.MIXED) {
+ return true;
+ }
+ if (!checkFormatForAllProperties(propValue)) {
+ Log.e(TAG, "Property value" + propValue + "has an invalid data format");
+ return false;
+ }
+ if (mPropToValidValue.containsKey(propValue.prop)) {
+ return checkDataEnum(propValue);
+ }
+ if (mPropToValidBitFlag.containsKey(propValue.prop)) {
+ return checkValidBitFlag(propValue);
+ }
+ return true;
+ }
+
+ private boolean checkValidBitFlag(VehiclePropValue propValue) {
+ int flagCombination = mPropToValidBitFlag.get(propValue.prop);
+ for (int value : propValue.value.int32Values) {
+ if ((value & flagCombination) != value) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean checkFormatForAllProperties(VehiclePropValue propValue) {
+ int propId = propValue.prop;
+ VehiclePropValue.RawValue rawValue = propValue.value;
+ //Records sum size of int32values, floatValue, int64Values, bytes, String
+ int sizeOfAllValue = rawValue.int32Values.size() + rawValue.floatValues.size()
+ + rawValue.int64Values.size() + rawValue.bytes.size()
+ + rawValue.stringValue.length();
+ if (sizeOfAllValue == 0) {
+ Log.e(TAG, "Property value is empty: " + propValue);
+ return false;
+ }
+ switch (propId & VehiclePropertyType.MASK) {
+ case VehiclePropertyType.BOOLEAN:
+ case VehiclePropertyType.INT32:
+ return sizeOfAllValue == 1 && rawValue.int32Values.size() == 1;
+ case VehiclePropertyType.FLOAT:
+ return sizeOfAllValue == 1 && rawValue.floatValues.size() == 1;
+ case VehiclePropertyType.INT64:
+ return sizeOfAllValue == 1 && rawValue.int64Values.size() == 1;
+ case VehiclePropertyType.FLOAT_VEC:
+ return sizeOfAllValue == rawValue.floatValues.size();
+ case VehiclePropertyType.INT64_VEC:
+ return sizeOfAllValue == rawValue.int64Values.size();
+ case VehiclePropertyType.INT32_VEC:
+ return sizeOfAllValue == rawValue.int32Values.size();
+ case VehiclePropertyType.BYTES:
+ return sizeOfAllValue == rawValue.bytes.size();
+ case VehiclePropertyType.STRING:
+ return sizeOfAllValue == rawValue.stringValue.length();
+ default:
+ throw new IllegalArgumentException("Unexpected property type for propId: "
+ + Integer.toHexString(propId));
+ }
+ }
+ private boolean checkDataEnum(VehiclePropValue propValue) {
+ int propId = propValue.prop;
+ VehiclePropValue.RawValue rawValue = propValue.value;
+ Set<Integer> validValue = mPropToValidValue.get(propId);
+ for (int value : rawValue.int32Values) {
+ if (!validValue.contains(value)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static List<Integer> getIntegersFromDataEnums(Class clazz) {
+ Field[] fields = clazz.getDeclaredFields();
+ List<Integer> integerList = new ArrayList<>(5);
+ for (Field f : fields) {
+ if (f.getType() == int.class) {
+ try {
+ integerList.add(f.getInt(clazz));
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to get value");
+ }
+ }
+ }
+ return integerList;
+ }
+
+ // Generate all combinations at once
+ private static int generateAllCombination(Class clazz) {
+ List<Integer> allBits = getIntegersFromDataEnums(clazz);
+ int combination = allBits.get(0);
+ for (int i = 1; i < allBits.size(); i++) {
+ combination |= allBits.get(i);
+ }
+ return combination;
+ }
}
diff --git a/tests/carservice_test/src/com/android/car/CarDrivingRestrictionsTest.java b/tests/carservice_test/src/com/android/car/CarDrivingRestrictionsTest.java
index 520e910..890981a 100644
--- a/tests/carservice_test/src/com/android/car/CarDrivingRestrictionsTest.java
+++ b/tests/carservice_test/src/com/android/car/CarDrivingRestrictionsTest.java
@@ -29,6 +29,7 @@
import android.car.drivingstate.CarUxRestrictionsManager;
import android.hardware.automotive.vehicle.V2_0.VehicleGear;
import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
+import android.os.Build;
import android.os.SystemClock;
import android.util.Log;
@@ -211,9 +212,14 @@
.setTimestamp(SystemClock.elapsedRealtimeNanos())
.build());
drivingEvent = listener.waitForDrivingStateChange();
- assertNotNull(drivingEvent);
- assertThat(drivingEvent.eventValue).isEqualTo(CarDrivingStateEvent.DRIVING_STATE_IDLING);
-
+ if (Build.IS_DEBUGGABLE) {
+ // In userdebug build, payloadChecker in HAL drops the invalid event.
+ assertNull(drivingEvent);
+ } else {
+ assertNotNull(drivingEvent);
+ assertThat(drivingEvent.eventValue).isEqualTo(
+ CarDrivingStateEvent.DRIVING_STATE_IDLING);
+ }
// Now, send in an invalid speed value as well, now the driving state will be unknown and
// the UX restrictions will change to fully restricted.
listener.reset();
@@ -224,13 +230,19 @@
.setTimestamp(SystemClock.elapsedRealtimeNanos())
.build());
drivingEvent = listener.waitForDrivingStateChange();
- assertNotNull(drivingEvent);
- assertThat(drivingEvent.eventValue).isEqualTo(CarDrivingStateEvent.DRIVING_STATE_UNKNOWN);
- restrictions = listener.waitForUxRestrictionsChange();
- assertNotNull(restrictions);
- assertTrue(restrictions.isRequiresDistractionOptimization());
- assertThat(restrictions.getActiveRestrictions())
- .isEqualTo(CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED);
+ if (Build.IS_DEBUGGABLE) {
+ // In userdebug build, payloadChecker in HAL drops the invalid event.
+ assertNull(drivingEvent);
+ } else {
+ assertNotNull(drivingEvent);
+ assertThat(drivingEvent.eventValue).isEqualTo(
+ CarDrivingStateEvent.DRIVING_STATE_UNKNOWN);
+ restrictions = listener.waitForUxRestrictionsChange();
+ assertNotNull(restrictions);
+ assertTrue(restrictions.isRequiresDistractionOptimization());
+ assertThat(restrictions.getActiveRestrictions())
+ .isEqualTo(CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED);
+ }
mCarDrivingStateManager.unregisterListener();
mCarUxRManager.unregisterListener();
}
diff --git a/tests/carservice_unit_test/src/com/android/car/hal/PropertyHalServiceIdsTest.java b/tests/carservice_unit_test/src/com/android/car/hal/PropertyHalServiceIdsTest.java
index 59498cb..e128b19 100644
--- a/tests/carservice_unit_test/src/com/android/car/hal/PropertyHalServiceIdsTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/hal/PropertyHalServiceIdsTest.java
@@ -17,12 +17,22 @@
package com.android.car.hal;
import android.car.Car;
+import android.car.VehicleHvacFanDirection;
import android.car.VehiclePropertyIds;
+import android.hardware.automotive.vehicle.V2_0.VehicleGear;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
+import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
+import android.hardware.automotive.vehicle.V2_0.VehicleUnit;
import android.hardware.automotive.vehicle.V2_0.VehicleVendorPermission;
+import android.os.SystemClock;
import android.util.Log;
import androidx.test.runner.AndroidJUnit4;
+import com.android.car.vehiclehal.VehiclePropValueBuilder;
+
+import com.google.common.truth.Truth;
+
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
@@ -53,6 +63,32 @@
VehiclePropertyIds.HVAC_FAN_SPEED, VehiclePropertyIds.DOOR_LOCK};
private static final List<Integer> CONFIG_ARRAY = new ArrayList<>();
private static final List<Integer> CONFIG_ARRAY_INVALID = new ArrayList<>();
+ //payload test
+ private static final VehiclePropValue GEAR_WITH_VALID_VALUE =
+ VehiclePropValueBuilder.newBuilder(VehicleProperty.GEAR_SELECTION)
+ .addIntValue(VehicleGear.GEAR_DRIVE)
+ .setTimestamp(SystemClock.elapsedRealtimeNanos()).build();
+ private static final VehiclePropValue GEAR_WITH_EXTRA_VALUE =
+ VehiclePropValueBuilder.newBuilder(VehicleProperty.GEAR_SELECTION)
+ .addIntValue(VehicleGear.GEAR_DRIVE)
+ .addIntValue(VehicleGear.GEAR_1)
+ .setTimestamp(SystemClock.elapsedRealtimeNanos()).build();
+ private static final VehiclePropValue GEAR_WITH_INVALID_VALUE =
+ VehiclePropValueBuilder.newBuilder(VehicleProperty.GEAR_SELECTION)
+ .addIntValue(VehicleUnit.KILOPASCAL)
+ .setTimestamp(SystemClock.elapsedRealtimeNanos()).build();
+ private static final VehiclePropValue GEAR_WITH_INVALID_TYPE_VALUE =
+ VehiclePropValueBuilder.newBuilder(VehicleProperty.GEAR_SELECTION)
+ .addFloatValue(1.0f)
+ .setTimestamp(SystemClock.elapsedRealtimeNanos()).build();
+ private static final VehiclePropValue HVAC_FAN_DIRECTIONS_VALID =
+ VehiclePropValueBuilder.newBuilder(VehicleProperty.HVAC_FAN_DIRECTION)
+ .addIntValue(VehicleHvacFanDirection.FACE | VehicleHvacFanDirection.FLOOR)
+ .setTimestamp(SystemClock.elapsedRealtimeNanos()).build();
+ private static final VehiclePropValue HVAC_FAN_DIRECTIONS_INVALID =
+ VehiclePropValueBuilder.newBuilder(VehicleProperty.HVAC_FAN_DIRECTION)
+ .addIntValue(VehicleHvacFanDirection.FACE | 0x100)
+ .setTimestamp(SystemClock.elapsedRealtimeNanos()).build();
@Before
public void setUp() {
mPropertyHalServiceIds = new PropertyHalServiceIds();
@@ -90,7 +126,6 @@
Assert.assertEquals(Car.PERMISSION_CONTROL_CAR_CLIMATE,
mPropertyHalServiceIds.getWritePermission(VehiclePropertyIds.HVAC_FAN_SPEED));
}
-
/**
* Test {@link PropertyHalServiceIds#customizeVendorPermission(List)}
*/
@@ -143,4 +178,19 @@
}
}
+ /**
+ * Test {@link PropertyHalServiceIds#checkPayload(VehiclePropValue)}
+ */
+ @Test
+ public void testPayload() {
+ Truth.assertThat(mPropertyHalServiceIds.checkPayload(GEAR_WITH_VALID_VALUE)).isTrue();
+ Truth.assertThat(mPropertyHalServiceIds.checkPayload(GEAR_WITH_EXTRA_VALUE)).isFalse();
+ Truth.assertThat(mPropertyHalServiceIds.checkPayload(GEAR_WITH_INVALID_VALUE)).isFalse();
+ Truth.assertThat(mPropertyHalServiceIds.checkPayload(GEAR_WITH_INVALID_TYPE_VALUE))
+ .isFalse();
+
+ Truth.assertThat(mPropertyHalServiceIds.checkPayload(HVAC_FAN_DIRECTIONS_VALID)).isTrue();
+ Truth.assertThat(mPropertyHalServiceIds.checkPayload(HVAC_FAN_DIRECTIONS_INVALID))
+ .isFalse();
+ }
}