Snap for 4826885 from 9eb8e1bcd0ef89878328a2a30011b8e572c5a9ff to pi-release
Change-Id: I4cb5a61067fd7c56a7665555509f3d8b007a9940
diff --git a/car-lib/api/current.txt b/car-lib/api/current.txt
index 8adacdf..9cd2e4c 100644
--- a/car-lib/api/current.txt
+++ b/car-lib/api/current.txt
@@ -230,7 +230,6 @@
public final class CarUxRestrictionsManager {
method public android.car.drivingstate.CarUxRestrictions getCurrentCarUxRestrictions() throws android.car.CarNotConnectedException;
method public synchronized void registerListener(android.car.drivingstate.CarUxRestrictionsManager.OnUxRestrictionsChangedListener) throws android.car.CarNotConnectedException, java.lang.IllegalArgumentException;
- method public synchronized void registerListener(android.car.drivingstate.CarUxRestrictionsManager.onUxRestrictionsChangedListener) throws android.car.CarNotConnectedException, java.lang.IllegalArgumentException;
method public synchronized void unregisterListener() throws android.car.CarNotConnectedException;
}
@@ -238,11 +237,6 @@
method public abstract void onUxRestrictionsChanged(android.car.drivingstate.CarUxRestrictions);
}
- public static abstract interface CarUxRestrictionsManager.onUxRestrictionsChangedListener {
- method public abstract void dummy();
- method public abstract void onUxRestrictionsChanged(android.car.drivingstate.CarUxRestrictions);
- }
-
}
package android.car.hardware {
@@ -293,6 +287,7 @@
public final class CarSensorManager {
method public android.car.hardware.CarSensorEvent getLatestSensorEvent(int) throws android.car.CarNotConnectedException;
+ method public java.util.List<android.car.hardware.CarPropertyConfig> getPropertyList() throws android.car.CarNotConnectedException;
method public int[] getSupportedSensors() throws android.car.CarNotConnectedException;
method public boolean isSensorSupported(int) throws android.car.CarNotConnectedException;
method public static boolean isSensorSupported(int[], int);
diff --git a/car-lib/src/android/car/drivingstate/CarUxRestrictionsManager.java b/car-lib/src/android/car/drivingstate/CarUxRestrictionsManager.java
index 9ef3c86..57e7d60 100644
--- a/car-lib/src/android/car/drivingstate/CarUxRestrictionsManager.java
+++ b/car-lib/src/android/car/drivingstate/CarUxRestrictionsManager.java
@@ -226,35 +226,4 @@
listener.onUxRestrictionsChanged(restrictionInfo);
}
}
-
- /**
- * To be removed after updating the support library with the new car stubs lib.
- * b/80506092 has more details.
- *
- */
- public interface onUxRestrictionsChangedListener {
- /**
- * To be removed see b/80506092 for details.
- * @param restrictionInfo
- */
- void onUxRestrictionsChanged(CarUxRestrictions restrictionInfo);
- /**
- * Temp workaround. To be removed.
- * To differentiate from the new OnUxRestrictionsChangedListener for clients calling
- * registerListener with an anonymous class or lambda functions.
- */
- void dummy();
- }
-
- /**
- * To be removed after updating the support library with the new car stubs lib.
- * b/80506092 has more details.
- *
- */
- public synchronized void registerListener(@NonNull onUxRestrictionsChangedListener listener)
- throws CarNotConnectedException, IllegalArgumentException {
- // Intentionally left NOP.
- }
-
-
}
diff --git a/car-lib/src/android/car/hardware/CarSensorManager.java b/car-lib/src/android/car/hardware/CarSensorManager.java
index fc97c9d..f6cc8df 100644
--- a/car-lib/src/android/car/hardware/CarSensorManager.java
+++ b/car-lib/src/android/car/hardware/CarSensorManager.java
@@ -268,11 +268,9 @@
@Override
public void onChangeEvent(CarPropertyValue value) {
- synchronized (this) {
- CarSensorManager manager = mManager.get();
- if (manager != null) {
- manager.handleOnChangeEvent(value, mListener);
- }
+ CarSensorManager manager = mManager.get();
+ if (manager != null) {
+ manager.handleOnChangeEvent(value, mListener);
}
}
@@ -325,9 +323,15 @@
return new int[0];
}
- private List<CarPropertyConfig> getPropertyList() throws CarNotConnectedException {
+ /**
+ * Get list of properties represented by CarSensorManager for this car.
+ * @return List of CarPropertyConfig objects available via Car Cabin Manager.
+ * @throws CarNotConnectedException if the connection to the car service has been lost.
+ */
+ public List<CarPropertyConfig> getPropertyList() throws CarNotConnectedException {
return mCarPropertyMgr.getPropertyList(mSensorConfigIds);
}
+
/**
* Tells if given sensor is supported or not.
* @param sensorType
@@ -463,22 +467,27 @@
}
private CarSensorEvent createCarSensorEvent(CarPropertyValue propertyValue) {
- Class<?> actualClass = propertyValue.getValue().getClass();
CarSensorEvent event = null;
- if (actualClass == Float.class) {
+ Object o = propertyValue.getValue();
+ if (o instanceof Float) {
event = new CarSensorEvent(propertyValue.getPropertyId(),
propertyValue.getTimestamp(), 1, 0, 0);
- event.floatValues[0] = (float) propertyValue.getValue();
- } else if (actualClass == Integer.class) {
+ event.floatValues[0] = (float) o;
+ } else if (o instanceof Integer) {
event = new CarSensorEvent(propertyValue.getPropertyId(),
propertyValue.getTimestamp(), 0, 1, 0);
- event.intValues[0] = (int) propertyValue.getValue();
- } else if (actualClass == Boolean.class) {
+ event.intValues[0] = (int) o;
+ } else if (o instanceof Boolean) {
event = new CarSensorEvent(propertyValue.getPropertyId(),
propertyValue.getTimestamp(), 0, 1, 0);
- event.intValues[0] = (boolean) propertyValue.getValue() ? 1 : 0;
- } else {
- // TODO: handle int64_vec and mixed type
+ event.intValues[0] = (boolean) o ? 1 : 0;
+ } else if (o instanceof Long[]) {
+ Long[] value = (Long[]) o;
+ event = new CarSensorEvent(propertyValue.getPropertyId(),
+ propertyValue.getTimestamp(), 0, 0, value.length);
+ for (int i = 0; i < value.length; i++) {
+ event.longValues[i] = value[i];
+ }
}
return event;
}
@@ -503,6 +512,7 @@
for (CarPropertyConfig p : propertyConfigs) {
if (p.getPropertyId() == type) {
b = createWheelDistanceTickBundle(p.getConfigArray());
+ break;
}
}
break;
diff --git a/car-lib/src/android/car/hardware/CarVendorExtensionManager.java b/car-lib/src/android/car/hardware/CarVendorExtensionManager.java
index ce2831c..9f06764 100644
--- a/car-lib/src/android/car/hardware/CarVendorExtensionManager.java
+++ b/car-lib/src/android/car/hardware/CarVendorExtensionManager.java
@@ -28,6 +28,7 @@
import com.android.internal.annotations.GuardedBy;
import java.lang.ref.WeakReference;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -44,41 +45,18 @@
private final static boolean DBG = false;
private final static String TAG = CarVendorExtensionManager.class.getSimpleName();
private final CarPropertyManager mPropertyManager;
- private CarPropertyEventListenerToBase mListenerToBase = null;
@GuardedBy("mLock")
private final ArraySet<CarVendorExtensionCallback> mCallbacks = new ArraySet<>();
private final Object mLock = new Object();
- private static class CarPropertyEventListenerToBase implements
- CarPropertyManager.CarPropertyEventListener{
- private final WeakReference<CarVendorExtensionManager> mManager;
-
- CarPropertyEventListenerToBase(CarVendorExtensionManager manager) {
- mManager = new WeakReference<>(manager);
- }
-
- @Override
- public void onChangeEvent(CarPropertyValue value) {
- CarVendorExtensionManager manager = mManager.get();
- if (manager != null) {
- manager.handleOnChangeEvent(value);
- }
- }
-
- @Override
- public void onErrorEvent(int propertyId, int zone) {
- CarVendorExtensionManager manager = mManager.get();
- if (manager != null) {
- manager.handleOnErrorEvent(propertyId, zone);
- }
- }
- }
+ @GuardedBy("mLock")
+ private CarPropertyEventListenerToBase mListenerToBase = null;
private void handleOnChangeEvent(CarPropertyValue value) {
Collection<CarVendorExtensionCallback> callbacks;
- synchronized (this) {
- callbacks = new ArraySet<>(mCallbacks);
+ synchronized (mLock) {
+ callbacks = new ArrayList<>(mCallbacks);
}
for (CarVendorExtensionCallback l: callbacks) {
l.onChangeEvent(value);
@@ -87,14 +65,13 @@
private void handleOnErrorEvent(int propertyId, int zone) {
Collection<CarVendorExtensionCallback> listeners;
- synchronized (this) {
- listeners = new ArraySet<>(mCallbacks);
+ synchronized (mLock) {
+ listeners = new ArrayList<>(mCallbacks);
}
- if (!listeners.isEmpty()) {
- for (CarVendorExtensionCallback l: listeners) {
- l.onErrorEvent(propertyId, zone);
- }
+ for (CarVendorExtensionCallback l: listeners) {
+ l.onErrorEvent(propertyId, zone);
}
+
}
/**
@@ -242,4 +219,28 @@
public void onCarDisconnected() {
mPropertyManager.onCarDisconnected();
}
+ private static class CarPropertyEventListenerToBase implements
+ CarPropertyManager.CarPropertyEventListener {
+ private final WeakReference<CarVendorExtensionManager> mManager;
+
+ CarPropertyEventListenerToBase(CarVendorExtensionManager manager) {
+ mManager = new WeakReference<>(manager);
+ }
+
+ @Override
+ public void onChangeEvent(CarPropertyValue value) {
+ CarVendorExtensionManager manager = mManager.get();
+ if (manager != null) {
+ manager.handleOnChangeEvent(value);
+ }
+ }
+
+ @Override
+ public void onErrorEvent(int propertyId, int zone) {
+ CarVendorExtensionManager manager = mManager.get();
+ if (manager != null) {
+ manager.handleOnErrorEvent(propertyId, zone);
+ }
+ }
+ }
}
diff --git a/car-lib/src/android/car/hardware/property/CarPropertyManager.java b/car-lib/src/android/car/hardware/property/CarPropertyManager.java
index d59ce58..5c094e7 100644
--- a/car-lib/src/android/car/hardware/property/CarPropertyManager.java
+++ b/car-lib/src/android/car/hardware/property/CarPropertyManager.java
@@ -30,7 +30,7 @@
import android.util.Log;
import android.util.SparseArray;
-import com.android.car.internal.CarRatedListeners2;
+import com.android.car.internal.CarRatedFloatListeners;
import com.android.car.internal.SingleMessageHandler;
import java.lang.ref.WeakReference;
@@ -370,7 +370,7 @@
}
- private class CarPropertyListeners extends CarRatedListeners2<CarPropertyEventListener> {
+ private class CarPropertyListeners extends CarRatedFloatListeners<CarPropertyEventListener> {
CarPropertyListeners(float rate) {
super(rate);
}
diff --git a/car-lib/src/android/car/user/CarUserManagerHelper.java b/car-lib/src/android/car/user/CarUserManagerHelper.java
index 7585ec7..4a69f0b 100644
--- a/car-lib/src/android/car/user/CarUserManagerHelper.java
+++ b/car-lib/src/android/car/user/CarUserManagerHelper.java
@@ -17,6 +17,7 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.car.settings.CarSettings;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -227,6 +228,16 @@
}
/**
+ * Checks whether the user is default user.
+ *
+ * @param userInfo User to check against system user.
+ * @return {@code true} if is default user, {@code false} otherwise.
+ */
+ public boolean isDefaultUser(UserInfo userInfo) {
+ return userInfo.id == CarSettings.DEFAULT_USER_ID_TO_BOOT_INTO;
+ }
+
+ /**
* Checks whether passed in user is the foreground user.
*
* @param userInfo User to check.
@@ -409,6 +420,13 @@
return false;
}
+ // Not allow to delete the default user for now. Since default user is the one to
+ // boot into.
+ if (isHeadlessSystemUser() && isDefaultUser(userInfo)) {
+ Log.w(TAG, "User " + userInfo.id + " is the default user, could not be removed.");
+ return false;
+ }
+
if (userInfo.id == getCurrentForegroundUserId()) {
startNewGuestSession(guestUserName);
}
diff --git a/car-lib/src/com/android/car/internal/CarRatedListeners2.java b/car-lib/src/com/android/car/internal/CarRatedFloatListeners.java
similarity index 82%
rename from car-lib/src/com/android/car/internal/CarRatedListeners2.java
rename to car-lib/src/com/android/car/internal/CarRatedFloatListeners.java
index 8708cd4..c59a2fa 100644
--- a/car-lib/src/com/android/car/internal/CarRatedListeners2.java
+++ b/car-lib/src/com/android/car/internal/CarRatedFloatListeners.java
@@ -23,22 +23,24 @@
/**
* Represent listeners for a property grouped by their rate.
- * @param <EventListenerType>
+ * T is a type of EventListener such as CarPropertyEventListener
+ * in {@link android.car.hardware.property.CarPropertyManager}
+ * @param <T>
* @hide
*/
-public class CarRatedListeners2<EventListenerType> {
- private final Map<EventListenerType, Float> mListenersToRate = new HashMap<>(4);
+public class CarRatedFloatListeners<T> {
+ private final Map<T, Float> mListenersToRate = new HashMap<>(4);
private float mUpdateRate;
protected long mLastUpdateTime = -1;
- protected CarRatedListeners2(float rate) {
+ protected CarRatedFloatListeners(float rate) {
mUpdateRate = rate;
}
/** Check listener */
- public boolean contains(EventListenerType listener) {
+ public boolean contains(T listener) {
return mListenersToRate.containsKey(listener);
}
/** Return current rate after updating */
@@ -52,7 +54,7 @@
* @param listener
* @return true if rate was updated. Otherwise, returns false.
*/
- public boolean remove(EventListenerType listener) {
+ public boolean remove(T listener) {
mListenersToRate.remove(listener);
if (mListenersToRate.isEmpty()) {
return false;
@@ -76,7 +78,7 @@
* @param updateRate
* @return true if rate was updated. Otherwise, returns false.
*/
- public boolean addAndUpdateRate(EventListenerType listener, float updateRate) {
+ public boolean addAndUpdateRate(T listener, float updateRate) {
Float oldUpdateRate = mListenersToRate.put(listener, updateRate);
if (mUpdateRate < updateRate) {
mUpdateRate = updateRate;
@@ -87,7 +89,7 @@
return false;
}
- public Collection<EventListenerType> getListeners() {
+ public Collection<T> getListeners() {
return mListenersToRate.keySet();
}
}
diff --git a/service/src/com/android/car/CarDrivingStateService.java b/service/src/com/android/car/CarDrivingStateService.java
index 7f0eeea..bc0fbc1 100644
--- a/service/src/com/android/car/CarDrivingStateService.java
+++ b/service/src/com/android/car/CarDrivingStateService.java
@@ -17,6 +17,7 @@
package com.android.car;
import android.annotation.Nullable;
+import android.car.VehicleAreaType;
import android.car.drivingstate.CarDrivingStateEvent;
import android.car.drivingstate.CarDrivingStateEvent.CarDrivingState;
import android.car.drivingstate.ICarDrivingState;
@@ -53,10 +54,11 @@
// List of clients listening to driving state events.
private final List<DrivingStateClient> mDrivingStateClients = new ArrayList<>();
// Array of properties that the service needs to listen to from CarPropertyService for deriving
- // the driving state. ToDo (ramperry@) - fine tune this list - b/69859926
+ // the driving state.
private static final int[] REQUIRED_PROPERTIES = {
VehicleProperty.PERF_VEHICLE_SPEED,
- VehicleProperty.GEAR_SELECTION};
+ VehicleProperty.GEAR_SELECTION,
+ VehicleProperty.PARKING_BRAKE_ON};
private CarDrivingStateEvent mCurrentDrivingState;
// For dumpsys logging
private final LinkedList<Utils.TransitionLog> mTransitionLogs = new LinkedList<>();
@@ -64,6 +66,9 @@
private long mLastGearTimestamp = NOT_RECEIVED;
private float mLastSpeed;
private long mLastSpeedTimestamp = NOT_RECEIVED;
+ private boolean mLastParkingBrakeState;
+ private long mLastParkingBrakeTimestamp = NOT_RECEIVED;
+ private List<Integer> mSupportedGears;
public CarDrivingStateService(Context context, CarPropertyService propertyService) {
mContext = context;
@@ -72,12 +77,15 @@
}
@Override
- public void init() {
+ public synchronized void init() {
if (!checkPropertySupport()) {
Log.e(TAG, "init failure. Driving state will always be fully restrictive");
return;
}
subscribeToProperties();
+ mCurrentDrivingState = createDrivingStateEvent(inferDrivingStateLocked());
+ addTransitionLog(TAG + " Boot", CarDrivingStateEvent.DRIVING_STATE_UNKNOWN,
+ mCurrentDrivingState.eventValue, mCurrentDrivingState.timeStamp);
}
@Override
@@ -269,11 +277,17 @@
@Override
public void dump(PrintWriter writer) {
- writer.println("Driving state chane log:");
+ writer.println("Driving state change log:");
for (Utils.TransitionLog tLog : mTransitionLogs) {
writer.println(tLog);
}
writer.println("Current Driving State: " + mCurrentDrivingState.eventValue);
+ if (mSupportedGears != null) {
+ writer.println("Supported gears:");
+ for (Integer gear : mSupportedGears) {
+ writer.print("Gear:" + gear);
+ }
+ }
}
/**
@@ -315,6 +329,9 @@
}
break;
case VehicleProperty.GEAR_SELECTION:
+ if (mSupportedGears == null) {
+ mSupportedGears = getSupportedGears();
+ }
int curGear = (Integer) value.getValue();
if (DBG) {
Log.d(TAG, "Gear: " + curGear + "@" + curTimestamp);
@@ -326,6 +343,19 @@
Log.d(TAG, "Ignoring Gear with older timestamp:" + curTimestamp);
}
break;
+ case VehicleProperty.PARKING_BRAKE_ON:
+ boolean curParkingBrake = (boolean) value.getValue();
+ if (DBG) {
+ Log.d(TAG, "Parking Brake: " + curParkingBrake + "@" + curTimestamp);
+ }
+ if (curTimestamp > mLastParkingBrakeTimestamp) {
+ mLastParkingBrakeTimestamp = curTimestamp;
+ mLastParkingBrakeState = curParkingBrake;
+ } else if (DBG) {
+ Log.d(TAG, "Ignoring Parking Brake status with an older timestamp:"
+ + curTimestamp);
+ }
+ break;
default:
Log.e(TAG, "Received property event for unhandled propId=" + propId);
break;
@@ -357,6 +387,16 @@
}
}
+ private List<Integer> getSupportedGears() {
+ List<CarPropertyConfig> properyList = mPropertyService.getPropertyList();
+ for (CarPropertyConfig p : properyList) {
+ if (p.getPropertyId() == VehicleProperty.GEAR_SELECTION) {
+ return p.getConfigArray();
+ }
+ }
+ return null;
+ }
+
private void addTransitionLog(String name, int from, int to, long timestamp) {
if (mTransitionLogs.size() >= MAX_TRANSITION_LOG_SIZE) {
mTransitionLogs.remove();
@@ -369,31 +409,35 @@
/**
* Infers the current driving state of the car from the other Car Sensor properties like
* Current Gear, Speed etc.
- * ToDo (ramperry@) - Fine tune this - b/69859926
*
* @return Current driving state
*/
@CarDrivingState
private int inferDrivingStateLocked() {
- /*
- Simple logic to start off deriving driving state:
- 1. If gear == parked, then Driving State is parked.
- 2. If gear != parked,
- 2a. if speed == 0, then driving state is idling
- 2b. if speed != 0, then driving state is moving
- 2c. if speed unavailable, then driving state is unknown
- This logic needs to be tested and iterated on. Tracked in b/69859926
- */
+ updateVehiclePropertiesIfNeeded();
if (DBG) {
Log.d(TAG, "Last known Gear:" + mLastGear + " Last known speed:" + mLastSpeed);
}
- if (mLastGearTimestamp == NOT_RECEIVED) {
- return CarDrivingStateEvent.DRIVING_STATE_UNKNOWN;
- } else if (mLastGear == VehicleGear.GEAR_PARK) {
+
+ /*
+ Logic to start off deriving driving state:
+ 1. If gear == parked, then Driving State is parked.
+ 2. If gear != parked,
+ 2a. if parking brake is applied, then Driving state is parked.
+ 2b. if parking brake is not applied or unknown/unavailable, then driving state
+ is still unknown.
+ 3. If driving state is unknown at the end of step 2,
+ 3a. if speed == 0, then driving state is idling
+ 3b. if speed != 0, then driving state is moving
+ 3c. if speed unavailable, then driving state is unknown
+ */
+
+ if (isVehicleKnownToBeParked()) {
return CarDrivingStateEvent.DRIVING_STATE_PARKED;
}
- if (mLastSpeedTimestamp == NOT_RECEIVED) {
+ // We don't know if the vehicle is parked, let's look at the speed.
+ if (mLastSpeedTimestamp == NOT_RECEIVED || mLastSpeed < 0) {
return CarDrivingStateEvent.DRIVING_STATE_UNKNOWN;
} else if (mLastSpeed == 0f) {
return CarDrivingStateEvent.DRIVING_STATE_IDLING;
@@ -402,6 +446,89 @@
}
}
+ /**
+ * Find if we have signals to know if the vehicle is parked
+ *
+ * @return true if we have enough information to say the vehicle is parked.
+ * false, if the vehicle is either not parked or if we don't have any information.
+ */
+ private boolean isVehicleKnownToBeParked() {
+ // If we know the gear is in park, return true
+ if (mLastGearTimestamp != NOT_RECEIVED && mLastGear == VehicleGear.GEAR_PARK) {
+ return true;
+ } else if (mLastParkingBrakeTimestamp != NOT_RECEIVED) {
+ // if gear is not in park or unknown, look for status of parking brake if transmission
+ // type is manual.
+ if (isCarManualTransmissionType()) {
+ return mLastParkingBrakeState;
+ }
+ }
+ // if neither information is available, return false to indicate we can't determine
+ // if the vehicle is parked.
+ return false;
+ }
+
+ /**
+ * If Supported gears information is available and GEAR_PARK is not one of the supported gears,
+ * transmission type is considered to be Manual. Automatic transmission is assumed otherwise.
+ */
+ private boolean isCarManualTransmissionType() {
+ if (mSupportedGears != null
+ && !mSupportedGears.isEmpty()
+ && !mSupportedGears.contains(VehicleGear.GEAR_PARK)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Try querying the gear selection and parking brake if we haven't received the event yet.
+ * This could happen if the gear change occurred before car service booted up like in the
+ * case of a HU restart in the middle of a drive. Since gear and parking brake are
+ * on-change only properties, we could be in this situation where we will have to query
+ * VHAL.
+ */
+ private void updateVehiclePropertiesIfNeeded() {
+ if (mLastGearTimestamp == NOT_RECEIVED) {
+ CarPropertyValue propertyValue = mPropertyService.getProperty(
+ VehicleProperty.GEAR_SELECTION,
+ VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL);
+ if (propertyValue != null) {
+ mLastGear = (Integer) propertyValue.getValue();
+ mLastGearTimestamp = propertyValue.getTimestamp();
+ if (DBG) {
+ Log.d(TAG, "updateVehiclePropertiesIfNeeded: gear:" + mLastGear);
+ }
+ }
+ }
+
+ if (mLastParkingBrakeTimestamp == NOT_RECEIVED) {
+ CarPropertyValue propertyValue = mPropertyService.getProperty(
+ VehicleProperty.PARKING_BRAKE_ON,
+ VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL);
+ if (propertyValue != null) {
+ mLastParkingBrakeState = (boolean) propertyValue.getValue();
+ mLastParkingBrakeTimestamp = propertyValue.getTimestamp();
+ if (DBG) {
+ Log.d(TAG, "updateVehiclePropertiesIfNeeded: brake:" + mLastParkingBrakeState);
+ }
+ }
+ }
+
+ if (mLastSpeedTimestamp == NOT_RECEIVED) {
+ CarPropertyValue propertyValue = mPropertyService.getProperty(
+ VehicleProperty.PERF_VEHICLE_SPEED,
+ VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL);
+ if (propertyValue != null) {
+ mLastSpeed = (float) propertyValue.getValue();
+ mLastSpeedTimestamp = propertyValue.getTimestamp();
+ if (DBG) {
+ Log.d(TAG, "updateVehiclePropertiesIfNeeded: speed:" + mLastSpeed);
+ }
+ }
+ }
+ }
+
private static CarDrivingStateEvent createDrivingStateEvent(int eventValue) {
return new CarDrivingStateEvent(eventValue, SystemClock.elapsedRealtimeNanos());
}
diff --git a/service/src/com/android/car/CarUxRestrictionsManagerService.java b/service/src/com/android/car/CarUxRestrictionsManagerService.java
index 449a485..23c4d32 100644
--- a/service/src/com/android/car/CarUxRestrictionsManagerService.java
+++ b/service/src/com/android/car/CarUxRestrictionsManagerService.java
@@ -50,6 +50,7 @@
private static final boolean DBG = false;
private static final int MAX_TRANSITION_LOG_SIZE = 20;
private static final int PROPERTY_UPDATE_RATE = 5; // Update rate in Hz
+ private static final float SPEED_NOT_AVAILABLE = -1.0F;
private final Context mContext;
private final CarDrivingStateService mDrivingStateService;
private final CarPropertyService mCarPropertyService;
@@ -77,7 +78,7 @@
}
@Override
- public void init() {
+ public synchronized void init() {
try {
if (!mHelper.loadUxRestrictionsFromXml()) {
Log.e(TAG, "Error reading Ux Restrictions Mapping. Falling back to defaults");
@@ -93,6 +94,38 @@
// subscribe to property service for speed
mCarPropertyService.registerListener(VehicleProperty.PERF_VEHICLE_SPEED,
PROPERTY_UPDATE_RATE, mICarPropertyEventListener);
+ initializeUxRestrictions();
+ }
+
+ // Update current restrictions by getting the current driving state and speed.
+ private void initializeUxRestrictions() {
+ CarDrivingStateEvent currentDrivingStateEvent =
+ mDrivingStateService.getCurrentDrivingState();
+ // if we don't have enough information from the CarPropertyService to compute the UX
+ // restrictions, then leave the UX restrictions unchanged from what it was initialized to
+ // in the constructor.
+ if (currentDrivingStateEvent == null || currentDrivingStateEvent.eventValue
+ == CarDrivingStateEvent.DRIVING_STATE_UNKNOWN) {
+ return;
+ }
+ int currentDrivingState = currentDrivingStateEvent.eventValue;
+ Float currentSpeed = getCurrentSpeed();
+ if (currentSpeed == SPEED_NOT_AVAILABLE) {
+ return;
+ }
+ // At this point the underlying CarPropertyService has provided us enough information to
+ // compute the UX restrictions that could be potentially different from the initial UX
+ // restrictions.
+ handleDispatchUxRestrictions(currentDrivingState, currentSpeed);
+ }
+
+ private Float getCurrentSpeed() {
+ CarPropertyValue value = mCarPropertyService.getProperty(VehicleProperty.PERF_VEHICLE_SPEED,
+ 0);
+ if (value != null) {
+ return (Float) value.getValue();
+ }
+ return SPEED_NOT_AVAILABLE;
}
@Override
@@ -156,7 +189,6 @@
return null;
}
-
/**
* Unregister the given UX Restrictions listener
*
@@ -282,10 +314,10 @@
return;
}
int drivingState = event.eventValue;
- CarPropertyValue value = mCarPropertyService.getProperty(VehicleProperty.PERF_VEHICLE_SPEED,
- 0);
- if (value != null) {
- mCurrentMovingSpeed = (Float) value.getValue();
+ Float speed = getCurrentSpeed();
+
+ if (speed != SPEED_NOT_AVAILABLE) {
+ mCurrentMovingSpeed = speed;
} else if (drivingState == CarDrivingStateEvent.DRIVING_STATE_PARKED
|| drivingState == CarDrivingStateEvent.DRIVING_STATE_UNKNOWN) {
// If speed is unavailable, but the driving state is parked or unknown, it can still be
@@ -352,7 +384,6 @@
uxRestrictions = getDefaultRestrictions(currentDrivingState);
} else {
uxRestrictions = mHelper.getUxRestrictions(currentDrivingState, speed);
-
}
if (DBG) {
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/car_assistant.xml b/tests/EmbeddedKitchenSinkApp/res/layout/car_assistant.xml
index 759a73e..89d67be 100644
--- a/tests/EmbeddedKitchenSinkApp/res/layout/car_assistant.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/car_assistant.xml
@@ -17,8 +17,13 @@
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
- android:id="@+id/voice_button"
+ android:id="@+id/voice_button_intent"
android:layout_gravity="center"
android:src="@drawable/ic_voice_assistant_mic"
style="@style/OverviewButton"/>
-</LinearLayout>
\ No newline at end of file
+ <ImageView
+ android:id="@+id/voice_button_service"
+ android:layout_gravity="center"
+ android:src="@drawable/ic_voice_assistant_mic"
+ style="@style/OverviewButton"/>
+</LinearLayout>
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/assistant/CarAssistantFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/assistant/CarAssistantFragment.java
index 147b017..4bee453 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/assistant/CarAssistantFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/assistant/CarAssistantFragment.java
@@ -31,15 +31,18 @@
public class CarAssistantFragment extends Fragment {
- private ImageView mMic;
+ private ImageView mMicIntent;
+ private ImageView mMicService;
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.car_assistant, container, false);
- mMic = (ImageView) v.findViewById(R.id.voice_button);
+ mMicIntent = (ImageView) v.findViewById(R.id.voice_button_intent);
+ mMicService = (ImageView) v.findViewById(R.id.voice_button_service);
Context context = getContext();
- mMic.setOnClickListener(new View.OnClickListener() {
+
+ mMicIntent.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
@@ -54,6 +57,14 @@
}
}
});
+ mMicService.setOnClickListener(v1 -> {
+ v1.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
+ boolean success = getActivity().showAssist(null);
+ if (!success) {
+ Toast.makeText(context,
+ "Assistant app is not available.", Toast.LENGTH_SHORT).show();
+ }
+ });
return v;
}
}
diff --git a/tests/carservice_test/src/com/android/car/CarDrivingRestrictionsTest.java b/tests/carservice_test/src/com/android/car/CarDrivingRestrictionsTest.java
index 9be9b10..31ed4b2 100644
--- a/tests/carservice_test/src/com/android/car/CarDrivingRestrictionsTest.java
+++ b/tests/carservice_test/src/com/android/car/CarDrivingRestrictionsTest.java
@@ -19,6 +19,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import android.car.Car;
@@ -82,13 +83,8 @@
mCarUxRManager.registerListener(listener);
// With no gear value available, driving state should be unknown
listener.reset();
- getMockedVehicleHal().injectEvent(
- VehiclePropValueBuilder.newBuilder(VehicleProperty.PERF_VEHICLE_SPEED)
- .addFloatValue(0.0f)
- .setTimestamp(SystemClock.elapsedRealtimeNanos())
- .build());
-
// Test Parked state and corresponding restrictions based on car_ux_restrictions_map.xml
+ Log.d(TAG, "Injecting gear park");
getMockedVehicleHal().injectEvent(
VehiclePropValueBuilder.newBuilder(VehicleProperty.GEAR_SELECTION)
.addIntValue(VehicleGear.GEAR_PARK)
@@ -98,9 +94,17 @@
assertNotNull(drivingEvent);
assertThat(drivingEvent.eventValue).isEqualTo(CarDrivingStateEvent.DRIVING_STATE_PARKED);
+ Log.d(TAG, "Injecting speed 0");
+ getMockedVehicleHal().injectEvent(
+ VehiclePropValueBuilder.newBuilder(VehicleProperty.PERF_VEHICLE_SPEED)
+ .addFloatValue(0.0f)
+ .setTimestamp(SystemClock.elapsedRealtimeNanos())
+ .build());
+
// Switch gear to drive. Driving state changes to Idling but the UX restrictions don't
// change between parked and idling.
listener.reset();
+ Log.d(TAG, "Injecting gear drive");
getMockedVehicleHal().injectEvent(
VehiclePropValueBuilder.newBuilder(VehicleProperty.GEAR_SELECTION)
.addIntValue(VehicleGear.GEAR_DRIVE)
@@ -112,6 +116,7 @@
// Test Moving state and corresponding restrictions based on car_ux_restrictions_map.xml
listener.reset();
+ Log.d(TAG, "Injecting speed 30");
getMockedVehicleHal().injectEvent(
VehiclePropValueBuilder.newBuilder(VehicleProperty.PERF_VEHICLE_SPEED)
.addFloatValue(30.0f)
@@ -128,6 +133,7 @@
// Test Idling state and corresponding restrictions based on car_ux_restrictions_map.xml
listener.reset();
+ Log.d(TAG, "Injecting speed 0");
getMockedVehicleHal().injectEvent(
VehiclePropValueBuilder.newBuilder(VehicleProperty.PERF_VEHICLE_SPEED)
.addFloatValue(0.0f)
@@ -142,6 +148,83 @@
assertThat(restrictions.getActiveRestrictions())
.isEqualTo(CarUxRestrictions.UX_RESTRICTIONS_BASELINE);
+ // Apply Parking brake. Supported gears is not provided in this test and hence
+ // Automatic transmission should be assumed and hence parking brake state should not
+ // make a difference to the driving state.
+ listener.reset();
+ Log.d(TAG, "Injecting parking brake on");
+ getMockedVehicleHal().injectEvent(
+ VehiclePropValueBuilder.newBuilder(VehicleProperty.PARKING_BRAKE_ON)
+ .setBooleanValue(true)
+ .setTimestamp(SystemClock.elapsedRealtimeNanos())
+ .build());
+ drivingEvent = listener.waitForDrivingStateChange();
+ assertNull(drivingEvent);
+
+ mCarDrivingStateManager.unregisterListener();
+ mCarUxRManager.unregisterListener();
+ }
+
+ @Test
+ public void testDrivingStateChangeForMalformedInputs()
+ throws CarNotConnectedException, InterruptedException {
+ CarDrivingStateEvent drivingEvent;
+ CarUxRestrictions restrictions;
+ DrivingStateListener listener = new DrivingStateListener();
+ mCarDrivingStateManager.registerListener(listener);
+ mCarUxRManager.registerListener(listener);
+
+ // Start with gear = park and speed = 0 to begin with a known state.
+ listener.reset();
+ Log.d(TAG, "Injecting gear park");
+ getMockedVehicleHal().injectEvent(
+ VehiclePropValueBuilder.newBuilder(VehicleProperty.GEAR_SELECTION)
+ .addIntValue(VehicleGear.GEAR_PARK)
+ .setTimestamp(SystemClock.elapsedRealtimeNanos())
+ .build());
+ drivingEvent = listener.waitForDrivingStateChange();
+ assertNotNull(drivingEvent);
+ assertThat(drivingEvent.eventValue).isEqualTo(CarDrivingStateEvent.DRIVING_STATE_PARKED);
+
+ Log.d(TAG, "Injecting speed 0");
+ getMockedVehicleHal().injectEvent(
+ VehiclePropValueBuilder.newBuilder(VehicleProperty.PERF_VEHICLE_SPEED)
+ .addFloatValue(0.0f)
+ .setTimestamp(SystemClock.elapsedRealtimeNanos())
+ .build());
+
+ // Inject an invalid gear. Since speed is still valid, idling will be the expected
+ // driving state
+ listener.reset();
+ Log.d(TAG, "Injecting gear -1");
+ getMockedVehicleHal().injectEvent(
+ VehiclePropValueBuilder.newBuilder(VehicleProperty.GEAR_SELECTION)
+ .addIntValue(-1)
+ .setTimestamp(SystemClock.elapsedRealtimeNanos())
+ .build());
+ drivingEvent = listener.waitForDrivingStateChange();
+ assertNotNull(drivingEvent);
+ assertThat(drivingEvent.eventValue).isEqualTo(CarDrivingStateEvent.DRIVING_STATE_IDLING);
+
+ // Now, send in an invalid speed value as well, now the driving state will be unknown and
+ // the UX restrictions will change to fully restricted.
+ listener.reset();
+ Log.d(TAG, "Injecting speed -1");
+ getMockedVehicleHal().injectEvent(
+ VehiclePropValueBuilder.newBuilder(VehicleProperty.PERF_VEHICLE_SPEED)
+ .addFloatValue(-1.0f)
+ .setTimestamp(SystemClock.elapsedRealtimeNanos())
+ .build());
+ drivingEvent = listener.waitForDrivingStateChange();
+ assertNotNull(drivingEvent);
+ assertThat(drivingEvent.eventValue).isEqualTo(CarDrivingStateEvent.DRIVING_STATE_UNKNOWN);
+ restrictions = listener.waitForUxRestrictionsChange();
+ assertNotNull(restrictions);
+ assertTrue(restrictions.isRequiresDistractionOptimization());
+ assertThat(restrictions.getActiveRestrictions())
+ .isEqualTo(CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED);
+ mCarDrivingStateManager.unregisterListener();
+ mCarUxRManager.unregisterListener();
}
/**