Allow settings a freeform string restriction mode
Allow OEMs to create any restriction mode they want, as identified
by its string. Previously we were hard-coding integer values, such
as passenger mode. We realized this wasn't going to be scalable as
OEMs started requesting modes for "teen" and different autonomy
levels. This change is backward-compatible with the existing XML
config.
- Change APIs from using hard-coded IntDefs to Strings
- XML configuration is the same, but parsing logic needs to change
- Use a new JSON format to make it more readable and still efficient
- Backward compatible with original schema
Bug: 147762570
Test: atest CarServiceTest && atest AndroidCarApiTest
Change-Id: Id97512f351ef12b65b42285540a51b295a257a43
Merged-In: Id97512f351ef12b65b42285540a51b295a257a43
(cherry picked from commit 0b0cf28bd1ae479ddfdc99d3cdd9a104cb79c04b)
diff --git a/car-lib/src/android/car/drivingstate/CarUxRestrictionsConfiguration.java b/car-lib/src/android/car/drivingstate/CarUxRestrictionsConfiguration.java
index 1148856..fc367d0 100644
--- a/car-lib/src/android/car/drivingstate/CarUxRestrictionsConfiguration.java
+++ b/car-lib/src/android/car/drivingstate/CarUxRestrictionsConfiguration.java
@@ -20,13 +20,11 @@
import static android.car.drivingstate.CarDrivingStateEvent.DRIVING_STATE_PARKED;
import static android.car.drivingstate.CarDrivingStateEvent.DRIVING_STATE_UNKNOWN;
import static android.car.drivingstate.CarUxRestrictionsManager.UX_RESTRICTION_MODE_BASELINE;
-import static android.car.drivingstate.CarUxRestrictionsManager.UX_RESTRICTION_MODE_PASSENGER;
import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.car.drivingstate.CarDrivingStateEvent.CarDrivingState;
-import android.car.drivingstate.CarUxRestrictionsManager.UxRestrictionMode;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -65,14 +63,6 @@
private static final String JSON_NAME_IDLING_RESTRICTIONS = "idling_restrictions";
private static final String JSON_NAME_PARKED_RESTRICTIONS = "parked_restrictions";
private static final String JSON_NAME_UNKNOWN_RESTRICTIONS = "unknown_restrictions";
- private static final String JSON_NAME_PASSENGER_MOVING_RESTRICTIONS =
- "passenger_moving_restrictions";
- private static final String JSON_NAME_PASSENGER_IDLING_RESTRICTIONS =
- "passenger_idling_restrictions";
- private static final String JSON_NAME_PASSENGER_PARKED_RESTRICTIONS =
- "passenger_parked_restrictions";
- private static final String JSON_NAME_PASSENGER_UNKNOWN_RESTRICTIONS =
- "passenger_unknown_restrictions";
private static final String JSON_NAME_REQ_OPT = "req_opt";
private static final String JSON_NAME_RESTRICTIONS = "restrictions";
private static final String JSON_NAME_SPEED_RANGE = "speed_range";
@@ -82,10 +72,10 @@
private final int mMaxContentDepth;
private final int mMaxCumulativeContentItems;
private final int mMaxStringLength;
- private final Map<Integer, List<RestrictionsPerSpeedRange>> mPassengerUxRestrictions =
- new ArrayMap<>(DRIVING_STATES.length);
- private final Map<Integer, List<RestrictionsPerSpeedRange>> mBaselineUxRestrictions =
- new ArrayMap<>(DRIVING_STATES.length);
+ /**
+ * Mapping of a restriction mode name to its restrictions.
+ */
+ private final Map<String, RestrictionModeContainer> mRestrictionModes = new ArrayMap<>();
// null means the port is not configured. It should apply to default display.
@Nullable
@@ -98,24 +88,23 @@
mMaxCumulativeContentItems = builder.mMaxCumulativeContentItems;
mMaxStringLength = builder.mMaxStringLength;
- for (int drivingState : DRIVING_STATES) {
- List<RestrictionsPerSpeedRange> baseline = new ArrayList<>();
- for (RestrictionsPerSpeedRange r : builder.mBaselineUxRestrictions.get(drivingState)) {
- baseline.add(r);
+ // make an immutable copy from the builder
+ for (Map.Entry<String, RestrictionModeContainer> entry :
+ builder.mRestrictionModes.entrySet()) {
+ String mode = entry.getKey();
+ RestrictionModeContainer container = new RestrictionModeContainer();
+ for (int drivingState : DRIVING_STATES) {
+ container.setRestrictionsForDriveState(drivingState,
+ Collections.unmodifiableList(
+ entry.getValue().getRestrictionsForDriveState(drivingState)));
}
- mBaselineUxRestrictions.put(drivingState, baseline);
-
- List<RestrictionsPerSpeedRange> passenger = new ArrayList<>();
- for (RestrictionsPerSpeedRange r : builder.mPassengerUxRestrictions.get(drivingState)) {
- passenger.add(r);
- }
- mPassengerUxRestrictions.put(drivingState, passenger);
+ mRestrictionModes.put(mode, container);
}
}
/**
* Returns the restrictions for
- * {@link UxRestrictionMode#UX_RESTRICTION_MODE_BASELINE}
+ * {@link CarUxRestrictionsManager#UX_RESTRICTION_MODE_BASELINE}
* based on current driving state.
*
* @param drivingState Driving state.
@@ -131,25 +120,35 @@
* Returns the restrictions based on current driving state and restriction mode.
*
* <p>Restriction mode allows a different set of restrictions to be applied in the same driving
- * state. See values in {@link UxRestrictionMode}.
+ * state.
*
* @param drivingState Driving state.
* See values in {@link CarDrivingStateEvent.CarDrivingState}.
* @param currentSpeed Current speed in meter per second.
- * @param mode Current UX Restriction mode.
+ * @param mode Current UX Restriction mode.
*/
public CarUxRestrictions getUxRestrictions(@CarDrivingState int drivingState,
- float currentSpeed, @UxRestrictionMode int mode) {
+ float currentSpeed, @NonNull String mode) {
+ Objects.requireNonNull(mode, "mode must not be null");
RestrictionsPerSpeedRange restriction = null;
- if (mode == UX_RESTRICTION_MODE_PASSENGER) {
- restriction = findUxRestrictionsInList(
- currentSpeed, mPassengerUxRestrictions.get(drivingState));
+ if (mRestrictionModes.containsKey(mode)) {
+ restriction = findUxRestrictionsInList(currentSpeed,
+ mRestrictionModes.get(mode).getRestrictionsForDriveState(drivingState));
}
+
if (restriction == null) {
- // Mode is baseline, or passenger mode does not specify restrictions for current driving
- // state.
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG,
+ String.format("No restrictions specified for (mode: %s, drive state: %s)",
+ mode,
+ drivingState));
+ }
+ // Either mode does not have any configuration or the mode does not have a configuration
+ // for the specific drive state. In either case, fall-back to baseline configuration.
restriction = findUxRestrictionsInList(
- currentSpeed, mBaselineUxRestrictions.get(drivingState));
+ currentSpeed,
+ mRestrictionModes.get(UX_RESTRICTION_MODE_BASELINE)
+ .getRestrictionsForDriveState(drivingState));
}
if (restriction == null) {
@@ -231,7 +230,8 @@
/**
* Writes current configuration as Json.
*/
- public void writeJson(JsonWriter writer) throws IOException {
+ public void writeJson(@NonNull JsonWriter writer) throws IOException {
+ Objects.requireNonNull(writer, "writer must not be null");
// We need to be lenient to accept infinity number (as max speed).
writer.setLenient(true);
@@ -246,38 +246,29 @@
mMaxCumulativeContentItems);
writer.name(JSON_NAME_MAX_STRING_LENGTH).value(mMaxStringLength);
+ for (Map.Entry<String, RestrictionModeContainer> entry : mRestrictionModes.entrySet()) {
+ writer.name(entry.getKey());
+ writeRestrictionMode(writer, entry.getValue());
+ }
+
+ writer.endObject();
+ }
+
+ private void writeRestrictionMode(JsonWriter writer, RestrictionModeContainer container)
+ throws IOException {
+ writer.beginObject();
writer.name(JSON_NAME_PARKED_RESTRICTIONS);
- writeRestrictionsList(writer,
- mBaselineUxRestrictions.get(DRIVING_STATE_PARKED));
+ writeRestrictionsList(writer, container.getRestrictionsForDriveState(DRIVING_STATE_PARKED));
writer.name(JSON_NAME_IDLING_RESTRICTIONS);
- writeRestrictionsList(writer,
- mBaselineUxRestrictions.get(DRIVING_STATE_IDLING));
+ writeRestrictionsList(writer, container.getRestrictionsForDriveState(DRIVING_STATE_IDLING));
writer.name(JSON_NAME_MOVING_RESTRICTIONS);
- writeRestrictionsList(writer,
- mBaselineUxRestrictions.get(DRIVING_STATE_MOVING));
+ writeRestrictionsList(writer, container.getRestrictionsForDriveState(DRIVING_STATE_MOVING));
writer.name(JSON_NAME_UNKNOWN_RESTRICTIONS);
writeRestrictionsList(writer,
- mBaselineUxRestrictions.get(DRIVING_STATE_UNKNOWN));
-
- writer.name(JSON_NAME_PASSENGER_PARKED_RESTRICTIONS);
- writeRestrictionsList(writer,
- mPassengerUxRestrictions.get(DRIVING_STATE_PARKED));
-
- writer.name(JSON_NAME_PASSENGER_IDLING_RESTRICTIONS);
- writeRestrictionsList(writer,
- mPassengerUxRestrictions.get(DRIVING_STATE_IDLING));
-
- writer.name(JSON_NAME_PASSENGER_MOVING_RESTRICTIONS);
- writeRestrictionsList(writer,
- mPassengerUxRestrictions.get(DRIVING_STATE_MOVING));
-
- writer.name(JSON_NAME_PASSENGER_UNKNOWN_RESTRICTIONS);
- writeRestrictionsList(writer,
- mPassengerUxRestrictions.get(DRIVING_STATE_UNKNOWN));
-
+ container.getRestrictionsForDriveState(DRIVING_STATE_UNKNOWN));
writer.endObject();
}
@@ -319,12 +310,19 @@
}
/**
- * Reads Json as UX restriction configuration.
+ * Reads Json as UX restriction configuration with the specified schema version.
+ *
+ * <p>Supports reading files persisted in multiple JSON schemas, including the pre-R version 1
+ * format, and the R format version 2.
*/
- public static CarUxRestrictionsConfiguration readJson(JsonReader reader) throws IOException {
+ public static CarUxRestrictionsConfiguration readJson(@NonNull JsonReader reader,
+ int schemaVersion) throws IOException {
+ Objects.requireNonNull(reader, "reader must not be null");
// We need to be lenient to accept infinity number (as max speed).
reader.setLenient(true);
+ RestrictionConfigurationParser parser = createConfigurationParser(schemaVersion);
+
Builder builder = new Builder();
reader.beginObject();
while (reader.hasNext()) {
@@ -346,6 +344,58 @@
case JSON_NAME_MAX_STRING_LENGTH:
builder.setMaxStringLength(reader.nextInt());
break;
+ default:
+ parser.readJson(reader, name, builder);
+ }
+ }
+ reader.endObject();
+ return builder.build();
+ }
+
+ private static RestrictionConfigurationParser createConfigurationParser(int schemaVersion) {
+ switch (schemaVersion) {
+ case 1:
+ return new V1RestrictionConfigurationParser();
+ case 2:
+ return new V2RestrictionConfigurationParser();
+ default:
+ throw new IllegalArgumentException(
+ "No parser supported for schemaVersion " + schemaVersion);
+ }
+ }
+
+ private interface RestrictionConfigurationParser {
+ /**
+ * Handle reading any information within a particular name and add data to the builder.
+ */
+ void readJson(JsonReader reader, String name, Builder builder) throws IOException;
+ }
+
+ private static class V2RestrictionConfigurationParser implements
+ RestrictionConfigurationParser {
+ @Override
+ public void readJson(JsonReader reader, String name, Builder builder) throws IOException {
+ readRestrictionsMode(reader, name, builder);
+ }
+ }
+
+ private static class V1RestrictionConfigurationParser implements
+ RestrictionConfigurationParser {
+
+ private static final String JSON_NAME_PASSENGER_MOVING_RESTRICTIONS =
+ "passenger_moving_restrictions";
+ private static final String JSON_NAME_PASSENGER_IDLING_RESTRICTIONS =
+ "passenger_idling_restrictions";
+ private static final String JSON_NAME_PASSENGER_PARKED_RESTRICTIONS =
+ "passenger_parked_restrictions";
+ private static final String JSON_NAME_PASSENGER_UNKNOWN_RESTRICTIONS =
+ "passenger_unknown_restrictions";
+
+ private static final String PASSENGER_MODE_NAME_FOR_MIGRATION = "passenger";
+
+ @Override
+ public void readJson(JsonReader reader, String name, Builder builder) throws IOException {
+ switch (name) {
case JSON_NAME_PARKED_RESTRICTIONS:
readRestrictionsList(reader, DRIVING_STATE_PARKED,
UX_RESTRICTION_MODE_BASELINE, builder);
@@ -364,31 +414,54 @@
break;
case JSON_NAME_PASSENGER_PARKED_RESTRICTIONS:
readRestrictionsList(reader, DRIVING_STATE_PARKED,
- UX_RESTRICTION_MODE_PASSENGER, builder);
+ PASSENGER_MODE_NAME_FOR_MIGRATION, builder);
break;
case JSON_NAME_PASSENGER_IDLING_RESTRICTIONS:
readRestrictionsList(reader, DRIVING_STATE_IDLING,
- UX_RESTRICTION_MODE_PASSENGER, builder);
+ PASSENGER_MODE_NAME_FOR_MIGRATION, builder);
break;
case JSON_NAME_PASSENGER_MOVING_RESTRICTIONS:
readRestrictionsList(reader, DRIVING_STATE_MOVING,
- UX_RESTRICTION_MODE_PASSENGER, builder);
+ PASSENGER_MODE_NAME_FOR_MIGRATION, builder);
break;
case JSON_NAME_PASSENGER_UNKNOWN_RESTRICTIONS:
readRestrictionsList(reader, DRIVING_STATE_UNKNOWN,
- UX_RESTRICTION_MODE_PASSENGER, builder);
+ PASSENGER_MODE_NAME_FOR_MIGRATION, builder);
break;
default:
Log.e(TAG, "Unknown name parsing json config: " + name);
reader.skipValue();
}
}
+ }
+
+ private static void readRestrictionsMode(JsonReader reader, String mode, Builder builder)
+ throws IOException {
+ reader.beginObject();
+ while (reader.hasNext()) {
+ String name = reader.nextName();
+ switch (name) {
+ case JSON_NAME_PARKED_RESTRICTIONS:
+ readRestrictionsList(reader, DRIVING_STATE_PARKED, mode, builder);
+ break;
+ case JSON_NAME_IDLING_RESTRICTIONS:
+ readRestrictionsList(reader, DRIVING_STATE_IDLING, mode, builder);
+ break;
+ case JSON_NAME_MOVING_RESTRICTIONS:
+ readRestrictionsList(reader, DRIVING_STATE_MOVING, mode, builder);
+ break;
+ case JSON_NAME_UNKNOWN_RESTRICTIONS:
+ readRestrictionsList(reader, DRIVING_STATE_UNKNOWN, mode, builder);
+ break;
+ default:
+ Log.e(TAG, "Unknown name parsing restriction mode json config: " + name);
+ }
+ }
reader.endObject();
- return builder.build();
}
private static void readRestrictionsList(JsonReader reader, @CarDrivingState int drivingState,
- @UxRestrictionMode int mode, Builder builder) throws IOException {
+ String mode, Builder builder) throws IOException {
reader.beginArray();
while (reader.hasNext()) {
DrivingStateRestrictions drivingStateRestrictions = readRestrictions(reader);
@@ -443,9 +516,8 @@
@Override
public int hashCode() {
- return Objects.hash(mPhysicalPort,
- mMaxStringLength, mMaxCumulativeContentItems, mMaxContentDepth,
- mBaselineUxRestrictions, mPassengerUxRestrictions);
+ return Objects.hash(mPhysicalPort, mMaxStringLength, mMaxCumulativeContentItems,
+ mMaxContentDepth, mRestrictionModes);
}
@Override
@@ -461,14 +533,14 @@
return mPhysicalPort == other.mPhysicalPort
&& hasSameParameters(other)
- && mBaselineUxRestrictions.equals(other.mBaselineUxRestrictions)
- && mPassengerUxRestrictions.equals(other.mPassengerUxRestrictions);
+ && mRestrictionModes.equals(other.mRestrictionModes);
}
/**
* Compares {@code this} configuration object with {@code other} on restriction parameters.
*/
- public boolean hasSameParameters(CarUxRestrictionsConfiguration other) {
+ public boolean hasSameParameters(@NonNull CarUxRestrictionsConfiguration other) {
+ Objects.requireNonNull(other, "other must not be null");
return mMaxContentDepth == other.mMaxContentDepth
&& mMaxCumulativeContentItems == other.mMaxCumulativeContentItems
&& mMaxStringLength == other.mMaxStringLength;
@@ -477,17 +549,16 @@
/**
* Dump the driving state to UX restrictions mapping.
*/
- public void dump(PrintWriter writer) {
+ public void dump(@NonNull PrintWriter writer) {
+ Objects.requireNonNull(writer, "writer must not be null");
writer.println("Physical display port: " + mPhysicalPort);
- writer.println("===========================================");
- writer.println("Baseline mode UXR:");
- writer.println("-------------------------------------------");
- dumpRestrictions(writer, mBaselineUxRestrictions);
-
- writer.println("Passenger mode UXR:");
- writer.println("-------------------------------------------");
- dumpRestrictions(writer, mPassengerUxRestrictions);
+ for (Map.Entry<String, RestrictionModeContainer> entry : mRestrictionModes.entrySet()) {
+ writer.println("===========================================");
+ writer.println(entry.getKey() + " mode UXR:");
+ writer.println("-------------------------------------------");
+ dumpRestrictions(writer, entry.getValue().mDriveStateUxRestrictions);
+ }
writer.println("Max String length: " + mMaxStringLength);
writer.println("Max Cumulative Content Items: " + mMaxCumulativeContentItems);
@@ -531,7 +602,7 @@
// Parcelable methods/fields.
// Used by Parcel methods to ensure de/serialization order.
- private static final int[] DRIVING_STATES = new int[] {
+ private static final int[] DRIVING_STATES = new int[]{
DRIVING_STATE_UNKNOWN,
DRIVING_STATE_PARKED,
DRIVING_STATE_IDLING,
@@ -541,16 +612,16 @@
public static final Parcelable.Creator<CarUxRestrictionsConfiguration> CREATOR =
new Parcelable.Creator<CarUxRestrictionsConfiguration>() {
- @Override
- public CarUxRestrictionsConfiguration createFromParcel(Parcel source) {
- return new CarUxRestrictionsConfiguration(source);
- }
+ @Override
+ public CarUxRestrictionsConfiguration createFromParcel(Parcel source) {
+ return new CarUxRestrictionsConfiguration(source);
+ }
- @Override
- public CarUxRestrictionsConfiguration[] newArray(int size) {
- return new CarUxRestrictionsConfiguration[size];
- }
- };
+ @Override
+ public CarUxRestrictionsConfiguration[] newArray(int size) {
+ return new CarUxRestrictionsConfiguration[size];
+ }
+ };
@Override
public int describeContents() {
@@ -558,16 +629,18 @@
}
private CarUxRestrictionsConfiguration(Parcel in) {
- for (int drivingState : DRIVING_STATES) {
- List<RestrictionsPerSpeedRange> restrictions = new ArrayList<>();
- in.readTypedList(restrictions, RestrictionsPerSpeedRange.CREATOR);
- mBaselineUxRestrictions.put(drivingState, restrictions);
+ int modesCount = in.readInt();
+ for (int i = 0; i < modesCount; i++) {
+ String modeName = in.readString();
+ RestrictionModeContainer container = new RestrictionModeContainer();
+ for (int drivingState : DRIVING_STATES) {
+ List<RestrictionsPerSpeedRange> restrictions = new ArrayList<>();
+ in.readTypedList(restrictions, RestrictionsPerSpeedRange.CREATOR);
+ container.setRestrictionsForDriveState(drivingState, restrictions);
+ }
+ mRestrictionModes.put(modeName, container);
}
- for (int drivingState : DRIVING_STATES) {
- List<RestrictionsPerSpeedRange> restrictions = new ArrayList<>();
- in.readTypedList(restrictions, RestrictionsPerSpeedRange.CREATOR);
- mPassengerUxRestrictions.put(drivingState, restrictions);
- }
+
boolean nullPhysicalPort = in.readBoolean();
byte physicalPort = in.readByte();
mPhysicalPort = nullPhysicalPort ? null : physicalPort;
@@ -579,11 +652,12 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- for (int drivingState : DRIVING_STATES) {
- dest.writeTypedList(mBaselineUxRestrictions.get(drivingState));
- }
- for (int drivingState : DRIVING_STATES) {
- dest.writeTypedList(mPassengerUxRestrictions.get(drivingState));
+ dest.writeInt(mRestrictionModes.size());
+ for (Map.Entry<String, RestrictionModeContainer> entry : mRestrictionModes.entrySet()) {
+ dest.writeString(entry.getKey());
+ for (int drivingState : DRIVING_STATES) {
+ dest.writeTypedList(entry.getValue().getRestrictionsForDriveState(drivingState));
+ }
}
boolean nullPhysicalPort = mPhysicalPort == null;
dest.writeBoolean(nullPhysicalPort);
@@ -626,16 +700,10 @@
private int mMaxCumulativeContentItems = UX_RESTRICTIONS_UNKNOWN;
private int mMaxStringLength = UX_RESTRICTIONS_UNKNOWN;
- public Map<Integer, List<RestrictionsPerSpeedRange>> mPassengerUxRestrictions =
- new ArrayMap<>(DRIVING_STATES.length);
- public Map<Integer, List<RestrictionsPerSpeedRange>> mBaselineUxRestrictions =
- new ArrayMap<>(DRIVING_STATES.length);
+ public final Map<String, RestrictionModeContainer> mRestrictionModes = new ArrayMap<>();
public Builder() {
- for (int drivingState : DRIVING_STATES) {
- mBaselineUxRestrictions.put(drivingState, new ArrayList<>());
- mPassengerUxRestrictions.put(drivingState, new ArrayList<>());
- }
+ mRestrictionModes.put(UX_RESTRICTION_MODE_BASELINE, new RestrictionModeContainer());
}
/**
@@ -665,16 +733,16 @@
/**
* Sets UX restrictions with speed range.
*
- * @param drivingState Restrictions will be set for this Driving state.
- * See constants in {@link CarDrivingStateEvent}.
- * @param speedRange If set, restrictions will only apply when current speed is within
- * the range. Only {@link CarDrivingStateEvent#DRIVING_STATE_MOVING}
- * supports speed range. {@code null} implies the full speed range,
- * i.e. zero to {@link SpeedRange#MAX_SPEED}.
+ * @param drivingState Restrictions will be set for this Driving state.
+ * See constants in {@link CarDrivingStateEvent}.
+ * @param speedRange If set, restrictions will only apply when current speed is
+ * within the range. Only
+ * {@link CarDrivingStateEvent#DRIVING_STATE_MOVING}
+ * supports speed range. {@code null} implies the full speed
+ * range, i.e. zero to {@link SpeedRange#MAX_SPEED}.
* @param requiresOptimization Whether distraction optimization (DO) is required for this
* driving state.
- * @param restrictions See constants in {@link CarUxRestrictions}.
- *
+ * @param restrictions See constants in {@link CarUxRestrictions}.
* @deprecated Use {@link #setUxRestrictions(int, DrivingStateRestrictions)} instead.
*/
@Deprecated
@@ -690,10 +758,9 @@
/**
* Sets UX restriction.
*
- * @param drivingState Restrictions will be set for this Driving state.
- * See constants in {@link CarDrivingStateEvent}.
+ * @param drivingState Restrictions will be set for this Driving state.
+ * See constants in {@link CarDrivingStateEvent}.
* @param drivingStateRestrictions Restrictions to set.
- *
* @return This builder object for method chaining.
*/
public Builder setUxRestrictions(
@@ -705,22 +772,13 @@
"Non-moving driving state should not specify speed range.");
}
- List<RestrictionsPerSpeedRange> restrictions;
- switch (drivingStateRestrictions.mMode) {
- case UX_RESTRICTION_MODE_BASELINE:
- restrictions = mBaselineUxRestrictions.get(drivingState);
- break;
- case UX_RESTRICTION_MODE_PASSENGER:
- restrictions = mPassengerUxRestrictions.get(drivingState);
- break;
- default:
- String mode = CarUxRestrictionsManager.modeToString(
- drivingStateRestrictions.mMode);
- throw new IllegalArgumentException("Unrecognized restriction mode " + mode);
- }
- restrictions.add(new RestrictionsPerSpeedRange(
- drivingStateRestrictions.mMode, drivingStateRestrictions.mReqOpt,
- drivingStateRestrictions.mRestrictions, speedRange));
+ RestrictionModeContainer container = mRestrictionModes.computeIfAbsent(
+ drivingStateRestrictions.mMode, mode -> new RestrictionModeContainer());
+
+ container.getRestrictionsForDriveState(drivingState).add(
+ new RestrictionsPerSpeedRange(
+ drivingStateRestrictions.mMode, drivingStateRestrictions.mReqOpt,
+ drivingStateRestrictions.mRestrictions, speedRange));
return this;
}
@@ -757,15 +815,22 @@
addDefaultRestrictionsToBaseline();
validateBaselineModeRestrictions();
- validatePassengerModeRestrictions();
+ for (String mode : mRestrictionModes.keySet()) {
+ if (UX_RESTRICTION_MODE_BASELINE.equals(mode)) {
+ continue;
+ }
+ validateModeRestrictions(mode);
+ }
return new CarUxRestrictionsConfiguration(this);
}
private void addDefaultRestrictionsToBaseline() {
+ RestrictionModeContainer container = mRestrictionModes.get(
+ UX_RESTRICTION_MODE_BASELINE);
for (int drivingState : DRIVING_STATES) {
List<RestrictionsPerSpeedRange> restrictions =
- mBaselineUxRestrictions.get(drivingState);
+ container.getRestrictionsForDriveState(drivingState);
if (restrictions.size() == 0) {
Log.i(TAG, "Using default restrictions for driving state: "
+ getDrivingStateName(drivingState));
@@ -776,9 +841,11 @@
}
private void validateBaselineModeRestrictions() {
+ RestrictionModeContainer container = mRestrictionModes.get(
+ UX_RESTRICTION_MODE_BASELINE);
for (int drivingState : DRIVING_STATES) {
List<RestrictionsPerSpeedRange> restrictions =
- mBaselineUxRestrictions.get(drivingState);
+ container.getRestrictionsForDriveState(drivingState);
if (drivingState != DRIVING_STATE_MOVING) {
// Note: For non-moving state, setUxRestrictions() rejects UxRestriction with
// speed range, so we don't check here.
@@ -809,13 +876,17 @@
}
}
- private void validatePassengerModeRestrictions() {
- List<RestrictionsPerSpeedRange> passengerMovingRestrictions =
- mPassengerUxRestrictions.get(DRIVING_STATE_MOVING);
- Collections.sort(passengerMovingRestrictions,
+ private void validateModeRestrictions(String mode) {
+ if (!mRestrictionModes.containsKey(mode)) {
+ return;
+ }
+ RestrictionModeContainer container = mRestrictionModes.get(mode);
+ List<RestrictionsPerSpeedRange> movingRestrictions =
+ container.getRestrictionsForDriveState(DRIVING_STATE_MOVING);
+ Collections.sort(movingRestrictions,
Comparator.comparing(RestrictionsPerSpeedRange::getSpeedRange));
- validateContinuousSpeedRange(passengerMovingRestrictions);
+ validateContinuousSpeedRange(movingRestrictions);
}
/**
@@ -909,7 +980,7 @@
mMaxSpeed = maxSpeed;
}
- /**
+ /**
* Return if the given speed is in the range of [minSpeed, maxSpeed).
*
* @param speed Speed to check
@@ -965,16 +1036,15 @@
* These UX restrictions can also specified to be only applicable to certain speed range and
* restriction mode.
*
- * @see UxRestrictionMode
- * @see Builder.SpeedRange
- *
* @hide
+ * @see Builder.SpeedRange
*/
public static final class DrivingStateRestrictions {
- private int mMode = UX_RESTRICTION_MODE_BASELINE;
+ private String mMode = UX_RESTRICTION_MODE_BASELINE;
private boolean mReqOpt = true;
private int mRestrictions = CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED;
- @Nullable private Builder.SpeedRange mSpeedRange;
+ @Nullable
+ private Builder.SpeedRange mSpeedRange;
/**
* Sets whether Distraction Optimization (DO) is required. Defaults to {@code true}.
@@ -999,8 +1069,8 @@
* Sets restriction mode to apply to.
* Defaults to {@link CarUxRestrictionsManager#UX_RESTRICTION_MODE_BASELINE}.
*/
- public DrivingStateRestrictions setMode(@UxRestrictionMode int mode) {
- mMode = mode;
+ public DrivingStateRestrictions setMode(@NonNull String mode) {
+ mMode = Objects.requireNonNull(mode, "mode must not be null");
return this;
}
@@ -1016,7 +1086,7 @@
@Override
public String toString() {
return new StringBuilder()
- .append("Mode: ").append(CarUxRestrictionsManager.modeToString(mMode))
+ .append("Mode: ").append(mMode)
.append(". Requires DO? ").append(mReqOpt)
.append(". Restrictions: ").append(Integer.toBinaryString(mRestrictions))
.append(". SpeedRange: ")
@@ -1030,8 +1100,7 @@
* Speed range is valid only for the {@link CarDrivingStateEvent#DRIVING_STATE_MOVING}.
*/
private static final class RestrictionsPerSpeedRange implements Parcelable {
- @UxRestrictionMode
- final int mMode;
+ final String mMode;
final boolean mReqOpt;
final int mRestrictions;
@Nullable
@@ -1041,13 +1110,13 @@
this(UX_RESTRICTION_MODE_BASELINE, reqOpt, restrictions, null);
}
- RestrictionsPerSpeedRange(@UxRestrictionMode int mode, boolean reqOpt, int restrictions,
+ RestrictionsPerSpeedRange(@NonNull String mode, boolean reqOpt, int restrictions,
@Nullable Builder.SpeedRange speedRange) {
if (!reqOpt && restrictions != CarUxRestrictions.UX_RESTRICTIONS_BASELINE) {
throw new IllegalArgumentException(
"Driving optimization is not required but UX restrictions is required.");
}
- mMode = mode;
+ mMode = Objects.requireNonNull(mode, "mode must not be null");
mReqOpt = reqOpt;
mRestrictions = restrictions;
mSpeedRange = speedRange;
@@ -1071,7 +1140,7 @@
return false;
}
RestrictionsPerSpeedRange other = (RestrictionsPerSpeedRange) obj;
- return mMode == other.mMode
+ return Objects.equals(mMode, other.mMode)
&& mReqOpt == other.mReqOpt
&& mRestrictions == other.mRestrictions
&& Objects.equals(mSpeedRange, other.mSpeedRange);
@@ -1080,7 +1149,7 @@
@Override
public String toString() {
return new StringBuilder()
- .append("[Mode is ").append(CarUxRestrictionsManager.modeToString(mMode))
+ .append("[Mode is ").append(mMode)
.append("; Requires DO? ").append(mReqOpt)
.append("; Restrictions: ").append(Integer.toBinaryString(mRestrictions))
.append("; Speed range: ")
@@ -1110,7 +1179,7 @@
}
protected RestrictionsPerSpeedRange(Parcel in) {
- mMode = in.readInt();
+ mMode = in.readString();
mReqOpt = in.readBoolean();
mRestrictions = in.readInt();
// Whether speed range is specified.
@@ -1125,7 +1194,7 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mMode);
+ dest.writeString(mMode);
dest.writeBoolean(mReqOpt);
dest.writeInt(mRestrictions);
// Whether speed range is specified.
@@ -1136,4 +1205,55 @@
}
}
}
+
+ /**
+ * All the restriction configurations for a particular mode.
+ */
+ private static final class RestrictionModeContainer {
+ /**
+ * Mapping from drive state to the list of applicable restrictions.
+ */
+ private final Map<Integer, List<RestrictionsPerSpeedRange>> mDriveStateUxRestrictions =
+ new ArrayMap<>(DRIVING_STATES.length);
+
+ RestrictionModeContainer() {
+ for (int drivingState : DRIVING_STATES) {
+ mDriveStateUxRestrictions.put(drivingState, new ArrayList<>());
+ }
+ }
+
+ /**
+ * Returns the restrictions for a particular drive state.
+ */
+ @NonNull
+ List<RestrictionsPerSpeedRange> getRestrictionsForDriveState(
+ @CarDrivingState int driveState) {
+ // Guaranteed not to be null since a container is initialized with empty lists for
+ // each drive state in the constructor.
+ return mDriveStateUxRestrictions.get(driveState);
+ }
+
+ void setRestrictionsForDriveState(@CarDrivingState int driveState,
+ @NonNull List<RestrictionsPerSpeedRange> restrictions) {
+ Objects.requireNonNull(restrictions, "null restrictions are not allows");
+ mDriveStateUxRestrictions.put(driveState, restrictions);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof RestrictionModeContainer)) {
+ return false;
+ }
+ RestrictionModeContainer container = (RestrictionModeContainer) obj;
+ return Objects.equals(mDriveStateUxRestrictions, container.mDriveStateUxRestrictions);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mDriveStateUxRestrictions);
+ }
+ }
}
diff --git a/car-lib/src/android/car/drivingstate/CarUxRestrictionsManager.java b/car-lib/src/android/car/drivingstate/CarUxRestrictionsManager.java
index be194b8..1557e6e 100644
--- a/car-lib/src/android/car/drivingstate/CarUxRestrictionsManager.java
+++ b/car-lib/src/android/car/drivingstate/CarUxRestrictionsManager.java
@@ -16,7 +16,6 @@
package android.car.drivingstate;
-import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -32,11 +31,10 @@
import com.android.internal.annotations.GuardedBy;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
/**
* API to register and get the User Experience restrictions imposed based on the car's driving
@@ -53,31 +51,7 @@
*
* @hide
*/
- public static final int UX_RESTRICTION_MODE_BASELINE = 0;
- /**
- * Passenger restriction mode uses UX restrictions for {@link #UX_RESTRICTION_MODE_PASSENGER},
- * set through {@link CarUxRestrictionsConfiguration.Builder.UxRestrictions#setMode(int)}.
- *
- * <p>If a new {@link CarUxRestrictions} is available upon mode transition, it'll be immediately
- * dispatched to listeners.
- *
- * <p>If passenger mode restrictions is not configured for current driving state, it will fall
- * back to {@link #UX_RESTRICTION_MODE_BASELINE}.
- *
- * <p>Caller are responsible for determining and executing the criteria for entering and exiting
- * this mode. Exiting by setting mode to {@link #UX_RESTRICTION_MODE_BASELINE}.
- *
- * @hide
- */
- public static final int UX_RESTRICTION_MODE_PASSENGER = 1;
-
- /** @hide */
- @IntDef(prefix = { "UX_RESTRICTION_MODE_" }, value = {
- UX_RESTRICTION_MODE_BASELINE,
- UX_RESTRICTION_MODE_PASSENGER
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface UxRestrictionMode {}
+ public static final String UX_RESTRICTION_MODE_BASELINE = "baseline";
private int mDisplayId = Display.INVALID_DISPLAY;
private final ICarUxRestrictionsManager mUxRService;
@@ -187,7 +161,6 @@
*
* @param configs Map of display Id to UX restrictions configurations to be persisted.
* @return {@code true} if input config was successfully saved; {@code false} otherwise.
- *
* @hide
*/
@RequiresPermission(value = Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION)
@@ -225,10 +198,22 @@
/**
* Sets restriction mode. Returns {@code true} if the operation succeeds.
*
+ * <p>The default mode is {@link #UX_RESTRICTION_MODE_BASELINE}.
+ *
+ * <p>If a new {@link CarUxRestrictions} is available upon mode transition, it'll
+ * be immediately dispatched to listeners.
+ *
+ * <p>If the given mode is not configured for current driving state, it
+ * will fall back to the default value.
+ *
+ * <p>If a configuration was set for a passenger mode before an upgrade to Android R, that
+ * passenger configuration is now called "passenger".
+ *
* @hide
*/
@RequiresPermission(value = Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION)
- public boolean setRestrictionMode(@UxRestrictionMode int mode) {
+ public boolean setRestrictionMode(@NonNull String mode) {
+ Objects.requireNonNull(mode, "mode must not be null");
try {
return mUxRService.setRestrictionMode(mode);
} catch (RemoteException e) {
@@ -239,11 +224,16 @@
/**
* Returns the current restriction mode.
*
+ * <p>The default mode is {@link #UX_RESTRICTION_MODE_BASELINE}.
+ *
+ * <p>If a configuration was set for a passenger mode before an upgrade to Android R, that
+ * passenger configuration is now called "passenger".
+ *
* @hide
*/
@RequiresPermission(value = Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION)
- @UxRestrictionMode
- public int getRestrictionMode() {
+ @NonNull
+ public String getRestrictionMode() {
try {
return mUxRService.getRestrictionMode();
} catch (RemoteException e) {
@@ -259,7 +249,6 @@
*
* @param config UX restrictions configuration to be persisted.
* @return {@code true} if input config was successfully saved; {@code false} otherwise.
- *
* @hide
*/
@RequiresPermission(value = Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION)
@@ -278,7 +267,6 @@
* This methods is only for test purpose, please do not use in production.
*
* @return current staged configuration, {@code null} if it's not available
- *
* @hide
*/
@Nullable
@@ -295,7 +283,6 @@
* Gets the current configurations.
*
* @return current configurations that is in effect.
- *
* @hide
*/
@RequiresPermission(value = Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION)
@@ -308,20 +295,6 @@
}
/**
- * @hide
- */
- public static String modeToString(@UxRestrictionMode int mode) {
- switch (mode) {
- case UX_RESTRICTION_MODE_BASELINE:
- return "baseline";
- case UX_RESTRICTION_MODE_PASSENGER:
- return "passenger";
- default:
- throw new IllegalArgumentException("Unrecognized restriction mode " + mode);
- }
- }
-
- /**
* Class that implements the listener interface and gets called back from the
* {@link com.android.car.CarDrivingStateService} across the binder interface.
*/
diff --git a/car-lib/src/android/car/drivingstate/ICarUxRestrictionsManager.aidl b/car-lib/src/android/car/drivingstate/ICarUxRestrictionsManager.aidl
index 7f3a6e9..8c9afd7 100644
--- a/car-lib/src/android/car/drivingstate/ICarUxRestrictionsManager.aidl
+++ b/car-lib/src/android/car/drivingstate/ICarUxRestrictionsManager.aidl
@@ -34,6 +34,8 @@
boolean saveUxRestrictionsConfigurationForNextBoot(in List<CarUxRestrictionsConfiguration> configs) = 3;
List<CarUxRestrictionsConfiguration> getStagedConfigs() = 4;
List<CarUxRestrictionsConfiguration> getConfigs() = 5;
- boolean setRestrictionMode(int mode) = 6;
- int getRestrictionMode() = 7;
+ // 6 removed. Do not use - boolean setRestrictionMode(int mode) = 6;
+ // 7 removed. Do not use - int getRestrictionMode() = 7;
+ boolean setRestrictionMode(String mode) = 10;
+ String getRestrictionMode() = 11;
}
diff --git a/service/res/values/attrs.xml b/service/res/values/attrs.xml
index eec51a8..e026cae 100644
--- a/service/res/values/attrs.xml
+++ b/service/res/values/attrs.xml
@@ -99,14 +99,11 @@
<flag name="no_voice_transcription" value="256"/>
<flag name="fully_restricted" value="511"/>
</attr>
- <!-- UX restrictions service supports returning different sets of UX restrictions for
- the same driving state, through configurations for each "mode". -->
- <attr name="mode">
- <!-- Default mode. -->
- <flag name="baseline" value="0"/>
- <!-- Mode for passenger to interact with system. -->
- <flag name="passenger" value="1"/>
- </attr>
+ <!-- UX restrictions service supports returning different sets of UX restrictions for the
+ same driving state, through configurations for each "mode". These modes can be
+ specified by any string name, for example, "passenger". "baseline" is the default
+ value. -->
+ <attr name="mode" format="string"/>
</declare-styleable>
<!-- 2. Some of UX restrictions can be parametrized. -->
diff --git a/service/src/com/android/car/CarUxRestrictionsConfigurationXmlParser.java b/service/src/com/android/car/CarUxRestrictionsConfigurationXmlParser.java
index 0a37491..15e71d1 100644
--- a/service/src/com/android/car/CarUxRestrictionsConfigurationXmlParser.java
+++ b/service/src/com/android/car/CarUxRestrictionsConfigurationXmlParser.java
@@ -270,7 +270,7 @@
}
int restrictions = UX_RESTRICTIONS_UNKNOWN;
- int restrictionMode = UX_RESTRICTION_MODE_BASELINE;
+ String restrictionMode = UX_RESTRICTION_MODE_BASELINE;
boolean requiresOpt = true;
while (RESTRICTIONS.equals(parser.getName())
&& parser.getEventType() == XmlResourceParser.START_TAG) {
@@ -281,12 +281,14 @@
CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED);
requiresOpt = a.getBoolean(
R.styleable.UxRestrictions_Restrictions_requiresDistractionOptimization, true);
- restrictionMode = a.getInt(
- R.styleable.UxRestrictions_Restrictions_mode, UX_RESTRICTION_MODE_BASELINE);
+ restrictionMode = a.getString(R.styleable.UxRestrictions_Restrictions_mode);
a.recycle();
parser.next();
}
+ if (restrictionMode == null) {
+ restrictionMode = UX_RESTRICTION_MODE_BASELINE;
+ }
return new DrivingStateRestrictions()
.setDistractionOptimizationRequired(requiresOpt)
.setRestrictions(restrictions)
diff --git a/service/src/com/android/car/CarUxRestrictionsManagerService.java b/service/src/com/android/car/CarUxRestrictionsManagerService.java
index bb49ff2..0ea757f 100644
--- a/service/src/com/android/car/CarUxRestrictionsManagerService.java
+++ b/service/src/com/android/car/CarUxRestrictionsManagerService.java
@@ -24,6 +24,7 @@
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.car.Car;
@@ -51,6 +52,7 @@
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.JsonReader;
+import android.util.JsonToken;
import android.util.JsonWriter;
import android.util.Log;
import android.util.Slog;
@@ -70,6 +72,8 @@
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -78,6 +82,7 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
/**
@@ -86,7 +91,7 @@
* <p>
* <h1>UX Restrictions Configuration</h1>
* When this service starts, it will first try reading the configuration set through
- * {@link #saveUxRestrictionsConfigurationForNextBoot(CarUxRestrictionsConfiguration)}.
+ * {@link #saveUxRestrictionsConfigurationForNextBoot(List)}.
* If one is not available, it will try reading the configuration saved in
* {@code R.xml.car_ux_restrictions_map}. If XML is somehow unavailable, it will
* fall back to a hard-coded configuration.
@@ -104,6 +109,17 @@
private static final float SPEED_NOT_AVAILABLE = -1.0F;
private static final byte DEFAULT_PORT = 0;
+ private static final int UNKNOWN_JSON_SCHEMA_VERSION = -1;
+ private static final int JSON_SCHEMA_VERSION_V1 = 1;
+ private static final int JSON_SCHEMA_VERSION_V2 = 2;
+
+ @IntDef({UNKNOWN_JSON_SCHEMA_VERSION, JSON_SCHEMA_VERSION_V1, JSON_SCHEMA_VERSION_V2})
+ @Retention(RetentionPolicy.SOURCE)
+ private @interface JsonSchemaVersion {}
+
+ private static final String JSON_NAME_SCHEMA_VERSION = "schema_version";
+ private static final String JSON_NAME_RESTRICTIONS = "restrictions";
+
@VisibleForTesting
static final String CONFIG_FILENAME_PRODUCTION = "ux_restrictions_prod_config.json";
@VisibleForTesting
@@ -127,8 +143,7 @@
private Map<Byte, CarUxRestrictionsConfiguration> mCarUxRestrictionsConfigurations;
private Map<Byte, CarUxRestrictions> mCurrentUxRestrictions;
- @CarUxRestrictionsManager.UxRestrictionMode
- private int mRestrictionMode = UX_RESTRICTION_MODE_BASELINE;
+ private String mRestrictionMode = UX_RESTRICTION_MODE_BASELINE;
// Flag to disable broadcasting UXR changes - for development purposes
@GuardedBy("this")
@@ -183,8 +198,7 @@
*
* <p>Reads config from the following sources in order:
* <ol>
- * <li>saved config set by
- * {@link #saveUxRestrictionsConfigurationForNextBoot(CarUxRestrictionsConfiguration)};
+ * <li>saved config set by {@link #saveUxRestrictionsConfigurationForNextBoot(List)};
* <li>XML resource config from {@code R.xml.car_ux_restrictions_map};
* <li>hardcoded default config.
* </ol>
@@ -434,24 +448,24 @@
*
* <p>Defaults to {@link CarUxRestrictionsManager#UX_RESTRICTION_MODE_BASELINE}.
*
- * @param mode See values in {@link CarUxRestrictionsManager.UxRestrictionMode}.
+ * @param mode the restriction mode
* @return {@code true} if mode was successfully changed; {@code false} otherwise.
* @see CarUxRestrictionsConfiguration.DrivingStateRestrictions
* @see CarUxRestrictionsConfiguration.Builder
*/
@Override
- public synchronized boolean setRestrictionMode(
- @CarUxRestrictionsManager.UxRestrictionMode int mode) {
+ public synchronized boolean setRestrictionMode(@NonNull String mode) {
ICarImpl.assertPermission(mContext, Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION);
+ Objects.requireNonNull(mode, "mode must not be null");
- if (mRestrictionMode == mode) {
+ if (mRestrictionMode.equals(mode)) {
return true;
}
addTransitionLog(TAG, mRestrictionMode, mode, System.currentTimeMillis(),
"Restriction mode");
mRestrictionMode = mode;
- logd("Set restriction mode to: " + CarUxRestrictionsManager.modeToString(mode));
+ logd("Set restriction mode to: " + mode);
handleDispatchUxRestrictions(
mDrivingStateService.getCurrentDrivingState().eventValue, getCurrentSpeed());
@@ -459,8 +473,8 @@
}
@Override
- @CarUxRestrictionsManager.UxRestrictionMode
- public synchronized int getRestrictionMode() {
+ @NonNull
+ public synchronized String getRestrictionMode() {
ICarImpl.assertPermission(mContext, Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION);
return mRestrictionMode;
@@ -483,11 +497,15 @@
}
try (JsonWriter jsonWriter = new JsonWriter(
new OutputStreamWriter(fos, StandardCharsets.UTF_8))) {
+ jsonWriter.beginObject();
+ jsonWriter.name(JSON_NAME_SCHEMA_VERSION).value(JSON_SCHEMA_VERSION_V2);
+ jsonWriter.name(JSON_NAME_RESTRICTIONS);
jsonWriter.beginArray();
for (CarUxRestrictionsConfiguration config : configs) {
config.writeJson(jsonWriter);
}
jsonWriter.endArray();
+ jsonWriter.endObject();
} catch (IOException e) {
Log.e(TAG, "Could not persist config", e);
stagedFile.failWrite(fos);
@@ -504,15 +522,26 @@
return null;
}
+ // Take one pass at the file to check the version and then a second pass to read the
+ // contents. We could assess the version and read in one pass, but we're preferring
+ // clarity over complexity here.
+ int schemaVersion = readFileSchemaVersion(file);
+
AtomicFile configFile = new AtomicFile(file);
try (JsonReader reader = new JsonReader(
new InputStreamReader(configFile.openRead(), StandardCharsets.UTF_8))) {
List<CarUxRestrictionsConfiguration> configs = new ArrayList<>();
- reader.beginArray();
- while (reader.hasNext()) {
- configs.add(CarUxRestrictionsConfiguration.readJson(reader));
+ switch (schemaVersion) {
+ case JSON_SCHEMA_VERSION_V1:
+ readV1Json(reader, configs);
+ break;
+ case JSON_SCHEMA_VERSION_V2:
+ readV2Json(reader, configs);
+ break;
+ default:
+ Log.e(TAG, "Unable to parse schema for version " + schemaVersion);
}
- reader.endArray();
+
return configs;
} catch (IOException e) {
Log.e(TAG, "Could not read persisted config file " + file.getName(), e);
@@ -520,6 +549,68 @@
return null;
}
+ private void readV1Json(JsonReader reader,
+ List<CarUxRestrictionsConfiguration> configs) throws IOException {
+ readRestrictionsArray(reader, configs, JSON_SCHEMA_VERSION_V1);
+ }
+
+ private void readV2Json(JsonReader reader,
+ List<CarUxRestrictionsConfiguration> configs) throws IOException {
+ reader.beginObject();
+ while (reader.hasNext()) {
+ String name = reader.nextName();
+ switch (name) {
+ case JSON_NAME_RESTRICTIONS:
+ readRestrictionsArray(reader, configs, JSON_SCHEMA_VERSION_V2);
+ break;
+ default:
+ reader.skipValue();
+ }
+ }
+ reader.endObject();
+ }
+
+ private int readFileSchemaVersion(File file) {
+ AtomicFile configFile = new AtomicFile(file);
+ try (JsonReader reader = new JsonReader(
+ new InputStreamReader(configFile.openRead(), StandardCharsets.UTF_8))) {
+ List<CarUxRestrictionsConfiguration> configs = new ArrayList<>();
+ if (reader.peek() == JsonToken.BEGIN_ARRAY) {
+ // only schema V1 beings with an array - no need to keep reading
+ reader.close();
+ return JSON_SCHEMA_VERSION_V1;
+ } else {
+ reader.beginObject();
+ while (reader.hasNext()) {
+ String name = reader.nextName();
+ switch (name) {
+ case JSON_NAME_SCHEMA_VERSION:
+ int schemaVersion = reader.nextInt();
+ // got the version, no need to continue reading
+ reader.close();
+ return schemaVersion;
+ default:
+ reader.skipValue();
+ }
+ }
+ reader.endObject();
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Could not read persisted config file " + file.getName(), e);
+ }
+ return UNKNOWN_JSON_SCHEMA_VERSION;
+ }
+
+ private void readRestrictionsArray(JsonReader reader,
+ List<CarUxRestrictionsConfiguration> configs, @JsonSchemaVersion int schemaVersion)
+ throws IOException {
+ reader.beginArray();
+ while (reader.hasNext()) {
+ configs.add(CarUxRestrictionsConfiguration.readJson(reader, schemaVersion));
+ }
+ reader.endArray();
+ }
+
/**
* Enable/disable UX restrictions change broadcast blocking.
* Setting this to true will stop broadcasts of UX restriction change to listeners.
@@ -940,7 +1031,8 @@
.build();
}
- private void addTransitionLog(String name, int from, int to, long timestamp, String extra) {
+ private void addTransitionLog(String name, String from, String to, long timestamp,
+ String extra) {
if (mTransitionLogs.size() >= MAX_TRANSITION_LOG_SIZE) {
mTransitionLogs.remove();
}
diff --git a/service/src/com/android/car/Utils.java b/service/src/com/android/car/Utils.java
index 2b468b7..41ab67b 100644
--- a/service/src/com/android/car/Utils.java
+++ b/service/src/com/android/car/Utils.java
@@ -142,18 +142,18 @@
*/
public static class TransitionLog {
private String mServiceName; // name of the service or tag
- private int mFromState; // old state
- private int mToState; // new state
+ private Object mFromState; // old state
+ private Object mToState; // new state
private long mTimestampMs; // System.currentTimeMillis()
private String mExtra; // Additional information as a String
- public TransitionLog(String name, int fromState, int toState, long timestamp,
+ public TransitionLog(String name, Object fromState, Object toState, long timestamp,
String extra) {
this(name, fromState, toState, timestamp);
mExtra = extra;
}
- public TransitionLog(String name, int fromState, int toState, long timeStamp) {
+ public TransitionLog(String name, Object fromState, Object toState, long timeStamp) {
mServiceName = name;
mFromState = fromState;
mToState = toState;
diff --git a/tests/UxRestrictionsSample/src/com/google/android/car/uxr/sample/MainActivity.java b/tests/UxRestrictionsSample/src/com/google/android/car/uxr/sample/MainActivity.java
index 89f3349..f4eff56 100644
--- a/tests/UxRestrictionsSample/src/com/google/android/car/uxr/sample/MainActivity.java
+++ b/tests/UxRestrictionsSample/src/com/google/android/car/uxr/sample/MainActivity.java
@@ -19,7 +19,6 @@
import static android.car.drivingstate.CarDrivingStateEvent.DRIVING_STATE_MOVING;
import static android.car.drivingstate.CarDrivingStateEvent.DRIVING_STATE_PARKED;
import static android.car.drivingstate.CarUxRestrictionsManager.UX_RESTRICTION_MODE_BASELINE;
-import static android.car.drivingstate.CarUxRestrictionsManager.UX_RESTRICTION_MODE_PASSENGER;
import android.app.AlertDialog;
import android.car.Car;
@@ -50,6 +49,7 @@
public static final String TAG = "UxRDemo";
private static final String DIALOG_FRAGMENT_TAG = "dialog_fragment_tag";
+ private static final String UX_RESTRICTION_MODE_PASSENGER = "passenger";
private Car mCar;
private CarDrivingStateManager mCarDrivingStateManager;
@@ -187,7 +187,7 @@
return;
}
- int mode = mCarUxRestrictionsManager.getRestrictionMode();
+ String mode = mCarUxRestrictionsManager.getRestrictionMode();
switch (mode) {
case UX_RESTRICTION_MODE_BASELINE:
mCarUxRestrictionsManager.setRestrictionMode(UX_RESTRICTION_MODE_PASSENGER);
@@ -218,7 +218,7 @@
.setRestrictions(baseline))
.setUxRestrictions(DRIVING_STATE_MOVING,
new DrivingStateRestrictions()
- .setMode(CarUxRestrictionsManager.UX_RESTRICTION_MODE_PASSENGER)
+ .setMode(UX_RESTRICTION_MODE_PASSENGER)
.setDistractionOptimizationRequired(passenger != 0)
.setRestrictions(passenger))
.setUxRestrictions(DRIVING_STATE_IDLING,
@@ -227,7 +227,7 @@
.setRestrictions(baseline))
.setUxRestrictions(DRIVING_STATE_IDLING,
new DrivingStateRestrictions()
- .setMode(CarUxRestrictionsManager.UX_RESTRICTION_MODE_PASSENGER)
+ .setMode(UX_RESTRICTION_MODE_PASSENGER)
.setDistractionOptimizationRequired(passenger != 0)
.setRestrictions(passenger))
.build();
diff --git a/tests/android_car_api_test/src/android/car/apitest/CarUxRestrictionsConfigurationTest.java b/tests/android_car_api_test/src/android/car/apitest/CarUxRestrictionsConfigurationTest.java
index a040442..030b133 100644
--- a/tests/android_car_api_test/src/android/car/apitest/CarUxRestrictionsConfigurationTest.java
+++ b/tests/android_car_api_test/src/android/car/apitest/CarUxRestrictionsConfigurationTest.java
@@ -19,12 +19,12 @@
import static android.car.drivingstate.CarDrivingStateEvent.DRIVING_STATE_IDLING;
import static android.car.drivingstate.CarDrivingStateEvent.DRIVING_STATE_MOVING;
import static android.car.drivingstate.CarDrivingStateEvent.DRIVING_STATE_PARKED;
+import static android.car.drivingstate.CarDrivingStateEvent.DRIVING_STATE_UNKNOWN;
import static android.car.drivingstate.CarUxRestrictions.UX_RESTRICTIONS_BASELINE;
import static android.car.drivingstate.CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED;
import static android.car.drivingstate.CarUxRestrictions.UX_RESTRICTIONS_NO_VIDEO;
import static android.car.drivingstate.CarUxRestrictionsConfiguration.Builder.SpeedRange.MAX_SPEED;
import static android.car.drivingstate.CarUxRestrictionsManager.UX_RESTRICTION_MODE_BASELINE;
-import static android.car.drivingstate.CarUxRestrictionsManager.UX_RESTRICTION_MODE_PASSENGER;
import android.car.drivingstate.CarUxRestrictions;
import android.car.drivingstate.CarUxRestrictionsConfiguration;
@@ -38,11 +38,14 @@
import junit.framework.TestCase;
+import org.junit.Test;
+
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
+import java.io.StringReader;
/**
* Unit test for UXR config and its subclasses.
@@ -50,6 +53,9 @@
@SmallTest
public class CarUxRestrictionsConfigurationTest extends TestCase {
+ private static final String UX_RESTRICTION_MODE_PASSENGER = "passenger";
+
+ @Test
// This test verifies the expected way to build config would succeed.
public void testConstruction() {
new Builder().build();
@@ -124,13 +130,13 @@
public void testBuilderValidation_PassengerModeNoSpeedRangeOverlap() {
Builder builder = new Builder();
builder.setUxRestrictions(DRIVING_STATE_MOVING, new DrivingStateRestrictions()
- .setDistractionOptimizationRequired(true)
- .setRestrictions(UX_RESTRICTIONS_FULLY_RESTRICTED)
- .setSpeedRange(new Builder.SpeedRange(1f, 2f)));
+ .setDistractionOptimizationRequired(true)
+ .setRestrictions(UX_RESTRICTIONS_FULLY_RESTRICTED)
+ .setSpeedRange(new Builder.SpeedRange(1f, 2f)));
builder.setUxRestrictions(DRIVING_STATE_MOVING, new DrivingStateRestrictions()
- .setDistractionOptimizationRequired(true)
- .setRestrictions(UX_RESTRICTIONS_FULLY_RESTRICTED)
- .setSpeedRange(new Builder.SpeedRange(1f)));
+ .setDistractionOptimizationRequired(true)
+ .setRestrictions(UX_RESTRICTIONS_FULLY_RESTRICTED)
+ .setSpeedRange(new Builder.SpeedRange(1f)));
try {
builder.build();
fail();
@@ -294,7 +300,7 @@
new Builder.SpeedRange(1f);
Builder.SpeedRange s2 =
new Builder.SpeedRange(1f);
- assertTrue(s1.compareTo(s2) == 0);
+ assertEquals(0, s1.compareTo(s2));
}
public void testSpeedRangeComparison_SameMinDifferentMax() {
@@ -319,20 +325,20 @@
Builder.SpeedRange s1, s2;
s1 = new Builder.SpeedRange(0f);
- assertTrue(s1.equals(s1));
+ assertEquals(s1, s1);
s1 = new Builder.SpeedRange(1f);
s2 = new Builder.SpeedRange(1f);
- assertTrue(s1.compareTo(s2) == 0);
- assertTrue(s1.equals(s2));
+ assertEquals(0, s1.compareTo(s2));
+ assertEquals(s1, s2);
s1 = new Builder.SpeedRange(0f, 1f);
s2 = new Builder.SpeedRange(0f, 1f);
- assertTrue(s1.equals(s2));
+ assertEquals(s1, s2);
s1 = new Builder.SpeedRange(0f, MAX_SPEED);
s2 = new Builder.SpeedRange(0f, MAX_SPEED);
- assertTrue(s1.equals(s2));
+ assertEquals(s1, s2);
s1 = new Builder.SpeedRange(0f);
s2 = new Builder.SpeedRange(1f);
@@ -347,7 +353,7 @@
CarUxRestrictionsConfiguration config =
new Builder().build();
- verifyConfigThroughJsonSerialization(config);
+ verifyConfigThroughJsonSerialization(config, /* schemaVersion= */ 2);
}
public void testJsonSerialization_RestrictionParameters() {
@@ -357,7 +363,7 @@
.setMaxContentDepth(1)
.build();
- verifyConfigThroughJsonSerialization(config);
+ verifyConfigThroughJsonSerialization(config, /* schemaVersion= */ 2);
}
public void testJsonSerialization_NonMovingStateRestrictions() {
@@ -365,7 +371,7 @@
.setUxRestrictions(DRIVING_STATE_PARKED, false, UX_RESTRICTIONS_BASELINE)
.build();
- verifyConfigThroughJsonSerialization(config);
+ verifyConfigThroughJsonSerialization(config, /* schemaVersion= */ 2);
}
public void testJsonSerialization_MovingStateNoSpeedRange() {
@@ -373,7 +379,7 @@
.setUxRestrictions(DRIVING_STATE_MOVING, true, UX_RESTRICTIONS_FULLY_RESTRICTED)
.build();
- verifyConfigThroughJsonSerialization(config);
+ verifyConfigThroughJsonSerialization(config, /* schemaVersion= */ 2);
}
public void testJsonSerialization_MovingStateWithSpeedRange() {
@@ -388,7 +394,7 @@
.setSpeedRange(new Builder.SpeedRange(5f, MAX_SPEED)))
.build();
- verifyConfigThroughJsonSerialization(config);
+ verifyConfigThroughJsonSerialization(config, /* schemaVersion= */ 2);
}
public void testJsonSerialization_UxRestrictionMode() {
@@ -409,11 +415,79 @@
.setRestrictions(UX_RESTRICTIONS_NO_VIDEO))
.build();
- verifyConfigThroughJsonSerialization(config);
+ verifyConfigThroughJsonSerialization(config, /* schemaVersion= */ 2);
+ }
+
+ @Test
+ public void testJsonSerialization_ReadsV1() throws Exception {
+ String v1LegacyJsonFormat = "{\"physical_port\":1,\"max_content_depth\":2,"
+ + "\"max_cumulative_content_items\":20,\"max_string_length\":21,"
+ + "\"parked_restrictions\":[{\"req_opt\":false,\"restrictions\":0}],"
+ + "\"idling_restrictions\":[{\"req_opt\":true,\"restrictions\":7}],"
+ + "\"moving_restrictions\":[{\"req_opt\":true,\"restrictions\":8}],"
+ + "\"unknown_restrictions\":[{\"req_opt\":true,\"restrictions\":511}],"
+ + "\"passenger_parked_restrictions\":[{\"req_opt\":false,\"restrictions\":0}],"
+ + "\"passenger_idling_restrictions\":[{\"req_opt\":true,\"restrictions\":56}],"
+ + "\"passenger_moving_restrictions\":[{\"req_opt\":true,\"restrictions\":57}],"
+ + "\"passenger_unknown_restrictions\":[{\"req_opt\":true,\"restrictions\":510}]}";
+ CarUxRestrictionsConfiguration expectedConfig = new Builder()
+ .setPhysicalPort((byte) 1)
+ .setMaxContentDepth(2)
+ .setMaxCumulativeContentItems(20)
+ .setMaxStringLength(21)
+ .setUxRestrictions(DRIVING_STATE_PARKED, new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(false)
+ .setRestrictions(UX_RESTRICTIONS_BASELINE)
+ .setMode(UX_RESTRICTION_MODE_BASELINE))
+ .setUxRestrictions(DRIVING_STATE_IDLING, new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(true)
+ .setRestrictions(7)
+ .setMode(UX_RESTRICTION_MODE_BASELINE))
+ .setUxRestrictions(DRIVING_STATE_MOVING, new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(true)
+ .setRestrictions(8)
+ .setMode(UX_RESTRICTION_MODE_BASELINE))
+ .setUxRestrictions(DRIVING_STATE_UNKNOWN, new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(true)
+ .setRestrictions(511)
+ .setMode(UX_RESTRICTION_MODE_BASELINE))
+ .setUxRestrictions(DRIVING_STATE_PARKED, new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(false)
+ .setRestrictions(UX_RESTRICTIONS_BASELINE)
+ .setMode(UX_RESTRICTION_MODE_PASSENGER))
+ .setUxRestrictions(DRIVING_STATE_IDLING, new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(true)
+ .setRestrictions(56)
+ .setMode(UX_RESTRICTION_MODE_PASSENGER))
+ .setUxRestrictions(DRIVING_STATE_MOVING, new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(true)
+ .setRestrictions(57)
+ .setMode(UX_RESTRICTION_MODE_PASSENGER))
+ .setUxRestrictions(DRIVING_STATE_UNKNOWN, new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(true)
+ .setRestrictions(510)
+ .setMode(UX_RESTRICTION_MODE_PASSENGER))
+ .build();
+
+ CarUxRestrictionsConfiguration deserialized = CarUxRestrictionsConfiguration.readJson(
+ new JsonReader(new StringReader(v1LegacyJsonFormat)), /* schemaVersion= */ 1);
+ assertEquals(expectedConfig, deserialized);
+ }
+
+
+ @Test
+ public void testJsonSerialization_ReadUnsupportedVersion_ThrowsException() throws Exception {
+ int unsupportedVersion = -1;
+ try {
+ CarUxRestrictionsConfiguration deserialized = CarUxRestrictionsConfiguration.readJson(
+ new JsonReader(new StringReader("")), unsupportedVersion);
+ } catch (IllegalArgumentException e) {
+ // expected exception
+ }
}
public void testDump() {
- CarUxRestrictionsConfiguration[] configs = new CarUxRestrictionsConfiguration[] {
+ CarUxRestrictionsConfiguration[] configs = new CarUxRestrictionsConfiguration[]{
// Driving state with no speed range
new Builder()
.setUxRestrictions(DRIVING_STATE_PARKED, false, UX_RESTRICTIONS_BASELINE)
@@ -490,8 +564,8 @@
assertTrue(dump.contains("Speed Range"));
assertTrue(dump.contains("Requires DO?"));
assertTrue(dump.contains("Restrictions"));
- assertTrue(dump.contains("Passenger mode"));
- assertTrue(dump.contains("Baseline mode"));
+ assertTrue(dump.contains("passenger mode"));
+ assertTrue(dump.contains("baseline mode"));
}
public void testSetUxRestrictions_UnspecifiedModeDefaultsToBaseline() {
@@ -506,7 +580,7 @@
assertEquals(UX_RESTRICTIONS_NO_VIDEO, restrictions.getActiveRestrictions());
assertTrue(restrictions.isSameRestrictions(
- config.getUxRestrictions(DRIVING_STATE_PARKED, 0f, UX_RESTRICTIONS_BASELINE)));
+ config.getUxRestrictions(DRIVING_STATE_PARKED, 0f, UX_RESTRICTION_MODE_BASELINE)));
}
public void testSetUxRestrictions_PassengerMode() {
@@ -530,7 +604,41 @@
assertEquals(UX_RESTRICTIONS_NO_VIDEO, baseline.getActiveRestrictions());
}
- public void testPassengerModeFallbackToBaseline() {
+ @Test
+ public void testGetUxRestrictions_WithUndefinedMode_FallbackToBaseline() {
+ CarUxRestrictionsConfiguration config = new Builder()
+ .setUxRestrictions(DRIVING_STATE_PARKED, new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(true)
+ .setRestrictions(UX_RESTRICTIONS_NO_VIDEO))
+ .build();
+
+ CarUxRestrictions passenger = config.getUxRestrictions(
+ DRIVING_STATE_PARKED, 0f, UX_RESTRICTION_MODE_PASSENGER);
+ assertTrue(passenger.isRequiresDistractionOptimization());
+ assertEquals(UX_RESTRICTIONS_NO_VIDEO, passenger.getActiveRestrictions());
+ }
+
+ @Test
+ public void testPassengerMode_GetMovingWhenNotDefined_FallbackToBaseline() {
+ CarUxRestrictionsConfiguration config = new Builder()
+ .setUxRestrictions(DRIVING_STATE_MOVING, new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(true)
+ .setRestrictions(UX_RESTRICTIONS_NO_VIDEO))
+ .setUxRestrictions(DRIVING_STATE_PARKED, new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(false)
+ .setRestrictions(UX_RESTRICTIONS_BASELINE)
+ .setMode(UX_RESTRICTION_MODE_PASSENGER))
+ .build();
+
+ // Retrieve with passenger mode for a moving state
+ CarUxRestrictions passenger = config.getUxRestrictions(
+ DRIVING_STATE_MOVING, 1f, UX_RESTRICTION_MODE_PASSENGER);
+ assertTrue(passenger.isRequiresDistractionOptimization());
+ assertEquals(UX_RESTRICTIONS_NO_VIDEO, passenger.getActiveRestrictions());
+ }
+
+ @Test
+ public void testPassengerMode_GetSpeedOutsideDefinedRange_FallbackToBaseline() {
CarUxRestrictionsConfiguration config = new Builder()
.setUxRestrictions(DRIVING_STATE_MOVING, new DrivingStateRestrictions()
.setDistractionOptimizationRequired(true)
@@ -605,8 +713,8 @@
new DrivingStateRestrictions().setRestrictions(UX_RESTRICTIONS_NO_VIDEO))
.build();
- assertTrue(one.equals(other));
- assertTrue(one.hashCode() == other.hashCode());
+ assertEquals(one, other);
+ assertEquals(one.hashCode(), other.hashCode());
}
public void testConfigurationEquals_DifferentRestrictions() {
@@ -681,14 +789,15 @@
CarUxRestrictionsConfiguration deserialized =
CarUxRestrictionsConfiguration.CREATOR.createFromParcel(parcel);
assertEquals(deserialized, config);
- assertTrue(deserialized.getPhysicalPort() == null);
+ assertNull(deserialized.getPhysicalPort());
}
/**
* Writes input config as json, then reads a config out of json.
* Asserts the deserialized config is the same as input.
*/
- private void verifyConfigThroughJsonSerialization(CarUxRestrictionsConfiguration config) {
+ private void verifyConfigThroughJsonSerialization(CarUxRestrictionsConfiguration config,
+ int schemaVersion) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
try (JsonWriter writer = new JsonWriter(new OutputStreamWriter(out))) {
config.writeJson(writer);
@@ -700,8 +809,8 @@
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
try (JsonReader reader = new JsonReader(new InputStreamReader(in))) {
CarUxRestrictionsConfiguration deserialized = CarUxRestrictionsConfiguration.readJson(
- reader);
- assertTrue(config.equals(deserialized));
+ reader, schemaVersion);
+ assertEquals(config, deserialized);
} catch (Exception e) {
e.printStackTrace();
fail();
diff --git a/tests/carservice_test/src/com/android/car/CarUxRestrictionsConfigurationXmlParserTest.java b/tests/carservice_test/src/com/android/car/CarUxRestrictionsConfigurationXmlParserTest.java
index 30649e0..bb3c7bf 100644
--- a/tests/carservice_test/src/com/android/car/CarUxRestrictionsConfigurationXmlParserTest.java
+++ b/tests/carservice_test/src/com/android/car/CarUxRestrictionsConfigurationXmlParserTest.java
@@ -19,7 +19,6 @@
import static android.car.drivingstate.CarDrivingStateEvent.DRIVING_STATE_MOVING;
import static android.car.drivingstate.CarDrivingStateEvent.DRIVING_STATE_PARKED;
import static android.car.drivingstate.CarUxRestrictionsManager.UX_RESTRICTION_MODE_BASELINE;
-import static android.car.drivingstate.CarUxRestrictionsManager.UX_RESTRICTION_MODE_PASSENGER;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -45,6 +44,9 @@
@RunWith(AndroidJUnit4.class)
@MediumTest
public class CarUxRestrictionsConfigurationXmlParserTest {
+
+ private static final String UX_RESTRICTION_MODE_PASSENGER = "passenger";
+
private Context getContext() {
return InstrumentationRegistry.getTargetContext();
}
@@ -167,7 +169,7 @@
Set<Byte> expected = new ArraySet<>();
expected.add((byte) 1);
expected.add((byte) 2);
- for (CarUxRestrictionsConfiguration config: configs) {
+ for (CarUxRestrictionsConfiguration config : configs) {
assertTrue(expected.contains(config.getPhysicalPort()));
}
}
@@ -179,7 +181,7 @@
CarUxRestrictionsConfigurationXmlParser.parse(
getContext(), R.xml.ux_restrictions_multiple_display_ports);
- for (CarUxRestrictionsConfiguration config: configs) {
+ for (CarUxRestrictionsConfiguration config : configs) {
CarUxRestrictions r = config.getUxRestrictions(DRIVING_STATE_PARKED, 0f);
assertEquals(1, r.getMaxContentDepth());
assertEquals(1, r.getMaxCumulativeContentItems());
diff --git a/tests/carservice_test/src/com/android/car/CarUxRestrictionsManagerServiceTest.java b/tests/carservice_test/src/com/android/car/CarUxRestrictionsManagerServiceTest.java
index 0aa89f9..0b77d8e 100644
--- a/tests/carservice_test/src/com/android/car/CarUxRestrictionsManagerServiceTest.java
+++ b/tests/carservice_test/src/com/android/car/CarUxRestrictionsManagerServiceTest.java
@@ -15,9 +15,17 @@
*/
package com.android.car;
+import static android.car.drivingstate.CarDrivingStateEvent.DRIVING_STATE_IDLING;
+import static android.car.drivingstate.CarDrivingStateEvent.DRIVING_STATE_MOVING;
+import static android.car.drivingstate.CarDrivingStateEvent.DRIVING_STATE_PARKED;
+import static android.car.drivingstate.CarDrivingStateEvent.DRIVING_STATE_UNKNOWN;
+import static android.car.drivingstate.CarUxRestrictions.UX_RESTRICTIONS_BASELINE;
+import static android.car.drivingstate.CarUxRestrictionsManager.UX_RESTRICTION_MODE_BASELINE;
+
import static com.android.car.CarUxRestrictionsManagerService.CONFIG_FILENAME_PRODUCTION;
import static com.android.car.CarUxRestrictionsManagerService.CONFIG_FILENAME_STAGED;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -40,7 +48,6 @@
import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.util.JsonReader;
import android.util.JsonWriter;
import androidx.test.InstrumentationRegistry;
@@ -59,12 +66,12 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
-import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
+import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
@@ -72,6 +79,9 @@
@RunWith(AndroidJUnit4.class)
@MediumTest
public class CarUxRestrictionsManagerServiceTest {
+
+ private static final String UX_RESTRICTION_MODE_PASSENGER = "passenger";
+
private CarUxRestrictionsManagerService mService;
@Mock
@@ -115,7 +125,15 @@
CarUxRestrictionsConfiguration config = createEmptyConfig();
assertTrue(mService.saveUxRestrictionsConfigurationForNextBoot(Arrays.asList(config)));
- assertTrue(readFile(staged).equals(config));
+
+ String expectedConfig = "{\"schema_version\":2,\"restrictions\":[{\"physical_port\":null,"
+ + "\"max_content_depth\":-1,\"max_cumulative_content_items\":-1,"
+ + "\"max_string_length\":-1,\"baseline\":{\"parked_restrictions\":[{\"req_opt"
+ + "\":true,\"restrictions\":511}],\"idling_restrictions\":[{\"req_opt\":true,"
+ + "\"restrictions\":511}],\"moving_restrictions\":[{\"req_opt\":true,"
+ + "\"restrictions\":511}],\"unknown_restrictions\":[{\"req_opt\":true,"
+ + "\"restrictions\":511}]}}]}";
+ assertEquals(readFile(staged.toPath()), expectedConfig);
// Verify prod config file was not created.
assertFalse(new File(mTempSystemCarDir, CONFIG_FILENAME_PRODUCTION).exists());
}
@@ -162,6 +180,71 @@
}
@Test
+ public void testLoadConfig_SupportsLegacyV1() throws IOException {
+ String v1LegacyJsonFormat = "[{\"physical_port\":1,\"max_content_depth\":2,"
+ + "\"max_cumulative_content_items\":20,\"max_string_length\":21,"
+ + "\"parked_restrictions\":[{\"req_opt\":false,\"restrictions\":0}],"
+ + "\"idling_restrictions\":[{\"req_opt\":true,\"restrictions\":7}],"
+ + "\"moving_restrictions\":[{\"req_opt\":true,\"restrictions\":8}],"
+ + "\"unknown_restrictions\":[{\"req_opt\":true,\"restrictions\":511}],"
+ + "\"passenger_parked_restrictions\":[{\"req_opt\":false,\"restrictions\":0}],"
+ + "\"passenger_idling_restrictions\":[{\"req_opt\":true,\"restrictions\":56}],"
+ + "\"passenger_moving_restrictions\":[{\"req_opt\":true,\"restrictions\":57}],"
+ + "\"passenger_unknown_restrictions\":[{\"req_opt\":true,\"restrictions\":510}]}]";
+ setupMockFileFromString(CONFIG_FILENAME_PRODUCTION, v1LegacyJsonFormat);
+
+ CarUxRestrictionsConfiguration actual = mService.loadConfig().get(0);
+
+ CarUxRestrictionsConfiguration expectedConfig = new Builder()
+ .setPhysicalPort((byte) 1)
+ .setMaxContentDepth(2)
+ .setMaxCumulativeContentItems(20)
+ .setMaxStringLength(21)
+ .setUxRestrictions(DRIVING_STATE_PARKED,
+ new CarUxRestrictionsConfiguration.DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(false)
+ .setRestrictions(UX_RESTRICTIONS_BASELINE)
+ .setMode(UX_RESTRICTION_MODE_BASELINE))
+ .setUxRestrictions(DRIVING_STATE_IDLING,
+ new CarUxRestrictionsConfiguration.DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(true)
+ .setRestrictions(7)
+ .setMode(UX_RESTRICTION_MODE_BASELINE))
+ .setUxRestrictions(DRIVING_STATE_MOVING,
+ new CarUxRestrictionsConfiguration.DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(true)
+ .setRestrictions(8)
+ .setMode(UX_RESTRICTION_MODE_BASELINE))
+ .setUxRestrictions(DRIVING_STATE_UNKNOWN,
+ new CarUxRestrictionsConfiguration.DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(true)
+ .setRestrictions(511)
+ .setMode(UX_RESTRICTION_MODE_BASELINE))
+ .setUxRestrictions(DRIVING_STATE_PARKED,
+ new CarUxRestrictionsConfiguration.DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(false)
+ .setRestrictions(UX_RESTRICTIONS_BASELINE)
+ .setMode(UX_RESTRICTION_MODE_PASSENGER))
+ .setUxRestrictions(DRIVING_STATE_IDLING,
+ new CarUxRestrictionsConfiguration.DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(true)
+ .setRestrictions(56)
+ .setMode(UX_RESTRICTION_MODE_PASSENGER))
+ .setUxRestrictions(DRIVING_STATE_MOVING,
+ new CarUxRestrictionsConfiguration.DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(true)
+ .setRestrictions(57)
+ .setMode(UX_RESTRICTION_MODE_PASSENGER))
+ .setUxRestrictions(DRIVING_STATE_UNKNOWN,
+ new CarUxRestrictionsConfiguration.DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(true)
+ .setRestrictions(510)
+ .setMode(UX_RESTRICTION_MODE_PASSENGER))
+ .build();
+ assertTrue(actual.equals(expectedConfig));
+ }
+
+ @Test
public void testLoadConfig_PromoteStagedFileWhenParked() throws Exception {
CarUxRestrictionsConfiguration expected = createEmptyConfig();
// Staged file contains actual config. Ignore prod since it should be overwritten by staged.
@@ -466,14 +549,21 @@
return f;
}
- private CarUxRestrictionsConfiguration readFile(File file) throws Exception {
- try (JsonReader reader = new JsonReader(
- new InputStreamReader(new FileInputStream(file), "UTF-8"))) {
- CarUxRestrictionsConfiguration config = null;
- reader.beginArray();
- config = CarUxRestrictionsConfiguration.readJson(reader);
- reader.endArray();
- return config;
+ private File setupMockFileFromString(String filename, String config)
+ throws IOException {
+ File f = new File(mTempSystemCarDir, filename);
+ assertTrue(f.createNewFile());
+
+ if (config != null) {
+ try (FileOutputStream writer = new FileOutputStream(f)) {
+ byte[] bytes = config.getBytes();
+ writer.write(bytes);
+ }
}
+ return f;
+ }
+
+ private String readFile(Path path) throws Exception {
+ return new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
}
}