getStagedConfigs() {
CarServiceUtils.assertPermission(mContext,
Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION);
File stagedConfig = getFile(CONFIG_FILENAME_STAGED);
if (stagedConfig.exists()) {
logd("Attempting to read staged config");
return readPersistedConfig(stagedConfig);
} else {
return null;
}
}
/**
* 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}.
*
* Defaults to {@link CarUxRestrictionsManager#UX_RESTRICTION_MODE_BASELINE}.
*
* @param mode the restriction mode
* @return {@code true} if mode was successfully changed; {@code false} otherwise.
* @see CarUxRestrictionsConfiguration.DrivingStateRestrictions
* @see CarUxRestrictionsConfiguration.Builder
*/
@Override
public boolean setRestrictionMode(@NonNull String mode) {
CarServiceUtils.assertPermission(mContext,
Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION);
Objects.requireNonNull(mode, "mode must not be null");
synchronized (mLock) {
if (mRestrictionMode.equals(mode)) {
return true;
}
addTransitionLogLocked(TAG, mRestrictionMode, mode, System.currentTimeMillis(),
"Restriction mode");
mRestrictionMode = mode;
logd("Set restriction mode to: " + mode);
handleDrivingStateEventLocked(mDrivingStateService.getCurrentDrivingState());
}
return true;
}
@Override
@NonNull
public String getRestrictionMode() {
CarServiceUtils.assertPermission(mContext,
Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION);
synchronized (mLock) {
return mRestrictionMode;
}
}
/**
* Returns all supported restriction modes
*/
@NonNull
public List getSupportedRestrictionModes() {
Set modes = new HashSet<>();
Collection configs;
synchronized (mLock) {
configs = mCarUxRestrictionsConfigurations.values();
}
for (CarUxRestrictionsConfiguration config : configs) {
modes.addAll(config.getSupportedRestrictionModes());
}
return new ArrayList<>(modes);
}
/**
* Writes configuration into the specified file.
*
* IO access on file is not thread safe. Caller should ensure threading protection.
*/
private boolean persistConfig(List configs, String filename) {
File file = getFile(filename);
AtomicFile stagedFile = new AtomicFile(file);
FileOutputStream fos;
try {
fos = stagedFile.startWrite();
} catch (IOException e) {
Slogf.e(TAG, "Could not open file to persist config", e);
return false;
}
try (JsonWriter jsonWriter = new JsonWriter(
new OutputStreamWriter(fos, StandardCharsets.UTF_8))) {
writeJson(jsonWriter, configs);
} catch (IOException e) {
Slogf.e(TAG, "Could not persist config", e);
stagedFile.failWrite(fos);
return false;
}
stagedFile.finishWrite(fos);
return true;
}
@VisibleForTesting
void writeJson(JsonWriter jsonWriter, List configs)
throws IOException {
jsonWriter.beginObject();
jsonWriter.name(JSON_NAME_SCHEMA_VERSION).value(JSON_SCHEMA_VERSION_V2);
jsonWriter.name(JSON_NAME_RESTRICTIONS);
jsonWriter.beginArray();
for (int i = 0; i < configs.size(); i++) {
CarUxRestrictionsConfiguration config = configs.get(i);
config.writeJson(jsonWriter);
}
jsonWriter.endArray();
jsonWriter.endObject();
}
@Nullable
private List readPersistedConfig(File file) {
if (!file.exists()) {
Slogf.e(TAG, "Could not find config file: " + file.getName());
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 configs = new ArrayList<>();
switch (schemaVersion) {
case JSON_SCHEMA_VERSION_V1:
readV1Json(reader, configs);
break;
case JSON_SCHEMA_VERSION_V2:
readV2Json(reader, configs);
break;
default:
Slogf.e(TAG, "Unable to parse schema for version " + schemaVersion);
}
return configs;
} catch (IOException e) {
Slogf.e(TAG, "Could not read persisted config file " + file.getName(), e);
}
return null;
}
private void readV1Json(JsonReader reader,
List configs) throws IOException {
readRestrictionsArray(reader, configs, JSON_SCHEMA_VERSION_V1);
}
private void readV2Json(JsonReader reader,
List 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))) {
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) {
Slogf.e(TAG, "Could not read persisted config file " + file.getName(), e);
}
return UNKNOWN_JSON_SCHEMA_VERSION;
}
private void readRestrictionsArray(JsonReader reader,
List 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.
* This method works only on debug builds and the caller of this method needs to have the same
* signature of the car service.
*/
public void setUxRChangeBroadcastEnabled(boolean enable) {
if (!isDebugBuild()) {
Slogf.e(TAG, "Cannot set UX restriction change broadcast.");
return;
}
// Check if the caller has the same signature as that of the car service.
if (mContext.getPackageManager().checkSignatures(Process.myUid(), Binder.getCallingUid())
!= PackageManager.SIGNATURE_MATCH) {
throw new SecurityException(
"Caller " + mContext.getPackageManager().getNameForUid(Binder.getCallingUid())
+ " does not have the right signature");
}
synchronized (mLock) {
if (enable) {
// if enabling it back, send the current restrictions
mUxRChangeBroadcastEnabled = true;
handleDrivingStateEventLocked(
mDrivingStateService.getCurrentDrivingState());
} else {
// fake parked state, so if the system is currently restricted, the restrictions are
// relaxed.
handleDispatchUxRestrictionsLocked(DRIVING_STATE_PARKED, /* speed= */ 0f,
mDisplayIds);
mUxRChangeBroadcastEnabled = false;
}
}
}
private boolean isDebugBuild() {
return BuildHelper.isUserDebugBuild() || BuildHelper.isEngBuild();
}
@Override
@ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
public void dump(IndentingPrintWriter writer) {
synchronized (mLock) {
writer.println("*CarUxRestrictionsManagerService*");
writer.println("UX Restrictions Clients:");
writer.increaseIndent();
BinderHelper.dumpRemoteCallbackList(mUxRClients, writer);
writer.decreaseIndent();
for (int i = 0; i < mCurrentUxRestrictions.size(); i++) {
writer.printf("Display id: %d UXR: %s\n", mCurrentUxRestrictions.keyAt(i),
mCurrentUxRestrictions.valueAt(i));
}
if (isDebugBuild()) {
writer.println("mUxRChangeBroadcastEnabled? " + mUxRChangeBroadcastEnabled);
}
writer.println("UX Restriction configurations:");
for (CarUxRestrictionsConfiguration config :
mCarUxRestrictionsConfigurations.values()) {
config.dump(writer);
}
writer.println("UX Restriction change log:");
for (TransitionLog tlog : mTransitionLogs) {
writer.println(tlog);
}
}
}
@Override
@ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
public void dumpProto(ProtoOutputStream proto) {}
/**
* {@link CarDrivingStateEvent} listener registered with the {@link CarDrivingStateService}
* for getting driving state change notifications.
*/
private final ICarDrivingStateChangeListener mICarDrivingStateChangeEventListener =
new ICarDrivingStateChangeListener.Stub() {
@Override
public void onDrivingStateChanged(CarDrivingStateEvent event) {
logd("Driving State Changed:" + event.eventValue);
synchronized (mLock) {
handleDrivingStateEventLocked(event);
}
}
};
/**
* Handles the driving state change events coming from the {@link CarDrivingStateService} on
* the given displays.
* Maps the driving state to the corresponding UX Restrictions and dispatch the
* UX Restriction change to the registered clients.
*/
@GuardedBy("mLock")
void handleDrivingStateEventOnDisplaysLocked(CarDrivingStateEvent event,
IntArray displayIds) {
if (event == null) {
return;
}
int drivingState = event.eventValue;
Optional currentSpeed = getCurrentSpeed();
if (currentSpeed.isPresent()) {
mCurrentMovingSpeed = currentSpeed.get();
handleDispatchUxRestrictionsLocked(drivingState, mCurrentMovingSpeed, displayIds);
} else if (drivingState != DRIVING_STATE_MOVING) {
// If speed is unavailable, but the driving state is parked or unknown, it can still be
// handled.
logd("Speed null when driving state is: " + drivingState);
handleDispatchUxRestrictionsLocked(drivingState, /* speed= */ 0f, displayIds);
} else {
// If we get here, it means the car is moving while the speed is unavailable.
// This only happens in the case of a fault. We should take the safest route by assuming
// the car is moving at a speed in the highest speed range.
Slogf.e(TAG, "Unexpected: Speed null when driving state is: " + drivingState);
logd("Treating speed null when driving state is: " + drivingState
+ " as in highest speed range");
handleDispatchUxRestrictionsLocked(DRIVING_STATE_MOVING, /* speed= */ MAX_SPEED,
displayIds);
}
}
/**
* Handles the driving state change events coming from the {@link CarDrivingStateService} on
* all displays.
*
* Maps the driving state to the corresponding UX Restrictions and dispatch the
* UX Restriction change to the registered clients.
*/
@VisibleForTesting
@GuardedBy("mLock")
void handleDrivingStateEventLocked(CarDrivingStateEvent event) {
handleDrivingStateEventOnDisplaysLocked(event, mDisplayIds);
}
/**
* {@link CarPropertyEvent} listener registered with the {@link CarPropertyService} for getting
* speed change notifications.
*/
private final ICarPropertyEventListener mICarPropertyEventListener =
new ICarPropertyEventListener.Stub() {
@Override
public void onEvent(List events) throws RemoteException {
synchronized (mLock) {
for (int i = 0; i < events.size(); i++) {
CarPropertyEvent event = events.get(i);
if ((event.getEventType()
== CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE)
&& (event.getCarPropertyValue().getPropertyId()
== VehicleProperty.PERF_VEHICLE_SPEED)) {
handleSpeedChangeLocked(
Math.abs((Float) event.getCarPropertyValue().getValue()));
}
}
}
}
};
@GuardedBy("mLock")
private void handleSpeedChangeLocked(@FloatRange(from = 0f) float newSpeed) {
if (newSpeed == mCurrentMovingSpeed) {
// Ignore if speed hasn't changed
return;
}
mCurrentMovingSpeed = newSpeed;
int currentDrivingState = mDrivingStateService.getCurrentDrivingState().eventValue;
if (currentDrivingState != DRIVING_STATE_MOVING) {
// Ignore speed changes if the vehicle is not moving
return;
}
handleDispatchUxRestrictionsLocked(currentDrivingState, mCurrentMovingSpeed, mDisplayIds);
}
/**
* Handle dispatching UX restrictions change on a set of display ids.
*
* This method also handles the special case when the car is moving but its speed
* is unavailable in which case highest speed will be assumed.
*
* @param currentDrivingState driving state of the vehicle
* @param speed speed of the vehicle
* @param displayIds Ids of displays on which to dispatch Ux restrictions change
*/
@GuardedBy("mLock")
private void handleDispatchUxRestrictionsLocked(@CarDrivingState int currentDrivingState,
@FloatRange(from = 0f) float speed, IntArray displayIds) {
Objects.requireNonNull(mCarUxRestrictionsConfigurations,
"mCarUxRestrictionsConfigurations must be initialized");
Objects.requireNonNull(mCurrentUxRestrictions,
"mCurrentUxRestrictions must be initialized");
if (isDebugBuild() && !mUxRChangeBroadcastEnabled) {
Slogf.d(TAG, "Not dispatching UX Restriction due to setting");
return;
}
// keep track of only those changed UxR for dispatching.
SparseArray updatedUxRestrictions = new SparseArray<>(
INITIAL_DISPLAYS_SIZE);
IntArray displaysToDispatch = new IntArray(INITIAL_DISPLAYS_SIZE);
for (int i = 0; i < displayIds.size(); i++) {
int displayId = displayIds.get(i);
if (DBG) {
Slogf.d(TAG,
"handleDispatchUxRestrictionsLocked: Recalculating UxR for display %d...",
displayId);
}
CarUxRestrictions uxRestrictions;
// Map logical display to DisplayIdentifier to get UxR from config based on
// DisplayIdentifier.
DisplayIdentifier displayIdentifier = getDisplayIdentifier(displayId);
if (displayIdentifier == null) {
Slogf.w(TAG,
"handleDispatchUxRestrictionsLocked: cannot map display id %d to"
+ " DisplayIdentifier based on which to get UxR from config,"
+ " defaulting to fully restricted restrictions",
displayId);
uxRestrictions = createFullyRestrictedRestrictions();
} else {
if (DBG) {
Slogf.d(TAG,
"handleDispatchUxRestrictionsLocked: mapped display id %d to"
+ " DisplayIdentifier %s", displayId, displayIdentifier);
}
CarUxRestrictionsConfiguration config = mCarUxRestrictionsConfigurations.get(
displayIdentifier);
if (config == null) {
Slogf.w(TAG,
"handleDispatchUxRestrictionsLocked: cannot find UxR"
+ " DisplayIdentifier %s from config, defaulting to fully restricted"
+ " restrictions", displayIdentifier);
// If UxR config is not found for a physical port, assume it's fully restricted.
uxRestrictions = createFullyRestrictedRestrictions();
} else {
uxRestrictions = config.getUxRestrictions(
currentDrivingState, speed, mRestrictionMode);
}
}
if (!mCurrentUxRestrictions.contains(displayId)) {
// This should never happen.
Slogf.wtf(TAG, "handleDispatchUxRestrictionsLocked: Unrecognized display %d:"
+ " in new UxR", displayId);
continue;
}
CarUxRestrictions currentUxRestrictions = mCurrentUxRestrictions.get(displayId);
if (DBG) {
Slogf.d(TAG, "Display id %d\tDO old->new: %b -> %b", displayId,
currentUxRestrictions.isRequiresDistractionOptimization(),
uxRestrictions.isRequiresDistractionOptimization());
Slogf.d(TAG, "Display id %d\tUxR old->new: 0x%x -> 0x%x", displayId,
currentUxRestrictions.getActiveRestrictions(),
uxRestrictions.getActiveRestrictions());
}
if (!currentUxRestrictions.isSameRestrictions(uxRestrictions)) {
if (DBG) {
Slogf.d(TAG, "Updating UxR on display %d", displayId);
}
// Update UxR for displayId in place.
mCurrentUxRestrictions.put(displayId, uxRestrictions);
// Ignore dispatching if the restrictions have not changed.
displaysToDispatch.add(displayId);
updatedUxRestrictions.put(displayId, uxRestrictions);
addTransitionLogLocked(currentUxRestrictions, uxRestrictions);
}
}
if (displaysToDispatch.size() != 0) {
dispatchRestrictionsToClientsLocked(updatedUxRestrictions, displaysToDispatch);
}
}
private void dispatchRestrictionsToClientsLocked(
SparseArray updatedUxRestrictions,
IntArray displaysToDispatch) {
logd("dispatching to clients");
boolean success = mClientDispatchHandler.post(() -> {
int numClients = mUxRClients.beginBroadcast();
for (int i = 0; i < numClients; i++) {
ICarUxRestrictionsChangeListener callback = mUxRClients.getBroadcastItem(i);
RemoteCallbackListCookie cookie =
(RemoteCallbackListCookie) mUxRClients.getBroadcastCookie(i);
if (displaysToDispatch.indexOf(cookie.mDisplayId) == -1) {
continue;
}
CarUxRestrictions restrictions = updatedUxRestrictions.get(cookie.mDisplayId);
if (restrictions == null) {
// Don't dispatch to displays without configurations
Slogf.w(TAG, "Can't find UxR on display %d for dispatching",
cookie.mDisplayId);
continue;
}
if (DBG) {
Slogf.d(TAG, "Dispatching UxR change %s to display %d", restrictions,
cookie.mDisplayId);
}
try {
callback.onUxRestrictionsChanged(restrictions);
} catch (RemoteException e) {
Slogf.e(TAG, "Dispatch to listener %s failed for restrictions (%s)", callback,
restrictions);
}
}
mUxRClients.finishBroadcast();
});
if (!success) {
Slogf.e(TAG, "Failed to post Ux Restrictions changes event to dispatch handler");
}
}
@GuardedBy("mLock")
private void initDisplayIdsLocked() {
// Populate logical display ids of all displays in all occupant zones.
List occupantZoneInfos =
mCarOccupantZoneService.getAllOccupantZones();
for (int i = 0; i < occupantZoneInfos.size(); i++) {
CarOccupantZoneManager.OccupantZoneInfo occupantZoneInfo = occupantZoneInfos.get(i);
int zoneId = occupantZoneInfo.zoneId;
int[] displayIds = mCarOccupantZoneService.getAllDisplaysForOccupantZone(zoneId);
for (int j = 0; j < displayIds.length; j++) {
int displayId = displayIds[j];
Slogf.i(TAG, "initDisplayIdsLocked: adding display: %d", displayId);
mDisplayIds.add(displayId);
}
}
// Find the default display id by zone from driver main display.
// This will be used when UxR config does not specify display id.
IntArray displayIds = mCarOccupantZoneService.getAllDisplayIdsForDriver(DISPLAY_TYPE_MAIN);
if (DBG) {
Slogf.d(TAG, "Driver displayIds: " + Arrays.toString(displayIds.toArray()));
}
if (displayIds.size() > 0) {
int driverMainDisplayId = displayIds.get(0);
DisplayIdentifier displayIdentifier = getDisplayIdentifier(driverMainDisplayId);
if (displayIdentifier == null) {
Slogf.w(TAG, "initDisplayIdsLocked: cannot map driver main display id %d"
+ " to DisplayIdentifier", driverMainDisplayId);
displayIdentifier = new DisplayIdentifier(
CarOccupantZoneManager.OccupantZoneInfo.INVALID_ZONE_ID,
CarOccupantZoneManager.DISPLAY_TYPE_UNKNOWN);
}
// The first display id from the driver displays will be the default display id.
Slogf.i(TAG, "initDisplayIdsLocked: setting default display id by zone to %s",
displayIdentifier);
mDefaultDisplayIdentifier = displayIdentifier;
} else {
Slogf.w(TAG, "initDisplayIdsLocked: driver main display not found");
mDefaultDisplayIdentifier = new DisplayIdentifier(
CarOccupantZoneManager.OccupantZoneInfo.INVALID_ZONE_ID,
CarOccupantZoneManager.DISPLAY_TYPE_UNKNOWN);
}
}
@Nullable
private DisplayIdentifier getDisplayIdentifier(int displayId) {
CarOccupantZoneService.DisplayConfig displayConfig =
mCarOccupantZoneService.findDisplayConfigForDisplayId(displayId);
if (displayConfig == null) return null;
DisplayIdentifier displayIdentifier = new DisplayIdentifier(
displayConfig.occupantZoneId, displayConfig.displayType);
return displayIdentifier;
}
@Nullable
private DisplayIdentifier getDisplayIdentifierFromPort(int port) {
CarOccupantZoneService.DisplayConfig displayConfig =
mCarOccupantZoneService.findDisplayConfigForPort(port);
if (displayConfig == null) return null;
DisplayIdentifier displayIdentifier = new DisplayIdentifier(
displayConfig.occupantZoneId, displayConfig.displayType);
return displayIdentifier;
}
@GuardedBy("mLock")
private Map convertToMapLocked(
List configs) {
validateConfigs(configs);
Map result = new ArrayMap<>();
if (configs.size() == 1) {
CarUxRestrictionsConfiguration config = configs.get(0);
if (config.getPhysicalPort() == null
&& config.getOccupantZoneId() == OccupantZoneInfo.INVALID_ZONE_ID) {
// If no display is specified, the default from driver's main display
// is assumed.
result.put(mDefaultDisplayIdentifier, config);
return result;
}
}
for (int i = 0; i < configs.size(); i++) {
CarUxRestrictionsConfiguration config = configs.get(i);
DisplayIdentifier displayIdentifier;
// A display is specified either by physical port or the combination of occupant zone id
// and display type.
// Note: physical port and the combination of occupant zone id and display type won't
// coexist. This has been checked when parsing the UxR config.
if (config.getPhysicalPort() != null) {
displayIdentifier = getDisplayIdentifierFromPort(config.getPhysicalPort());
if (displayIdentifier != null) {
if (DBG) {
Slogf.d(TAG,
"convertToMapLocked: port %d is mapped to DisplayIdentifier %s",
config.getPhysicalPort(), displayIdentifier);
}
} else {
Slogf.w(TAG,
"convertToMapLocked: port %d can't be mapped to DisplayIdentifier",
config.getPhysicalPort());
continue;
}
} else {
displayIdentifier = new DisplayIdentifier(
config.getOccupantZoneId(), config.getDisplayType());
}
result.put(displayIdentifier, config);
}
return result;
}
/**
* Validates configs for multi-display:
*
* - Each sets either display port or (occupant zone id, display type);
*
- Display port is unique;
*
- The combination of (occupant zone id, display type) is unique.
*
*/
@VisibleForTesting
void validateConfigs(List configs) {
if (configs.size() == 0) {
throw new IllegalArgumentException("Empty configuration.");
}
if (configs.size() == 1) {
return;
}
Set existingPorts = new ArraySet<>(configs.size());
Set existingDisplayIdentifiers = new ArraySet<>(configs.size());
for (int i = 0; i < configs.size(); i++) {
CarUxRestrictionsConfiguration config = configs.get(i);
Integer port = config.getPhysicalPort();
int occupantZoneId = config.getOccupantZoneId();
if (port == null && occupantZoneId == OccupantZoneInfo.INVALID_ZONE_ID) {
// Size was checked above; safe to assume there are multiple configs.
throw new IllegalArgumentException(
"Input contains multiple configurations; "
+ "each must set physical port or the combination of occupant zone id "
+ "and display type");
}
if (port != null) {
if (!existingPorts.add(port)) {
throw new IllegalStateException("Multiple configurations for port " + port);
}
} else {
// TODO(b/241589812): Validate occupant zone.
DisplayIdentifier displayIdentifier = new DisplayIdentifier(
occupantZoneId, config.getDisplayType());
if (!existingDisplayIdentifiers.add(displayIdentifier)) {
throw new IllegalStateException(
"Multiple configurations for " + displayIdentifier.toString());
}
}
}
}
private CarUxRestrictions createUnrestrictedRestrictions() {
return new CarUxRestrictions.Builder(/* reqOpt= */ false,
CarUxRestrictions.UX_RESTRICTIONS_BASELINE, SystemClock.elapsedRealtimeNanos())
.build();
}
private CarUxRestrictions createFullyRestrictedRestrictions() {
return new CarUxRestrictions.Builder(
/*reqOpt= */ true,
CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED,
SystemClock.elapsedRealtimeNanos()).build();
}
CarUxRestrictionsConfiguration createDefaultConfig(DisplayIdentifier displayIdentifier) {
return new CarUxRestrictionsConfiguration.Builder()
.setOccupantZoneId(displayIdentifier.occupantZoneId)
.setDisplayType(displayIdentifier.displayType)
.setUxRestrictions(DRIVING_STATE_PARKED,
false, CarUxRestrictions.UX_RESTRICTIONS_BASELINE)
.setUxRestrictions(DRIVING_STATE_IDLING,
false, CarUxRestrictions.UX_RESTRICTIONS_BASELINE)
.setUxRestrictions(DRIVING_STATE_MOVING,
true, CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED)
.setUxRestrictions(DRIVING_STATE_UNKNOWN,
true, CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED)
.build();
}
@GuardedBy("mLock")
private void addTransitionLogLocked(String name, String from, String to, long timestamp,
String extra) {
if (mTransitionLogs.size() >= MAX_TRANSITION_LOG_SIZE) {
mTransitionLogs.remove();
}
TransitionLog tLog = new TransitionLog(name, from, to, timestamp, extra);
mTransitionLogs.add(tLog);
}
@GuardedBy("mLock")
private void addTransitionLogLocked(
CarUxRestrictions oldRestrictions, CarUxRestrictions newRestrictions) {
if (mTransitionLogs.size() >= MAX_TRANSITION_LOG_SIZE) {
mTransitionLogs.remove();
}
StringBuilder extra = new StringBuilder();
extra.append(oldRestrictions.isRequiresDistractionOptimization() ? "DO -> " : "No DO -> ");
extra.append(newRestrictions.isRequiresDistractionOptimization() ? "DO" : "No DO");
TransitionLog tLog = new TransitionLog(TAG,
oldRestrictions.getActiveRestrictions(), newRestrictions.getActiveRestrictions(),
System.currentTimeMillis(), extra.toString());
mTransitionLogs.add(tLog);
}
private static void logd(String msg) {
if (DBG) {
Slogf.d(TAG, msg);
}
}
}