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();
     }
 
     /**