Support passenger mode in UXR configuration.
- Support configuration with passenger mode
- update API for setUxRestrictions()
- Passenger mode is stored in a map<driving state, restrictions>
parallel to Baseline mode; update related infra (dump, equals,
serialization, etc)
- Update validation because passenger mode allows partial
configuration - when config not available it falls back to baseline
There's a discrepancy in between API to set passenger mode and how
restrictions are stored, namely in API passenger mode is set at
restrictions level, but stored at driving state level (each
containing multiple restrictions). It's done this way to minimize
effort required to configure in API, and supports quick lookup in
storage.
Bug: 122595328
Test: atest AndroidCarApiTest
Change-Id: I4b4c3a91aeee4541c6d75ba51313a2ad09c72db5
diff --git a/car-lib/src/android/car/drivingstate/CarUxRestrictionsConfiguration.java b/car-lib/src/android/car/drivingstate/CarUxRestrictionsConfiguration.java
index 06d19df..4ca54a7 100644
--- a/car-lib/src/android/car/drivingstate/CarUxRestrictionsConfiguration.java
+++ b/car-lib/src/android/car/drivingstate/CarUxRestrictionsConfiguration.java
@@ -15,9 +15,18 @@
*/
package android.car.drivingstate;
+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.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;
@@ -27,10 +36,12 @@
import android.util.JsonWriter;
import android.util.Log;
+import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
import java.util.Map;
@@ -51,6 +62,14 @@
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";
@@ -60,7 +79,9 @@
private final int mMaxContentDepth;
private final int mMaxCumulativeContentItems;
private final int mMaxStringLength;
- private final Map<Integer, List<RestrictionsPerSpeedRange>> mUxRestrictions =
+ private final Map<Integer, List<RestrictionsPerSpeedRange>> mPassengerUxRestrictions =
+ new ArrayMap<>(DRIVING_STATES.length);
+ private final Map<Integer, List<RestrictionsPerSpeedRange>> mBaselineUxRestrictions =
new ArrayMap<>(DRIVING_STATES.length);
private CarUxRestrictionsConfiguration(CarUxRestrictionsConfiguration.Builder builder) {
@@ -69,51 +90,89 @@
mMaxStringLength = builder.mMaxStringLength;
for (int drivingState : DRIVING_STATES) {
- List<RestrictionsPerSpeedRange> list = new ArrayList<>();
- for (RestrictionsPerSpeedRange r : builder.mUxRestrictions.get(drivingState)) {
- list.add(r);
+ List<RestrictionsPerSpeedRange> baseline = new ArrayList<>();
+ for (RestrictionsPerSpeedRange r : builder.mBaselineUxRestrictions.get(drivingState)) {
+ baseline.add(r);
}
- mUxRestrictions.put(drivingState, list);
+ mBaselineUxRestrictions.put(drivingState, baseline);
+
+ List<RestrictionsPerSpeedRange> passenger = new ArrayList<>();
+ for (RestrictionsPerSpeedRange r : builder.mPassengerUxRestrictions.get(drivingState)) {
+ passenger.add(r);
+ }
+ mPassengerUxRestrictions.put(drivingState, passenger);
}
}
/**
- * Returns the restrictions based on current driving state and speed.
+ * Returns the restrictions for
+ * {@link UxRestrictionMode#UX_RESTRICTION_MODE_BASELINE}
+ * based on current driving state.
+ *
+ * @param drivingState Driving state.
+ * See values in {@link CarDrivingStateEvent.CarDrivingState}.
+ * @param currentSpeed Current speed in meter per second.
+ */
+ public CarUxRestrictions getUxRestrictions(
+ @CarDrivingState int drivingState, float currentSpeed) {
+ return getUxRestrictions(drivingState, currentSpeed, UX_RESTRICTION_MODE_BASELINE);
+ }
+
+ /**
+ * 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}.
+ *
+ * @param drivingState Driving state.
+ * See values in {@link CarDrivingStateEvent.CarDrivingState}.
+ * @param currentSpeed Current speed in meter per second.
+ * @param mode Current UX Restriction mode.
*/
public CarUxRestrictions getUxRestrictions(@CarDrivingState int drivingState,
- float currentSpeed) {
- List<RestrictionsPerSpeedRange> restrictions = mUxRestrictions.get(drivingState);
- if (restrictions.isEmpty()) {
+ float currentSpeed, @UxRestrictionMode int mode) {
+ RestrictionsPerSpeedRange restriction = null;
+ if (mode == UX_RESTRICTION_MODE_PASSENGER) {
+ restriction = findUxRestrictionsInList(
+ currentSpeed, mPassengerUxRestrictions.get(drivingState));
+ }
+ if (restriction == null) {
+ // Mode is baseline, or passenger mode does not specify restrictions for current driving
+ // state.
+ restriction = findUxRestrictionsInList(
+ currentSpeed, mBaselineUxRestrictions.get(drivingState));
+ }
+
+ if (restriction == null) {
if (Build.IS_ENG || Build.IS_USERDEBUG) {
throw new IllegalStateException("No restrictions for driving state "
+ getDrivingStateName(drivingState));
}
return createDefaultUxRestrictionsEvent();
}
-
- RestrictionsPerSpeedRange restriction = null;
- if (restrictions.size() == 1) {
- restriction = restrictions.get(0);
- } else {
- for (RestrictionsPerSpeedRange r : restrictions) {
- if (r.mSpeedRange != null && r.mSpeedRange.includes(currentSpeed)) {
- restriction = r;
- break;
- }
- }
- }
-
- if (restriction == null) {
- if (Build.IS_ENG || Build.IS_USERDEBUG) {
- throw new IllegalStateException(
- "No restrictions found for driving state " + drivingState
- + " at speed " + currentSpeed);
- }
- return createDefaultUxRestrictionsEvent();
- }
return createUxRestrictionsEvent(restriction.mReqOpt, restriction.mRestrictions);
}
+ @Nullable
+ private static RestrictionsPerSpeedRange findUxRestrictionsInList(float currentSpeed,
+ List<RestrictionsPerSpeedRange> restrictions) {
+ if (restrictions.isEmpty()) {
+ return null;
+ }
+
+ if (restrictions.size() == 1 && restrictions.get(0).mSpeedRange == null) {
+ // Single restriction with no speed range implies it covers all.
+ return restrictions.get(0);
+ }
+
+ for (RestrictionsPerSpeedRange r : restrictions) {
+ if (r.mSpeedRange != null && r.mSpeedRange.includes(currentSpeed)) {
+ return r;
+ }
+ }
+ return null;
+ }
+
private CarUxRestrictions createDefaultUxRestrictionsEvent() {
return createUxRestrictionsEvent(true,
CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED);
@@ -162,19 +221,35 @@
writer.name(JSON_NAME_PARKED_RESTRICTIONS);
writeRestrictionsList(writer,
- mUxRestrictions.get(CarDrivingStateEvent.DRIVING_STATE_PARKED));
+ mBaselineUxRestrictions.get(DRIVING_STATE_PARKED));
writer.name(JSON_NAME_IDLING_RESTRICTIONS);
writeRestrictionsList(writer,
- mUxRestrictions.get(CarDrivingStateEvent.DRIVING_STATE_IDLING));
+ mBaselineUxRestrictions.get(DRIVING_STATE_IDLING));
writer.name(JSON_NAME_MOVING_RESTRICTIONS);
writeRestrictionsList(writer,
- mUxRestrictions.get(CarDrivingStateEvent.DRIVING_STATE_MOVING));
+ mBaselineUxRestrictions.get(DRIVING_STATE_MOVING));
writer.name(JSON_NAME_UNKNOWN_RESTRICTIONS);
writeRestrictionsList(writer,
- mUxRestrictions.get(CarDrivingStateEvent.DRIVING_STATE_UNKNOWN));
+ 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));
writer.endObject();
}
@@ -203,6 +278,19 @@
writer.endObject();
}
+ @Override
+ public String toString() {
+ CharArrayWriter charWriter = new CharArrayWriter();
+ JsonWriter writer = new JsonWriter(charWriter);
+ writer.setIndent("\t");
+ try {
+ writeJson(writer);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return charWriter.toString();
+ }
+
/**
* Reads Json as UX restriction configuration.
*/
@@ -214,23 +302,51 @@
reader.beginObject();
while (reader.hasNext()) {
String name = reader.nextName();
- if (name.equals(JSON_NAME_MAX_CONTENT_DEPTH)) {
- builder.setMaxContentDepth(reader.nextInt());
- } else if (name.equals(JSON_NAME_MAX_CUMULATIVE_CONTENT_ITEMS)) {
- builder.setMaxCumulativeContentItems(reader.nextInt());
- } else if (name.equals(JSON_NAME_MAX_STRING_LENGTH)) {
- builder.setMaxStringLength(reader.nextInt());
- } else if (name.equals(JSON_NAME_PARKED_RESTRICTIONS)) {
- readRestrictionsList(reader, CarDrivingStateEvent.DRIVING_STATE_PARKED, builder);
- } else if (name.equals(JSON_NAME_IDLING_RESTRICTIONS)) {
- readRestrictionsList(reader, CarDrivingStateEvent.DRIVING_STATE_IDLING, builder);
- } else if (name.equals(JSON_NAME_MOVING_RESTRICTIONS)) {
- readRestrictionsList(reader, CarDrivingStateEvent.DRIVING_STATE_MOVING, builder);
- } else if (name.equals(JSON_NAME_UNKNOWN_RESTRICTIONS)) {
- readRestrictionsList(reader, CarDrivingStateEvent.DRIVING_STATE_UNKNOWN, builder);
- } else {
- Log.e(TAG, "Unknown name parsing json config: " + name);
- reader.skipValue();
+ switch (name) {
+ case JSON_NAME_MAX_CONTENT_DEPTH:
+ builder.setMaxContentDepth(reader.nextInt());
+ break;
+ case JSON_NAME_MAX_CUMULATIVE_CONTENT_ITEMS:
+ builder.setMaxCumulativeContentItems(reader.nextInt());
+ break;
+ case JSON_NAME_MAX_STRING_LENGTH:
+ builder.setMaxStringLength(reader.nextInt());
+ break;
+ case JSON_NAME_PARKED_RESTRICTIONS:
+ readRestrictionsList(reader, DRIVING_STATE_PARKED,
+ UX_RESTRICTION_MODE_BASELINE, builder);
+ break;
+ case JSON_NAME_IDLING_RESTRICTIONS:
+ readRestrictionsList(reader, DRIVING_STATE_IDLING,
+ UX_RESTRICTION_MODE_BASELINE, builder);
+ break;
+ case JSON_NAME_MOVING_RESTRICTIONS:
+ readRestrictionsList(reader, DRIVING_STATE_MOVING,
+ UX_RESTRICTION_MODE_BASELINE, builder);
+ break;
+ case JSON_NAME_UNKNOWN_RESTRICTIONS:
+ readRestrictionsList(reader, DRIVING_STATE_UNKNOWN,
+ UX_RESTRICTION_MODE_BASELINE, builder);
+ break;
+ case JSON_NAME_PASSENGER_PARKED_RESTRICTIONS:
+ readRestrictionsList(reader, DRIVING_STATE_PARKED,
+ UX_RESTRICTION_MODE_PASSENGER, builder);
+ break;
+ case JSON_NAME_PASSENGER_IDLING_RESTRICTIONS:
+ readRestrictionsList(reader, DRIVING_STATE_IDLING,
+ UX_RESTRICTION_MODE_PASSENGER, builder);
+ break;
+ case JSON_NAME_PASSENGER_MOVING_RESTRICTIONS:
+ readRestrictionsList(reader, DRIVING_STATE_MOVING,
+ UX_RESTRICTION_MODE_PASSENGER, builder);
+ break;
+ case JSON_NAME_PASSENGER_UNKNOWN_RESTRICTIONS:
+ readRestrictionsList(reader, DRIVING_STATE_UNKNOWN,
+ UX_RESTRICTION_MODE_PASSENGER, builder);
+ break;
+ default:
+ Log.e(TAG, "Unknown name parsing json config: " + name);
+ reader.skipValue();
}
}
reader.endObject();
@@ -238,16 +354,18 @@
}
private static void readRestrictionsList(JsonReader reader, @CarDrivingState int drivingState,
- Builder builder) throws IOException {
+ @UxRestrictionMode int mode, Builder builder) throws IOException {
reader.beginArray();
while (reader.hasNext()) {
- readRestrictions(reader, drivingState, builder);
+ DrivingStateRestrictions drivingStateRestrictions = readRestrictions(reader);
+ drivingStateRestrictions.setMode(mode);
+
+ builder.setUxRestrictions(drivingState, drivingStateRestrictions);
}
reader.endArray();
}
- private static void readRestrictions(JsonReader reader, @CarDrivingState int drivingState,
- Builder builder) throws IOException {
+ private static DrivingStateRestrictions readRestrictions(JsonReader reader) throws IOException {
reader.beginObject();
boolean reqOpt = false;
int restrictions = CarUxRestrictions.UX_RESTRICTIONS_BASELINE;
@@ -280,7 +398,13 @@
}
}
reader.endObject();
- builder.setUxRestrictions(drivingState, speedRange, reqOpt, restrictions);
+ DrivingStateRestrictions drivingStateRestrictions = new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(reqOpt)
+ .setRestrictions(restrictions);
+ if (speedRange != null) {
+ drivingStateRestrictions.setSpeedRange(speedRange);
+ }
+ return drivingStateRestrictions;
}
@Override
@@ -301,24 +425,27 @@
return false;
}
- // Compare UXR by driving state.
- if (!mUxRestrictions.keySet().equals(other.mUxRestrictions.keySet())) {
+ // Compare UXR for each restriction mode.
+ if (!areRestrictionsEqual(mBaselineUxRestrictions, other.mBaselineUxRestrictions)
+ || !areRestrictionsEqual(
+ mPassengerUxRestrictions, other.mPassengerUxRestrictions)) {
return false;
}
- for (int drivingState : mUxRestrictions.keySet()) {
- List<RestrictionsPerSpeedRange> restrictions = mUxRestrictions.get(
- drivingState);
- List<RestrictionsPerSpeedRange> otherRestrictions = other.mUxRestrictions.get(
- drivingState);
- if (restrictions.size() != otherRestrictions.size()) {
+ return true;
+ }
+
+ private boolean areRestrictionsEqual(Map<Integer, List<RestrictionsPerSpeedRange>> r1,
+ Map<Integer, List<RestrictionsPerSpeedRange>> r2) {
+ // Compare UXR by driving state.
+ if (!r1.keySet().equals(r2.keySet())) {
+ return false;
+ }
+ for (int drivingState : r1.keySet()) {
+ List<RestrictionsPerSpeedRange> restrictions = r1.get(drivingState);
+ List<RestrictionsPerSpeedRange> otherRestrictions = r2.get(drivingState);
+ if (!restrictions.equals(otherRestrictions)) {
return false;
}
- // Assuming the restrictions are sorted.
- for (int i = 0; i < restrictions.size(); i++) {
- if (!restrictions.get(i).equals(otherRestrictions.get(i))) {
- return false;
- }
- }
}
return true;
}
@@ -327,49 +454,61 @@
* Dump the driving state to UX restrictions mapping.
*/
public void dump(PrintWriter writer) {
- for (Integer state : mUxRestrictions.keySet()) {
- List<RestrictionsPerSpeedRange> list = mUxRestrictions.get(state);
- writer.println("===========================================");
- writer.println("Driving State to UXR");
- if (list != null) {
- writer.println("State:" + getDrivingStateName(state) + " num restrictions:"
- + list.size());
- for (RestrictionsPerSpeedRange r : list) {
- writer.println("Requires DO? " + r.mReqOpt
- + "\nRestrictions: 0x" + Integer.toHexString(r.mRestrictions)
- + "\nSpeed Range: " + (r.mSpeedRange == null
- ? "None"
- : r.mSpeedRange.mMinSpeed + " - " + r.mSpeedRange.mMaxSpeed));
- writer.println("===========================================");
- }
- }
- }
+ writer.println("===========================================");
+ writer.println("Baseline mode UXR:");
+ writer.println("-------------------------------------------");
+ dumpRestrictions(writer, mBaselineUxRestrictions);
+ writer.println("Passenger mode UXR:");
+ writer.println("-------------------------------------------");
+ dumpRestrictions(writer, mPassengerUxRestrictions);
+
writer.println("Max String length: " + mMaxStringLength);
writer.println("Max Cumulative Content Items: " + mMaxCumulativeContentItems);
writer.println("Max Content depth: " + mMaxContentDepth);
+ writer.println("===========================================");
+ }
+
+ private void dumpRestrictions(
+ PrintWriter writer, Map<Integer, List<RestrictionsPerSpeedRange>> restrictions) {
+ for (Integer state : restrictions.keySet()) {
+ List<RestrictionsPerSpeedRange> list = restrictions.get(state);
+ writer.println("State:" + getDrivingStateName(state)
+ + " num restrictions:" + list.size());
+ for (RestrictionsPerSpeedRange r : list) {
+ writer.println("Requires DO? " + r.mReqOpt
+ + "\nRestrictions: 0x" + Integer.toHexString(r.mRestrictions)
+ + "\nSpeed Range: "
+ + (r.mSpeedRange == null
+ ? "None"
+ : (r.mSpeedRange.mMinSpeed + " - " + r.mSpeedRange.mMaxSpeed)));
+ writer.println("-------------------------------------------");
+ }
+ }
}
private static String getDrivingStateName(@CarDrivingState int state) {
switch (state) {
- case 0:
+ case DRIVING_STATE_PARKED:
return "parked";
- case 1:
+ case DRIVING_STATE_IDLING:
return "idling";
- case 2:
+ case DRIVING_STATE_MOVING:
return "moving";
- default:
+ case DRIVING_STATE_UNKNOWN:
return "unknown";
+ default:
+ throw new IllegalArgumentException("Unrecognized state value: " + state);
}
}
// Parcelable methods/fields.
// Used by Parcel methods to ensure de/serialization order.
- private static final int[] DRIVING_STATES = new int[]{
- CarDrivingStateEvent.DRIVING_STATE_UNKNOWN,
- CarDrivingStateEvent.DRIVING_STATE_PARKED,
- CarDrivingStateEvent.DRIVING_STATE_IDLING,
- CarDrivingStateEvent.DRIVING_STATE_MOVING
+ private static final int[] DRIVING_STATES = new int[] {
+ DRIVING_STATE_UNKNOWN,
+ DRIVING_STATE_PARKED,
+ DRIVING_STATE_IDLING,
+ DRIVING_STATE_MOVING,
};
public static final Parcelable.Creator<CarUxRestrictionsConfiguration> CREATOR =
@@ -395,7 +534,12 @@
for (int drivingState : DRIVING_STATES) {
List<RestrictionsPerSpeedRange> restrictions = new ArrayList<>();
in.readTypedList(restrictions, RestrictionsPerSpeedRange.CREATOR);
- mUxRestrictions.put(drivingState, restrictions);
+ mBaselineUxRestrictions.put(drivingState, restrictions);
+ }
+ for (int drivingState : DRIVING_STATES) {
+ List<RestrictionsPerSpeedRange> restrictions = new ArrayList<>();
+ in.readTypedList(restrictions, RestrictionsPerSpeedRange.CREATOR);
+ mPassengerUxRestrictions.put(drivingState, restrictions);
}
mMaxContentDepth = in.readInt();
mMaxCumulativeContentItems = in.readInt();
@@ -405,7 +549,10 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
for (int drivingState : DRIVING_STATES) {
- dest.writeTypedList(mUxRestrictions.get(drivingState), 0);
+ dest.writeTypedList(mBaselineUxRestrictions.get(drivingState));
+ }
+ for (int drivingState : DRIVING_STATES) {
+ dest.writeTypedList(mPassengerUxRestrictions.get(drivingState));
}
dest.writeInt(mMaxContentDepth);
dest.writeInt(mMaxCumulativeContentItems);
@@ -423,26 +570,31 @@
private int mMaxCumulativeContentItems = UX_RESTRICTIONS_UNKNOWN;
private int mMaxStringLength = UX_RESTRICTIONS_UNKNOWN;
- private Map<Integer, List<RestrictionsPerSpeedRange>> mUxRestrictions =
+ public Map<Integer, List<RestrictionsPerSpeedRange>> mPassengerUxRestrictions =
+ new ArrayMap<>(DRIVING_STATES.length);
+ public Map<Integer, List<RestrictionsPerSpeedRange>> mBaselineUxRestrictions =
new ArrayMap<>(DRIVING_STATES.length);
public Builder() {
for (int drivingState : DRIVING_STATES) {
- mUxRestrictions.put(drivingState, new ArrayList<>());
+ mBaselineUxRestrictions.put(drivingState, new ArrayList<>());
+ mPassengerUxRestrictions.put(drivingState, new ArrayList<>());
}
}
/**
- * Sets ux restrictions for driving state.
+ * Sets UX restrictions for driving state.
*/
public Builder setUxRestrictions(@CarDrivingState int drivingState,
boolean requiresOptimization,
@CarUxRestrictions.CarUxRestrictionsInfo int restrictions) {
- return this.setUxRestrictions(drivingState, null, requiresOptimization, restrictions);
+ return this.setUxRestrictions(drivingState, new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(requiresOptimization)
+ .setRestrictions(restrictions));
}
/**
- * Sets ux restrictions with speed range.
+ * Sets UX restrictions with speed range.
*
* @param drivingState Restrictions will be set for this Driving state.
* See constants in {@link CarDrivingStateEvent}.
@@ -453,26 +605,57 @@
* @param requiresOptimization Whether distraction optimization (DO) is required for this
* driving state.
* @param restrictions See constants in {@link CarUxRestrictions}.
+ *
+ * @deprecated Use {@link #setUxRestrictions(int, DrivingStateRestrictions)} instead.
*/
+ @Deprecated
public Builder setUxRestrictions(@CarDrivingState int drivingState,
- SpeedRange speedRange, boolean requiresOptimization,
+ @NonNull SpeedRange speedRange, boolean requiresOptimization,
@CarUxRestrictions.CarUxRestrictionsInfo int restrictions) {
- if (drivingState != CarDrivingStateEvent.DRIVING_STATE_MOVING) {
- if (speedRange != null) {
- throw new IllegalArgumentException(
- "Non-moving driving state cannot specify speed range.");
- }
- if (mUxRestrictions.get(drivingState).size() > 0) {
- throw new IllegalArgumentException("Non-moving driving state cannot have "
- + "more than one set of restrictions.");
- }
+ return setUxRestrictions(drivingState, new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(requiresOptimization)
+ .setRestrictions(restrictions)
+ .setSpeedRange(speedRange));
+ }
+
+ /**
+ * Sets UX restriction.
+ *
+ * @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(
+ int drivingState, DrivingStateRestrictions drivingStateRestrictions) {
+ SpeedRange speedRange = drivingStateRestrictions.mSpeedRange;
+
+ if (drivingState != DRIVING_STATE_MOVING && speedRange != null) {
+ throw new IllegalArgumentException(
+ "Non-moving driving state should not specify speed range.");
}
- mUxRestrictions.get(drivingState).add(
- new RestrictionsPerSpeedRange(requiresOptimization, restrictions, speedRange));
+ 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));
return this;
}
+
/**
* Sets max string length.
*/
@@ -501,9 +684,19 @@
* @return CarUxRestrictionsConfiguration based on builder configuration.
*/
public CarUxRestrictionsConfiguration build() {
- // Create default restriction for unspecified driving state.
+ // Unspecified driving state should be fully restricted to be safe.
+ addDefaultRestrictionsToBaseline();
+
+ validateBaselineModeRestrictions();
+ validatePassengerModeRestrictions();
+
+ return new CarUxRestrictionsConfiguration(this);
+ }
+
+ private void addDefaultRestrictionsToBaseline() {
for (int drivingState : DRIVING_STATES) {
- List<RestrictionsPerSpeedRange> restrictions = mUxRestrictions.get(drivingState);
+ List<RestrictionsPerSpeedRange> restrictions =
+ mBaselineUxRestrictions.get(drivingState);
if (restrictions.size() == 0) {
Log.i(TAG, "Using default restrictions for driving state: "
+ getDrivingStateName(drivingState));
@@ -511,61 +704,107 @@
true, CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED));
}
}
+ }
- // Configuration validation.
+ private void validateBaselineModeRestrictions() {
for (int drivingState : DRIVING_STATES) {
- List<RestrictionsPerSpeedRange> restrictions = mUxRestrictions.get(drivingState);
-
- if (drivingState == CarDrivingStateEvent.DRIVING_STATE_MOVING) {
- // Sort restrictions based on speed range.
- Collections.sort(restrictions,
- (r1, r2) -> r1.mSpeedRange.compareTo(r2.mSpeedRange));
-
- if (!isAllSpeedRangeCovered(restrictions)) {
- throw new IllegalStateException(
- "Moving state should cover full speed range.");
- }
- } else {
+ List<RestrictionsPerSpeedRange> restrictions =
+ mBaselineUxRestrictions.get(drivingState);
+ if (drivingState != DRIVING_STATE_MOVING) {
+ // Note: For non-moving state, setUxRestrictions() rejects UxRestriction with
+ // speed range, so we don't check here.
if (restrictions.size() != 1) {
- throw new IllegalStateException("Non-moving driving state should contain "
- + "one set of restriction rules.");
+ throw new IllegalStateException("Non-moving driving state should "
+ + "contain one set of restriction rules.");
}
}
+
+ // If there are multiple restrictions, each one should specify speed range.
+ if (restrictions.size() > 1 && restrictions.stream().anyMatch(
+ restriction -> restriction.mSpeedRange == null)) {
+ StringBuilder error = new StringBuilder();
+ for (RestrictionsPerSpeedRange restriction : restrictions) {
+ error.append(restriction.toString()).append('\n');
+ }
+ throw new IllegalStateException(
+ "Every restriction in MOVING state should contain driving state.\n"
+ + error.toString());
+ }
+
+ // Sort restrictions based on speed range.
+ Collections.sort(restrictions,
+ Comparator.comparing(RestrictionsPerSpeedRange::getSpeedRange));
+
+ validateRangeOfSpeed(restrictions);
+ validateContinuousSpeedRange(restrictions);
}
- return new CarUxRestrictionsConfiguration(this);
+ }
+
+ private void validatePassengerModeRestrictions() {
+ List<RestrictionsPerSpeedRange> passengerMovingRestrictions =
+ mPassengerUxRestrictions.get(DRIVING_STATE_MOVING);
+ Collections.sort(passengerMovingRestrictions,
+ Comparator.comparing(RestrictionsPerSpeedRange::getSpeedRange));
+
+ validateContinuousSpeedRange(passengerMovingRestrictions);
}
/**
- * restrictions should be sorted based on speed range.
+ * Validates if combined speed ranges of given restrictions.
+ *
+ * <p>Restrictions are considered to contain valid speed ranges if:
+ * <ul>
+ * <li>None contains a speed range - implies full range; or
+ * <li>Combination covers range [0 - MAX_SPEED]
+ * </ul>
+ *
+ * Throws exception on invalidate input.
+ *
+ * @param restrictions Restrictions to be checked. Must be sorted.
*/
- private boolean isAllSpeedRangeCovered(List<RestrictionsPerSpeedRange> restrictions) {
+ private void validateRangeOfSpeed(List<RestrictionsPerSpeedRange> restrictions) {
if (restrictions.size() == 1) {
- if (restrictions.get(0).mSpeedRange == null) {
+ SpeedRange speedRange = restrictions.get(0).mSpeedRange;
+ if (speedRange == null) {
// Single restriction with null speed range implies that
// it applies to the entire driving state.
- return true;
+ return;
}
- return restrictions.get(0).mSpeedRange.mMinSpeed == 0
- && Float.compare(restrictions.get(0).mSpeedRange.mMaxSpeed,
- SpeedRange.MAX_SPEED) == 0;
}
+ if (Float.compare(restrictions.get(0).mSpeedRange.mMinSpeed, 0) != 0) {
+ throw new IllegalStateException(
+ "Speed range min speed should start at 0.");
+ }
+ float lastMaxSpeed = restrictions.get(restrictions.size() - 1).mSpeedRange.mMaxSpeed;
+ if (Float.compare(lastMaxSpeed, SpeedRange.MAX_SPEED) != 0) {
+ throw new IllegalStateException(
+ "Max speed of last restriction should be MAX_SPEED.");
+ }
+ }
- if (restrictions.get(0).mSpeedRange.mMinSpeed != 0) {
- Log.e(TAG, "Speed range min speed should start at 0.");
- return false;
- }
+ /**
+ * Validates if combined speed ranges of given restrictions are continuous, meaning they:
+ * <ul>
+ * <li>Do not overlap; and
+ * <li>Do not contain gap
+ * </ul>
+ *
+ * <p>Namely the max speed of current range equals the min speed of next range.
+ *
+ * Throws exception on invalidate input.
+ *
+ * @param restrictions Restrictions to be checked. Must be sorted.
+ */
+ private void validateContinuousSpeedRange(List<RestrictionsPerSpeedRange> restrictions) {
for (int i = 1; i < restrictions.size(); i++) {
RestrictionsPerSpeedRange prev = restrictions.get(i - 1);
RestrictionsPerSpeedRange curr = restrictions.get(i);
// If current min != prev.max, there's either an overlap or a gap in speed range.
if (Float.compare(curr.mSpeedRange.mMinSpeed, prev.mSpeedRange.mMaxSpeed) != 0) {
- Log.e(TAG, "Mis-configured speed range. Possibly speed range overlap or gap.");
- return false;
+ throw new IllegalArgumentException(
+ "Mis-configured speed range. Possibly speed range overlap or gap.");
}
}
- // The last speed range should have max speed.
- float lastMaxSpeed = restrictions.get(restrictions.size() - 1).mSpeedRange.mMaxSpeed;
- return lastMaxSpeed == SpeedRange.MAX_SPEED;
}
/**
@@ -590,9 +829,6 @@
if (minSpeed == MAX_SPEED) {
throw new IllegalArgumentException("Min speed cannot be MAX_SPEED.");
}
- if (maxSpeed < 0) {
- throw new IllegalArgumentException("Max speed cannot be negative.");
- }
if (minSpeed > maxSpeed) {
throw new IllegalArgumentException("Min speed " + minSpeed
+ " should not be greater than max speed " + maxSpeed);
@@ -608,13 +844,7 @@
* @return {@code true} if in range; {@code false} otherwise.
*/
public boolean includes(float speed) {
- if (speed < mMinSpeed) {
- return false;
- }
- if (mMaxSpeed == MAX_SPEED) {
- return true;
- }
- return speed < mMaxSpeed;
+ return mMinSpeed <= speed && speed < mMaxSpeed;
}
@Override
@@ -640,35 +870,116 @@
return this.compareTo(other) == 0;
}
+
+ @Override
+ public String toString() {
+ return new StringBuilder()
+ .append("[min: ").append(mMinSpeed)
+ .append("; max: ").append(mMaxSpeed == MAX_SPEED ? "max_speed" : mMaxSpeed)
+ .append("]")
+ .toString();
+ }
+ }
+ }
+
+ /**
+ * UX restrictions to be applied to a driving state through {@link
+ * Builder#setUxRestrictions(int, CarUxRestrictionsConfiguration.DrivingStateRestrictions)}.
+ * These UX restrictions can also specified to be only applicable to certain speed range and
+ * restriction mode.
+ *
+ * @see UxRestrictionMode
+ * @see Builder.SpeedRange
+ *
+ * @hide
+ */
+ public static final class DrivingStateRestrictions {
+ private int mMode = UX_RESTRICTION_MODE_BASELINE;
+ private boolean mReqOpt = true;
+ private int mRestrictions = CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED;
+ @Nullable private Builder.SpeedRange mSpeedRange;
+
+ /**
+ * Sets whether Distraction Optimization (DO) is required. Defaults to {@code true}.
+ */
+ public DrivingStateRestrictions setDistractionOptimizationRequired(
+ boolean distractionOptimizationRequired) {
+ mReqOpt = distractionOptimizationRequired;
+ return this;
+ }
+
+ /**
+ * Sets active restrictions.
+ * Defaults to {@link CarUxRestrictions#UX_RESTRICTIONS_FULLY_RESTRICTED}.
+ */
+ public DrivingStateRestrictions setRestrictions(
+ @CarUxRestrictions.CarUxRestrictionsInfo int restrictions) {
+ mRestrictions = restrictions;
+ return this;
+ }
+
+ /**
+ * Sets restriction mode to apply to.
+ * Defaults to {@link CarUxRestrictionsManager#UX_RESTRICTION_MODE_BASELINE}.
+ */
+ public DrivingStateRestrictions setMode(@UxRestrictionMode int mode) {
+ mMode = mode;
+ return this;
+ }
+
+ /**
+ * Sets speed range to apply to. Optional value. Not setting one means the restrictions
+ * apply to full speed range, namely {@code 0} to {@link Builder.SpeedRange#MAX_SPEED}.
+ */
+ public DrivingStateRestrictions setSpeedRange(@NonNull Builder.SpeedRange speedRange) {
+ mSpeedRange = speedRange;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder()
+ .append("Mode: ").append(CarUxRestrictionsManager.modeToString(mMode))
+ .append(". Requires DO? ").append(mReqOpt)
+ .append(". Restrictions: ").append(Integer.toBinaryString(mRestrictions))
+ .append(". SpeedRange: ")
+ .append(mSpeedRange == null ? "null" : mSpeedRange.toString())
+ .toString();
}
}
/**
* Container for UX restrictions for a speed range.
* Speed range is valid only for the {@link CarDrivingStateEvent#DRIVING_STATE_MOVING}.
- * @hide
*/
- public static final class RestrictionsPerSpeedRange implements Parcelable {
+ private static final class RestrictionsPerSpeedRange implements Parcelable {
+ @UxRestrictionMode
+ final int mMode;
final boolean mReqOpt;
final int mRestrictions;
@Nullable
final Builder.SpeedRange mSpeedRange;
- public RestrictionsPerSpeedRange(boolean reqOpt, int restrictions) {
- this(reqOpt, restrictions, null);
+ RestrictionsPerSpeedRange(boolean reqOpt, int restrictions) {
+ this(UX_RESTRICTION_MODE_BASELINE, reqOpt, restrictions, null);
}
- public RestrictionsPerSpeedRange(boolean reqOpt, int restrictions,
+ RestrictionsPerSpeedRange(@UxRestrictionMode int 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;
mReqOpt = reqOpt;
mRestrictions = restrictions;
mSpeedRange = speedRange;
}
+ public Builder.SpeedRange getSpeedRange() {
+ return mSpeedRange;
+ }
+
@Override
public boolean equals(Object obj) {
if (this == obj) {
@@ -678,12 +989,25 @@
return false;
}
RestrictionsPerSpeedRange other = (RestrictionsPerSpeedRange) obj;
- return mReqOpt == other.mReqOpt
+ return mMode == other.mMode
+ && mReqOpt == other.mReqOpt
&& mRestrictions == other.mRestrictions
&& ((mSpeedRange == null && other.mSpeedRange == null) || mSpeedRange.equals(
other.mSpeedRange));
}
+ @Override
+ public String toString() {
+ return new StringBuilder()
+ .append("[Mode is ").append(CarUxRestrictionsManager.modeToString(mMode))
+ .append("; Requires DO? ").append(mReqOpt)
+ .append("; Restrictions: ").append(Integer.toBinaryString(mRestrictions))
+ .append("; Speed range: ")
+ .append(mSpeedRange == null ? "null" : mSpeedRange.toString())
+ .append(']')
+ .toString();
+ }
+
// Parcelable methods/fields.
public static final Creator<RestrictionsPerSpeedRange> CREATOR =
@@ -705,6 +1029,7 @@
}
protected RestrictionsPerSpeedRange(Parcel in) {
+ mMode = in.readInt();
mReqOpt = in.readBoolean();
mRestrictions = in.readInt();
// Whether speed range is specified.
@@ -719,6 +1044,7 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mMode);
dest.writeBoolean(mReqOpt);
dest.writeInt(mRestrictions);
// Whether speed range is specified.
diff --git a/car-lib/src/android/car/drivingstate/CarUxRestrictionsManager.java b/car-lib/src/android/car/drivingstate/CarUxRestrictionsManager.java
index 06875dd..0a0eb87 100644
--- a/car-lib/src/android/car/drivingstate/CarUxRestrictionsManager.java
+++ b/car-lib/src/android/car/drivingstate/CarUxRestrictionsManager.java
@@ -16,6 +16,7 @@
package android.car.drivingstate;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -30,6 +31,8 @@
import android.os.RemoteException;
import android.util.Log;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
/**
@@ -42,6 +45,37 @@
private static final boolean VDBG = false;
private static final int MSG_HANDLE_UX_RESTRICTIONS_CHANGE = 0;
+ /**
+ * Baseline restriction mode is the default UX restrictions used for driving state.
+ *
+ * @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 {}
+
private final Context mContext;
private final ICarUxRestrictionsManager mUxRService;
private final EventCallbackHandler mEventCallbackHandler;
@@ -112,30 +146,6 @@
}
/**
- * Set a new {@link CarUxRestrictionsConfiguration} for next trip.
- * <p>
- * Saving a new configuration does not affect current configuration. The new configuration will
- * only be used after UX Restrictions service restarts when the vehicle is parked.
- * <p>
- * Requires Permission:
- * {@link android.car.Manifest.permission#CAR_UX_RESTRICTIONS_CONFIGURATION}.
- *
- * @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)
- public synchronized boolean saveUxRestrictionsConfigurationForNextBoot(
- CarUxRestrictionsConfiguration config) {
- try {
- return mUxRService.saveUxRestrictionsConfigurationForNextBoot(config);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Unregister the registered {@link OnUxRestrictionsChangedListener}
*/
public synchronized void unregisterListener() {
@@ -168,6 +178,59 @@
}
/**
+ * Sets restriction mode. Returns {@code true} if the operation succeeds.
+ *
+ * @hide
+ */
+ @RequiresPermission(value = Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION)
+ public boolean setRestrictionMode(@UxRestrictionMode int mode) {
+ try {
+ return mUxRService.setRestrictionMode(mode);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the current restriction mode.
+ *
+ * @hide
+ */
+ @RequiresPermission(value = Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION)
+ @UxRestrictionMode
+ public int getRestrictionMode() {
+ try {
+ return mUxRService.getRestrictionMode();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set a new {@link CarUxRestrictionsConfiguration} for next trip.
+ * <p>
+ * Saving a new configuration does not affect current configuration. The new configuration will
+ * only be used after UX Restrictions service restarts when the vehicle is parked.
+ * <p>
+ * Requires Permission:
+ * {@link android.car.Manifest.permission#CAR_UX_RESTRICTIONS_CONFIGURATION}.
+ *
+ * @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)
+ public synchronized boolean saveUxRestrictionsConfigurationForNextBoot(
+ CarUxRestrictionsConfiguration config) {
+ try {
+ return mUxRService.saveUxRestrictionsConfigurationForNextBoot(config);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Get the current staged configuration, staged config file will only be accessible after
* the boot up completed or user has been switched.
* This methods is only for test purpose, please do not use in production.
@@ -207,6 +270,20 @@
}
/**
+ * @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 8f48c5e..76d5a3b 100644
--- a/car-lib/src/android/car/drivingstate/ICarUxRestrictionsManager.aidl
+++ b/car-lib/src/android/car/drivingstate/ICarUxRestrictionsManager.aidl
@@ -34,4 +34,6 @@
boolean saveUxRestrictionsConfigurationForNextBoot(in CarUxRestrictionsConfiguration config) = 3;
CarUxRestrictionsConfiguration getStagedConfig() = 4;
CarUxRestrictionsConfiguration getConfig() = 5;
+ boolean setRestrictionMode(int mode) = 6;
+ int getRestrictionMode() = 7;
}
diff --git a/service/src/com/android/car/BluetoothDeviceConnectionPolicy.java b/service/src/com/android/car/BluetoothDeviceConnectionPolicy.java
index 6b2169a..aa52e73 100644
--- a/service/src/com/android/car/BluetoothDeviceConnectionPolicy.java
+++ b/service/src/com/android/car/BluetoothDeviceConnectionPolicy.java
@@ -811,7 +811,7 @@
mPropertyEventListener);
// Get Current restrictions and handle them
handleUxRestrictionsChanged(mUxRService.getCurrentUxRestrictions());
- // Register for future changes to the UxRestrictions
+ // Register for future changes to the DrivingStateRestrictions
mUxRService.registerUxRestrictionsChangeListener(mUxRListener);
mUserServiceHelper.registerServiceCallback(mServiceCallback);
}
diff --git a/service/src/com/android/car/CarUxRestrictionsManagerService.java b/service/src/com/android/car/CarUxRestrictionsManagerService.java
index 0fc83dc..247cb6c 100644
--- a/service/src/com/android/car/CarUxRestrictionsManagerService.java
+++ b/service/src/com/android/car/CarUxRestrictionsManagerService.java
@@ -16,6 +16,8 @@
package com.android.car;
+import static android.car.drivingstate.CarUxRestrictionsManager.UX_RESTRICTION_MODE_BASELINE;
+
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import android.annotation.Nullable;
@@ -24,6 +26,7 @@
import android.car.drivingstate.CarDrivingStateEvent.CarDrivingState;
import android.car.drivingstate.CarUxRestrictions;
import android.car.drivingstate.CarUxRestrictionsConfiguration;
+import android.car.drivingstate.CarUxRestrictionsManager;
import android.car.drivingstate.ICarDrivingStateChangeListener;
import android.car.drivingstate.ICarUxRestrictionsChangeListener;
import android.car.drivingstate.ICarUxRestrictionsManager;
@@ -95,7 +98,10 @@
private final CarPropertyService mCarPropertyService;
// List of clients listening to UX restriction events.
private final List<UxRestrictionsClient> mUxRClients = new ArrayList<>();
- private CarUxRestrictionsConfiguration mCarUxRestrictionsConfiguration;
+ @VisibleForTesting
+ CarUxRestrictionsConfiguration mCarUxRestrictionsConfiguration;
+ @CarUxRestrictionsManager.UxRestrictionMode
+ private int mRestrictionMode = UX_RESTRICTION_MODE_BASELINE;
private CarUxRestrictions mCurrentUxRestrictions;
private float mCurrentMovingSpeed;
// Flag to disable broadcasting UXR changes - for development purposes
@@ -359,6 +365,42 @@
}
/**
+ * Sets the restriction mode to use. Restriction mode allows a different set of restrictions to
+ * be applied in the same driving state. Restrictions for each mode can be configured through
+ * {@link CarUxRestrictionsConfiguration}.
+ *
+ * <p>Defaults to {@link CarUxRestrictionsManager#UX_RESTRICTION_MODE_BASELINE}.
+ *
+ * @param mode See values in {@link CarUxRestrictionsManager.UxRestrictionMode}.
+ * @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) {
+ if (mRestrictionMode == mode) {
+ return true;
+ }
+
+ addTransitionLog(TAG, mRestrictionMode, mode, System.currentTimeMillis(),
+ "Restriction mode");
+ mRestrictionMode = mode;
+ logd("Set restriction mode to: " + CarUxRestrictionsManager.modeToString(mode));
+
+ handleDispatchUxRestrictions(
+ mDrivingStateService.getCurrentDrivingState().eventValue, getCurrentSpeed());
+ return true;
+ }
+
+ @Override
+ @CarUxRestrictionsManager.UxRestrictionMode
+ public synchronized int getRestrictionMode() {
+ return mRestrictionMode;
+ }
+
+ /**
* Writes configuration into the specified file.
*
* IO access on file is not thread safe. Caller should ensure threading protection.
@@ -592,8 +634,8 @@
return;
}
- CarUxRestrictions uxRestrictions =
- mCarUxRestrictionsConfiguration.getUxRestrictions(currentDrivingState, speed);
+ CarUxRestrictions uxRestrictions = mCarUxRestrictionsConfiguration.getUxRestrictions(
+ currentDrivingState, speed, mRestrictionMode);
if (DBG) {
Log.d(TAG, String.format("DO old->new: %b -> %b",
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 f86c76e..5b991a9 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
@@ -23,9 +23,13 @@
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;
+import android.car.drivingstate.CarUxRestrictionsConfiguration.Builder;
+import android.car.drivingstate.CarUxRestrictionsConfiguration.DrivingStateRestrictions;
import android.util.JsonReader;
import android.util.JsonWriter;
@@ -47,39 +51,41 @@
// This test verifies the expected way to build config would succeed.
public void testConstruction() {
- new CarUxRestrictionsConfiguration.Builder().build();
+ new Builder().build();
- new CarUxRestrictionsConfiguration.Builder()
+ new Builder()
.setMaxStringLength(1)
.build();
- new CarUxRestrictionsConfiguration.Builder()
+ new Builder()
.setUxRestrictions(DRIVING_STATE_PARKED, false, UX_RESTRICTIONS_BASELINE)
.build();
- new CarUxRestrictionsConfiguration.Builder()
+ new Builder()
.setUxRestrictions(DRIVING_STATE_MOVING, true, UX_RESTRICTIONS_FULLY_RESTRICTED)
.build();
- new CarUxRestrictionsConfiguration.Builder()
- .setUxRestrictions(DRIVING_STATE_MOVING,
- new CarUxRestrictionsConfiguration.Builder.SpeedRange(0f, MAX_SPEED),
- true, UX_RESTRICTIONS_FULLY_RESTRICTED)
+ new Builder()
+ .setUxRestrictions(DRIVING_STATE_MOVING, new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(true)
+ .setRestrictions(UX_RESTRICTIONS_FULLY_RESTRICTED)
+ .setSpeedRange(new Builder.SpeedRange(0f, MAX_SPEED)))
.build();
- new CarUxRestrictionsConfiguration.Builder()
- .setUxRestrictions(DRIVING_STATE_MOVING,
- new CarUxRestrictionsConfiguration.Builder.SpeedRange(0f, 1f),
- true, UX_RESTRICTIONS_FULLY_RESTRICTED)
- .setUxRestrictions(DRIVING_STATE_MOVING,
- new CarUxRestrictionsConfiguration.Builder.SpeedRange(1f, MAX_SPEED),
- true, UX_RESTRICTIONS_FULLY_RESTRICTED)
+ new Builder()
+ .setUxRestrictions(DRIVING_STATE_MOVING, new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(true)
+ .setRestrictions(UX_RESTRICTIONS_FULLY_RESTRICTED)
+ .setSpeedRange(new Builder.SpeedRange(0f, 1f)))
+ .setUxRestrictions(DRIVING_STATE_MOVING, new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(true)
+ .setRestrictions(UX_RESTRICTIONS_FULLY_RESTRICTED)
+ .setSpeedRange(new Builder.SpeedRange(1f, MAX_SPEED)))
.build();
}
public void testUnspecifiedDrivingStateUsesDefaultRestriction() {
- CarUxRestrictionsConfiguration config =
- new CarUxRestrictionsConfiguration.Builder().build();
+ CarUxRestrictionsConfiguration config = new Builder().build();
CarUxRestrictions parkedRestrictions = config.getUxRestrictions(DRIVING_STATE_PARKED, 0f);
assertTrue(parkedRestrictions.isRequiresDistractionOptimization());
@@ -90,183 +96,245 @@
assertEquals(movingRestrictions.getActiveRestrictions(), UX_RESTRICTIONS_FULLY_RESTRICTED);
}
- public void testBuilderValidation_MultipleSpeedRange_NonZeroStart() {
- CarUxRestrictionsConfiguration.Builder builder =
- new CarUxRestrictionsConfiguration.Builder();
- builder.setUxRestrictions(DRIVING_STATE_MOVING,
- new CarUxRestrictionsConfiguration.Builder.SpeedRange(1, 2),
- true, UX_RESTRICTIONS_FULLY_RESTRICTED);
- builder.setUxRestrictions(DRIVING_STATE_MOVING,
- new CarUxRestrictionsConfiguration.Builder.SpeedRange(2, MAX_SPEED),
- true, UX_RESTRICTIONS_FULLY_RESTRICTED);
+ public void testBuilderValidation_UnspecifiedStateUsesRestrictiveDefault() {
+ CarUxRestrictionsConfiguration config = new Builder()
+ .setUxRestrictions(DRIVING_STATE_MOVING, true, UX_RESTRICTIONS_FULLY_RESTRICTED)
+ .build();
+ assertTrue(config.getUxRestrictions(DRIVING_STATE_PARKED, 0f)
+ .isRequiresDistractionOptimization());
+ assertTrue(config.getUxRestrictions(DRIVING_STATE_IDLING, 0f)
+ .isRequiresDistractionOptimization());
+ }
+ public void testBuilderValidation_NonMovingStateHasOneRestriction() {
+ Builder builder = new Builder();
+ builder.setUxRestrictions(DRIVING_STATE_IDLING,
+ true, UX_RESTRICTIONS_NO_VIDEO);
+ builder.setUxRestrictions(DRIVING_STATE_IDLING,
+ false, UX_RESTRICTIONS_BASELINE);
try {
builder.build();
fail();
- } catch (IllegalStateException e) {
+ } catch (Exception e) {
+ // Expected exception.
+ }
+ }
+
+ 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)));
+ builder.setUxRestrictions(DRIVING_STATE_MOVING, new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(true)
+ .setRestrictions(UX_RESTRICTIONS_FULLY_RESTRICTED)
+ .setSpeedRange(new Builder.SpeedRange(1f)));
+ try {
+ builder.build();
+ fail();
+ } catch (Exception e) {
+ // Expected exception.
+ }
+ }
+
+ public void testBuilderValidation_PassengerModeCanSpecifySubsetOfSpeedRange() {
+ CarUxRestrictionsConfiguration config = new Builder()
+ .setUxRestrictions(DRIVING_STATE_MOVING, new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(true)
+ .setRestrictions(UX_RESTRICTIONS_FULLY_RESTRICTED)
+ .setMode(UX_RESTRICTION_MODE_PASSENGER)
+ .setSpeedRange(new Builder.SpeedRange(1f, 2f)))
+ .build();
+
+ assertTrue(config.getUxRestrictions(DRIVING_STATE_MOVING, 1f, UX_RESTRICTION_MODE_PASSENGER)
+ .isRequiresDistractionOptimization());
+ }
+
+ public void testBuilderValidation_MultipleSpeedRange_NonZeroStart() {
+ Builder builder = new Builder();
+ builder.setUxRestrictions(DRIVING_STATE_MOVING,
+ new Builder.SpeedRange(1, 2),
+ true, UX_RESTRICTIONS_FULLY_RESTRICTED);
+ builder.setUxRestrictions(DRIVING_STATE_MOVING,
+ new Builder.SpeedRange(2, MAX_SPEED),
+ true, UX_RESTRICTIONS_FULLY_RESTRICTED);
+ try {
+ builder.build();
+ fail();
+ } catch (Exception e) {
// Expected exception.
}
}
public void testBuilderValidation_SpeedRange_NonZeroStart() {
- CarUxRestrictionsConfiguration.Builder builder =
- new CarUxRestrictionsConfiguration.Builder();
+ Builder builder = new Builder();
builder.setUxRestrictions(DRIVING_STATE_MOVING,
- new CarUxRestrictionsConfiguration.Builder.SpeedRange(1, MAX_SPEED),
+ new Builder.SpeedRange(1, MAX_SPEED),
true, UX_RESTRICTIONS_FULLY_RESTRICTED);
-
try {
builder.build();
fail();
- } catch (IllegalStateException e) {
+ } catch (Exception e) {
// Expected exception.
}
}
public void testBuilderValidation_SpeedRange_Overlap() {
- CarUxRestrictionsConfiguration.Builder builder =
- new CarUxRestrictionsConfiguration.Builder();
+ Builder builder = new Builder();
builder.setUxRestrictions(DRIVING_STATE_MOVING,
- new CarUxRestrictionsConfiguration.Builder.SpeedRange(0, 5), true,
+ new Builder.SpeedRange(0, 5), true,
UX_RESTRICTIONS_FULLY_RESTRICTED);
builder.setUxRestrictions(DRIVING_STATE_MOVING,
- new CarUxRestrictionsConfiguration.Builder.SpeedRange(4), true,
+ new Builder.SpeedRange(4), true,
UX_RESTRICTIONS_FULLY_RESTRICTED);
-
try {
builder.build();
fail();
- } catch (IllegalStateException e) {
+ } catch (Exception e) {
// Expected exception.
}
}
public void testBuilderValidation_SpeedRange_Gap() {
- CarUxRestrictionsConfiguration.Builder builder =
- new CarUxRestrictionsConfiguration.Builder();
+ Builder builder = new Builder();
builder.setUxRestrictions(DRIVING_STATE_MOVING,
- new CarUxRestrictionsConfiguration.Builder.SpeedRange(0, 5), true,
+ new Builder.SpeedRange(0, 5), true,
UX_RESTRICTIONS_FULLY_RESTRICTED);
builder.setUxRestrictions(DRIVING_STATE_MOVING,
- new CarUxRestrictionsConfiguration.Builder.SpeedRange(8), true,
+ new Builder.SpeedRange(8), true,
UX_RESTRICTIONS_FULLY_RESTRICTED);
-
try {
builder.build();
fail();
- } catch (IllegalStateException e) {
+ } catch (Exception e) {
// Expected exception.
}
}
public void testBuilderValidation_NonMovingStateCannotUseSpeedRange() {
- CarUxRestrictionsConfiguration.Builder builder =
- new CarUxRestrictionsConfiguration.Builder();
+ Builder builder = new Builder();
try {
builder.setUxRestrictions(DRIVING_STATE_PARKED,
- new CarUxRestrictionsConfiguration.Builder.SpeedRange(0, 5), true,
+ new Builder.SpeedRange(0, 5), true,
UX_RESTRICTIONS_FULLY_RESTRICTED);
- } catch (IllegalArgumentException e) {
+ fail();
+ } catch (Exception e) {
+ // Expected exception.
+ }
+ }
+
+ public void testBuilderValidation_MultipleMovingRestrictionsShouldAllContainSpeedRange() {
+ Builder builder = new Builder();
+ builder.setUxRestrictions(DRIVING_STATE_MOVING, new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(true)
+ .setRestrictions(UX_RESTRICTIONS_FULLY_RESTRICTED));
+ builder.setUxRestrictions(DRIVING_STATE_MOVING, new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(true)
+ .setRestrictions(UX_RESTRICTIONS_FULLY_RESTRICTED)
+ .setSpeedRange(new Builder.SpeedRange(1f)));
+ try {
+ builder.build();
+ fail();
+ } catch (Exception e) {
// Expected exception.
}
}
public void testSpeedRange_Construction() {
- new CarUxRestrictionsConfiguration.Builder.SpeedRange(0f);
- new CarUxRestrictionsConfiguration.Builder.SpeedRange(0f, 1f);
- new CarUxRestrictionsConfiguration.Builder.SpeedRange(0f, MAX_SPEED);
+ new Builder.SpeedRange(0f);
+ new Builder.SpeedRange(0f, 1f);
+ new Builder.SpeedRange(0f, MAX_SPEED);
}
public void testSpeedRange_NegativeMax() {
try {
- new CarUxRestrictionsConfiguration.Builder.SpeedRange(2f, -1f);
- } catch (IllegalArgumentException e) {
+ new Builder.SpeedRange(2f, -1f);
+ } catch (Exception e) {
// Expected exception.
}
}
public void testSpeedRange_MinGreaterThanMax() {
try {
- new CarUxRestrictionsConfiguration.Builder.SpeedRange(5f, 2f);
- } catch (IllegalArgumentException e) {
+ new Builder.SpeedRange(5f, 2f);
+ } catch (Exception e) {
// Expected exception.
}
}
public void testSpeedRangeComparison_DifferentMin() {
- CarUxRestrictionsConfiguration.Builder.SpeedRange s1 =
- new CarUxRestrictionsConfiguration.Builder.SpeedRange(1f);
- CarUxRestrictionsConfiguration.Builder.SpeedRange s2 =
- new CarUxRestrictionsConfiguration.Builder.SpeedRange(2f);
+ Builder.SpeedRange s1 =
+ new Builder.SpeedRange(1f);
+ Builder.SpeedRange s2 =
+ new Builder.SpeedRange(2f);
assertTrue(s1.compareTo(s2) < 0);
assertTrue(s2.compareTo(s1) > 0);
}
public void testSpeedRangeComparison_SameMin() {
- CarUxRestrictionsConfiguration.Builder.SpeedRange s1 =
- new CarUxRestrictionsConfiguration.Builder.SpeedRange(1f);
- CarUxRestrictionsConfiguration.Builder.SpeedRange s2 =
- new CarUxRestrictionsConfiguration.Builder.SpeedRange(1f);
+ Builder.SpeedRange s1 =
+ new Builder.SpeedRange(1f);
+ Builder.SpeedRange s2 =
+ new Builder.SpeedRange(1f);
assertTrue(s1.compareTo(s2) == 0);
}
public void testSpeedRangeComparison_SameMinDifferentMax() {
- CarUxRestrictionsConfiguration.Builder.SpeedRange s1 =
- new CarUxRestrictionsConfiguration.Builder.SpeedRange(0f, 1f);
- CarUxRestrictionsConfiguration.Builder.SpeedRange s2 =
- new CarUxRestrictionsConfiguration.Builder.SpeedRange(0f, 2f);
+ Builder.SpeedRange s1 =
+ new Builder.SpeedRange(0f, 1f);
+ Builder.SpeedRange s2 =
+ new Builder.SpeedRange(0f, 2f);
assertTrue(s1.compareTo(s2) < 0);
assertTrue(s2.compareTo(s1) > 0);
}
public void testSpeedRangeComparison_MaxSpeed() {
- CarUxRestrictionsConfiguration.Builder.SpeedRange s1 =
- new CarUxRestrictionsConfiguration.Builder.SpeedRange(0f, 1f);
- CarUxRestrictionsConfiguration.Builder.SpeedRange s2 =
- new CarUxRestrictionsConfiguration.Builder.SpeedRange(0f);
+ Builder.SpeedRange s1 =
+ new Builder.SpeedRange(0f, 1f);
+ Builder.SpeedRange s2 =
+ new Builder.SpeedRange(0f);
assertTrue(s1.compareTo(s2) < 0);
assertTrue(s2.compareTo(s1) > 0);
}
public void testSpeedRangeEquals() {
- CarUxRestrictionsConfiguration.Builder.SpeedRange s1, s2;
+ Builder.SpeedRange s1, s2;
- s1 = new CarUxRestrictionsConfiguration.Builder.SpeedRange(0f);
+ s1 = new Builder.SpeedRange(0f);
assertTrue(s1.equals(s1));
- s1 = new CarUxRestrictionsConfiguration.Builder.SpeedRange(1f);
- s2 = new CarUxRestrictionsConfiguration.Builder.SpeedRange(1f);
+ s1 = new Builder.SpeedRange(1f);
+ s2 = new Builder.SpeedRange(1f);
assertTrue(s1.compareTo(s2) == 0);
assertTrue(s1.equals(s2));
- s1 = new CarUxRestrictionsConfiguration.Builder.SpeedRange(0f, 1f);
- s2 = new CarUxRestrictionsConfiguration.Builder.SpeedRange(0f, 1f);
+ s1 = new Builder.SpeedRange(0f, 1f);
+ s2 = new Builder.SpeedRange(0f, 1f);
assertTrue(s1.equals(s2));
- s1 = new CarUxRestrictionsConfiguration.Builder.SpeedRange(0f, MAX_SPEED);
- s2 = new CarUxRestrictionsConfiguration.Builder.SpeedRange(0f, MAX_SPEED);
+ s1 = new Builder.SpeedRange(0f, MAX_SPEED);
+ s2 = new Builder.SpeedRange(0f, MAX_SPEED);
assertTrue(s1.equals(s2));
- s1 = new CarUxRestrictionsConfiguration.Builder.SpeedRange(0f);
- s2 = new CarUxRestrictionsConfiguration.Builder.SpeedRange(1f);
+ s1 = new Builder.SpeedRange(0f);
+ s2 = new Builder.SpeedRange(1f);
assertFalse(s1.equals(s2));
- s1 = new CarUxRestrictionsConfiguration.Builder.SpeedRange(0f, 1f);
- s2 = new CarUxRestrictionsConfiguration.Builder.SpeedRange(0f, 2f);
+ s1 = new Builder.SpeedRange(0f, 1f);
+ s2 = new Builder.SpeedRange(0f, 2f);
assertFalse(s1.equals(s2));
}
- // TODO: add more tests that each verifies setting one filed in builder can be
- // successfully retrieved out of saved json.
public void testJsonSerialization_DefaultConstructor() {
CarUxRestrictionsConfiguration config =
- new CarUxRestrictionsConfiguration.Builder().build();
+ new Builder().build();
verifyConfigThroughJsonSerialization(config);
}
public void testJsonSerialization_RestrictionParameters() {
- CarUxRestrictionsConfiguration config = new CarUxRestrictionsConfiguration.Builder()
+ CarUxRestrictionsConfiguration config = new Builder()
.setMaxStringLength(1)
.setMaxCumulativeContentItems(1)
.setMaxContentDepth(1)
@@ -276,7 +344,7 @@
}
public void testJsonSerialization_NonMovingStateRestrictions() {
- CarUxRestrictionsConfiguration config = new CarUxRestrictionsConfiguration.Builder()
+ CarUxRestrictionsConfiguration config = new Builder()
.setUxRestrictions(DRIVING_STATE_PARKED, false, UX_RESTRICTIONS_BASELINE)
.build();
@@ -284,7 +352,7 @@
}
public void testJsonSerialization_MovingStateNoSpeedRange() {
- CarUxRestrictionsConfiguration config = new CarUxRestrictionsConfiguration.Builder()
+ CarUxRestrictionsConfiguration config = new Builder()
.setUxRestrictions(DRIVING_STATE_MOVING, true, UX_RESTRICTIONS_FULLY_RESTRICTED)
.build();
@@ -292,13 +360,36 @@
}
public void testJsonSerialization_MovingStateWithSpeedRange() {
- CarUxRestrictionsConfiguration config = new CarUxRestrictionsConfiguration.Builder()
- .setUxRestrictions(DRIVING_STATE_MOVING,
- new CarUxRestrictionsConfiguration.Builder.SpeedRange(0f, 5f),
- true, UX_RESTRICTIONS_FULLY_RESTRICTED)
- .setUxRestrictions(DRIVING_STATE_MOVING,
- new CarUxRestrictionsConfiguration.Builder.SpeedRange(5f, MAX_SPEED),
- true, UX_RESTRICTIONS_FULLY_RESTRICTED)
+ CarUxRestrictionsConfiguration config = new Builder()
+ .setUxRestrictions(DRIVING_STATE_MOVING, new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(true)
+ .setRestrictions(UX_RESTRICTIONS_FULLY_RESTRICTED)
+ .setSpeedRange(new Builder.SpeedRange(0f, 5f)))
+ .setUxRestrictions(DRIVING_STATE_MOVING, new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(true)
+ .setRestrictions(UX_RESTRICTIONS_FULLY_RESTRICTED)
+ .setSpeedRange(new Builder.SpeedRange(5f, MAX_SPEED)))
+ .build();
+
+ verifyConfigThroughJsonSerialization(config);
+ }
+
+ public void testJsonSerialization_UxRestrictionMode() {
+ CarUxRestrictionsConfiguration config = new Builder()
+ // Passenger mode
+ .setUxRestrictions(DRIVING_STATE_MOVING, new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(false)
+ .setRestrictions(UX_RESTRICTIONS_BASELINE)
+ .setMode(UX_RESTRICTION_MODE_PASSENGER))
+ // Explicitly specify baseline mode
+ .setUxRestrictions(DRIVING_STATE_PARKED, new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(true)
+ .setRestrictions(UX_RESTRICTIONS_NO_VIDEO)
+ .setMode(UX_RESTRICTION_MODE_BASELINE))
+ // Implicitly defaults to baseline mode
+ .setUxRestrictions(DRIVING_STATE_IDLING, new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(true)
+ .setRestrictions(UX_RESTRICTIONS_NO_VIDEO))
.build();
verifyConfigThroughJsonSerialization(config);
@@ -307,31 +398,45 @@
public void testDump() {
CarUxRestrictionsConfiguration[] configs = new CarUxRestrictionsConfiguration[] {
// Driving state with no speed range
- new CarUxRestrictionsConfiguration.Builder()
+ new Builder()
.setUxRestrictions(DRIVING_STATE_PARKED, false, UX_RESTRICTIONS_BASELINE)
.setUxRestrictions(DRIVING_STATE_IDLING, true, UX_RESTRICTIONS_NO_VIDEO)
.setUxRestrictions(DRIVING_STATE_MOVING, true, UX_RESTRICTIONS_NO_VIDEO)
.build(),
// Parameters
- new CarUxRestrictionsConfiguration.Builder()
+ new Builder()
.setMaxStringLength(1)
.setMaxContentDepth(1)
.setMaxCumulativeContentItems(1)
.build(),
// Driving state with single speed range
- new CarUxRestrictionsConfiguration.Builder()
- .setUxRestrictions(DRIVING_STATE_MOVING,
- new CarUxRestrictionsConfiguration.Builder.SpeedRange(0f),
- true, UX_RESTRICTIONS_NO_VIDEO)
+ new Builder()
+ .setUxRestrictions(DRIVING_STATE_MOVING, new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(true)
+ .setRestrictions(UX_RESTRICTIONS_NO_VIDEO)
+ .setSpeedRange(new Builder.SpeedRange(0f)))
.build(),
// Driving state with multiple speed ranges
- new CarUxRestrictionsConfiguration.Builder()
- .setUxRestrictions(DRIVING_STATE_MOVING,
- new CarUxRestrictionsConfiguration.Builder.SpeedRange(0f, 1f),
- true, UX_RESTRICTIONS_NO_VIDEO)
- .setUxRestrictions(DRIVING_STATE_MOVING,
- new CarUxRestrictionsConfiguration.Builder.SpeedRange(1f),
- true, UX_RESTRICTIONS_NO_VIDEO)
+ new Builder()
+ .setUxRestrictions(DRIVING_STATE_MOVING, new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(true)
+ .setRestrictions(UX_RESTRICTIONS_NO_VIDEO)
+ .setSpeedRange(new Builder.SpeedRange(0f, 1f)))
+ .setUxRestrictions(DRIVING_STATE_MOVING, new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(true)
+ .setRestrictions(UX_RESTRICTIONS_NO_VIDEO)
+ .setSpeedRange(new Builder.SpeedRange(1f)))
+ .build(),
+ // Driving state with passenger mode
+ new Builder()
+ .setUxRestrictions(DRIVING_STATE_MOVING, new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(false)
+ .setRestrictions(UX_RESTRICTIONS_BASELINE)
+ .setMode(UX_RESTRICTION_MODE_PASSENGER))
+ .setUxRestrictions(DRIVING_STATE_MOVING, new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(true)
+ .setRestrictions(UX_RESTRICTIONS_NO_VIDEO)
+ .setMode(UX_RESTRICTION_MODE_BASELINE))
.build(),
};
@@ -341,14 +446,19 @@
}
public void testDumpContainsNecessaryInfo() {
-
- CarUxRestrictionsConfiguration config = new CarUxRestrictionsConfiguration.Builder()
- .setUxRestrictions(DRIVING_STATE_MOVING,
- new CarUxRestrictionsConfiguration.Builder.SpeedRange(0f, 1f),
- true, UX_RESTRICTIONS_NO_VIDEO)
- .setUxRestrictions(DRIVING_STATE_MOVING,
- new CarUxRestrictionsConfiguration.Builder.SpeedRange(1f),
- true, UX_RESTRICTIONS_NO_VIDEO)
+ CarUxRestrictionsConfiguration config = new Builder()
+ .setUxRestrictions(DRIVING_STATE_MOVING, new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(true)
+ .setRestrictions(UX_RESTRICTIONS_NO_VIDEO)
+ .setSpeedRange(new Builder.SpeedRange(0f, 1f)))
+ .setUxRestrictions(DRIVING_STATE_MOVING, new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(true)
+ .setRestrictions(UX_RESTRICTIONS_NO_VIDEO)
+ .setSpeedRange(new Builder.SpeedRange(1f)))
+ .setUxRestrictions(DRIVING_STATE_MOVING, new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(false)
+ .setRestrictions(UX_RESTRICTIONS_BASELINE)
+ .setMode(UX_RESTRICTION_MODE_PASSENGER))
.build();
ByteArrayOutputStream output = new ByteArrayOutputStream();
try (PrintWriter writer = new PrintWriter(output)) {
@@ -363,8 +473,74 @@
assertTrue(dump.contains("Speed Range"));
assertTrue(dump.contains("Requires DO?"));
assertTrue(dump.contains("Restrictions"));
+ assertTrue(dump.contains("Passenger mode"));
+ assertTrue(dump.contains("Baseline mode"));
}
+ public void testSetUxRestrictions_UnspecifiedModeDefaultsToBaseline() {
+ CarUxRestrictionsConfiguration config = new Builder()
+ .setUxRestrictions(DRIVING_STATE_PARKED, new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(true)
+ .setRestrictions(UX_RESTRICTIONS_NO_VIDEO))
+ .build();
+
+ CarUxRestrictions restrictions = config.getUxRestrictions(DRIVING_STATE_PARKED, 0f);
+ assertTrue(restrictions.isRequiresDistractionOptimization());
+ assertEquals(UX_RESTRICTIONS_NO_VIDEO, restrictions.getActiveRestrictions());
+
+ assertTrue(restrictions.isSameRestrictions(
+ config.getUxRestrictions(DRIVING_STATE_PARKED, 0f, UX_RESTRICTIONS_BASELINE)));
+ }
+
+ public void testSetUxRestrictions_PassengerMode() {
+ CarUxRestrictionsConfiguration config = new Builder()
+ .setUxRestrictions(DRIVING_STATE_PARKED, new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(false)
+ .setRestrictions(UX_RESTRICTIONS_BASELINE)
+ .setMode(UX_RESTRICTION_MODE_PASSENGER))
+ .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);
+ assertFalse(passenger.isRequiresDistractionOptimization());
+
+ CarUxRestrictions baseline = config.getUxRestrictions(
+ DRIVING_STATE_PARKED, 0f, UX_RESTRICTION_MODE_BASELINE);
+ assertTrue(baseline.isRequiresDistractionOptimization());
+ assertEquals(UX_RESTRICTIONS_NO_VIDEO, baseline.getActiveRestrictions());
+ }
+
+ public void testPassengerModeFallbackToBaseline() {
+ CarUxRestrictionsConfiguration config = new Builder()
+ .setUxRestrictions(DRIVING_STATE_MOVING, new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(true)
+ .setRestrictions(UX_RESTRICTIONS_NO_VIDEO))
+ .setUxRestrictions(DRIVING_STATE_MOVING, new DrivingStateRestrictions()
+ .setDistractionOptimizationRequired(false)
+ .setRestrictions(UX_RESTRICTIONS_BASELINE)
+ .setMode(UX_RESTRICTION_MODE_PASSENGER)
+ .setSpeedRange(new Builder.SpeedRange(3f)))
+ .build();
+
+ // Retrieve at speed within passenger mode range.
+ CarUxRestrictions passenger = config.getUxRestrictions(
+ DRIVING_STATE_MOVING, 5f, UX_RESTRICTION_MODE_PASSENGER);
+ assertFalse(passenger.isRequiresDistractionOptimization());
+
+ // Retrieve with passenger mode but outside speed range
+ CarUxRestrictions baseline = config.getUxRestrictions(
+ DRIVING_STATE_MOVING, 1f, UX_RESTRICTION_MODE_PASSENGER);
+ assertTrue(baseline.isRequiresDistractionOptimization());
+ assertEquals(UX_RESTRICTIONS_NO_VIDEO, baseline.getActiveRestrictions());
+ }
+
+ /**
+ * 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) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
try (JsonWriter writer = new JsonWriter(new OutputStreamWriter(out))) {