Merge "Support for hotspot client visibility." am: cf84f31de4
am: e1e4b55d46

Change-Id: Iaf16f5400654bdf36ce5c49df2bac74c89733550
diff --git a/car-lib/src/android/car/Car.java b/car-lib/src/android/car/Car.java
index 67a00e3..cc18688 100644
--- a/car-lib/src/android/car/Car.java
+++ b/car-lib/src/android/car/Car.java
@@ -24,6 +24,8 @@
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
+import android.app.Activity;
+import android.app.Service;
 import android.car.cluster.CarInstrumentClusterManager;
 import android.car.cluster.ClusterActivityState;
 import android.car.content.pm.CarPackageManager;
@@ -725,11 +727,10 @@
                 mConnectionState = STATE_CONNECTED;
                 mService = newService;
             }
-            if (mServiceConnectionListenerClient != null) {
-                mServiceConnectionListenerClient.onServiceConnected(name, service);
-            }
             if (mStatusChangeCallback != null) {
                 mStatusChangeCallback.onLifecycleChanged(Car.this, true);
+            } else if (mServiceConnectionListenerClient != null) {
+                mServiceConnectionListenerClient.onServiceConnected(name, service);
             }
         }
 
@@ -742,11 +743,13 @@
                 }
                 handleCarDisconnectLocked();
             }
-            if (mServiceConnectionListenerClient != null) {
-                mServiceConnectionListenerClient.onServiceDisconnected(name);
-            }
             if (mStatusChangeCallback != null) {
                 mStatusChangeCallback.onLifecycleChanged(Car.this, false);
+            } else if (mServiceConnectionListenerClient != null) {
+                mServiceConnectionListenerClient.onServiceDisconnected(name);
+            } else {
+                // This client does not handle car service restart, so should be terminated.
+                finishClient();
             }
         }
     };
@@ -1136,12 +1139,15 @@
     @Nullable
     public Object getCarManager(String serviceName) {
         CarManagerBase manager;
-        ICar service = getICarOrThrow();
         synchronized (mLock) {
+            if (mService == null) {
+                Log.w(TAG_CAR, "getCarManager not working while car service not ready");
+                return null;
+            }
             manager = mServiceMap.get(serviceName);
             if (manager == null) {
                 try {
-                    IBinder binder = service.getCarService(serviceName);
+                    IBinder binder = mService.getCarService(serviceName);
                     if (binder == null) {
                         Log.w(TAG_CAR, "getCarManager could not get binder for service:"
                                 + serviceName);
@@ -1155,7 +1161,7 @@
                     }
                     mServiceMap.put(serviceName, manager);
                 } catch (RemoteException e) {
-                    throw e.rethrowFromSystemServer();
+                    handleRemoteExceptionFromCarService(e);
                 }
             }
         }
@@ -1171,84 +1177,139 @@
         return CONNECTION_TYPE_EMBEDDED;
     }
 
+    /** @hide */
+    Context getContext() {
+        return mContext;
+    }
+
+    /** @hide */
+    Handler getEventHandler() {
+        return mEventHandler;
+    }
+
+    /** @hide */
+    <T> T handleRemoteExceptionFromCarService(RemoteException e, T returnValue) {
+        handleRemoteExceptionFromCarService(e);
+        return returnValue;
+    }
+
+    /** @hide */
+    void handleRemoteExceptionFromCarService(RemoteException e) {
+        Log.w(TAG_CAR, "Car service has crashed", e);
+    }
+
+
+    private void finishClient() {
+        if (mContext == null) {
+            throw new IllegalStateException("Car service has crashed, null Context");
+        }
+        if (mContext instanceof Activity) {
+            Activity activity = (Activity) mContext;
+            if (!activity.isFinishing()) {
+                Log.w(TAG_CAR, "Car service crashed, client not handling it, finish Activity");
+                activity.finish();
+            }
+            return;
+        } else if (mContext instanceof Service) {
+            Service service = (Service) mContext;
+            throw new RuntimeException("Car service has crashed, client not handle it:"
+                    + service.getPackageName() + "," + service.getClass().getSimpleName());
+        }
+        throw new IllegalStateException("Car service crashed, client not handling it.");
+    }
+
+    /** @hide */
+    public static <T> T handleRemoteExceptionFromCarService(Service service, RemoteException e,
+            T returnValue) {
+        handleRemoteExceptionFromCarService(service, e);
+        return returnValue;
+    }
+
+    /** @hide */
+    public static  void handleRemoteExceptionFromCarService(Service service, RemoteException e) {
+        Log.w(TAG_CAR,
+                "Car service has crashed, client:" + service.getPackageName() + ","
+                        + service.getClass().getSimpleName(), e);
+        service.stopSelf();
+    }
+
     @Nullable
     private CarManagerBase createCarManager(String serviceName, IBinder binder) {
         CarManagerBase manager = null;
         switch (serviceName) {
             case AUDIO_SERVICE:
-                manager = new CarAudioManager(binder, mContext, mEventHandler);
+                manager = new CarAudioManager(this, binder);
                 break;
             case SENSOR_SERVICE:
-                manager = new CarSensorManager(binder, mContext, mEventHandler);
+                manager = new CarSensorManager(this, binder);
                 break;
             case INFO_SERVICE:
-                manager = new CarInfoManager(binder);
+                manager = new CarInfoManager(this, binder);
                 break;
             case APP_FOCUS_SERVICE:
-                manager = new CarAppFocusManager(binder, mEventHandler);
+                manager = new CarAppFocusManager(this, binder);
                 break;
             case PACKAGE_SERVICE:
-                manager = new CarPackageManager(binder, mContext);
+                manager = new CarPackageManager(this, binder);
                 break;
             case CAR_NAVIGATION_SERVICE:
-                manager = new CarNavigationStatusManager(binder);
+                manager = new CarNavigationStatusManager(this, binder);
                 break;
             case CABIN_SERVICE:
-                manager = new CarCabinManager(binder, mContext, mEventHandler);
+                manager = new CarCabinManager(this, binder);
                 break;
             case DIAGNOSTIC_SERVICE:
-                manager = new CarDiagnosticManager(binder, mContext, mEventHandler);
+                manager = new CarDiagnosticManager(this, binder);
                 break;
             case HVAC_SERVICE:
-                manager = new CarHvacManager(binder, mContext, mEventHandler);
+                manager = new CarHvacManager(this, binder);
                 break;
             case POWER_SERVICE:
-                manager = new CarPowerManager(binder, mContext, mEventHandler);
+                manager = new CarPowerManager(this, binder);
                 break;
             case PROJECTION_SERVICE:
-                manager = new CarProjectionManager(binder, mEventHandler);
+                manager = new CarProjectionManager(this, binder);
                 break;
             case PROPERTY_SERVICE:
-                manager = new CarPropertyManager(ICarProperty.Stub.asInterface(binder),
-                    mEventHandler);
+                manager = new CarPropertyManager(this, ICarProperty.Stub.asInterface(binder));
                 break;
             case VENDOR_EXTENSION_SERVICE:
-                manager = new CarVendorExtensionManager(binder, mEventHandler);
+                manager = new CarVendorExtensionManager(this, binder);
                 break;
             case CAR_INSTRUMENT_CLUSTER_SERVICE:
-                manager = new CarInstrumentClusterManager(binder, mEventHandler);
+                manager = new CarInstrumentClusterManager(this, binder);
                 break;
             case TEST_SERVICE:
                 /* CarTestManager exist in static library. So instead of constructing it here,
                  * only pass binder wrapper so that CarTestManager can be constructed outside. */
-                manager = new CarTestManagerBinderWrapper(binder);
+                manager = new CarTestManagerBinderWrapper(this, binder);
                 break;
             case VMS_SUBSCRIBER_SERVICE:
-                manager = new VmsSubscriberManager(binder);
+                manager = new VmsSubscriberManager(this, binder);
                 break;
             case BLUETOOTH_SERVICE:
-                manager = new CarBluetoothManager(binder, mContext);
+                manager = new CarBluetoothManager(this, binder);
                 break;
             case STORAGE_MONITORING_SERVICE:
-                manager = new CarStorageMonitoringManager(binder, mEventHandler);
+                manager = new CarStorageMonitoringManager(this, binder);
                 break;
             case CAR_DRIVING_STATE_SERVICE:
-                manager = new CarDrivingStateManager(binder, mContext, mEventHandler);
+                manager = new CarDrivingStateManager(this, binder);
                 break;
             case CAR_UX_RESTRICTION_SERVICE:
-                manager = new CarUxRestrictionsManager(binder, mContext, mEventHandler);
+                manager = new CarUxRestrictionsManager(this, binder);
                 break;
             case CAR_CONFIGURATION_SERVICE:
-                manager = new CarConfigurationManager(binder);
+                manager = new CarConfigurationManager(this, binder);
                 break;
             case CAR_TRUST_AGENT_ENROLLMENT_SERVICE:
-                manager = new CarTrustAgentEnrollmentManager(binder, mContext, mEventHandler);
+                manager = new CarTrustAgentEnrollmentManager(this, binder);
                 break;
             case CAR_MEDIA_SERVICE:
-                manager = new CarMediaManager(binder);
+                manager = new CarMediaManager(this, binder);
                 break;
             case CAR_BUGREPORT_SERVICE:
-                manager = new CarBugreportManager(binder, mContext);
+                manager = new CarBugreportManager(this, binder);
                 break;
             default:
                 break;
@@ -1281,15 +1342,6 @@
         }
     }
 
-    private ICar getICarOrThrow() throws IllegalStateException {
-        synchronized (mLock) {
-            if (mService == null) {
-                throw new IllegalStateException("not connected");
-            }
-            return mService;
-        }
-    }
-
     private void tearDownCarManagersLocked() {
         // All disconnected handling should be only doing its internal cleanup.
         for (CarManagerBase manager: mServiceMap.values()) {
diff --git a/car-lib/src/android/car/CarAppFocusManager.java b/car-lib/src/android/car/CarAppFocusManager.java
index ff0c25d..b8936af 100644
--- a/car-lib/src/android/car/CarAppFocusManager.java
+++ b/car-lib/src/android/car/CarAppFocusManager.java
@@ -17,7 +17,6 @@
 package android.car;
 
 import android.annotation.IntDef;
-import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
 
@@ -35,7 +34,7 @@
  * should run in the system, and other app setting the flag for the matching app should
  * lead into other app to stop.
  */
-public final class CarAppFocusManager implements CarManagerBase {
+public final class CarAppFocusManager extends CarManagerBase {
     /**
      * Listener to get notification for app getting information on application type status changes.
      */
@@ -114,7 +113,6 @@
     public @interface AppFocusRequestResult {}
 
     private final IAppFocus mService;
-    private final Handler mHandler;
     private final Map<OnAppFocusChangedListener, IAppFocusListenerImpl> mChangeBinders =
             new HashMap<>();
     private final Map<OnAppFocusOwnershipCallback, IAppFocusOwnershipCallbackImpl>
@@ -123,9 +121,9 @@
     /**
      * @hide
      */
-    CarAppFocusManager(IBinder service, Handler handler) {
+    CarAppFocusManager(Car car, IBinder service) {
+        super(car);
         mService = IAppFocus.Stub.asInterface(service);
-        mHandler = handler;
     }
 
     /**
@@ -149,7 +147,7 @@
         try {
             mService.registerFocusListener(binder, appType);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -169,7 +167,8 @@
         try {
             mService.unregisterFocusListener(binder, appType);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
+            // continue for local clean-up
         }
         synchronized (this) {
             binder.removeAppType(appType);
@@ -197,7 +196,7 @@
                 mService.unregisterFocusListener(binder, appType);
             }
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -209,7 +208,7 @@
         try {
             return mService.getActiveAppTypes();
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, new int[0]);
         }
     }
 
@@ -229,7 +228,7 @@
         try {
             return mService.isOwningFocus(binder, appType);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, false);
         }
     }
 
@@ -261,7 +260,7 @@
         try {
             return mService.requestAppFocus(binder, appType);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, APP_FOCUS_REQUEST_FAILED);
         }
     }
 
@@ -286,7 +285,8 @@
         try {
             mService.abandonAppFocus(binder, appType);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
+            // continue for local clean-up
         }
         synchronized (this) {
             binder.removeAppType(appType);
@@ -314,7 +314,7 @@
                 mService.abandonAppFocus(binder, appType);
             }
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -359,11 +359,8 @@
             if (manager == null || listener == null) {
                 return;
             }
-            manager.mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    listener.onAppFocusChanged(appType, active);
-                }
+            manager.getEventHandler().post(() -> {
+                listener.onAppFocusChanged(appType, active);
             });
         }
     }
@@ -403,11 +400,8 @@
             if (manager == null || callback == null) {
                 return;
             }
-            manager.mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    callback.onAppFocusOwnershipLost(appType);
-                }
+            manager.getEventHandler().post(() -> {
+                callback.onAppFocusOwnershipLost(appType);
             });
         }
 
@@ -418,11 +412,8 @@
             if (manager == null || callback == null) {
                 return;
             }
-            manager.mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    callback.onAppFocusOwnershipGranted(appType);
-                }
+            manager.getEventHandler().post(() -> {
+                callback.onAppFocusOwnershipGranted(appType);
             });
         }
     }
diff --git a/car-lib/src/android/car/CarBluetoothManager.java b/car-lib/src/android/car/CarBluetoothManager.java
index c85cec7..c8e46ca 100644
--- a/car-lib/src/android/car/CarBluetoothManager.java
+++ b/car-lib/src/android/car/CarBluetoothManager.java
@@ -17,7 +17,6 @@
 
 import android.Manifest;
 import android.annotation.RequiresPermission;
-import android.content.Context;
 import android.os.IBinder;
 import android.os.RemoteException;
 
@@ -26,9 +25,8 @@
  *
  * @hide
  */
-public final class CarBluetoothManager implements CarManagerBase {
+public final class CarBluetoothManager extends CarManagerBase {
     private static final String TAG = "CarBluetoothManager";
-    private final Context mContext;
     private final ICarBluetooth mService;
 
     /**
@@ -50,7 +48,7 @@
         try {
             mService.connectDevices();
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -59,8 +57,8 @@
      *
      * @hide
      */
-    public CarBluetoothManager(IBinder service, Context context) {
-        mContext = context;
+    public CarBluetoothManager(Car car, IBinder service) {
+        super(car);
         mService = ICarBluetooth.Stub.asInterface(service);
     }
 
diff --git a/car-lib/src/android/car/CarBugreportManager.java b/car-lib/src/android/car/CarBugreportManager.java
index f5d3b1d..99f2c7c 100644
--- a/car-lib/src/android/car/CarBugreportManager.java
+++ b/car-lib/src/android/car/CarBugreportManager.java
@@ -20,7 +20,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
-import android.content.Context;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
@@ -39,10 +38,9 @@
  *
  * @hide
  */
-public final class CarBugreportManager implements CarManagerBase {
+public final class CarBugreportManager extends CarManagerBase {
 
     private final ICarBugreportService mService;
-    private Handler mHandler;
 
     /**
      * Callback from carbugreport manager. Callback methods are always called on the main thread.
@@ -153,9 +151,9 @@
      *
      * Should not be obtained directly by clients, use {@link Car#getCarManager(String)} instead.
      */
-    public CarBugreportManager(IBinder service, Context context) {
+    public CarBugreportManager(Car car, IBinder service) {
+        super(car);
         mService = ICarBugreportService.Stub.asInterface(service);
-        mHandler = new Handler(context.getMainLooper());
     }
 
     /**
@@ -185,10 +183,10 @@
         Preconditions.checkNotNull(callback);
         try {
             CarBugreportManagerCallbackWrapper wrapper =
-                    new CarBugreportManagerCallbackWrapper(callback, mHandler);
+                    new CarBugreportManagerCallbackWrapper(callback, getEventHandler());
             mService.requestBugreport(output, extraOutput, wrapper);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         } finally {
             IoUtils.closeQuietly(output);
             IoUtils.closeQuietly(extraOutput);
diff --git a/car-lib/src/android/car/CarInfoManager.java b/car-lib/src/android/car/CarInfoManager.java
index bf09495..e9a170d 100644
--- a/car-lib/src/android/car/CarInfoManager.java
+++ b/car-lib/src/android/car/CarInfoManager.java
@@ -29,7 +29,7 @@
  * Utility to retrieve various static information from car. Each data are grouped as {@link Bundle}
  * and relevant data can be checked from {@link Bundle} using pre-specified keys.
  */
-public final class CarInfoManager implements CarManagerBase{
+public final class CarInfoManager extends CarManagerBase {
 
     private final CarPropertyManager mCarPropertyMgr;
     /**
@@ -261,9 +261,10 @@
     }
 
     /** @hide */
-    CarInfoManager(IBinder service) {
+    CarInfoManager(Car car, IBinder service) {
+        super(car);
         ICarProperty mCarPropertyService = ICarProperty.Stub.asInterface(service);
-        mCarPropertyMgr = new CarPropertyManager(mCarPropertyService, null);
+        mCarPropertyMgr = new CarPropertyManager(car, mCarPropertyService);
     }
 
     /** @hide */
diff --git a/car-lib/src/android/car/CarManagerBase.java b/car-lib/src/android/car/CarManagerBase.java
index 737f356..8d30fdf 100644
--- a/car-lib/src/android/car/CarManagerBase.java
+++ b/car-lib/src/android/car/CarManagerBase.java
@@ -16,10 +16,44 @@
 
 package android.car;
 
+import android.content.Context;
+import android.os.Handler;
+import android.os.RemoteException;
+
 /**
- * Common interface for Car*Manager
+ * Common base class for Car*Manager
  * @hide
  */
-public interface CarManagerBase {
-    void onCarDisconnected();
+public abstract class CarManagerBase {
+
+    protected final Car mCar;
+
+    public CarManagerBase(Car car) {
+        mCar = car;
+    }
+
+    protected Context getContext() {
+        return mCar.getContext();
+    }
+
+    protected Handler getEventHandler() {
+        return mCar.getEventHandler();
+    }
+
+    protected <T> T handleRemoteExceptionFromCarService(RemoteException e, T returnValue) {
+        return mCar.handleRemoteExceptionFromCarService(e, returnValue);
+    }
+
+    protected void handleRemoteExceptionFromCarService(RemoteException e) {
+        mCar.handleRemoteExceptionFromCarService(e);
+    }
+
+    /**
+     * Handle disconnection of car service (=crash). As car service has crashed already, this call
+     * should only clean up any listeners / callbacks passed from client. Clearing object passed
+     * to car service is not necessary as car service has crashed. Note that Car*Manager will not
+     * work any more as all binders are invalid. Client should re-create all Car*Managers when
+     * car service is restarted.
+     */
+    protected abstract void onCarDisconnected();
 }
diff --git a/car-lib/src/android/car/CarProjectionManager.java b/car-lib/src/android/car/CarProjectionManager.java
index 4c177f7..bc4107f 100644
--- a/car-lib/src/android/car/CarProjectionManager.java
+++ b/car-lib/src/android/car/CarProjectionManager.java
@@ -69,7 +69,7 @@
  * @hide
  */
 @SystemApi
-public final class CarProjectionManager implements CarManagerBase {
+public final class CarProjectionManager extends CarManagerBase {
     private static final String TAG = CarProjectionManager.class.getSimpleName();
 
     private final Binder mToken = new Binder();
@@ -194,7 +194,6 @@
     public static final int PROJECTION_AP_FAILED = 2;
 
     private final ICarProjection mService;
-    private final Handler mHandler;
     private final Executor mHandlerExecutor;
 
     @GuardedBy("mLock")
@@ -241,9 +240,10 @@
     /**
      * @hide
      */
-    public CarProjectionManager(IBinder service, Handler handler) {
+    public CarProjectionManager(Car car, IBinder service) {
+        super(car);
         mService = ICarProjection.Stub.asInterface(service);
-        mHandler = handler;
+        Handler handler = getEventHandler();
         mHandlerExecutor = handler::post;
     }
 
@@ -448,7 +448,8 @@
                 mService.unregisterKeyEventHandler(mBinderHandler);
             }
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
+            return;
         }
 
         mHandledEvents = events;
@@ -466,7 +467,7 @@
             try {
                 mService.registerProjectionRunner(serviceIntent);
             } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
+                handleRemoteExceptionFromCarService(e);
             }
         }
     }
@@ -483,7 +484,7 @@
             try {
                 mService.unregisterProjectionRunner(serviceIntent);
             } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
+                handleRemoteExceptionFromCarService(e);
             }
         }
     }
@@ -507,14 +508,14 @@
     public void startProjectionAccessPoint(@NonNull ProjectionAccessPointCallback callback) {
         Preconditions.checkNotNull(callback, "callback cannot be null");
         synchronized (mLock) {
-            Looper looper = mHandler.getLooper();
+            Looper looper = getEventHandler().getLooper();
             ProjectionAccessPointCallbackProxy proxy =
                     new ProjectionAccessPointCallbackProxy(this, looper, callback);
             try {
                 mService.startProjectionAccessPoint(proxy.getMessenger(), mAccessPointProxyToken);
                 mProjectionAccessPointCallbackProxy = proxy;
             } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
+                handleRemoteExceptionFromCarService(e);
             }
         }
     }
@@ -535,7 +536,7 @@
             }
             return channelList;
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, Collections.emptyList());
         }
     }
 
@@ -556,7 +557,7 @@
         try {
             mService.stopProjectionAccessPoint(mAccessPointProxyToken);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -575,7 +576,7 @@
         try {
             return mService.requestBluetoothProfileInhibit(device, profile, mToken);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, false);
         }
     }
 
@@ -593,7 +594,7 @@
         try {
             return mService.releaseBluetoothProfileInhibit(device, profile, mToken);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, false);
         }
     }
 
@@ -611,7 +612,7 @@
         try {
             mService.updateProjectionStatus(status, mToken);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -634,12 +635,12 @@
                 try {
                     mService.registerProjectionStatusListener(mCarProjectionStatusListener);
                 } catch (RemoteException e) {
-                    throw e.rethrowFromSystemServer();
+                    handleRemoteExceptionFromCarService(e);
                 }
             } else {
                 // Already subscribed to Car Service, immediately notify listener with the current
                 // projection status in the event handler thread.
-                mHandler.post(() ->
+                getEventHandler().post(() ->
                         listener.onProjectionStatusChanged(
                                 mCarProjectionStatusListener.mCurrentState,
                                 mCarProjectionStatusListener.mCurrentPackageName,
@@ -671,7 +672,7 @@
             mService.unregisterProjectionStatusListener(mCarProjectionStatusListener);
             mCarProjectionStatusListener = null;
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -695,7 +696,7 @@
         try {
             return mService.getProjectionOptions();
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, Bundle.EMPTY);
         }
     }
 
@@ -838,7 +839,7 @@
                 List<ProjectionStatus> details) {
             CarProjectionManager mgr = mManagerRef.get();
             if (mgr != null) {
-                mgr.mHandler.post(() -> {
+                mgr.getEventHandler().post(() -> {
                     mCurrentState = projectionState;
                     mCurrentPackageName = packageName;
                     mDetails = Collections.unmodifiableList(details);
diff --git a/car-lib/src/android/car/VehiclePropertyIds.java b/car-lib/src/android/car/VehiclePropertyIds.java
index c335e58..ada2b2d 100644
--- a/car-lib/src/android/car/VehiclePropertyIds.java
+++ b/car-lib/src/android/car/VehiclePropertyIds.java
@@ -338,38 +338,44 @@
     /**
      * Distance units for display
      * Requires permission {@link Car#PERMISSION_READ_DISPLAY_UNITS} to read the property.
-     * Requires permission {@link Car#PERMISSION_CONTROL_DISPLAY_UNITS} to write the property.
+     * Requires permission {@link Car#PERMISSION_CONTROL_DISPLAY_UNITS} and
+     * {@link Car#PERMISSION_VENDOR_EXTENSION}to write the property.
      */
     public static final int DISTANCE_DISPLAY_UNITS = 289408512;
     /**
      * Fuel volume units for display
      * Requires permission {@link Car#PERMISSION_READ_DISPLAY_UNITS} to read the property.
-     * Requires permission {@link Car#PERMISSION_CONTROL_DISPLAY_UNITS} to write the property.
+     * Requires permission {@link Car#PERMISSION_CONTROL_DISPLAY_UNITS} and
+     * {@link Car#PERMISSION_VENDOR_EXTENSION}to write the property.
      */
     public static final int FUEL_VOLUME_DISPLAY_UNITS = 289408513;
     /**
      * Tire pressure units for display
      * Requires permission {@link Car#PERMISSION_READ_DISPLAY_UNITS} to read the property.
-     * Requires permission {@link Car#PERMISSION_CONTROL_DISPLAY_UNITS} to write the property.
+     * Requires permission {@link Car#PERMISSION_CONTROL_DISPLAY_UNITS} and
+     * {@link Car#PERMISSION_VENDOR_EXTENSION}to write the property.
      */
     public static final int TIRE_PRESSURE_DISPLAY_UNITS = 289408514;
     /**
      * EV battery units for display
      * Requires permission {@link Car#PERMISSION_READ_DISPLAY_UNITS} to read the property.
-     * Requires permission {@link Car#PERMISSION_CONTROL_DISPLAY_UNITS} to write the property.
+     * Requires permission {@link Car#PERMISSION_CONTROL_DISPLAY_UNITS} and
+     * {@link Car#PERMISSION_VENDOR_EXTENSION}to write the property.
      */
     public static final int EV_BATTERY_DISPLAY_UNITS = 289408515;
     /**
      * Speed Units for display
      * Requires permission {@link Car#PERMISSION_READ_DISPLAY_UNITS} to read the property.
-     * Requires permission {@link Car#PERMISSION_CONTROL_DISPLAY_UNITS} to write the property.
+     * Requires permission {@link Car#PERMISSION_CONTROL_DISPLAY_UNITS} and
+     * {@link Car#PERMISSION_VENDOR_EXTENSION}to write the property.
      * @hide
      */
     public static final int VEHICLE_SPEED_DISPLAY_UNITS = 289408516;
     /**
      * Fuel consumption units for display
      * Requires permission {@link Car#PERMISSION_READ_DISPLAY_UNITS} to read the property.
-     * Requires permission {@link Car#PERMISSION_CONTROL_DISPLAY_UNITS} to write the property.
+     * Requires permission {@link Car#PERMISSION_CONTROL_DISPLAY_UNITS} and
+     * {@link Car#PERMISSION_VENDOR_EXTENSION}to write the property.
      */
     public static final int FUEL_CONSUMPTION_UNITS_DISTANCE_OVER_VOLUME = 287311364;
     /**
diff --git a/car-lib/src/android/car/cluster/CarInstrumentClusterManager.java b/car-lib/src/android/car/cluster/CarInstrumentClusterManager.java
index 54d10e2..2b633a0 100644
--- a/car-lib/src/android/car/cluster/CarInstrumentClusterManager.java
+++ b/car-lib/src/android/car/cluster/CarInstrumentClusterManager.java
@@ -17,10 +17,10 @@
 package android.car.cluster;
 
 import android.annotation.SystemApi;
+import android.car.Car;
 import android.car.CarManagerBase;
 import android.content.Intent;
 import android.os.Bundle;
-import android.os.Handler;
 import android.os.IBinder;
 
 /**
@@ -35,7 +35,7 @@
  */
 @Deprecated
 @SystemApi
-public class CarInstrumentClusterManager implements CarManagerBase {
+public class CarInstrumentClusterManager extends CarManagerBase {
     /**
      * @deprecated use {@link android.car.Car#CATEGORY_NAVIGATION} instead
      *
@@ -101,7 +101,8 @@
     }
 
     /** @hide */
-    public CarInstrumentClusterManager(IBinder service, Handler handler) {
+    public CarInstrumentClusterManager(Car car, IBinder service) {
+        super(car);
         // No-op
     }
 
diff --git a/car-lib/src/android/car/cluster/renderer/IInstrumentCluster.aidl b/car-lib/src/android/car/cluster/renderer/IInstrumentCluster.aidl
index 7deecc7..4f41796 100644
--- a/car-lib/src/android/car/cluster/renderer/IInstrumentCluster.aidl
+++ b/car-lib/src/android/car/cluster/renderer/IInstrumentCluster.aidl
@@ -28,6 +28,8 @@
     /**
      * Returns {@link IInstrumentClusterNavigation} that will be passed to the navigation
      * application.
+     *
+     * TODO(b/141992448) : remove blocking call
      */
     IInstrumentClusterNavigation getNavigationService();
 
diff --git a/car-lib/src/android/car/cluster/renderer/IInstrumentClusterHelper.aidl b/car-lib/src/android/car/cluster/renderer/IInstrumentClusterHelper.aidl
new file mode 100644
index 0000000..680e241
--- /dev/null
+++ b/car-lib/src/android/car/cluster/renderer/IInstrumentClusterHelper.aidl
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.car.cluster.renderer;
+
+import android.content.Intent;
+import android.os.Bundle;
+
+/**
+ * Helper binder API for InstrumentClusterRenderingService. This contains binder calls to car
+ * service.
+ *
+ * @hide
+ */
+interface IInstrumentClusterHelper {
+    /**
+     * Start an activity to specified display / user. The activity is considered as
+     * in fixed mode for the display and will be re-launched if the activity crashes, the package
+     * is updated or goes to background for whatever reason.
+     * Only one activity can exist in fixed mode for the target display and calling this multiple
+     * times with different {@code Intent} will lead into making all previous activities into
+     * non-fixed normal state (= will not be re-launched.)
+     *
+     * Do not change binder transaction number.
+     */
+    boolean startFixedActivityModeForDisplayAndUser(in Intent intent,
+            in Bundle activityOptionsBundle, int userId) = 0;
+    /**
+     * The activity lauched on the display is no longer in fixed mode. Re-launching or finishing
+     * should not trigger re-launfhing any more. Note that Activity for non-current user will
+     * be auto-stopped and there is no need to call this for user swiching. Note that this does not
+     * stop the activity but it will not be re-launched any more.
+     *
+     * Do not change binder transaction number.
+     */
+    void stopFixedActivityMode(int displayId) = 1;
+}
diff --git a/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java b/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java
index b692c02..3e463d1 100644
--- a/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java
+++ b/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java
@@ -22,6 +22,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.UserIdInt;
 import android.app.ActivityOptions;
 import android.app.Service;
 import android.car.Car;
@@ -82,6 +83,15 @@
  */
 @SystemApi
 public abstract class InstrumentClusterRenderingService extends Service {
+    /**
+     * Key to pass IInstrumentClusterHelper binder in onBind call {@link Intent} through extra
+     * {@link Bundle). Both extra bundle and binder itself use this key.
+     *
+     * @hide
+     */
+    public static final String EXTRA_BUNDLE_KEY_FOR_INSTRUMENT_CLUSTER_HELPER =
+            "android.car.cluster.renderer.IInstrumentClusterHelper";
+
     private static final String TAG = CarLibLog.TAG_CLUSTER;
 
     /**
@@ -93,16 +103,22 @@
     private static final int NAVIGATION_STATE_EVENT_ID = 1;
     private static final String BITMAP_QUERY_WIDTH = "w";
     private static final String BITMAP_QUERY_HEIGHT = "h";
+    private static final String BITMAP_QUERY_OFFLANESALPHA = "offLanesAlpha";
+
+    private final Handler mUiHandler = new Handler(Looper.getMainLooper());
 
     private final Object mLock = new Object();
+    // Main thread only
     private RendererBinder mRendererBinder;
-    private Handler mUiHandler = new Handler(Looper.getMainLooper());
     private ActivityOptions mActivityOptions;
     private ClusterActivityState mActivityState;
     private ComponentName mNavigationComponent;
     @GuardedBy("mLock")
     private ContextOwner mNavContextOwner;
 
+    @GuardedBy("mLock")
+    private IInstrumentClusterHelper mInstrumentClusterHelper;
+
     private static final int IMAGE_CACHE_SIZE_BYTES = 4 * 1024 * 1024; /* 4 mb */
     private final LruCache<String, Bitmap> mCache = new LruCache<String, Bitmap>(
             IMAGE_CACHE_SIZE_BYTES) {
@@ -164,6 +180,18 @@
             Log.d(TAG, "onBind, intent: " + intent);
         }
 
+        Bundle bundle = intent.getBundleExtra(EXTRA_BUNDLE_KEY_FOR_INSTRUMENT_CLUSTER_HELPER);
+        IBinder binder = null;
+        if (bundle != null) {
+            binder = bundle.getBinder(EXTRA_BUNDLE_KEY_FOR_INSTRUMENT_CLUSTER_HELPER);
+        }
+        if (binder == null) {
+            Log.wtf(TAG, "IInstrumentClusterHelper not passed through binder");
+        } else {
+            synchronized (mLock) {
+                mInstrumentClusterHelper = IInstrumentClusterHelper.Stub.asInterface(binder);
+            }
+        }
         if (mRendererBinder == null) {
             mRendererBinder = new RendererBinder(getNavigationRenderer());
         }
@@ -203,6 +231,76 @@
     public void onNavigationComponentReleased() {
     }
 
+    @Nullable
+    private IInstrumentClusterHelper getClusterHelper() {
+        synchronized (mLock) {
+            if (mInstrumentClusterHelper == null) {
+                Log.w("mInstrumentClusterHelper still null, should wait until onBind",
+                        new RuntimeException());
+            }
+            return mInstrumentClusterHelper;
+        }
+    }
+
+    /**
+     * Start Activity in fixed mode.
+     *
+     * <p>Activity launched in this way will stay visible across crash, package updatge
+     * or other Activity launch. So this should be carefully used for case like apps running
+     * in instrument cluster.</p>
+     *
+     * <p> Only one Activity can stay in this mode for a display and launching other Activity
+     * with this call means old one get out of the mode. Alternatively
+     * {@link #stopFixedActivityMode(int)} can be called to get the top activitgy out of this
+     * mode.</p>
+     *
+     * @param intent Should include specific {@code ComponentName}.
+     * @param options Should include target display.
+     * @param userId Target user id
+     * @return {@code true} if succeeded. {@code false} may mean the target component is not ready
+     *         or available. Note that failure can happen during early boot-up stage even if the
+     *         target Activity is in normal state and client should retry when it fails. Once it is
+     *         successfully launched, car service will guarantee that it is running across crash or
+     *         other events.
+     *
+     * @hide
+     */
+    protected boolean startFixedActivityModeForDisplayAndUser(@NonNull Intent intent,
+            @NonNull ActivityOptions options, @UserIdInt int userId) {
+        IInstrumentClusterHelper helper = getClusterHelper();
+        if (helper == null) {
+            return false;
+        }
+        try {
+            return helper.startFixedActivityModeForDisplayAndUser(intent, options.toBundle(),
+                    userId);
+        } catch (RemoteException e) {
+            Log.w("Remote exception from car service", e);
+            // Probably car service will restart and rebind. So do nothing.
+        }
+        return false;
+    }
+
+
+    /**
+     * Stop fixed mode for top Activity in the display. Crashing or launching other Activity
+     * will not re-launch the top Activity any more.
+     *
+     * @hide
+     */
+    protected void stopFixedActivityMode(int displayId) {
+        IInstrumentClusterHelper helper = getClusterHelper();
+        if (helper == null) {
+            return;
+        }
+        try {
+            helper.stopFixedActivityMode(displayId);
+        } catch (RemoteException e) {
+            Log.w("Remote exception from car service, displayId:" + displayId, e);
+            // Probably car service will restart and rebind. So do nothing.
+        }
+    }
+
     /**
      * Updates the cluster navigation activity by checking which activity to show (an activity of
      * the {@link #mNavContextOwner}). If not yet launched, it will do so.
@@ -379,16 +477,19 @@
     @CallSuper
     @Override
     protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
-        writer.println("**" + getClass().getSimpleName() + "**");
-        writer.println("renderer binder: " + mRendererBinder);
-        if (mRendererBinder != null) {
-            writer.println("navigation renderer: " + mRendererBinder.mNavigationRenderer);
+        synchronized (mLock) {
+            writer.println("**" + getClass().getSimpleName() + "**");
+            writer.println("renderer binder: " + mRendererBinder);
+            if (mRendererBinder != null) {
+                writer.println("navigation renderer: " + mRendererBinder.mNavigationRenderer);
+            }
+            writer.println("navigation focus owner: " + getNavigationContextOwner());
+            writer.println("activity options: " + mActivityOptions);
+            writer.println("activity state: " + mActivityState);
+            writer.println("current nav component: " + mNavigationComponent);
+            writer.println("current nav packages: " + getNavigationContextOwner().mPackageNames);
+            writer.println("mInstrumentClusterHelper" + mInstrumentClusterHelper);
         }
-        writer.println("navigation focus owner: " + getNavigationContextOwner());
-        writer.println("activity options: " + mActivityOptions);
-        writer.println("activity state: " + mActivityState);
-        writer.println("current nav component: " + mNavigationComponent);
-        writer.println("current nav packages: " + getNavigationContextOwner().mPackageNames);
     }
 
     private class RendererBinder extends IInstrumentCluster.Stub {
@@ -547,8 +648,18 @@
     }
 
     /**
+     * See {@link #getBitmap(Uri, int, int, float)}
+     *
+     * @hide
+     */
+    @Nullable
+    public Bitmap getBitmap(Uri uri, int width, int height) {
+        return getBitmap(uri, width, height, 1f);
+    }
+
+    /**
      * Fetches a bitmap from the navigation context owner (application holding navigation focus)
-     * of the given width and height. The fetched bitmaps are cached.
+     * of the given width and height and off lane opacity. The fetched bitmaps are cached.
      * It returns null if:
      * <ul>
      * <li>there is no navigation context owner
@@ -557,12 +668,16 @@
      * </ul>
      * This is a costly operation. Returned bitmaps should be fetched on a secondary thread.
      *
+     * @throws IllegalArgumentException if width, height <= 0, or 0 > offLanesAlpha > 1
      * @hide
      */
     @Nullable
-    public Bitmap getBitmap(Uri uri, int width, int height) throws InvalidSizeException {
+    public Bitmap getBitmap(Uri uri, int width, int height, float offLanesAlpha) {
         if (width <= 0 || height <= 0) {
-            throw new InvalidSizeException("Width and height must be > 0");
+            throw new IllegalArgumentException("Width and height must be > 0");
+        }
+        if (offLanesAlpha < 0 || offLanesAlpha > 1) {
+            throw new IllegalArgumentException("offLanesAlpha must be between [0, 1]");
         }
 
         try {
@@ -575,6 +690,7 @@
             uri = uri.buildUpon()
                     .appendQueryParameter(BITMAP_QUERY_WIDTH, String.valueOf(width))
                     .appendQueryParameter(BITMAP_QUERY_HEIGHT, String.valueOf(height))
+                    .appendQueryParameter(BITMAP_QUERY_OFFLANESALPHA, String.valueOf(offLanesAlpha))
                     .build();
 
             String host = uri.getHost();
diff --git a/car-lib/src/android/car/cluster/renderer/InvalidSizeException.java b/car-lib/src/android/car/cluster/renderer/InvalidSizeException.java
deleted file mode 100644
index b89cd9f..0000000
--- a/car-lib/src/android/car/cluster/renderer/InvalidSizeException.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.car.cluster.renderer;
-
-/**
- * Exception denoting invalid size of an object.
- * eg.) Minimum size of an image
- *
- * @hide
- */
-public class InvalidSizeException extends Exception {
-    public InvalidSizeException(String message) {
-        super(message);
-    }
-
-    public InvalidSizeException(String message, Throwable cause) {
-        super(message, cause);
-    }
-
-    public InvalidSizeException(Throwable cause) {
-        super(cause);
-    }
-}
diff --git a/car-lib/src/android/car/content/pm/CarAppBlockingPolicyService.java b/car-lib/src/android/car/content/pm/CarAppBlockingPolicyService.java
index 5b0a6bd..f95063a 100644
--- a/car-lib/src/android/car/content/pm/CarAppBlockingPolicyService.java
+++ b/car-lib/src/android/car/content/pm/CarAppBlockingPolicyService.java
@@ -17,6 +17,7 @@
 
 import android.annotation.SystemApi;
 import android.app.Service;
+import android.car.Car;
 import android.content.Intent;
 import android.os.Handler;
 import android.os.IBinder;
@@ -76,7 +77,7 @@
             try {
                 setter.setAppBlockingPolicy(policy);
             } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
+                Car.handleRemoteExceptionFromCarService(CarAppBlockingPolicyService.this, e);
             }
         }
     }
diff --git a/car-lib/src/android/car/content/pm/CarPackageManager.java b/car-lib/src/android/car/content/pm/CarPackageManager.java
index d23633d..7498c65 100644
--- a/car-lib/src/android/car/content/pm/CarPackageManager.java
+++ b/car-lib/src/android/car/content/pm/CarPackageManager.java
@@ -19,9 +19,9 @@
 import android.annotation.IntDef;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
+import android.car.Car;
 import android.car.CarManagerBase;
 import android.content.ComponentName;
-import android.content.Context;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
@@ -32,7 +32,7 @@
 /**
  * Provides car specific API related with package management.
  */
-public final class CarPackageManager implements CarManagerBase {
+public final class CarPackageManager extends CarManagerBase {
     private static final String TAG = "CarPackageManager";
 
     /**
@@ -70,12 +70,11 @@
     public @interface SetPolicyFlags {}
 
     private final ICarPackageManager mService;
-    private final Context mContext;
 
     /** @hide */
-    public CarPackageManager(IBinder service, Context context) {
+    public CarPackageManager(Car car, IBinder service) {
+        super(car);
         mService = ICarPackageManager.Stub.asInterface(service);
-        mContext = context;
     }
 
     /** @hide */
@@ -115,7 +114,7 @@
         try {
             mService.setAppBlockingPolicy(packageName, policy, flags);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -128,7 +127,7 @@
         try {
             mService.restartTask(taskId);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -151,7 +150,7 @@
         try {
             return mService.isActivityBackedBySafeActivity(activityName);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, false);
         }
     }
 
@@ -165,7 +164,7 @@
         try {
             mService.setEnableActivityBlocking(enable);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -181,7 +180,7 @@
         try {
             return mService.isActivityDistractionOptimized(packageName, className);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, false);
         }
     }
 
@@ -197,7 +196,7 @@
         try {
             return mService.isServiceDistractionOptimized(packageName, className);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, false);
         }
     }
 }
diff --git a/car-lib/src/android/car/diagnostic/CarDiagnosticManager.java b/car-lib/src/android/car/diagnostic/CarDiagnosticManager.java
index 1559dd4..c9c8b6f 100644
--- a/car-lib/src/android/car/diagnostic/CarDiagnosticManager.java
+++ b/car-lib/src/android/car/diagnostic/CarDiagnosticManager.java
@@ -23,8 +23,6 @@
 import android.car.CarLibLog;
 import android.car.CarManagerBase;
 import android.car.diagnostic.ICarDiagnosticEventListener.Stub;
-import android.content.Context;
-import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
@@ -47,7 +45,7 @@
  * @hide
  */
 @SystemApi
-public final class CarDiagnosticManager implements CarManagerBase {
+public final class CarDiagnosticManager extends CarManagerBase {
     public static final int FRAME_TYPE_LIVE = 0;
     public static final int FRAME_TYPE_FREEZE = 1;
 
@@ -70,15 +68,16 @@
     /** Handles call back into clients. */
     private final SingleMessageHandler<CarDiagnosticEvent> mHandlerCallback;
 
-    private CarDiagnosticEventListenerToService mListenerToService;
+    private final CarDiagnosticEventListenerToService mListenerToService;
 
     private final CarPermission mVendorExtensionPermission;
 
     /** @hide */
-    public CarDiagnosticManager(IBinder service, Context context, Handler handler) {
+    public CarDiagnosticManager(Car car, IBinder service) {
+        super(car);
         mService = ICarDiagnostic.Stub.asInterface(service);
-        mHandlerCallback = new SingleMessageHandler<CarDiagnosticEvent>(handler.getLooper(),
-            MSG_DIAGNOSTIC_EVENTS) {
+        mHandlerCallback = new SingleMessageHandler<CarDiagnosticEvent>(
+                getEventHandler().getLooper(), MSG_DIAGNOSTIC_EVENTS) {
             @Override
             protected void handleEvent(CarDiagnosticEvent event) {
                 CarDiagnosticListeners listeners;
@@ -90,7 +89,9 @@
                 }
             }
         };
-        mVendorExtensionPermission = new CarPermission(context, Car.PERMISSION_VENDOR_EXTENSION);
+        mVendorExtensionPermission = new CarPermission(getContext(),
+                Car.PERMISSION_VENDOR_EXTENSION);
+        mListenerToService = new CarDiagnosticEventListenerToService(this);
     }
 
     @Override
@@ -98,7 +99,6 @@
     public void onCarDisconnected() {
         synchronized(mActiveListeners) {
             mActiveListeners.clear();
-            mListenerToService = null;
         }
     }
 
@@ -137,9 +137,6 @@
             OnDiagnosticEventListener listener, @FrameType int frameType, int rate) {
         assertFrameType(frameType);
         synchronized(mActiveListeners) {
-            if (null == mListenerToService) {
-                mListenerToService = new CarDiagnosticEventListenerToService(this);
-            }
             boolean needsServerUpdate = false;
             CarDiagnosticListeners listeners = mActiveListeners.get(frameType);
             if (listeners == null) {
@@ -184,7 +181,8 @@
                     mService.unregisterDiagnosticListener(frameType,
                         mListenerToService);
                 } catch (RemoteException e) {
-                    throw e.rethrowFromSystemServer();
+                    handleRemoteExceptionFromCarService(e);
+                    // continue for local clean-up
                 }
                 mActiveListeners.remove(frameType);
             } else if (needsServerUpdate) {
@@ -197,7 +195,7 @@
         try {
             return mService.registerOrUpdateDiagnosticListener(frameType, rate, mListenerToService);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, false);
         }
     }
 
@@ -212,7 +210,7 @@
         try {
             return mService.getLatestLiveFrame();
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, null);
         }
     }
 
@@ -229,7 +227,7 @@
         try {
             return mService.getFreezeFrameTimestamps();
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, new long[0]);
         }
     }
 
@@ -246,7 +244,7 @@
         try {
             return mService.getFreezeFrame(timestamp);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, null);
         }
     }
 
@@ -264,7 +262,7 @@
         try {
             return mService.clearFreezeFrames(timestamps);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, false);
         }
     }
 
@@ -276,7 +274,7 @@
         try {
             return mService.isLiveFrameSupported();
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, false);
         }
     }
 
@@ -288,7 +286,7 @@
         try {
             return mService.isFreezeFrameNotificationSupported();
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, false);
         }
     }
 
@@ -300,7 +298,7 @@
         try {
             return mService.isGetFreezeFrameSupported();
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, false);
         }
     }
 
@@ -318,7 +316,7 @@
         try {
             return mService.isClearFreezeFramesSupported();
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, false);
         }
     }
 
@@ -336,7 +334,7 @@
         try {
             return mService.isSelectiveClearFreezeFramesSupported();
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, false);
         }
     }
 
diff --git a/car-lib/src/android/car/drivingstate/CarDrivingStateManager.java b/car-lib/src/android/car/drivingstate/CarDrivingStateManager.java
index 9b0626f..4053c5c 100644
--- a/car-lib/src/android/car/drivingstate/CarDrivingStateManager.java
+++ b/car-lib/src/android/car/drivingstate/CarDrivingStateManager.java
@@ -22,7 +22,6 @@
 import android.annotation.TestApi;
 import android.car.Car;
 import android.car.CarManagerBase;
-import android.content.Context;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -40,13 +39,12 @@
  */
 @SystemApi
 @TestApi
-public final class CarDrivingStateManager implements CarManagerBase {
+public final class CarDrivingStateManager extends CarManagerBase {
     private static final String TAG = "CarDrivingStateMgr";
     private static final boolean DBG = false;
     private static final boolean VDBG = false;
     private static final int MSG_HANDLE_DRIVING_STATE_CHANGE = 0;
 
-    private final Context mContext;
     private final ICarDrivingState mDrivingService;
     private final EventCallbackHandler mEventCallbackHandler;
     private CarDrivingStateEventListener mDrvStateEventListener;
@@ -54,10 +52,10 @@
 
 
     /** @hide */
-    public CarDrivingStateManager(IBinder service, Context context, Handler handler) {
-        mContext = context;
+    public CarDrivingStateManager(Car car, IBinder service) {
+        super(car);
         mDrivingService = ICarDrivingState.Stub.asInterface(service);
-        mEventCallbackHandler = new EventCallbackHandler(this, handler.getLooper());
+        mEventCallbackHandler = new EventCallbackHandler(this, getEventHandler().getLooper());
     }
 
     /** @hide */
@@ -111,7 +109,7 @@
             // register to the Service for getting notified
             mDrivingService.registerDrivingStateChangeListener(mListenerToService);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -134,7 +132,7 @@
             mDrvStateEventListener = null;
             mListenerToService = null;
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -151,7 +149,7 @@
         try {
             return mDrivingService.getCurrentDrivingState();
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, null);
         }
     }
 
@@ -172,7 +170,7 @@
         try {
             mDrivingService.injectDrivingState(event);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
diff --git a/car-lib/src/android/car/drivingstate/CarUxRestrictionsManager.java b/car-lib/src/android/car/drivingstate/CarUxRestrictionsManager.java
index 9a3d5cf..be194b8 100644
--- a/car-lib/src/android/car/drivingstate/CarUxRestrictionsManager.java
+++ b/car-lib/src/android/car/drivingstate/CarUxRestrictionsManager.java
@@ -22,7 +22,6 @@
 import android.annotation.RequiresPermission;
 import android.car.Car;
 import android.car.CarManagerBase;
-import android.content.Context;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -43,7 +42,7 @@
  * API to register and get the User Experience restrictions imposed based on the car's driving
  * state.
  */
-public final class CarUxRestrictionsManager implements CarManagerBase {
+public final class CarUxRestrictionsManager extends CarManagerBase {
     private static final String TAG = "CarUxRManager";
     private static final boolean DBG = false;
     private static final boolean VDBG = false;
@@ -80,7 +79,6 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface UxRestrictionMode {}
 
-    private final Context mContext;
     private int mDisplayId = Display.INVALID_DISPLAY;
     private final ICarUxRestrictionsManager mUxRService;
     private final EventCallbackHandler mEventCallbackHandler;
@@ -89,10 +87,11 @@
     private CarUxRestrictionsChangeListenerToService mListenerToService;
 
     /** @hide */
-    public CarUxRestrictionsManager(IBinder service, Context context, Handler handler) {
-        mContext = context;
+    public CarUxRestrictionsManager(Car car, IBinder service) {
+        super(car);
         mUxRService = ICarUxRestrictionsManager.Stub.asInterface(service);
-        mEventCallbackHandler = new EventCallbackHandler(this, handler.getLooper());
+        mEventCallbackHandler = new EventCallbackHandler(this,
+                getEventHandler().getLooper());
     }
 
     /** @hide */
@@ -152,7 +151,7 @@
             // register to the Service to listen for changes.
             mUxRService.registerUxRestrictionsChangeListener(mListenerToService, displayId);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -172,7 +171,7 @@
         try {
             mUxRService.unregisterUxRestrictionsChangeListener(mListenerToService);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -197,7 +196,7 @@
         try {
             return mUxRService.saveUxRestrictionsConfigurationForNextBoot(configs);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, false);
         }
     }
 
@@ -219,7 +218,7 @@
         try {
             return mUxRService.getCurrentUxRestrictions(displayId);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, null);
         }
     }
 
@@ -233,7 +232,7 @@
         try {
             return mUxRService.setRestrictionMode(mode);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, false);
         }
     }
 
@@ -248,7 +247,7 @@
         try {
             return mUxRService.getRestrictionMode();
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, null);
         }
     }
 
@@ -288,7 +287,7 @@
         try {
             return mUxRService.getStagedConfigs();
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, null);
         }
     }
 
@@ -304,7 +303,7 @@
         try {
             return mUxRService.getConfigs();
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, null);
         }
     }
 
@@ -399,7 +398,7 @@
             return mDisplayId;
         }
 
-        mDisplayId = mContext.getDisplayId();
+        mDisplayId = getContext().getDisplayId();
         Log.i(TAG, "Context returns display ID " + mDisplayId);
 
         if (mDisplayId == Display.INVALID_DISPLAY) {
diff --git a/car-lib/src/android/car/hardware/CarSensorManager.java b/car-lib/src/android/car/hardware/CarSensorManager.java
index d61cb2e..082c8eb 100644
--- a/car-lib/src/android/car/hardware/CarSensorManager.java
+++ b/car-lib/src/android/car/hardware/CarSensorManager.java
@@ -26,9 +26,7 @@
 import android.car.hardware.property.CarPropertyManager;
 import android.car.hardware.property.CarPropertyManager.CarPropertyEventCallback;
 import android.car.hardware.property.ICarProperty;
-import android.content.Context;
 import android.os.Bundle;
-import android.os.Handler;
 import android.os.IBinder;
 import android.util.ArraySet;
 import android.util.Log;
@@ -46,7 +44,7 @@
  *  API for monitoring car sensor data.
  */
 @Deprecated
-public final class CarSensorManager implements CarManagerBase {
+public final class CarSensorManager extends CarManagerBase {
     private static final String TAG = "CarSensorManager";
     private final CarPropertyManager mCarPropertyMgr;
     /** @hide */
@@ -304,9 +302,10 @@
 
     }
     /** @hide */
-    public CarSensorManager(IBinder service, Context context, Handler handler) {
+    public CarSensorManager(Car car, IBinder service) {
+        super(car);
         ICarProperty mCarPropertyService = ICarProperty.Stub.asInterface(service);
-        mCarPropertyMgr = new CarPropertyManager(mCarPropertyService, handler);
+        mCarPropertyMgr = new CarPropertyManager(car, mCarPropertyService);
     }
 
     /** @hide */
diff --git a/car-lib/src/android/car/hardware/CarVendorExtensionManager.java b/car-lib/src/android/car/hardware/CarVendorExtensionManager.java
index e7df3b0..b796156 100644
--- a/car-lib/src/android/car/hardware/CarVendorExtensionManager.java
+++ b/car-lib/src/android/car/hardware/CarVendorExtensionManager.java
@@ -22,7 +22,6 @@
 import android.car.hardware.property.CarPropertyManager;
 import android.car.hardware.property.CarPropertyManager.CarPropertyEventCallback;
 import android.car.hardware.property.ICarProperty;
-import android.os.Handler;
 import android.os.IBinder;
 import android.util.ArraySet;
 
@@ -44,7 +43,7 @@
  */
 @Deprecated
 @SystemApi
-public final class CarVendorExtensionManager implements CarManagerBase {
+public final class CarVendorExtensionManager extends CarManagerBase {
 
     private final static boolean DBG = false;
     private final static String TAG = CarVendorExtensionManager.class.getSimpleName();
@@ -84,9 +83,10 @@
      * <p>Should not be obtained directly by clients, use {@link Car#getCarManager(String)} instead.
      * @hide
      */
-    public CarVendorExtensionManager(IBinder service, Handler handler) {
+    public CarVendorExtensionManager(Car car, IBinder service) {
+        super(car);
         ICarProperty mCarPropertyService = ICarProperty.Stub.asInterface(service);
-        mPropertyManager = new CarPropertyManager(mCarPropertyService, handler);
+        mPropertyManager = new CarPropertyManager(car, mCarPropertyService);
     }
 
     /**
@@ -206,6 +206,9 @@
     /** @hide */
     @Override
     public void onCarDisconnected() {
+        synchronized (mLock) {
+            mCallbacks.clear();
+        }
         mPropertyManager.onCarDisconnected();
     }
     private static class CarPropertyEventListenerToBase implements CarPropertyEventCallback {
diff --git a/car-lib/src/android/car/hardware/cabin/CarCabinManager.java b/car-lib/src/android/car/hardware/cabin/CarCabinManager.java
index 1c41a2b..7318176 100644
--- a/car-lib/src/android/car/hardware/cabin/CarCabinManager.java
+++ b/car-lib/src/android/car/hardware/cabin/CarCabinManager.java
@@ -25,8 +25,6 @@
 import android.car.hardware.property.CarPropertyManager;
 import android.car.hardware.property.CarPropertyManager.CarPropertyEventCallback;
 import android.car.hardware.property.ICarProperty;
-import android.content.Context;
-import android.os.Handler;
 import android.os.IBinder;
 import android.util.ArraySet;
 
@@ -58,7 +56,7 @@
  */
 @Deprecated
 @SystemApi
-public final class CarCabinManager implements CarManagerBase {
+public final class CarCabinManager extends CarManagerBase {
     private final static boolean DBG = false;
     private final static String TAG = "CarCabinManager";
     private final CarPropertyManager mCarPropertyMgr;
@@ -470,9 +468,10 @@
      * @param handler
      * @hide
      */
-    public CarCabinManager(IBinder service, Context context, Handler handler) {
+    public CarCabinManager(Car car, IBinder service) {
+        super(car);
         ICarProperty mCarPropertyService = ICarProperty.Stub.asInterface(service);
-        mCarPropertyMgr = new CarPropertyManager(mCarPropertyService, handler);
+        mCarPropertyMgr = new CarPropertyManager(car, mCarPropertyService);
     }
 
     /**
@@ -594,6 +593,10 @@
     /** @hide */
     @Override
     public void onCarDisconnected() {
+        // TODO(b/142730969) Fix synchronization to use separate mLock
+        synchronized (this) {
+            mCallbacks.clear();
+        }
         mCarPropertyMgr.onCarDisconnected();
     }
 }
diff --git a/car-lib/src/android/car/hardware/hvac/CarHvacManager.java b/car-lib/src/android/car/hardware/hvac/CarHvacManager.java
index b2b8014..3ab7631 100644
--- a/car-lib/src/android/car/hardware/hvac/CarHvacManager.java
+++ b/car-lib/src/android/car/hardware/hvac/CarHvacManager.java
@@ -25,8 +25,6 @@
 import android.car.hardware.property.CarPropertyManager;
 import android.car.hardware.property.CarPropertyManager.CarPropertyEventCallback;
 import android.car.hardware.property.ICarProperty;
-import android.content.Context;
-import android.os.Handler;
 import android.os.IBinder;
 import android.util.ArraySet;
 import android.util.Log;
@@ -46,7 +44,7 @@
  */
 @Deprecated
 @SystemApi
-public final class CarHvacManager implements CarManagerBase {
+public final class CarHvacManager extends CarManagerBase {
     private final static boolean DBG = false;
     private final static String TAG = "CarHvacManager";
     private final CarPropertyManager mCarPropertyMgr;
@@ -301,14 +299,15 @@
      *
      * Should not be obtained directly by clients, use {@link Car#getCarManager(String)} instead.
      * @param service
-     * @param context
-     * @param handler
+     *
      * @hide
      */
-    public CarHvacManager(IBinder service, Context context, Handler handler) {
+    public CarHvacManager(Car car, IBinder service) {
+        super(car);
         ICarProperty mCarPropertyService = ICarProperty.Stub.asInterface(service);
-        mCarPropertyMgr = new CarPropertyManager(mCarPropertyService, handler);
+        mCarPropertyMgr = new CarPropertyManager(car, mCarPropertyService);
     }
+
     /**
      * Implement wrappers for contained CarPropertyManager object
      * @param callback
@@ -432,6 +431,10 @@
 
     /** @hide */
     public void onCarDisconnected() {
+        // TODO(b/142730482) Fix synchronization to use separate mLock
+        synchronized (this) {
+            mCallbacks.clear();
+        }
         mCarPropertyMgr.onCarDisconnected();
     }
 }
diff --git a/car-lib/src/android/car/hardware/power/CarPowerManager.java b/car-lib/src/android/car/hardware/power/CarPowerManager.java
index 3d9a23a..4b0e8cf 100644
--- a/car-lib/src/android/car/hardware/power/CarPowerManager.java
+++ b/car-lib/src/android/car/hardware/power/CarPowerManager.java
@@ -19,8 +19,6 @@
 import android.annotation.SystemApi;
 import android.car.Car;
 import android.car.CarManagerBase;
-import android.content.Context;
-import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
@@ -35,15 +33,18 @@
  * @hide
  */
 @SystemApi
-public class CarPowerManager implements CarManagerBase {
+public class CarPowerManager extends CarManagerBase {
     private final static boolean DBG = false;
     private final static String TAG = "CarPowerManager";
 
     private final Object mLock = new Object();
     private final ICarPower mService;
 
+    @GuardedBy("mLock")
     private CarPowerStateListener mListener;
+    @GuardedBy("mLock")
     private CarPowerStateListenerWithCompletion mListenerWithCompletion;
+    @GuardedBy("mLock")
     private CompletableFuture<Void> mFuture;
     @GuardedBy("mLock")
     private ICarPowerStateListener mListenerToService;
@@ -131,7 +132,8 @@
      * @param handler
      * @hide
      */
-    public CarPowerManager(IBinder service, Context context, Handler handler) {
+    public CarPowerManager(Car car, IBinder service) {
+        super(car);
         mService = ICarPower.Stub.asInterface(service);
     }
 
@@ -143,7 +145,7 @@
         try {
             mService.requestShutdownOnNextSuspend();
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -155,7 +157,7 @@
         try {
             mService.scheduleNextWakeupTime(seconds);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -209,13 +211,23 @@
                 @Override
                 public void onStateChanged(int state) throws RemoteException {
                     if (useCompletion) {
-                        // Update CompletableFuture. This will recreate it or just clean it up.
-                        updateFuture(state);
+                        CarPowerStateListenerWithCompletion listenerWithCompletion;
+                        CompletableFuture<Void> future;
+                        synchronized (mLock) {
+                            // Update CompletableFuture. This will recreate it or just clean it up.
+                            updateFutureLocked(state);
+                            listenerWithCompletion = mListenerWithCompletion;
+                            future = mFuture;
+                        }
                         // Notify user that the state has changed and supply a future
-                        mListenerWithCompletion.onStateChanged(state, mFuture);
+                        listenerWithCompletion.onStateChanged(state, future);
                     } else {
+                        CarPowerStateListener listener;
+                        synchronized (mLock) {
+                            listener = mListener;
+                        }
                         // Notify the user without supplying a future
-                        mListener.onStateChanged(state);
+                        listener.onStateChanged(state);
                     }
                 }
             };
@@ -227,7 +239,7 @@
                 }
                 mListenerToService = listenerToService;
             } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
+                handleRemoteExceptionFromCarService(e);
             }
         }
     }
@@ -243,7 +255,7 @@
             mListenerToService = null;
             mListener = null;
             mListenerWithCompletion = null;
-            cleanupFuture();
+            cleanupFutureLocked();
         }
 
         if (listenerToService == null) {
@@ -254,12 +266,12 @@
         try {
             mService.unregisterListener(listenerToService);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
-    private void updateFuture(int state) {
-        cleanupFuture();
+    private void updateFutureLocked(int state) {
+        cleanupFutureLocked();
         if (state == CarPowerStateListener.SHUTDOWN_PREPARE) {
             // Create a CompletableFuture and pass it to the listener.
             // When the listener completes the future, tell
@@ -269,16 +281,20 @@
                 if (exception != null && !(exception instanceof CancellationException)) {
                     Log.e(TAG, "Exception occurred while waiting for future", exception);
                 }
+                ICarPowerStateListener listenerToService;
+                synchronized (mLock) {
+                    listenerToService = mListenerToService;
+                }
                 try {
-                    mService.finished(mListenerToService);
+                    mService.finished(listenerToService);
                 } catch (RemoteException e) {
-                    throw e.rethrowFromSystemServer();
+                    handleRemoteExceptionFromCarService(e);
                 }
             });
         }
     }
 
-    private void cleanupFuture() {
+    private void cleanupFutureLocked() {
         if (mFuture != null) {
             if (!mFuture.isDone()) {
                 mFuture.cancel(false);
@@ -290,13 +306,9 @@
     /** @hide */
     @Override
     public void onCarDisconnected() {
-        ICarPowerStateListener listenerToService;
         synchronized (mLock) {
-            listenerToService = mListenerToService;
-        }
-
-        if (listenerToService != null) {
-            clearListener();
+            mListener = null;
+            mListenerWithCompletion = null;
         }
     }
 }
diff --git a/car-lib/src/android/car/hardware/property/CarPropertyManager.java b/car-lib/src/android/car/hardware/property/CarPropertyManager.java
index 3f7da1d..e3651da 100644
--- a/car-lib/src/android/car/hardware/property/CarPropertyManager.java
+++ b/car-lib/src/android/car/hardware/property/CarPropertyManager.java
@@ -21,6 +21,7 @@
 import android.annotation.FloatRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.car.Car;
 import android.car.CarManagerBase;
 import android.car.hardware.CarPropertyConfig;
 import android.car.hardware.CarPropertyValue;
@@ -44,7 +45,7 @@
  * For details about the individual properties, see the descriptions in
  * hardware/interfaces/automotive/vehicle/types.hal
  */
-public class CarPropertyManager implements CarManagerBase {
+public class CarPropertyManager extends CarManagerBase {
     private static final boolean DBG = false;
     private static final String TAG = "CarPropertyManager";
     private static final int MSG_GENERIC_EVENT = 0;
@@ -93,11 +94,12 @@
      * Get an instance of the CarPropertyManager.
      *
      * Should not be obtained directly by clients, use {@link Car#getCarManager(String)} instead.
+     * @param car Car instance
      * @param service ICarProperty instance
-     * @param handler The handler to deal with CarPropertyEvent.
      * @hide
      */
-    public CarPropertyManager(@NonNull ICarProperty service, @Nullable Handler handler) {
+    public CarPropertyManager(Car car, @NonNull ICarProperty service) {
+        super(car);
         mService = service;
         try {
             List<CarPropertyConfig> configs = mService.getPropertyList();
@@ -108,11 +110,12 @@
             Log.e(TAG, "getPropertyList exception ", e);
             throw new RuntimeException(e);
         }
-        if (handler == null) {
+        Handler eventHandler = getEventHandler();
+        if (eventHandler == null) {
             mHandler = null;
             return;
         }
-        mHandler = new SingleMessageHandler<CarPropertyEvent>(handler.getLooper(),
+        mHandler = new SingleMessageHandler<CarPropertyEvent>(eventHandler.getLooper(),
             MSG_GENERIC_EVENT) {
             @Override
             protected void handleEvent(CarPropertyEvent event) {
@@ -206,7 +209,7 @@
         try {
             mService.registerListener(propertyId, rate, mCarPropertyEventToService);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, false);
         }
         return true;
     }
@@ -274,7 +277,8 @@
                 try {
                     mService.unregisterListener(propertyId, mCarPropertyEventToService);
                 } catch (RemoteException e) {
-                    throw e.rethrowFromSystemServer();
+                    handleRemoteExceptionFromCarService(e);
+                    // continue for local clean-up
                 }
                 mActivePropertyListener.remove(propertyId);
             } else if (needsServerUpdate) {
@@ -327,7 +331,7 @@
         try {
             return mService.getReadPermission(propId);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, "");
         }
     }
 
@@ -346,7 +350,7 @@
         try {
             return mService.getWritePermission(propId);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, "");
         }
     }
 
@@ -363,7 +367,7 @@
             return (propValue != null)
                     && (propValue.getStatus() == CarPropertyValue.STATUS_AVAILABLE);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, false);
         }
     }
 
@@ -449,7 +453,7 @@
             }
             return propVal;
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, null);
         }
     }
 
@@ -466,7 +470,7 @@
             CarPropertyValue<E> propVal = mService.getProperty(propId, areaId);
             return propVal;
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, null);
         }
     }
 
@@ -488,7 +492,7 @@
         try {
             mService.setProperty(new CarPropertyValue<>(propId, areaId, val));
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
diff --git a/car-lib/src/android/car/input/CarInputHandlingService.java b/car-lib/src/android/car/input/CarInputHandlingService.java
index 518cee1..0ea990f 100644
--- a/car-lib/src/android/car/input/CarInputHandlingService.java
+++ b/car-lib/src/android/car/input/CarInputHandlingService.java
@@ -19,6 +19,7 @@
 import android.annotation.MainThread;
 import android.annotation.SystemApi;
 import android.app.Service;
+import android.car.Car;
 import android.car.CarLibLog;
 import android.content.Intent;
 import android.os.Bundle;
@@ -101,7 +102,7 @@
         try {
             callbackBinder.transact(INPUT_CALLBACK_BINDER_CODE, dataIn, null, IBinder.FLAG_ONEWAY);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            Car.handleRemoteExceptionFromCarService(this, e);
         }
     }
 
diff --git a/car-lib/src/android/car/media/CarAudioManager.java b/car-lib/src/android/car/media/CarAudioManager.java
index dcd4514..2bc8fd7 100644
--- a/car-lib/src/android/car/media/CarAudioManager.java
+++ b/car-lib/src/android/car/media/CarAudioManager.java
@@ -22,10 +22,8 @@
 import android.car.Car;
 import android.car.CarLibLog;
 import android.car.CarManagerBase;
-import android.content.Context;
 import android.media.AudioAttributes;
 import android.os.Bundle;
-import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
@@ -52,7 +50,7 @@
  * - There is exactly one audio zone, which is the primary zone
  * - Each volume group represents a controllable STREAM_TYPE, same as AudioManager
  */
-public final class CarAudioManager implements CarManagerBase {
+public final class CarAudioManager extends CarManagerBase {
 
     /**
      * Zone id of the primary audio zone.
@@ -114,7 +112,7 @@
         try {
             return mService.isDynamicRoutingEnabled();
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, false);
         }
     }
 
@@ -147,7 +145,7 @@
         try {
             mService.setGroupVolume(zoneId, groupId, index, flags);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -177,7 +175,7 @@
         try {
             return mService.getGroupMaxVolume(zoneId, groupId);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, 0);
         }
     }
 
@@ -207,7 +205,7 @@
         try {
             return mService.getGroupMinVolume(zoneId, groupId);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, 0);
         }
     }
 
@@ -240,7 +238,7 @@
         try {
             return mService.getGroupVolume(zoneId, groupId);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, 0);
         }
     }
 
@@ -259,7 +257,7 @@
         try {
             mService.setFadeTowardFront(value);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -278,7 +276,7 @@
         try {
             mService.setBalanceTowardRight(value);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -300,7 +298,8 @@
         try {
             return mService.getExternalSources();
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
+            return new String[0];
         }
     }
 
@@ -330,7 +329,7 @@
         try {
             return mService.createAudioPatch(sourceAddress, usage, gainInMillibels);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, null);
         }
     }
 
@@ -350,7 +349,7 @@
         try {
             mService.releaseAudioPatch(patch);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -379,7 +378,7 @@
         try {
             return mService.getVolumeGroupCount(zoneId);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, 0);
         }
     }
 
@@ -409,7 +408,7 @@
         try {
             return mService.getVolumeGroupIdForUsage(zoneId, usage);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, 0);
         }
     }
 
@@ -436,7 +435,7 @@
         try {
             return mService.getAudioZoneIds();
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, new int[0]);
         }
     }
 
@@ -453,7 +452,7 @@
         try {
             return mService.getZoneIdForUid(uid);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, 0);
         }
     }
 
@@ -470,7 +469,7 @@
         try {
             return mService.setZoneIdForUid(zoneId, uid);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, false);
         }
     }
 
@@ -486,7 +485,7 @@
         try {
             return mService.clearZoneIdForUid(uid);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, false);
         }
     }
 
@@ -523,7 +522,7 @@
         try {
             return mService.getZoneIdForDisplayPortId(displayPortId);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, 0);
         }
     }
 
@@ -541,7 +540,7 @@
         try {
             return mService.getUsagesForVolumeGroupId(zoneId, groupId);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, new int[0]);
         }
     }
 
@@ -552,16 +551,16 @@
             try {
                 mService.unregisterVolumeCallback(mCarVolumeCallbackImpl.asBinder());
             } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
+                handleRemoteExceptionFromCarService(e);
             }
         }
     }
 
     /** @hide */
-    public CarAudioManager(IBinder service, Context context, Handler handler) {
+    public CarAudioManager(Car car, IBinder service) {
+        super(car);
         mService = ICarAudio.Stub.asInterface(service);
         mCarVolumeCallbacks = new ArrayList<>();
-
         try {
             mService.registerVolumeCallback(mCarVolumeCallbackImpl.asBinder());
         } catch (RemoteException e) {
diff --git a/car-lib/src/android/car/media/CarMediaManager.java b/car-lib/src/android/car/media/CarMediaManager.java
index 12c2dc8..8537ed6 100644
--- a/car-lib/src/android/car/media/CarMediaManager.java
+++ b/car-lib/src/android/car/media/CarMediaManager.java
@@ -29,7 +29,7 @@
  * API for updating and receiving updates to the primary media source in the car.
  * @hide
  */
-public final class CarMediaManager implements CarManagerBase {
+public final class CarMediaManager extends CarManagerBase {
 
     private final ICarMedia mService;
     private Map<MediaSourceChangedListener, ICarMediaSourceListener> mCallbackMap = new HashMap();
@@ -40,7 +40,8 @@
      * Should not be obtained directly by clients, use {@link Car#getCarManager(String)} instead.
      * @hide
      */
-    public CarMediaManager(IBinder service) {
+    public CarMediaManager(Car car, IBinder service) {
+        super(car);
         mService = ICarMedia.Stub.asInterface(service);
     }
 
@@ -67,7 +68,7 @@
         try {
             return mService.getMediaSource();
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, null);
         }
     }
 
@@ -81,7 +82,7 @@
         try {
             mService.setMediaSource(componentName);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -102,7 +103,7 @@
             mCallbackMap.put(callback, binderCallback);
             mService.registerMediaSourceListener(binderCallback);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -117,12 +118,14 @@
             ICarMediaSourceListener binderCallback = mCallbackMap.remove(callback);
             mService.unregisterMediaSourceListener(binderCallback);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
     /** @hide */
     @Override
     public synchronized void onCarDisconnected() {
+        // TODO(b/142733057) Fix synchronization to use separate mLock
+        mCallbackMap.clear();
     }
 }
diff --git a/car-lib/src/android/car/navigation/CarNavigationStatusManager.java b/car-lib/src/android/car/navigation/CarNavigationStatusManager.java
index a70e3c8..2aa2f10 100644
--- a/car-lib/src/android/car/navigation/CarNavigationStatusManager.java
+++ b/car-lib/src/android/car/navigation/CarNavigationStatusManager.java
@@ -24,14 +24,13 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.util.Log;
 
 /**
  * API for providing navigation status for instrument cluster.
  * @hide
  */
 @SystemApi
-public final class CarNavigationStatusManager implements CarManagerBase {
+public final class CarNavigationStatusManager extends CarManagerBase {
     private static final String TAG = CarLibLog.TAG_NAV;
 
     private final IInstrumentClusterNavigation mService;
@@ -40,7 +39,8 @@
      * Only for CarServiceLoader
      * @hide
      */
-    public CarNavigationStatusManager(IBinder service) {
+    public CarNavigationStatusManager(Car car, IBinder service) {
+        super(car);
         mService = IInstrumentClusterNavigation.Stub.asInterface(service);
     }
 
@@ -67,14 +67,13 @@
         try {
             mService.onNavigationStateChanged(bundle);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
     /** @hide */
     @Override
     public void onCarDisconnected() {
-        Log.e(TAG, "Car service disconnected");
     }
 
     /** Returns navigation features of instrument cluster */
@@ -83,7 +82,7 @@
         try {
             return mService.getInstrumentClusterInfo();
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, null);
         }
     }
 }
diff --git a/car-lib/src/android/car/settings/CarConfigurationManager.java b/car-lib/src/android/car/settings/CarConfigurationManager.java
index 34d5f4a..626ad39 100644
--- a/car-lib/src/android/car/settings/CarConfigurationManager.java
+++ b/car-lib/src/android/car/settings/CarConfigurationManager.java
@@ -16,6 +16,7 @@
 
 package android.car.settings;
 
+import android.car.Car;
 import android.car.CarManagerBase;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -23,13 +24,14 @@
 /**
  * Manager that exposes car configuration values that are stored on the system.
  */
-public class CarConfigurationManager implements CarManagerBase {
+public class CarConfigurationManager extends CarManagerBase {
     private static final String TAG = "CarConfigurationManager";
 
     private final ICarConfigurationManager mConfigurationService;
 
     /** @hide */
-    public CarConfigurationManager(IBinder service) {
+    public CarConfigurationManager(Car car, IBinder service) {
+        super(car);
         mConfigurationService = ICarConfigurationManager.Stub.asInterface(service);
     }
 
@@ -42,7 +44,7 @@
         try {
             return mConfigurationService.getSpeedBumpConfiguration();
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, null);
         }
     }
 
diff --git a/car-lib/src/android/car/storagemonitoring/CarStorageMonitoringManager.java b/car-lib/src/android/car/storagemonitoring/CarStorageMonitoringManager.java
index ff7b099..69c092b 100644
--- a/car-lib/src/android/car/storagemonitoring/CarStorageMonitoringManager.java
+++ b/car-lib/src/android/car/storagemonitoring/CarStorageMonitoringManager.java
@@ -19,7 +19,6 @@
 import android.annotation.SystemApi;
 import android.car.Car;
 import android.car.CarManagerBase;
-import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
 
@@ -37,7 +36,7 @@
  * @hide
  */
 @SystemApi
-public final class CarStorageMonitoringManager implements CarManagerBase {
+public final class CarStorageMonitoringManager extends CarManagerBase {
     private static final String TAG = CarStorageMonitoringManager.class.getSimpleName();
     private static final int MSG_IO_STATS_EVENT = 0;
 
@@ -77,9 +76,10 @@
     /**
      * @hide
      */
-    public CarStorageMonitoringManager(IBinder service, Handler handler) {
+    public CarStorageMonitoringManager(Car car, IBinder service) {
+        super(car);
         mService = ICarStorageMonitoring.Stub.asInterface(service);
-        mMessageHandler = new SingleMessageHandler<IoStats>(handler, MSG_IO_STATS_EVENT) {
+        mMessageHandler = new SingleMessageHandler<IoStats>(getEventHandler(), MSG_IO_STATS_EVENT) {
             @Override
             protected void handleEvent(IoStats event) {
                 for (IoStatsListener listener : mListeners) {
@@ -112,7 +112,7 @@
         try {
             return mService.getPreEolIndicatorStatus();
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, PRE_EOL_INFO_UNKNOWN);
         }
     }
 
@@ -130,7 +130,7 @@
         try {
             return mService.getWearEstimate();
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, null);
         }
     }
 
@@ -150,7 +150,7 @@
         try {
             return mService.getWearEstimateHistory();
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, Collections.emptyList());
         }
     }
 
@@ -169,7 +169,7 @@
         try {
             return mService.getBootIoStats();
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, Collections.emptyList());
         }
     }
 
@@ -199,7 +199,7 @@
         try {
             return mService.getShutdownDiskWriteAmount();
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, 0);
         }
     }
 
@@ -216,7 +216,7 @@
         try {
             return mService.getAggregateIoStats();
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, Collections.emptyList());
         }
     }
 
@@ -236,7 +236,7 @@
         try {
             return mService.getIoStatsDeltas();
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, Collections.emptyList());
         }
     }
 
@@ -259,7 +259,7 @@
             }
             mListeners.add(listener);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -277,7 +277,7 @@
                 mListenerToService = null;
             }
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 }
diff --git a/car-lib/src/android/car/test/CarTestManagerBinderWrapper.java b/car-lib/src/android/car/test/CarTestManagerBinderWrapper.java
index 5e39ea3..0a167cf 100644
--- a/car-lib/src/android/car/test/CarTestManagerBinderWrapper.java
+++ b/car-lib/src/android/car/test/CarTestManagerBinderWrapper.java
@@ -15,6 +15,7 @@
  */
 package android.car.test;
 
+import android.car.Car;
 import android.car.CarManagerBase;
 import android.os.IBinder;
 
@@ -22,10 +23,17 @@
  * Only for system testing
  * @hide
  */
-public class CarTestManagerBinderWrapper implements CarManagerBase {
+public class CarTestManagerBinderWrapper extends CarManagerBase {
     public final IBinder binder;
 
     public CarTestManagerBinderWrapper(IBinder binder) {
+        super(null); // This will not work safely but is only for keeping API.
+        this.binder = binder;
+    }
+
+    /** @hide */
+    public CarTestManagerBinderWrapper(Car car, IBinder binder) {
+        super(car);
         this.binder = binder;
     }
 
diff --git a/car-lib/src/android/car/trust/CarTrustAgentEnrollmentManager.java b/car-lib/src/android/car/trust/CarTrustAgentEnrollmentManager.java
index c82d515..9881420 100644
--- a/car-lib/src/android/car/trust/CarTrustAgentEnrollmentManager.java
+++ b/car-lib/src/android/car/trust/CarTrustAgentEnrollmentManager.java
@@ -24,8 +24,8 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.bluetooth.BluetoothDevice;
+import android.car.Car;
 import android.car.CarManagerBase;
-import android.content.Context;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -39,6 +39,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
+import java.util.Collections;
 import java.util.List;
 
 
@@ -67,7 +68,7 @@
  * @hide
  */
 @SystemApi
-public final class CarTrustAgentEnrollmentManager implements CarManagerBase {
+public final class CarTrustAgentEnrollmentManager extends CarManagerBase {
     private static final String TAG = "CarTrustEnrollMgr";
     private static final String KEY_HANDLE = "handle";
     private static final String KEY_ACTIVE = "active";
@@ -81,7 +82,6 @@
     private static final int MSG_ENROLL_TOKEN_STATE_CHANGED = 7;
     private static final int MSG_ENROLL_TOKEN_REMOVED = 8;
 
-    private final Context mContext;
     private final ICarTrustAgentEnrollment mEnrollmentService;
     private Object mListenerLock = new Object();
     @GuardedBy("mListenerLock")
@@ -114,10 +114,10 @@
 
 
     /** @hide */
-    public CarTrustAgentEnrollmentManager(IBinder service, Context context, Handler handler) {
-        mContext = context;
+    public CarTrustAgentEnrollmentManager(Car car, IBinder service) {
+        super(car);
         mEnrollmentService = ICarTrustAgentEnrollment.Stub.asInterface(service);
-        mEventCallbackHandler = new EventCallbackHandler(this, handler.getLooper());
+        mEventCallbackHandler = new EventCallbackHandler(this, getEventHandler().getLooper());
     }
 
     /** @hide */
@@ -134,7 +134,7 @@
         try {
             mEnrollmentService.startEnrollmentAdvertising();
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -146,7 +146,7 @@
         try {
             mEnrollmentService.stopEnrollmentAdvertising();
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -161,7 +161,7 @@
         try {
             mEnrollmentService.enrollmentHandshakeAccepted(device);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -173,7 +173,7 @@
         try {
             mEnrollmentService.terminateEnrollmentHandshake();
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -194,7 +194,7 @@
         try {
             return mEnrollmentService.isEscrowTokenActive(handle, uid);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, false);
         }
     }
 
@@ -209,7 +209,7 @@
         try {
             mEnrollmentService.removeEscrowToken(handle, uid);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -223,7 +223,7 @@
         try {
             mEnrollmentService.removeAllTrustedDevices(uid);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -238,7 +238,7 @@
         try {
             mEnrollmentService.setTrustedDeviceEnrollmentEnabled(isEnabled);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -253,7 +253,7 @@
         try {
             mEnrollmentService.setTrustedDeviceUnlockEnabled(isEnabled);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -278,7 +278,7 @@
                     mEnrollmentService.registerEnrollmentCallback(mListenerToEnrollmentService);
                     mEnrollmentCallback = callback;
                 } catch (RemoteException e) {
-                    throw e.rethrowFromSystemServer();
+                    handleRemoteExceptionFromCarService(e);
                 }
             }
         }
@@ -290,7 +290,7 @@
                 try {
                     mEnrollmentService.unregisterEnrollmentCallback(mListenerToEnrollmentService);
                 } catch (RemoteException e) {
-                    throw e.rethrowFromSystemServer();
+                    handleRemoteExceptionFromCarService(e);
                 }
                 mEnrollmentCallback = null;
             }
@@ -318,7 +318,7 @@
                     mEnrollmentService.registerBleCallback(mListenerToBleService);
                     mBleCallback = callback;
                 } catch (RemoteException e) {
-                    throw e.rethrowFromSystemServer();
+                    handleRemoteExceptionFromCarService(e);
                 }
             }
         }
@@ -330,7 +330,7 @@
                 try {
                     mEnrollmentService.unregisterBleCallback(mListenerToBleService);
                 } catch (RemoteException e) {
-                    throw e.rethrowFromSystemServer();
+                    handleRemoteExceptionFromCarService(e);
                 }
                 mBleCallback = null;
             }
@@ -351,7 +351,7 @@
         try {
             return mEnrollmentService.getEnrolledDeviceInfosForUser(uid);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, Collections.emptyList());
         }
     }
 
diff --git a/car-lib/src/android/car/vms/VmsPublisherClientService.java b/car-lib/src/android/car/vms/VmsPublisherClientService.java
index 309d0ee..ea75707 100644
--- a/car-lib/src/android/car/vms/VmsPublisherClientService.java
+++ b/car-lib/src/android/car/vms/VmsPublisherClientService.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.app.Service;
+import android.car.Car;
 import android.content.Intent;
 import android.os.Binder;
 import android.os.Build;
@@ -114,7 +115,7 @@
         try {
             mVmsPublisherService.publish(token, layer, publisherId, payload);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            Car.handleRemoteExceptionFromCarService(this, e);
         }
     }
 
@@ -134,7 +135,7 @@
             mVmsPublisherService.setLayersOffering(token, offering);
             VmsOperationRecorder.get().setLayersOffering(offering);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            Car.handleRemoteExceptionFromCarService(this, e);
         }
     }
 
@@ -172,6 +173,7 @@
             publisherId = mVmsPublisherService.getPublisherId(publisherInfo);
             Log.i(TAG, "Assigned publisher ID: " + publisherId);
         } catch (RemoteException e) {
+            // This will crash. To prevent crash, safer invalid return value should be defined.
             throw e.rethrowFromSystemServer();
         }
         VmsOperationRecorder.get().getPublisherId(publisherId);
@@ -191,7 +193,7 @@
         try {
             return mVmsPublisherService.getSubscriptions();
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return Car.handleRemoteExceptionFromCarService(this, e, null);
         }
     }
 
diff --git a/car-lib/src/android/car/vms/VmsSubscriberManager.java b/car-lib/src/android/car/vms/VmsSubscriberManager.java
index c02d3a5..edde982 100644
--- a/car-lib/src/android/car/vms/VmsSubscriberManager.java
+++ b/car-lib/src/android/car/vms/VmsSubscriberManager.java
@@ -19,6 +19,7 @@
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.car.Car;
 import android.car.CarManagerBase;
 import android.os.Binder;
 import android.os.IBinder;
@@ -39,7 +40,7 @@
  * @hide
  */
 @SystemApi
-public final class VmsSubscriberManager implements CarManagerBase {
+public final class VmsSubscriberManager extends CarManagerBase {
     private static final String TAG = "VmsSubscriberManager";
 
     private final IVmsSubscriberService mVmsSubscriberService;
@@ -75,7 +76,8 @@
      *
      * @hide
      */
-    public VmsSubscriberManager(IBinder service) {
+    public VmsSubscriberManager(Car car, IBinder service) {
+        super(car);
         mVmsSubscriberService = IVmsSubscriberService.Stub.asInterface(service);
         mSubscriberManagerClient = new IVmsSubscriberClient.Stub() {
             @Override
@@ -133,7 +135,7 @@
         try {
             mVmsSubscriberService.addVmsSubscriberToNotifications(mSubscriberManagerClient);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -148,7 +150,7 @@
         try {
             mVmsSubscriberService.removeVmsSubscriberToNotifications(mSubscriberManagerClient);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         } finally {
             synchronized (mClientCallbackLock) {
                 mClientCallback = null;
@@ -168,7 +170,7 @@
         try {
             return mVmsSubscriberService.getPublisherInfo(publisherId);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, null);
         }
     }
 
@@ -182,7 +184,7 @@
         try {
             return mVmsSubscriberService.getAvailableLayers();
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            return handleRemoteExceptionFromCarService(e, null);
         }
     }
 
@@ -199,7 +201,7 @@
             mVmsSubscriberService.addVmsSubscriber(mSubscriberManagerClient, layer);
             VmsOperationRecorder.get().subscribe(layer);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -218,7 +220,7 @@
                     mSubscriberManagerClient, layer, publisherId);
             VmsOperationRecorder.get().subscribe(layer, publisherId);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -231,7 +233,7 @@
             mVmsSubscriberService.addVmsSubscriberPassive(mSubscriberManagerClient);
             VmsOperationRecorder.get().startMonitoring();
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -248,7 +250,7 @@
             mVmsSubscriberService.removeVmsSubscriber(mSubscriberManagerClient, layer);
             VmsOperationRecorder.get().unsubscribe(layer);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -266,7 +268,7 @@
                     mSubscriberManagerClient, layer, publisherId);
             VmsOperationRecorder.get().unsubscribe(layer, publisherId);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -278,7 +280,7 @@
             mVmsSubscriberService.removeVmsSubscriberPassive(mSubscriberManagerClient);
             VmsOperationRecorder.get().stopMonitoring();
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -326,5 +328,9 @@
      */
     @Override
     public void onCarDisconnected() {
+        synchronized (mClientCallbackLock) {
+            mClientCallback = null;
+            mExecutor = null;
+        }
     }
 }
diff --git a/car-systemtest-lib/src/android/car/test/CarTestManager.java b/car-systemtest-lib/src/android/car/test/CarTestManager.java
index 52b01a6..3ee1067 100644
--- a/car-systemtest-lib/src/android/car/test/CarTestManager.java
+++ b/car-systemtest-lib/src/android/car/test/CarTestManager.java
@@ -27,18 +27,19 @@
  * @hide
  */
 @SystemApi
-public final class CarTestManager implements CarManagerBase {
+public final class CarTestManager extends CarManagerBase {
 
     private final ICarTest mService;
 
 
-    public CarTestManager(IBinder carServiceBinder) {
+    public CarTestManager(Car car, IBinder carServiceBinder) {
+        super(car);
         mService = ICarTest.Stub.asInterface(carServiceBinder);
     }
 
     @Override
     public void onCarDisconnected() {
-        // should not happen for embedded
+        // test will fail. nothing to do.
     }
 
     /**
@@ -52,7 +53,7 @@
         try {
             mService.stopCarService(token);
         } catch (RemoteException e) {
-            handleRemoteException(e);
+            handleRemoteExceptionFromCarService(e);
         }
     }
 
@@ -66,12 +67,7 @@
         try {
             mService.startCarService(token);
         } catch (RemoteException e) {
-            handleRemoteException(e);
+            handleRemoteExceptionFromCarService(e);
         }
     }
-
-    private static void handleRemoteException(RemoteException e) {
-        // let test fail
-        throw new RuntimeException(e);
-    }
 }
diff --git a/car_product/sepolicy/private/bluetooth.te b/car_product/sepolicy/private/bluetooth.te
new file mode 100644
index 0000000..6ba74c2
--- /dev/null
+++ b/car_product/sepolicy/private/bluetooth.te
@@ -0,0 +1 @@
+allow bluetooth mediametrics_service:service_manager find;
diff --git a/car_product/sepolicy/public/property_contexts b/car_product/sepolicy/public/property_contexts
index 8dbe0bc..9646ac9 100644
--- a/car_product/sepolicy/public/property_contexts
+++ b/car_product/sepolicy/public/property_contexts
@@ -1 +1,3 @@
+android.car.number_pre_created_guests            u:object_r:car_bootuser_prop:s0
+android.car.number_pre_created_users             u:object_r:car_bootuser_prop:s0
 android.car.systemuser.bootuseroverrideid        u:object_r:car_bootuser_prop:s0
diff --git a/service/AndroidManifest.xml b/service/AndroidManifest.xml
index ebea889..e2db410 100644
--- a/service/AndroidManifest.xml
+++ b/service/AndroidManifest.xml
@@ -475,6 +475,16 @@
         android:label="@string/car_permission_label_enroll_trust"
         android:description="@string/car_permission_desc_enroll_trust" />
 
+    <!-- Allows a test application to control car service's testing mode.
+         This is only for platform level testing.
+         <p>Protection level: signature|privileged
+    -->
+    <permission
+        android:name="android.car.permission.CAR_TEST_SERVICE"
+        android:protectionLevel="signature|privileged"
+        android:label="@string/car_permission_label_car_test_service"
+        android:description="@string/car_permission_desc_car_test_service" />
+
     <uses-permission android:name="android.permission.CALL_PHONE" />
     <uses-permission android:name="android.permission.DEVICE_POWER" />
     <uses-permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS" />
diff --git a/service/res/values/config.xml b/service/res/values/config.xml
index 3eb1007..81e0620 100644
--- a/service/res/values/config.xml
+++ b/service/res/values/config.xml
@@ -20,10 +20,6 @@
 <!-- Resources to configure car service based on each OEM's preference. -->
 
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Configuration to enable media center to autoplay when the media source is changed.
-         If this is set to true, media will play automatically when a media source is selected and
-         the selected media source supports media playback. -->
-    <bool name="autoPlayOnMediaSourceChanged">false</bool>
 
     <!--  Configuration to enable usage of dynamic audio routing. If this is set to false,
           dynamic audio routing is disabled and audio works in legacy mode. It may be useful
@@ -236,4 +232,13 @@
          resolve permission by itself to use any higher priority window type.
          Setting this string to empty will disable the feature. -->
     <string name="config_userNoticeUiService" translatable="false">com.google.android.car.kitchensink/.UserNoiticeDemoUiService</string>
+
+    <!-- Configuration to enable media center to autoplay when the media source is changed.
+         There are 3 supported configurations:
+         0 - never play on change
+         1 - always play
+         2 - adaptive, play based on last remembered playback state -->
+    <integer name="config_mediaSourceChangedAutoplay">2</integer>
+    <!-- Configuration to enable media center to autoplay on boot -->
+    <integer name="config_mediaBootAutoplay">2</integer>
 </resources>
diff --git a/service/res/values/strings.xml b/service/res/values/strings.xml
index ef731b8..1589180 100644
--- a/service/res/values/strings.xml
+++ b/service/res/values/strings.xml
@@ -256,6 +256,11 @@
     <string name="car_permission_label_enroll_trust">Enroll Trusted Device</string>
     <string name="car_permission_desc_enroll_trust">Allow Trusted Device Enrollment</string>
 
+    <!-- Permission text: Control car's test mode [CHAR LIMIT=NONE] -->
+    <string name="car_permission_label_car_test_service">Control car\u2019s test mode</string>
+    <!-- Permission text: Control car's test mode [CHAR LIMIT=NONE] -->
+    <string name="car_permission_desc_car_test_service">Control car\u2019s test mode</string>
+
     <!-- The default name of device enrolled as trust device [CHAR LIMIT=NONE] -->
     <string name="trust_device_default_name">My Device</string>
 
diff --git a/service/src/com/android/car/CarDrivingStateService.java b/service/src/com/android/car/CarDrivingStateService.java
index 584228d..66a2a7c 100644
--- a/service/src/com/android/car/CarDrivingStateService.java
+++ b/service/src/com/android/car/CarDrivingStateService.java
@@ -30,11 +30,15 @@
 import android.content.Context;
 import android.hardware.automotive.vehicle.V2_0.VehicleGear;
 import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.io.PrintWriter;
 import java.util.LinkedList;
 import java.util.List;
@@ -60,6 +64,8 @@
             VehicleProperty.PERF_VEHICLE_SPEED,
             VehicleProperty.GEAR_SELECTION,
             VehicleProperty.PARKING_BRAKE_ON};
+    private final HandlerThread mClientDispatchThread;
+    private final Handler mClientDispatchHandler;
     private CarDrivingStateEvent mCurrentDrivingState;
     // For dumpsys logging
     private final LinkedList<Utils.TransitionLog> mTransitionLogs = new LinkedList<>();
@@ -75,6 +81,9 @@
         mContext = context;
         mPropertyService = propertyService;
         mCurrentDrivingState = createDrivingStateEvent(CarDrivingStateEvent.DRIVING_STATE_UNKNOWN);
+        mClientDispatchThread = new HandlerThread("ClientDispatchThread");
+        mClientDispatchThread.start();
+        mClientDispatchHandler = new Handler(mClientDispatchThread.getLooper());
     }
 
     @Override
@@ -316,7 +325,8 @@
      * Handle events coming from {@link CarPropertyService}.  Compute the driving state, map it to
      * the corresponding UX Restrictions and dispatch the events to the registered clients.
      */
-    private synchronized void handlePropertyEvent(CarPropertyEvent event) {
+    @VisibleForTesting
+    synchronized void handlePropertyEvent(CarPropertyEvent event) {
         if (event.getEventType() != CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE) {
             return;
         }
@@ -387,9 +397,13 @@
             if (DBG) {
                 Log.d(TAG, "dispatching to " + mDrivingStateClients.size() + " clients");
             }
-            for (DrivingStateClient client : mDrivingStateClients) {
-                client.dispatchEventToClients(mCurrentDrivingState);
-            }
+            // Dispatch to clients on a separate thread to prevent a deadlock
+            final CarDrivingStateEvent currentDrivingStateEvent = mCurrentDrivingState;
+            mClientDispatchHandler.post(() -> {
+                for (DrivingStateClient client : mDrivingStateClients) {
+                    client.dispatchEventToClients(currentDrivingStateEvent);
+                }
+            });
         }
     }
 
diff --git a/service/src/com/android/car/CarLocalServices.java b/service/src/com/android/car/CarLocalServices.java
index 6c756b8..ea1b6f1 100644
--- a/service/src/com/android/car/CarLocalServices.java
+++ b/service/src/com/android/car/CarLocalServices.java
@@ -17,6 +17,7 @@
 package com.android.car;
 
 import android.annotation.Nullable;
+import android.car.Car;
 import android.car.hardware.power.CarPowerManager;
 import android.content.Context;
 import android.util.ArrayMap;
@@ -90,10 +91,12 @@
      */
     @Nullable
     public static CarPowerManager createCarPowerManager(Context context) {
+        // This does not require connection as binder will be passed to CarPowerManager directly.
+        Car car = new Car(context, /* service= */null, /* handler= */ null);
         CarPowerManagementService service = getService(CarPowerManagementService.class);
         if (service == null) {
             return null;
         }
-        return new CarPowerManager(service, context, null);
+        return new CarPowerManager(car, service);
     }
 }
diff --git a/service/src/com/android/car/CarMediaService.java b/service/src/com/android/car/CarMediaService.java
index 4841b0d..b69e666 100644
--- a/service/src/com/android/car/CarMediaService.java
+++ b/service/src/com/android/car/CarMediaService.java
@@ -77,6 +77,13 @@
     private static final String SHARED_PREF = "com.android.car.media.car_media_service";
     private static final String COMPONENT_NAME_SEPARATOR = ",";
     private static final String MEDIA_CONNECTION_ACTION = "com.android.car.media.MEDIA_CONNECTION";
+    private static final String EXTRA_AUTOPLAY = "com.android.car.media.autoplay";
+
+    // XML configuration options for autoplay on media source change.
+    private static final int AUTOPLAY_CONFIG_NEVER = 0;
+    private static final int AUTOPLAY_CONFIG_ALWAYS = 1;
+    // This mode uses the last stored playback state to determine whether to resume playback
+    private static final int AUTOPLAY_CONFIG_ADAPTIVE = 2;
 
     private final Context mContext;
     private final UserManager mUserManager;
@@ -89,8 +96,8 @@
     // null if playback has not been started yet.
     private MediaController mActiveUserMediaController;
     private SessionChangedListener mSessionsListener;
-    private boolean mStartPlayback;
-    private boolean mPlayOnMediaSourceChanged;
+    private int mPlayOnMediaSourceChangedConfig;
+    private int mPlayOnBootConfig;
 
     private boolean mPendingInit;
     private int mCurrentUser;
@@ -175,8 +182,9 @@
         userSwitchFilter.addAction(Intent.ACTION_USER_SWITCHED);
         mContext.registerReceiver(mUserSwitchReceiver, userSwitchFilter);
 
-        mPlayOnMediaSourceChanged =
-                mContext.getResources().getBoolean(R.bool.autoPlayOnMediaSourceChanged);
+        mPlayOnMediaSourceChangedConfig =
+                mContext.getResources().getInteger(R.integer.config_mediaSourceChangedAutoplay);
+        mPlayOnBootConfig = mContext.getResources().getInteger(R.integer.config_mediaBootAutoplay);
         mCurrentUser = ActivityManager.getCurrentUser();
         updateMediaSessionCallbackForCurrentUser();
     }
@@ -200,22 +208,34 @@
                 mPackageUpdateFilter, null, null);
         mIsPackageUpdateReceiverRegistered = true;
 
-        mPrimaryMediaComponent = getLastMediaSource();
+        mPrimaryMediaComponent =
+                isCurrentUserEphemeral() ? getDefaultMediaSource() : getLastMediaSource();
         mActiveUserMediaController = null;
-        String key = PLAYBACK_STATE_KEY + mCurrentUser;
-        mStartPlayback =
-                mSharedPrefs.getInt(key, PlaybackState.STATE_NONE) == PlaybackState.STATE_PLAYING;
+
         updateMediaSessionCallbackForCurrentUser();
         notifyListeners();
 
-        // Start a service on the current user that binds to the media browser of the current media
-        // source. We start a new service because this one runs on user 0, and MediaBrowser doesn't
-        // provide an API to connect on a specific user.
+        startMediaConnectorService(shouldStartPlayback(mPlayOnBootConfig), currentUser);
+    }
+
+    /**
+     * Starts a service on the current user that binds to the media browser of the current media
+     * source. We start a new service because this one runs on user 0, and MediaBrowser doesn't
+     * provide an API to connect on a specific user. Additionally, this service will attempt to
+     * resume playback using the MediaSession obtained via the media browser connection, which
+     * is more reliable than using active MediaSessions from MediaSessionManager.
+     */
+    private void startMediaConnectorService(boolean startPlayback, UserHandle currentUser) {
         Intent serviceStart = new Intent(MEDIA_CONNECTION_ACTION);
         serviceStart.setPackage(mContext.getResources().getString(R.string.serviceMediaConnection));
+        serviceStart.putExtra(EXTRA_AUTOPLAY, startPlayback);
         mContext.startForegroundServiceAsUser(serviceStart, currentUser);
     }
 
+    private boolean isCurrentUserEphemeral() {
+        return mUserManager.getUserInfo(mCurrentUser).isEphemeral();
+    }
+
     @Override
     public void release() {
         mMediaSessionUpdater.unregisterCallbacks();
@@ -297,10 +317,9 @@
                 if (!unlocked) {
                     return;
                 }
-                // No need to handle user0, non current foreground user, or ephemeral user.
+                // No need to handle user0, non current foreground user.
                 if (userHandle == UserHandle.USER_SYSTEM
-                        || userHandle != ActivityManager.getCurrentUser()
-                        || mUserManager.getUserInfo(userHandle).isEphemeral()) {
+                        || userHandle != ActivityManager.getCurrentUser()) {
                     return;
                 }
                 if (mPendingInit) {
@@ -474,7 +493,6 @@
 
         stop();
 
-        mStartPlayback = mPlayOnMediaSourceChanged;
         mPreviousMediaComponent = mPrimaryMediaComponent;
         mPrimaryMediaComponent = componentName;
         updateActiveMediaController(mMediaSessionManager
@@ -483,7 +501,9 @@
         if (mSharedPrefs != null) {
             if (mPrimaryMediaComponent != null && !TextUtils.isEmpty(
                     mPrimaryMediaComponent.flattenToString())) {
-                saveLastMediaSource(mPrimaryMediaComponent);
+                if (!isCurrentUserEphemeral()) {
+                    saveLastMediaSource(mPrimaryMediaComponent);
+                }
                 mRemovedMediaSourcePackage = null;
             }
         } else {
@@ -491,6 +511,9 @@
             Log.e(CarLog.TAG_MEDIA, "Error trying to save last media source, prefs uninitialized");
         }
         notifyListeners();
+        if (shouldStartPlayback(mPlayOnMediaSourceChangedConfig)) {
+            startMediaConnectorService(true, new UserHandle(mCurrentUser));
+        }
     }
 
     private void notifyListeners() {
@@ -509,9 +532,9 @@
     private MediaController.Callback mMediaControllerCallback = new MediaController.Callback() {
         @Override
         public void onPlaybackStateChanged(PlaybackState state) {
-            savePlaybackState(state);
-            // Try to start playback if the new state allows the play action
-            maybeRestartPlayback(state);
+            if (!isCurrentUserEphemeral()) {
+                savePlaybackState(state);
+            }
         }
     };
 
@@ -637,7 +660,10 @@
                 }
             }
         }
+        return getDefaultMediaSource();
+    }
 
+    private ComponentName getDefaultMediaSource() {
         String defaultMediaSource = mContext.getString(R.string.default_media_source);
         ComponentName defaultComponent = ComponentName.unflattenFromString(defaultMediaSource);
         if (isMediaService(defaultComponent)) {
@@ -657,23 +683,17 @@
 
     private void savePlaybackState(PlaybackState playbackState) {
         int state = playbackState != null ? playbackState.getState() : PlaybackState.STATE_NONE;
-        if (state == PlaybackState.STATE_PLAYING) {
-            // No longer need to request play if audio was resumed already via some other means,
-            // e.g. Assistant starts playback, user uses hardware button, etc.
-            mStartPlayback = false;
-        }
         if (mSharedPrefs != null) {
-            String key = PLAYBACK_STATE_KEY + mCurrentUser;
+            String key = getPlaybackStateKey();
             mSharedPrefs.edit().putInt(key, state).apply();
         }
     }
 
-    private void maybeRestartPlayback(PlaybackState state) {
-        if (mStartPlayback && state != null
-                && (state.getActions() & PlaybackState.ACTION_PLAY) != 0) {
-            play();
-            mStartPlayback = false;
-        }
+    /**
+     * Builds a string key for saving the playback state for a specific media source (and user)
+     */
+    private String getPlaybackStateKey() {
+        return PLAYBACK_STATE_KEY + mCurrentUser + mPrimaryMediaComponent.flattenToString();
     }
 
     /**
@@ -691,19 +711,39 @@
         for (MediaController controller : mediaControllers) {
             if (matchPrimaryMediaSource(controller.getPackageName(), getClassName(controller))) {
                 mActiveUserMediaController = controller;
+                PlaybackState state = mActiveUserMediaController.getPlaybackState();
+                if (!isCurrentUserEphemeral()) {
+                    savePlaybackState(state);
+                }
                 // Specify Handler to receive callbacks on, to avoid defaulting to the calling
                 // thread; this method can be called from the MediaSessionManager callback.
                 // Using the version of this method without passing a handler causes a
                 // RuntimeException for failing to create a Handler.
-                PlaybackState state = mActiveUserMediaController.getPlaybackState();
-                savePlaybackState(state);
                 mActiveUserMediaController.registerCallback(mMediaControllerCallback, mHandler);
-                maybeRestartPlayback(state);
                 return;
             }
         }
     }
 
+    /**
+     * Returns whether we should autoplay the current media source
+     */
+    private boolean shouldStartPlayback(int config) {
+        switch (config) {
+            case AUTOPLAY_CONFIG_NEVER:
+                return false;
+            case AUTOPLAY_CONFIG_ALWAYS:
+                return true;
+            case AUTOPLAY_CONFIG_ADAPTIVE:
+                return mSharedPrefs.getInt(getPlaybackStateKey(), PlaybackState.STATE_NONE)
+                        == PlaybackState.STATE_PLAYING;
+            default:
+                Log.e(CarLog.TAG_MEDIA, "Unsupported playback configuration: " + config);
+                return false;
+        }
+
+    }
+
     @NonNull
     private static String getClassName(@NonNull MediaController controller) {
         Bundle sessionExtras = controller.getExtras();
diff --git a/service/src/com/android/car/CarProjectionService.java b/service/src/com/android/car/CarProjectionService.java
index 057682f..02f5c3d 100644
--- a/service/src/com/android/car/CarProjectionService.java
+++ b/service/src/com/android/car/CarProjectionService.java
@@ -645,6 +645,11 @@
             public void onStopped() {
                 Log.i(TAG, "Local-only hotspot stopped.");
                 synchronized (mLock) {
+                    if (mLocalOnlyHotspotReservation != null) {
+                        // We must explicitly released old reservation object, otherwise it may
+                        // unexpectedly stop LOHS later because it overrode finalize() method.
+                        mLocalOnlyHotspotReservation.close();
+                    }
                     mLocalOnlyHotspotReservation = null;
                 }
                 sendApStopped();
diff --git a/service/src/com/android/car/CarPropertyService.java b/service/src/com/android/car/CarPropertyService.java
index 3b4e388..c9a14c9 100644
--- a/service/src/com/android/car/CarPropertyService.java
+++ b/service/src/com/android/car/CarPropertyService.java
@@ -18,6 +18,7 @@
 
 import static java.lang.Integer.toHexString;
 
+import android.car.Car;
 import android.car.hardware.CarPropertyConfig;
 import android.car.hardware.CarPropertyValue;
 import android.car.hardware.property.CarPropertyEvent;
@@ -348,6 +349,10 @@
             return;
         }
         ICarImpl.assertPermission(mContext, mHal.getWritePermission(propId));
+        // need an extra permission for writing display units properties.
+        if (mHal.isDisplayUnitsProperty(propId)) {
+            ICarImpl.assertPermission(mContext, Car.PERMISSION_VENDOR_EXTENSION);
+        }
         mHal.setProperty(prop);
     }
 
diff --git a/service/src/com/android/car/CarService.java b/service/src/com/android/car/CarService.java
index 509ecdd..fdc4995 100644
--- a/service/src/com/android/car/CarService.java
+++ b/service/src/com/android/car/CarService.java
@@ -25,6 +25,7 @@
 import android.os.Build;
 import android.os.IBinder;
 import android.os.IHwBinder.DeathRecipient;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
@@ -41,6 +42,8 @@
 
 public class CarService extends Service {
 
+    private static final boolean RESTART_CAR_SERVICE_WHEN_VHAL_CRASH = true;
+
     private static final long WAIT_FOR_VEHICLE_HAL_TIMEOUT_MS = 10_000;
 
     private static final boolean IS_USER_BUILD = "user".equals(Build.TYPE);
@@ -178,7 +181,13 @@
 
         @Override
         public void serviceDied(long cookie) {
-            Log.w(CarLog.TAG_SERVICE, "Vehicle HAL died.");
+            if (RESTART_CAR_SERVICE_WHEN_VHAL_CRASH) {
+                Log.wtf(CarLog.TAG_SERVICE, "***Vehicle HAL died. Car service will restart***");
+                Process.killProcess(Process.myPid());
+                return;
+            }
+
+            Log.wtf(CarLog.TAG_SERVICE, "***Vehicle HAL died.***");
 
             try {
                 mVehicle.unlinkToDeath(this);
diff --git a/service/src/com/android/car/CarTestService.java b/service/src/com/android/car/CarTestService.java
index 8c3f64d..776fd53 100644
--- a/service/src/com/android/car/CarTestService.java
+++ b/service/src/com/android/car/CarTestService.java
@@ -22,6 +22,8 @@
 import android.os.RemoteException;
 import android.util.Log;
 
+import com.android.internal.annotations.GuardedBy;
+
 import java.io.PrintWriter;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -39,6 +41,9 @@
     private final Context mContext;
     private final ICarImpl mICarImpl;
 
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
     private final Map<IBinder, TokenDeathRecipient> mTokens = new HashMap<>();
 
     CarTestService(Context context, ICarImpl carImpl) {
@@ -69,7 +74,7 @@
         Log.d(TAG, "stopCarService, token: " + token);
         ICarImpl.assertPermission(mContext, Car.PERMISSION_CAR_TEST_SERVICE);
 
-        synchronized (this) {
+        synchronized (mLock) {
             if (mTokens.containsKey(token)) {
                 Log.w(TAG, "Calling stopCarService twice with the same token.");
                 return;
@@ -80,7 +85,7 @@
             token.linkToDeath(deathRecipient, 0);
 
             if (mTokens.size() == 1) {
-                mICarImpl.release();
+                CarServiceUtils.runOnMainSync(mICarImpl::release);
             }
         }
     }
@@ -92,15 +97,17 @@
         releaseToken(token);
     }
 
-    private synchronized void releaseToken(IBinder token) {
+    private void releaseToken(IBinder token) {
         Log.d(TAG, "releaseToken, token: " + token);
-        DeathRecipient deathRecipient = mTokens.remove(token);
-        if (deathRecipient != null) {
-            token.unlinkToDeath(deathRecipient, 0);
-        }
+        synchronized (mLock) {
+            DeathRecipient deathRecipient = mTokens.remove(token);
+            if (deathRecipient != null) {
+                token.unlinkToDeath(deathRecipient, 0);
+            }
 
-        if (mTokens.size() == 0) {
-            CarServiceUtils.runOnMain(mICarImpl::init);
+            if (mTokens.size() == 0) {
+                CarServiceUtils.runOnMainSync(mICarImpl::init);
+            }
         }
     }
 
diff --git a/service/src/com/android/car/ICarImpl.java b/service/src/com/android/car/ICarImpl.java
index fe7c4d6..3c6129e 100644
--- a/service/src/com/android/car/ICarImpl.java
+++ b/service/src/com/android/car/ICarImpl.java
@@ -37,6 +37,7 @@
 import android.util.Slog;
 import android.util.TimingsTraceLog;
 
+import com.android.car.am.FixedActivityService;
 import com.android.car.audio.CarAudioService;
 import com.android.car.cluster.InstrumentClusterService;
 import com.android.car.garagemode.GarageModeService;
@@ -80,6 +81,7 @@
     private final CarPropertyService mCarPropertyService;
     private final CarNightService mCarNightService;
     private final AppFocusService mAppFocusService;
+    private final FixedActivityService mFixedActivityService;
     private final GarageModeService mGarageModeService;
     private final InstrumentClusterService mInstrumentClusterService;
     private final CarLocationService mCarLocationService;
@@ -149,6 +151,7 @@
         mAppFocusService = new AppFocusService(serviceContext, mSystemActivityMonitoringService);
         mCarAudioService = new CarAudioService(serviceContext);
         mCarNightService = new CarNightService(serviceContext, mCarPropertyService);
+        mFixedActivityService = new FixedActivityService(serviceContext);
         mInstrumentClusterService = new InstrumentClusterService(serviceContext,
                 mAppFocusService, mCarInputService);
         mSystemStateControllerService = new SystemStateControllerService(
@@ -177,6 +180,7 @@
         CarLocalServices.addService(SystemInterface.class, mSystemInterface);
         CarLocalServices.addService(CarDrivingStateService.class, mCarDrivingStateService);
         CarLocalServices.addService(PerUserCarServiceHelper.class, mPerUserCarServiceHelper);
+        CarLocalServices.addService(FixedActivityService.class, mFixedActivityService);
 
         // Be careful with order. Service depending on other service should be inited later.
         List<CarServiceBase> allServices = new ArrayList<>();
@@ -193,6 +197,7 @@
         allServices.add(mAppFocusService);
         allServices.add(mCarAudioService);
         allServices.add(mCarNightService);
+        allServices.add(mFixedActivityService);
         allServices.add(mInstrumentClusterService);
         allServices.add(mSystemStateControllerService);
         allServices.add(mPerUserCarServiceHelper);
diff --git a/service/src/com/android/car/am/FixedActivityService.java b/service/src/com/android/car/am/FixedActivityService.java
new file mode 100644
index 0000000..b8f8608
--- /dev/null
+++ b/service/src/com/android/car/am/FixedActivityService.java
@@ -0,0 +1,427 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car.am;
+
+import static com.android.car.CarLog.TAG_AM;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.app.ActivityManager.StackInfo;
+import android.app.ActivityOptions;
+import android.app.IActivityManager;
+import android.app.IProcessObserver;
+import android.app.TaskStackListener;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.Display;
+
+import com.android.car.CarLocalServices;
+import com.android.car.CarServiceBase;
+import com.android.car.user.CarUserService;
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * Monitors top activity for a display and guarantee activity in fixed mode is re-launched if it has
+ * crashed or gone to background for whatever reason.
+ *
+ * <p>This component also monitors the upddate of the target package and re-launch it once
+ * update is complete.</p>
+ */
+public final class FixedActivityService implements CarServiceBase {
+
+    private static final boolean DBG = false;
+
+    private static class RunningActivityInfo {
+        @NonNull
+        public final Intent intent;
+
+        @NonNull
+        public final ActivityOptions activityOptions;
+
+        @UserIdInt
+        public final int userId;
+
+        // Only used in a method for local book-keeping. So do not need a lock.
+        // This does not represent the current visibility.
+        public boolean isVisible;
+
+        RunningActivityInfo(@NonNull Intent intent, @NonNull ActivityOptions activityOptions,
+                @UserIdInt int userId) {
+            this.intent = intent;
+            this.activityOptions = activityOptions;
+            this.userId = userId;
+        }
+
+        @Override
+        public String toString() {
+            return "RunningActivityInfo{intent:" + intent + ",activityOptions:" + activityOptions
+                    + ",userId:" + userId + "}";
+        }
+    }
+
+    private final Context mContext;
+
+    private final IActivityManager mAm;
+
+    private final UserManager mUm;
+
+    private final CarUserService.UserCallback mUserCallback = new CarUserService.UserCallback() {
+        @Override
+        public void onUserLockChanged(@UserIdInt int userId, boolean unlocked) {
+            // Nothing to do
+        }
+
+        @Override
+        public void onSwitchUser(@UserIdInt int userId) {
+            synchronized (mLock) {
+                mRunningActivities.clear();
+            }
+        }
+    };
+
+    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            if (Intent.ACTION_PACKAGE_CHANGED.equals(action)
+                    || Intent.ACTION_PACKAGE_REPLACED.equals(
+                    action)) {
+                launchIfNecessary();
+            }
+        }
+    };
+
+    // It says listener but is actually callback.
+    private final TaskStackListener mTaskStackListener = new TaskStackListener() {
+        @Override
+        public void onTaskStackChanged() {
+            launchIfNecessary();
+        }
+    };
+
+    private final IProcessObserver mProcessObserver = new IProcessObserver.Stub() {
+        @Override
+        public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
+            launchIfNecessary();
+        }
+
+        @Override
+        public void onForegroundServicesChanged(int pid, int uid, int fgServiceTypes) {
+          // ignore
+        }
+
+        @Override
+        public void onProcessDied(int pid, int uid) {
+            launchIfNecessary();
+        }
+    };
+
+    private final Object mLock = new Object();
+
+    // key: displayId
+    @GuardedBy("mLock")
+    private final SparseArray<RunningActivityInfo> mRunningActivities =
+            new SparseArray<>(/* capacity= */ 1); // default to one cluster only case
+
+    @GuardedBy("mLock")
+    private boolean mEventMonitoringActive;
+
+    public FixedActivityService(Context context) {
+        mContext = context;
+        mAm = ActivityManager.getService();
+        mUm = context.getSystemService(UserManager.class);
+    }
+
+
+    @Override
+    public void init() {
+        // nothing to do
+    }
+
+    @Override
+    public void release() {
+        stopMonitoringEvents();
+    }
+
+    @Override
+    public void dump(PrintWriter writer) {
+        writer.println("*FixedActivityService*");
+        synchronized (mLock) {
+            writer.println("mRunningActivities:" + mRunningActivities
+                    + " ,mEventMonitoringActive:" + mEventMonitoringActive);
+        }
+    }
+
+    private void startMonitoringEvents() {
+        synchronized (mLock) {
+            if (mEventMonitoringActive) {
+                return;
+            }
+            mEventMonitoringActive = true;
+        }
+        CarUserService userService = CarLocalServices.getService(CarUserService.class);
+        userService.addUserCallback(mUserCallback);
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+        mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter,
+                /* broadcastPermission= */ null, /* scheduler= */ null);
+        try {
+            mAm.registerTaskStackListener(mTaskStackListener);
+            mAm.registerProcessObserver(mProcessObserver);
+        } catch (RemoteException e) {
+            Log.e(TAG_AM, "remote exception from AM", e);
+        }
+    }
+
+    private void stopMonitoringEvents() {
+        synchronized (mLock) {
+            if (!mEventMonitoringActive) {
+                return;
+            }
+            mEventMonitoringActive = false;
+        }
+        CarUserService userService = CarLocalServices.getService(CarUserService.class);
+        userService.removeUserCallback(mUserCallback);
+        try {
+            mAm.unregisterTaskStackListener(mTaskStackListener);
+            mAm.unregisterProcessObserver(mProcessObserver);
+        } catch (RemoteException e) {
+            Log.e(TAG_AM, "remote exception from AM", e);
+        }
+    }
+
+    @Nullable
+    private List<StackInfo> getStackInfos() {
+        try {
+            return mAm.getAllStackInfos();
+        } catch (RemoteException e) {
+            Log.e(TAG_AM, "remote exception from AM", e);
+        }
+        return null;
+    }
+
+    /**
+     * Launches all stored fixed mode activities if necessary.
+     * @param displayId Display id to check if it is visible. If check is not necessary, should pass
+     *        {@link Display#INVALID_DISPLAY}.
+     * @return true if fixed Activity for given {@code displayId} is visible / successfully
+     *         launched. It will return false for {@link Display#INVALID_DISPLAY} {@code displayId}.
+     */
+    private boolean launchIfNecessary(int displayId) {
+        List<StackInfo> infos = getStackInfos();
+        if (infos == null) {
+            Log.e(TAG_AM, "cannot get StackInfo from AM");
+            return false;
+        }
+        synchronized (mLock) {
+            if (mRunningActivities.size() == 0) {
+                // it must have been stopped.
+                if (DBG) {
+                    Log.i(TAG_AM, "empty activity list", new RuntimeException());
+                }
+                return false;
+            }
+            for (int i = 0; i < mRunningActivities.size(); i++) {
+                mRunningActivities.valueAt(i).isVisible = false;
+            }
+            for (StackInfo stackInfo : infos) {
+                RunningActivityInfo activityInfo = mRunningActivities.get(stackInfo.displayId);
+                if (activityInfo == null) {
+                    continue;
+                }
+                int topUserId = stackInfo.taskUserIds[stackInfo.taskUserIds.length - 1];
+                if (activityInfo.intent.getComponent().equals(stackInfo.topActivity)
+                        && activityInfo.userId == topUserId && stackInfo.visible) {
+                    // top one is matching.
+                    activityInfo.isVisible = true;
+                    continue;
+                }
+                if (DBG) {
+                    Log.i(TAG_AM, "Unmatched top activity:" + stackInfo.topActivity
+                            + " user:" + topUserId + " display:" + stackInfo.displayId);
+                }
+            }
+            for (int i = 0; i < mRunningActivities.size(); i++) {
+                RunningActivityInfo activityInfo = mRunningActivities.valueAt(i);
+                if (activityInfo.isVisible) {
+                    continue;
+                }
+                if (!isComponentAvailable(activityInfo.intent.getComponent(),
+                        activityInfo.userId) || !isUserAllowedToLaunchActivity(
+                        activityInfo.userId)) {
+                    continue;
+                }
+                Log.i(TAG_AM, "Launching Activity for fixed mode. Intent:" + activityInfo.intent
+                        + ",userId:" + UserHandle.of(activityInfo.userId) + ",displayId:"
+                        + mRunningActivities.keyAt(i));
+                try {
+                    mContext.startActivityAsUser(activityInfo.intent,
+                            activityInfo.activityOptions.toBundle(),
+                            UserHandle.of(activityInfo.userId));
+                    activityInfo.isVisible = true;
+                } catch (Exception e) { // Catch all for any app related issues.
+                    Log.w(TAG_AM, "Cannot start activity:" + activityInfo.intent, e);
+                }
+            }
+            RunningActivityInfo activityInfo = mRunningActivities.get(displayId);
+            if (activityInfo == null) {
+                return false;
+            }
+            return activityInfo.isVisible;
+        }
+    }
+
+    private void launchIfNecessary() {
+        launchIfNecessary(Display.INVALID_DISPLAY);
+    }
+
+    private void logComponentNotFound(ComponentName component, @UserIdInt  int userId,
+            Exception e) {
+        Log.e(TAG_AM, "Specified Component not found:" + component
+                + " for userid:" + userId, e);
+    }
+
+    private boolean isComponentAvailable(ComponentName component, @UserIdInt int userId) {
+        PackageInfo packageInfo;
+        try {
+            packageInfo = mContext.getPackageManager().getPackageInfoAsUser(
+                    component.getPackageName(), PackageManager.GET_ACTIVITIES, userId);
+        } catch (PackageManager.NameNotFoundException e) {
+            logComponentNotFound(component, userId, e);
+            return false;
+        }
+        if (packageInfo == null || packageInfo.activities == null) {
+            // may not be necessary but additional safety check
+            logComponentNotFound(component, userId, new RuntimeException());
+            return false;
+        }
+        String fullName = component.getClassName();
+        String shortName = component.getShortClassName();
+        for (ActivityInfo info : packageInfo.activities) {
+            if (info.name.equals(fullName) || info.name.equals(shortName)) {
+                return true;
+            }
+        }
+        logComponentNotFound(component, userId, new RuntimeException());
+        return false;
+    }
+
+    private boolean isUserAllowedToLaunchActivity(@UserIdInt int userId) {
+        int currentUser = ActivityManager.getCurrentUser();
+        if (userId == currentUser) {
+            return true;
+        }
+        int[] profileIds = mUm.getEnabledProfileIds(currentUser);
+        for (int id : profileIds) {
+            if (id == userId) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean isDisplayAllowedForFixedMode(int displayId) {
+        if (displayId == Display.DEFAULT_DISPLAY || displayId == Display.INVALID_DISPLAY) {
+            Log.w(TAG_AM, "Target display cannot be used for fixed mode, displayId:" + displayId,
+                    new RuntimeException());
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Checks {@link InstrumentClusterRenderingService#startFixedActivityModeForDisplayAndUser(
+     * Intent, ActivityOptions, int)}
+     */
+    public boolean startFixedActivityModeForDisplayAndUser(@NonNull Intent intent,
+            @NonNull ActivityOptions options, int displayId, @UserIdInt int userId) {
+        if (!isDisplayAllowedForFixedMode(displayId)) {
+            return false;
+        }
+        if (!isUserAllowedToLaunchActivity(userId)) {
+            Log.e(TAG_AM, "startFixedActivityModeForDisplayAndUser, requested user:" + userId
+                    + " cannot launch activity, Intent:" + intent);
+            return false;
+        }
+        ComponentName component = intent.getComponent();
+        if (component == null) {
+            Log.e(TAG_AM,
+                    "startFixedActivityModeForDisplayAndUser: No component specified for "
+                            + "requested Intent"
+                            + intent);
+            return false;
+        }
+        if (!isComponentAvailable(component, userId)) {
+            return false;
+        }
+        boolean startMonitoringEvents = false;
+        synchronized (mLock) {
+            if (mRunningActivities.size() == 0) {
+                startMonitoringEvents = true;
+            }
+            RunningActivityInfo activityInfo = mRunningActivities.get(displayId);
+            if (activityInfo == null) {
+                activityInfo = new RunningActivityInfo(intent, options, userId);
+                mRunningActivities.put(displayId, activityInfo);
+            }
+        }
+        boolean launched = launchIfNecessary(displayId);
+        if (!launched) {
+            synchronized (mLock) {
+                mRunningActivities.remove(displayId);
+            }
+        }
+        // If first trial fails, let client know and do not retry as it can be wrong setting.
+        if (startMonitoringEvents && launched) {
+            startMonitoringEvents();
+        }
+        return launched;
+    }
+
+    /** Check {@link InstrumentClusterRenderingService#stopFixedActivityMode(int)} */
+    public void stopFixedActivityMode(int displayId) {
+        if (!isDisplayAllowedForFixedMode(displayId)) {
+            return;
+        }
+        boolean stopMonitoringEvents = false;
+        synchronized (mLock) {
+            mRunningActivities.remove(displayId);
+            if (mRunningActivities.size() == 0) {
+                stopMonitoringEvents = true;
+            }
+        }
+        if (stopMonitoringEvents) {
+            stopMonitoringEvents();
+        }
+    }
+}
diff --git a/service/src/com/android/car/cluster/InstrumentClusterService.java b/service/src/com/android/car/cluster/InstrumentClusterService.java
index fd16da5..df6c476 100644
--- a/service/src/com/android/car/cluster/InstrumentClusterService.java
+++ b/service/src/com/android/car/cluster/InstrumentClusterService.java
@@ -15,17 +15,22 @@
  */
 package com.android.car.cluster;
 
+import static android.car.cluster.renderer.InstrumentClusterRenderingService.EXTRA_BUNDLE_KEY_FOR_INSTRUMENT_CLUSTER_HELPER;
+
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.app.ActivityOptions;
 import android.car.CarAppFocusManager;
 import android.car.cluster.IInstrumentClusterManagerCallback;
 import android.car.cluster.IInstrumentClusterManagerService;
 import android.car.cluster.renderer.IInstrumentCluster;
+import android.car.cluster.renderer.IInstrumentClusterHelper;
 import android.car.cluster.renderer.IInstrumentClusterNavigation;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
@@ -43,6 +48,7 @@
 import com.android.car.CarLog;
 import com.android.car.CarServiceBase;
 import com.android.car.R;
+import com.android.car.am.FixedActivityService;
 import com.android.car.user.CarUserService;
 import com.android.internal.annotations.GuardedBy;
 
@@ -69,16 +75,18 @@
      */
     @Deprecated
     private final ClusterManagerService mClusterManagerService = new ClusterManagerService();
-    private final Object mSync = new Object();
-    @GuardedBy("mSync")
+    private final Object mLock = new Object();
+    @GuardedBy("mLock")
     private ContextOwner mNavContextOwner = NO_OWNER;
-    @GuardedBy("mSync")
+    @GuardedBy("mLock")
     private IInstrumentCluster mRendererService;
     // If renderer service crashed / stopped and this class fails to rebind with it immediately,
     // we should wait some time before next attempt. This may happen during APK update for example.
+    @GuardedBy("mLock")
     private DeferredRebinder mDeferredRebinder;
     // Whether {@link android.car.cluster.renderer.InstrumentClusterRendererService} is bound
     // (although not necessarily connected)
+    @GuardedBy("mLock")
     private boolean mRendererBound = false;
 
     /**
@@ -92,7 +100,7 @@
             }
             IInstrumentCluster service = IInstrumentCluster.Stub.asInterface(binder);
             ContextOwner navContextOwner;
-            synchronized (mSync) {
+            synchronized (mLock) {
                 mRendererService = service;
                 navContextOwner = mNavContextOwner;
             }
@@ -107,19 +115,39 @@
                 Log.d(TAG, "onServiceDisconnected, name: " + name);
             }
             mContext.unbindService(this);
-            mRendererBound = false;
-
-            synchronized (mSync) {
+            DeferredRebinder rebinder;
+            synchronized (mLock) {
+                mRendererBound = false;
                 mRendererService = null;
+                if (mDeferredRebinder == null) {
+                    mDeferredRebinder = new DeferredRebinder();
+                }
+                rebinder = mDeferredRebinder;
             }
-
-            if (mDeferredRebinder == null) {
-                mDeferredRebinder = new DeferredRebinder();
-            }
-            mDeferredRebinder.rebind();
+            rebinder.rebind();
         }
     };
 
+    private final IInstrumentClusterHelper mInstrumentClusterHelper =
+            new IInstrumentClusterHelper.Stub() {
+                @Override
+                public boolean startFixedActivityModeForDisplayAndUser(Intent intent,
+                        Bundle activityOptionsBundle, int userId) {
+                    ActivityOptions options = new ActivityOptions(activityOptionsBundle);
+                    FixedActivityService service = CarLocalServices.getService(
+                            FixedActivityService.class);
+                    return service.startFixedActivityModeForDisplayAndUser(intent, options,
+                            options.getLaunchDisplayId(), userId);
+                }
+
+                @Override
+                public void stopFixedActivityMode(int displayId) {
+                    FixedActivityService service = CarLocalServices.getService(
+                            FixedActivityService.class);
+                    service.stopFixedActivityMode(displayId);
+                }
+            };
+
     public InstrumentClusterService(Context context, AppFocusService appFocusService,
             CarInputService carInputService) {
         mContext = context;
@@ -181,7 +209,7 @@
         IInstrumentCluster service;
         ContextOwner requester = new ContextOwner(uid, pid);
         ContextOwner newOwner = acquire ? requester : NO_OWNER;
-        synchronized (mSync) {
+        synchronized (mLock) {
             if ((acquire && Objects.equals(mNavContextOwner, requester))
                     || (!acquire && !Objects.equals(mNavContextOwner, requester))) {
                 // Nothing to do here. Either the same owner is acquiring twice, or someone is
@@ -221,6 +249,11 @@
 
         Intent intent = new Intent();
         intent.setComponent(ComponentName.unflattenFromString(rendererService));
+        // Litle bit inefficiency here as Intent.getIBinderExtra() is a hidden API.
+        Bundle bundle = new Bundle();
+        bundle.putBinder(EXTRA_BUNDLE_KEY_FOR_INSTRUMENT_CLUSTER_HELPER,
+                mInstrumentClusterHelper.asBinder());
+        intent.putExtra(EXTRA_BUNDLE_KEY_FOR_INSTRUMENT_CLUSTER_HELPER, bundle);
         return mContext.bindServiceAsUser(intent, mRendererServiceConnection,
                 Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM);
     }
@@ -262,7 +295,7 @@
 
     private IInstrumentCluster getInstrumentClusterRendererService() {
         IInstrumentCluster service;
-        synchronized (mSync) {
+        synchronized (mLock) {
             service = mRendererService;
         }
         return service;
diff --git a/service/src/com/android/car/hal/PropertyHalService.java b/service/src/com/android/car/hal/PropertyHalService.java
index 545fc2b..484e667 100644
--- a/service/src/com/android/car/hal/PropertyHalService.java
+++ b/service/src/com/android/car/hal/PropertyHalService.java
@@ -183,6 +183,14 @@
     }
 
     /**
+     * Return true if property is a display_units property
+     * @param propId
+     */
+    public boolean isDisplayUnitsProperty(int propId) {
+        return mPropIds.isPropertyToChangeUnits(propId);
+    }
+
+    /**
      * Set the property value.
      * @param prop
      */
diff --git a/service/src/com/android/car/hal/PropertyHalServiceIds.java b/service/src/com/android/car/hal/PropertyHalServiceIds.java
index 5409b4d..82a89d7 100644
--- a/service/src/com/android/car/hal/PropertyHalServiceIds.java
+++ b/service/src/com/android/car/hal/PropertyHalServiceIds.java
@@ -26,6 +26,8 @@
 import android.util.Pair;
 import android.util.SparseArray;
 
+import java.util.HashSet;
+
 /**
  * Helper class to define which property IDs are used by PropertyHalService.  This class binds the
  * read and write permissions to the property ID.
@@ -39,11 +41,12 @@
      * properties.
      */
     private final SparseArray<Pair<String, String>> mProps;
+    private final HashSet<Integer> mPropForUnits;
     private static final String TAG = "PropertyHalServiceIds";
 
     public PropertyHalServiceIds() {
         mProps = new SparseArray<>();
-
+        mPropForUnits = new HashSet<>();
         // Add propertyId and read/write permissions
         // Cabin Properties
         mProps.put(VehicleProperty.DOOR_POS, new Pair<>(
@@ -385,24 +388,31 @@
         mProps.put(VehicleProperty.CABIN_LIGHTS_SWITCH, new Pair<>(
                 Car.PERMISSION_CONTROL_INTERIOR_LIGHTS,
                 Car.PERMISSION_CONTROL_INTERIOR_LIGHTS));
+        // Display_Units
         mProps.put(VehicleProperty.DISTANCE_DISPLAY_UNITS, new Pair<>(
                 Car.PERMISSION_READ_DISPLAY_UNITS,
                 Car.PERMISSION_CONTROL_DISPLAY_UNITS));
+        mPropForUnits.add(VehicleProperty.DISTANCE_DISPLAY_UNITS);
         mProps.put(VehicleProperty.FUEL_VOLUME_DISPLAY_UNITS, new Pair<>(
                 Car.PERMISSION_READ_DISPLAY_UNITS,
                 Car.PERMISSION_CONTROL_DISPLAY_UNITS));
+        mPropForUnits.add(VehicleProperty.FUEL_VOLUME_DISPLAY_UNITS);
         mProps.put(VehicleProperty.TIRE_PRESSURE_DISPLAY_UNITS, new Pair<>(
                 Car.PERMISSION_READ_DISPLAY_UNITS,
                 Car.PERMISSION_CONTROL_DISPLAY_UNITS));
+        mPropForUnits.add(VehicleProperty.TIRE_PRESSURE_DISPLAY_UNITS);
         mProps.put(VehicleProperty.EV_BATTERY_DISPLAY_UNITS, new Pair<>(
                 Car.PERMISSION_READ_DISPLAY_UNITS,
                 Car.PERMISSION_CONTROL_DISPLAY_UNITS));
+        mPropForUnits.add(VehicleProperty.EV_BATTERY_DISPLAY_UNITS);
         mProps.put(VehicleProperty.FUEL_CONSUMPTION_UNITS_DISTANCE_OVER_VOLUME, new Pair<>(
                 Car.PERMISSION_READ_DISPLAY_UNITS,
                 Car.PERMISSION_CONTROL_DISPLAY_UNITS));
+        mPropForUnits.add(VehicleProperty.FUEL_CONSUMPTION_UNITS_DISTANCE_OVER_VOLUME);
         mProps.put(VehicleProperty.VEHICLE_SPEED_DISPLAY_UNITS, new Pair<>(
                 Car.PERMISSION_READ_DISPLAY_UNITS,
                 Car.PERMISSION_CONTROL_DISPLAY_UNITS));
+        mPropForUnits.add(VehicleProperty.VEHICLE_SPEED_DISPLAY_UNITS);
     }
 
     /**
@@ -469,4 +479,11 @@
             return insertVendorProperty(propId);
         }
     }
+
+    /**
+     * Check if the property is one of display units properties.
+     */
+    public boolean isPropertyToChangeUnits(int propertyId) {
+        return mPropForUnits.contains(propertyId);
+    }
 }
diff --git a/service/src/com/android/car/pm/ActivityBlockingActivity.java b/service/src/com/android/car/pm/ActivityBlockingActivity.java
index 9dcb70a..9756523 100644
--- a/service/src/com/android/car/pm/ActivityBlockingActivity.java
+++ b/service/src/com/android/car/pm/ActivityBlockingActivity.java
@@ -79,14 +79,19 @@
         // restrictions are lifted.
         // This Activity should be launched only after car service is initialized. Currently this
         // Activity is only launched from CPMS. So this is safe to do.
-        mCar = Car.createCar(this);
-        mUxRManager = (CarUxRestrictionsManager) mCar.getCarManager(
-                Car.CAR_UX_RESTRICTION_SERVICE);
-        // This activity would have been launched only in a restricted state.
-        // But ensuring when the service connection is established, that we are still
-        // in a restricted state.
-        handleUxRChange(mUxRManager.getCurrentCarUxRestrictions());
-        mUxRManager.registerListener(ActivityBlockingActivity.this::handleUxRChange);
+        mCar = Car.createCar(this, /* handler= */ null, Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER,
+                (car, ready) -> {
+                    if (!ready) {
+                        return;
+                    }
+                    mUxRManager = (CarUxRestrictionsManager) car.getCarManager(
+                            Car.CAR_UX_RESTRICTION_SERVICE);
+                    // This activity would have been launched only in a restricted state.
+                    // But ensuring when the service connection is established, that we are still
+                    // in a restricted state.
+                    handleUxRChange(mUxRManager.getCurrentCarUxRestrictions());
+                    mUxRManager.registerListener(ActivityBlockingActivity.this::handleUxRChange);
+                });
     }
 
     @Override
diff --git a/service/src/com/android/car/trust/CarTrustAgentEnrollmentService.java b/service/src/com/android/car/trust/CarTrustAgentEnrollmentService.java
index 0f54647..7f2923d 100644
--- a/service/src/com/android/car/trust/CarTrustAgentEnrollmentService.java
+++ b/service/src/com/android/car/trust/CarTrustAgentEnrollmentService.java
@@ -16,6 +16,7 @@
 
 package com.android.car.trust;
 
+import static android.car.Car.PERMISSION_CAR_ENROLL_TRUST;
 import static android.car.trust.CarTrustAgentEnrollmentManager.ENROLLMENT_HANDSHAKE_FAILURE;
 import static android.car.trust.CarTrustAgentEnrollmentManager.ENROLLMENT_NOT_ALLOWED;
 
@@ -33,6 +34,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.app.ActivityManager;
 import android.bluetooth.BluetoothDevice;
 import android.car.encryptionrunner.EncryptionRunner;
@@ -52,6 +54,7 @@
 import android.util.Log;
 
 import com.android.car.BLEStreamProtos.BLEOperationProto.OperationType;
+import com.android.car.ICarImpl;
 import com.android.car.R;
 import com.android.car.Utils;
 import com.android.internal.annotations.GuardedBy;
@@ -171,7 +174,9 @@
      * the enrollment of the trusted device.
      */
     @Override
+    @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
     public void startEnrollmentAdvertising() {
+        ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
         if (!mTrustedDeviceService.getSharedPrefs()
                 .getBoolean(TRUSTED_DEVICE_ENROLLMENT_ENABLED_KEY, true)) {
             Log.e(TAG, "Trusted Device Enrollment disabled");
@@ -192,7 +197,9 @@
      * Stop BLE advertisement for Enrollment
      */
     @Override
+    @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
     public void stopEnrollmentAdvertising() {
+        ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
         logEnrollmentEvent(STOP_ENROLLMENT_ADVERTISING);
         addEnrollmentServiceLog("stopEnrollmentAdvertising");
         mCarTrustAgentBleManager.stopEnrollmentAdvertising();
@@ -205,7 +212,9 @@
      * @param device the remote Bluetooth device that will receive the signal.
      */
     @Override
+    @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
     public void enrollmentHandshakeAccepted(BluetoothDevice device) {
+        ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
         logEnrollmentEvent(ENROLLMENT_HANDSHAKE_ACCEPTED);
         addEnrollmentServiceLog("enrollmentHandshakeAccepted");
         if (device == null || !device.equals(mRemoteEnrollmentDevice)) {
@@ -226,7 +235,9 @@
      * navigated away from the app before completing enrollment.
      */
     @Override
+    @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
     public void terminateEnrollmentHandshake() {
+        ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
         addEnrollmentServiceLog("terminateEnrollmentHandshake");
         // Disconnect from BLE
         mCarTrustAgentBleManager.disconnectRemoteDevice();
@@ -252,7 +263,9 @@
      * @return True if the escrow token is active, false if not
      */
     @Override
+    @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
     public boolean isEscrowTokenActive(long handle, int uid) {
+        ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
         if (mTokenActiveStateMap.get(handle) != null) {
             return mTokenActiveStateMap.get(handle);
         }
@@ -266,7 +279,9 @@
      * @param uid    user id
      */
     @Override
+    @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
     public void removeEscrowToken(long handle, int uid) {
+        ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
         mEnrollmentDelegate.removeEscrowToken(handle, uid);
         addEnrollmentServiceLog("removeEscrowToken (handle:" + handle + " uid:" + uid + ")");
     }
@@ -277,7 +292,9 @@
      * @param uid user id
      */
     @Override
+    @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
     public void removeAllTrustedDevices(int uid) {
+        ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
         for (TrustedDeviceInfo device : getEnrolledDeviceInfosForUser(uid)) {
             removeEscrowToken(device.getHandle(), uid);
         }
@@ -291,7 +308,9 @@
      * @param isEnabled {@code true} to enable; {@code false} to disable the feature.
      */
     @Override
+    @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
     public void setTrustedDeviceEnrollmentEnabled(boolean isEnabled) {
+        ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
         SharedPreferences.Editor editor = mTrustedDeviceService.getSharedPrefs().edit();
         editor.putBoolean(TRUSTED_DEVICE_ENROLLMENT_ENABLED_KEY, isEnabled);
         if (!editor.commit()) {
@@ -308,7 +327,9 @@
      *                  back.
      */
     @Override
+    @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
     public void setTrustedDeviceUnlockEnabled(boolean isEnabled) {
+        ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
         mTrustedDeviceService.getCarTrustAgentUnlockService()
                 .setTrustedDeviceUnlockEnabled(isEnabled);
     }
@@ -322,7 +343,9 @@
      */
     @NonNull
     @Override
+    @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
     public List<TrustedDeviceInfo> getEnrolledDeviceInfosForUser(int uid) {
+        ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
         Set<String> enrolledDeviceInfos = mTrustedDeviceService.getSharedPrefs().getStringSet(
                 String.valueOf(uid), new HashSet<>());
         List<TrustedDeviceInfo> trustedDeviceInfos = new ArrayList<>(enrolledDeviceInfos.size());
@@ -342,7 +365,9 @@
      * @param listener {@link ICarTrustAgentEnrollmentCallback}
      */
     @Override
+    @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
     public synchronized void registerEnrollmentCallback(ICarTrustAgentEnrollmentCallback listener) {
+        ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
         if (listener == null) {
             throw new IllegalArgumentException("Listener is null");
         }
@@ -835,8 +860,10 @@
      * @param listener client to unregister
      */
     @Override
+    @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
     public synchronized void unregisterEnrollmentCallback(
             ICarTrustAgentEnrollmentCallback listener) {
+        ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
         if (listener == null) {
             throw new IllegalArgumentException("Listener is null");
         }
@@ -858,7 +885,9 @@
      * @param listener {@link ICarTrustAgentBleCallback}
      */
     @Override
+    @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
     public synchronized void registerBleCallback(ICarTrustAgentBleCallback listener) {
+        ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
         if (listener == null) {
             throw new IllegalArgumentException("Listener is null");
         }
@@ -903,7 +932,9 @@
      * @param listener client to unregister
      */
     @Override
+    @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
     public synchronized void unregisterBleCallback(ICarTrustAgentBleCallback listener) {
+        ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
         if (listener == null) {
             throw new IllegalArgumentException("Listener is null");
         }
diff --git a/service/src/com/android/car/vms/VmsClientManager.java b/service/src/com/android/car/vms/VmsClientManager.java
index 710793c..66a0fe4 100644
--- a/service/src/com/android/car/vms/VmsClientManager.java
+++ b/service/src/com/android/car/vms/VmsClientManager.java
@@ -146,16 +146,16 @@
             CarUserService userService, CarUserManagerHelper userManagerHelper,
             VmsHalService halService) {
         this(context, brokerService, userService, userManagerHelper, halService,
-                Binder::getCallingUid);
+                new Handler(Looper.getMainLooper()), Binder::getCallingUid);
     }
 
     @VisibleForTesting
     VmsClientManager(Context context, VmsBrokerService brokerService,
             CarUserService userService, CarUserManagerHelper userManagerHelper,
-            VmsHalService halService, IntSupplier getCallingUid) {
+            VmsHalService halService, Handler handler, IntSupplier getCallingUid) {
         mContext = context;
         mPackageManager = context.getPackageManager();
-        mHandler = new Handler(Looper.getMainLooper());
+        mHandler = handler;
         mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
         mUserService = userService;
         mUserManagerHelper = userManagerHelper;
@@ -286,9 +286,11 @@
      * Returns all active subscriber clients.
      */
     public Collection<IVmsSubscriberClient> getAllSubscribers() {
-        return mSubscribers.values().stream()
-                .map(subscriber -> subscriber.mClient)
-                .collect(Collectors.toList());
+        synchronized (mLock) {
+            return mSubscribers.values().stream()
+                    .map(subscriber -> subscriber.mClient)
+                    .collect(Collectors.toList());
+        }
     }
 
     /**
diff --git a/tests/CarCtsDummyLauncher/src/com/android/car/dummylauncher/LauncherActivity.java b/tests/CarCtsDummyLauncher/src/com/android/car/dummylauncher/LauncherActivity.java
index 20fcfc0..71b3c2b 100644
--- a/tests/CarCtsDummyLauncher/src/com/android/car/dummylauncher/LauncherActivity.java
+++ b/tests/CarCtsDummyLauncher/src/com/android/car/dummylauncher/LauncherActivity.java
@@ -33,6 +33,7 @@
 
         View view = getLayoutInflater().inflate(R.layout.launcher_activity, null);
         setContentView(view);
+        reportFullyDrawn();
     }
 }
 
diff --git a/tests/CarDeveloperOptions/AndroidManifest.xml b/tests/CarDeveloperOptions/AndroidManifest.xml
index 5975572..046b386 100644
--- a/tests/CarDeveloperOptions/AndroidManifest.xml
+++ b/tests/CarDeveloperOptions/AndroidManifest.xml
@@ -21,7 +21,6 @@
     <original-package android:name="com.android.car.developeroptions" />
 
     <uses-permission android:name="android.permission.REQUEST_NETWORK_SCORES" />
-    <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
diff --git a/tests/CarDeveloperOptions/res/xml/development_settings.xml b/tests/CarDeveloperOptions/res/xml/development_settings.xml
index 9ed1285..a946526 100644
--- a/tests/CarDeveloperOptions/res/xml/development_settings.xml
+++ b/tests/CarDeveloperOptions/res/xml/development_settings.xml
@@ -30,11 +30,6 @@
             android:summary="@string/summary_placeholder"
             android:fragment="com.android.car.developeroptions.applications.ProcessStatsSummary" />
 
-        <com.android.car.developeroptions.BugreportPreference
-            android:key="bugreport"
-            android:title="@*android:string/bugreport_title"
-            android:dialogTitle="@*android:string/bugreport_title" />
-
         <Preference
             android:key="system_server_heap_dump"
             android:title="@string/capture_system_heap_dump_title" />
@@ -151,11 +146,6 @@
             android:summary="@string/enable_terminal_summary" />
 
         <SwitchPreference
-            android:key="bugreport_in_power"
-            android:title="@string/bugreport_in_power"
-            android:summary="@string/bugreport_in_power_summary" />
-
-        <SwitchPreference
             android:key="automatic_system_server_heap_dumps"
             android:title="@string/automatic_system_heap_dump_title"
             android:summary="@string/automatic_system_heap_dump_summary" />
diff --git a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/BugreportPreference.java b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/BugreportPreference.java
deleted file mode 100644
index 6acef70..0000000
--- a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/BugreportPreference.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.car.developeroptions;
-
-import android.app.ActivityManager;
-import android.app.settings.SettingsEnums;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.os.RemoteException;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-import android.widget.CheckedTextView;
-import android.widget.TextView;
-
-import androidx.appcompat.app.AlertDialog.Builder;
-
-import com.android.car.developeroptions.overlay.FeatureFactory;
-import com.android.settingslib.CustomDialogPreferenceCompat;
-
-public class BugreportPreference extends CustomDialogPreferenceCompat {
-
-    private static final String TAG = "BugreportPreference";
-
-    private CheckedTextView mInteractiveTitle;
-    private TextView mInteractiveSummary;
-    private CheckedTextView mFullTitle;
-    private TextView mFullSummary;
-
-    public BugreportPreference(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    protected void onPrepareDialogBuilder(Builder builder, DialogInterface.OnClickListener listener) {
-        super.onPrepareDialogBuilder(builder, listener);
-
-        final View dialogView = View.inflate(getContext(), R.layout.bugreport_options_dialog, null);
-        mInteractiveTitle = (CheckedTextView) dialogView.findViewById(R.id.bugreport_option_interactive_title);
-        mInteractiveSummary = (TextView) dialogView.findViewById(R.id.bugreport_option_interactive_summary);
-        mFullTitle = (CheckedTextView) dialogView.findViewById(R.id.bugreport_option_full_title);
-        mFullSummary = (TextView) dialogView.findViewById(R.id.bugreport_option_full_summary);
-        final View.OnClickListener l = new View.OnClickListener() {
-
-            @Override
-            public void onClick(View v) {
-                if (v == mFullTitle || v == mFullSummary) {
-                    mInteractiveTitle.setChecked(false);
-                    mFullTitle.setChecked(true);
-                }
-                if (v == mInteractiveTitle || v == mInteractiveSummary) {
-                    mInteractiveTitle.setChecked(true);
-                    mFullTitle.setChecked(false);
-                }
-            }
-        };
-        mInteractiveTitle.setOnClickListener(l);
-        mFullTitle.setOnClickListener(l);
-        mInteractiveSummary.setOnClickListener(l);
-        mFullSummary.setOnClickListener(l);
-
-        builder.setPositiveButton(com.android.internal.R.string.report, listener);
-        builder.setView(dialogView);
-    }
-
-    @Override
-    protected void onClick(DialogInterface dialog, int which) {
-        if (which == DialogInterface.BUTTON_POSITIVE) {
-
-            final Context context = getContext();
-            if (mFullTitle.isChecked()) {
-                Log.v(TAG, "Taking full bugreport right away");
-                FeatureFactory.getFactory(context).getMetricsFeatureProvider().action(context,
-                        SettingsEnums.ACTION_BUGREPORT_FROM_SETTINGS_FULL);
-                takeBugreport(ActivityManager.BUGREPORT_OPTION_FULL);
-            } else {
-                Log.v(TAG, "Taking interactive bugreport right away");
-                FeatureFactory.getFactory(context).getMetricsFeatureProvider().action(context,
-                        SettingsEnums.ACTION_BUGREPORT_FROM_SETTINGS_INTERACTIVE);
-                takeBugreport(ActivityManager.BUGREPORT_OPTION_INTERACTIVE);
-            }
-        }
-    }
-
-    private void takeBugreport(int bugreportType) {
-        try {
-            ActivityManager.getService().requestBugReport(bugreportType);
-        } catch (RemoteException e) {
-            Log.e(TAG, "error taking bugreport (bugreportType=" + bugreportType + ")", e);
-        }
-    }
-}
diff --git a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/development/BugReportInPowerPreferenceController.java b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/development/BugReportInPowerPreferenceController.java
deleted file mode 100644
index 1f22eb2..0000000
--- a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/development/BugReportInPowerPreferenceController.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.car.developeroptions.development;
-
-import android.content.Context;
-import android.os.UserManager;
-import android.provider.Settings;
-
-import androidx.annotation.VisibleForTesting;
-import androidx.preference.Preference;
-import androidx.preference.SwitchPreference;
-
-import com.android.car.developeroptions.core.PreferenceControllerMixin;
-import com.android.settingslib.development.DeveloperOptionsPreferenceController;
-
-public class BugReportInPowerPreferenceController extends
-        DeveloperOptionsPreferenceController implements Preference.OnPreferenceChangeListener,
-        PreferenceControllerMixin {
-
-    private static final String KEY_BUGREPORT_IN_POWER = "bugreport_in_power";
-
-    @VisibleForTesting
-    static int SETTING_VALUE_ON = 1;
-    @VisibleForTesting
-    static int SETTING_VALUE_OFF = 0;
-
-    private final UserManager mUserManager;
-
-    public BugReportInPowerPreferenceController(Context context) {
-        super(context);
-        mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
-    }
-
-    @Override
-    public boolean isAvailable() {
-        return !mUserManager.hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES);
-    }
-
-    @Override
-    public String getPreferenceKey() {
-        return KEY_BUGREPORT_IN_POWER;
-    }
-
-    @Override
-    public boolean onPreferenceChange(Preference preference, Object newValue) {
-        final boolean isEnabled = (Boolean) newValue;
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Global.BUGREPORT_IN_POWER_MENU,
-                isEnabled ? SETTING_VALUE_ON : SETTING_VALUE_OFF);
-        return true;
-    }
-
-    @Override
-    public void updateState(Preference preference) {
-        final int mode = Settings.Secure.getInt(mContext.getContentResolver(),
-                Settings.Global.BUGREPORT_IN_POWER_MENU, SETTING_VALUE_OFF);
-        ((SwitchPreference) mPreference).setChecked(mode != SETTING_VALUE_OFF);
-    }
-
-    @Override
-    protected void onDeveloperOptionsSwitchDisabled() {
-        super.onDeveloperOptionsSwitchDisabled();
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Global.BUGREPORT_IN_POWER_MENU, SETTING_VALUE_OFF);
-        ((SwitchPreference) mPreference).setChecked(false);
-    }
-}
diff --git a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/development/BugReportPreferenceController.java b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/development/BugReportPreferenceController.java
deleted file mode 100644
index 28fb9b5..0000000
--- a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/development/BugReportPreferenceController.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.car.developeroptions.development;
-
-import android.content.Context;
-import android.os.UserManager;
-
-import com.android.car.developeroptions.core.PreferenceControllerMixin;
-import com.android.settingslib.development.DeveloperOptionsPreferenceController;
-
-public class BugReportPreferenceController extends DeveloperOptionsPreferenceController implements
-        PreferenceControllerMixin {
-
-    private static final String KEY_BUGREPORT = "bugreport";
-
-    private final UserManager mUserManager;
-
-    public BugReportPreferenceController(Context context) {
-        super(context);
-
-        mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
-    }
-
-    @Override
-    public boolean isAvailable() {
-        return !mUserManager.hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES);
-    }
-
-    @Override
-    public String getPreferenceKey() {
-        return KEY_BUGREPORT;
-    }
-}
diff --git a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/development/DevelopmentSettingsDashboardFragment.java b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/development/DevelopmentSettingsDashboardFragment.java
index 95c8b6a..3253d21 100644
--- a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/development/DevelopmentSettingsDashboardFragment.java
+++ b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/development/DevelopmentSettingsDashboardFragment.java
@@ -403,7 +403,6 @@
             BluetoothA2dpConfigStore bluetoothA2dpConfigStore) {
         final List<AbstractPreferenceController> controllers = new ArrayList<>();
         controllers.add(new MemoryUsagePreferenceController(context));
-        controllers.add(new BugReportPreferenceController(context));
         controllers.add(new SystemServerHeapDumpPreferenceController(context));
         controllers.add(new LocalBackupPasswordPreferenceController(context));
         controllers.add(new StayAwakePreferenceController(context, lifecycle));
@@ -418,7 +417,6 @@
         controllers.add(new AdbPreferenceController(context, fragment));
         controllers.add(new ClearAdbKeysPreferenceController(context, fragment));
         controllers.add(new LocalTerminalPreferenceController(context));
-        controllers.add(new BugReportInPowerPreferenceController(context));
         controllers.add(new AutomaticSystemServerHeapDumpPreferenceController(context));
         controllers.add(new MockLocationAppPreferenceController(context, fragment));
         controllers.add(new DebugViewAttributesPreferenceController(context));
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioTestFragment.java
index b03e320..84b6bcf 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioTestFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioTestFragment.java
@@ -21,9 +21,7 @@
 import android.car.CarAppFocusManager.OnAppFocusChangedListener;
 import android.car.CarAppFocusManager.OnAppFocusOwnershipCallback;
 import android.car.media.CarAudioManager;
-import android.content.ComponentName;
 import android.content.Context;
-import android.content.ServiceConnection;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.hardware.display.DisplayManager;
@@ -34,7 +32,6 @@
 import android.media.HwAudioSource;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.IBinder;
 import android.os.Looper;
 import android.util.Log;
 import android.view.Display;
@@ -124,41 +121,39 @@
     private void connectCar() {
         mContext = getContext();
         mHandler = new Handler(Looper.getMainLooper());
-        mCar = Car.createCar(mContext, new ServiceConnection() {
-            @Override
-            public void onServiceConnected(ComponentName name, IBinder service) {
-                mAppFocusManager =
-                        (CarAppFocusManager) mCar.getCarManager(Car.APP_FOCUS_SERVICE);
-                OnAppFocusChangedListener listener = new OnAppFocusChangedListener() {
-                    @Override
-                    public void onAppFocusChanged(int appType, boolean active) {
+        mCar = Car.createCar(mContext, /* handler= */ null,
+                Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER, (car, ready) -> {
+                    if (!ready) {
+                        return;
                     }
-                };
-                mAppFocusManager.addFocusListener(listener,
-                        CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
-                mAppFocusManager.addFocusListener(listener,
-                        CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+                    mAppFocusManager =
+                            (CarAppFocusManager) car.getCarManager(Car.APP_FOCUS_SERVICE);
+                    OnAppFocusChangedListener listener = new OnAppFocusChangedListener() {
+                        @Override
+                        public void onAppFocusChanged(int appType, boolean active) {
+                        }
+                    };
+                    mAppFocusManager.addFocusListener(listener,
+                            CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+                    mAppFocusManager.addFocusListener(listener,
+                            CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
 
-                mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE);
+                    mCarAudioManager = (CarAudioManager) car.getCarManager(Car.AUDIO_SERVICE);
 
-                //take care of zone selection
-                int[] zoneList = mCarAudioManager.getAudioZoneIds();
-                Integer[] zoneArray = Arrays.stream(zoneList).boxed().toArray(Integer[]::new);
-                mZoneAdapter = new ArrayAdapter<>(mContext,
-                        android.R.layout.simple_spinner_item, zoneArray);
-                mZoneAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-                mZoneSpinner.setAdapter(mZoneAdapter);
-                mZoneSpinner.setEnabled(true);
+                    //take care of zone selection
+                    int[] zoneList = mCarAudioManager.getAudioZoneIds();
+                    Integer[] zoneArray = Arrays.stream(zoneList).boxed().toArray(Integer[]::new);
+                    mZoneAdapter = new ArrayAdapter<>(mContext,
+                            android.R.layout.simple_spinner_item, zoneArray);
+                    mZoneAdapter.setDropDownViewResource(
+                            android.R.layout.simple_spinner_dropdown_item);
+                    mZoneSpinner.setAdapter(mZoneAdapter);
+                    mZoneSpinner.setEnabled(true);
 
-                if (mCarAudioManager.isDynamicRoutingEnabled()) {
-                    setUpDisplayPlayer();
-                }
-            }
-            @Override
-            public void onServiceDisconnected(ComponentName name) {
-            }
-            });
-        mCar.connect();
+                    if (mCarAudioManager.isDynamicRoutingEnabled()) {
+                        setUpDisplayPlayer();
+                    }
+                });
     }
 
     private void initializePlayers() {
@@ -217,9 +212,16 @@
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
         Log.i(TAG, "onCreateView");
+        View view = inflater.inflate(R.layout.audio, container, false);
+        //Zone Spinner
+        setUpZoneSpinnerView(view);
+
+        //Display layout
+        setUpDisplayLayoutView(view);
+
         connectCar();
         initializePlayers();
-        View view = inflater.inflate(R.layout.audio, container, false);
+
         mAudioManager = (AudioManager) mContext.getSystemService(
                 Context.AUDIO_SERVICE);
         mAudioFocusHandler = new FocusHandler(
@@ -331,35 +333,6 @@
             }
         });
 
-        //Zone Spinner
-        mZoneSpinner = view.findViewById(R.id.zone_spinner);
-        mZoneSpinner.setEnabled(false);
-        mZoneSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
-            @Override
-            public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
-                handleZoneSelection();
-            }
-
-            @Override
-            public void onNothingSelected(AdapterView<?> parent) {
-            }
-        });
-
-
-        mDisplayLayout = view.findViewById(R.id.audio_display_layout);
-
-        mDisplaySpinner = view.findViewById(R.id.display_spinner);
-        mDisplaySpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
-            @Override
-            public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
-                handleDisplaySelection();
-            }
-
-            @Override
-            public void onNothingSelected(AdapterView<?> parent) {
-            }
-        });
-
         // Manage buttons for audio player for displays
         view.findViewById(R.id.button_display_media_play_start).setOnClickListener(v -> {
             startDisplayAudio();
@@ -375,6 +348,37 @@
         return view;
     }
 
+    private void setUpDisplayLayoutView(View view) {
+        mDisplayLayout = view.findViewById(R.id.audio_display_layout);
+
+        mDisplaySpinner = view.findViewById(R.id.display_spinner);
+        mDisplaySpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+            @Override
+            public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
+                handleDisplaySelection();
+            }
+
+            @Override
+            public void onNothingSelected(AdapterView<?> parent) {
+            }
+        });
+    }
+
+    private void setUpZoneSpinnerView(View view) {
+        mZoneSpinner = view.findViewById(R.id.zone_spinner);
+        mZoneSpinner.setEnabled(false);
+        mZoneSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+            @Override
+            public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
+                handleZoneSelection();
+            }
+
+            @Override
+            public void onNothingSelected(AdapterView<?> parent) {
+            }
+        });
+    }
+
     public void handleZoneSelection() {
         int position = mZoneSpinner.getSelectedItemPosition();
         int zone = mZoneAdapter.getItem(position);
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/InstrumentClusterFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/InstrumentClusterFragment.java
index 7657c38..8886913 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/InstrumentClusterFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/InstrumentClusterFragment.java
@@ -17,6 +17,7 @@
 
 import android.annotation.Nullable;
 import android.car.Car;
+import android.car.Car.CarServiceLifecycleListener;
 import android.car.CarAppFocusManager;
 import android.car.CarNotConnectedException;
 import android.car.cluster.navigation.NavigationState;
@@ -33,11 +34,8 @@
 import android.car.cluster.navigation.NavigationState.Step;
 import android.car.cluster.navigation.NavigationState.Timestamp;
 import android.car.navigation.CarNavigationStatusManager;
-import android.content.ComponentName;
-import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
 import android.os.Bundle;
-import android.os.IBinder;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -73,19 +71,19 @@
     private NavigationStateProto[] mNavStateData;
     private Button mTurnByTurnButton;
 
-    private ServiceConnection mCarServiceConnection = new ServiceConnection() {
-        @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            Log.d(TAG, "Connected to Car Service");
-            mCarNavigationStatusManager = (CarNavigationStatusManager) mCarApi
-                    .getCarManager(Car.CAR_NAVIGATION_SERVICE);
-            mCarAppFocusManager = (CarAppFocusManager) mCarApi
-                    .getCarManager(Car.APP_FOCUS_SERVICE);
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
+    private CarServiceLifecycleListener mCarServiceLifecycleListener = (car, ready) -> {
+        if (!ready) {
             Log.d(TAG, "Disconnect from Car Service");
+            return;
+        }
+        Log.d(TAG, "Connected to Car Service");
+        try {
+            mCarNavigationStatusManager = (CarNavigationStatusManager) car.getCarManager(
+                    Car.CAR_NAVIGATION_SERVICE);
+            mCarAppFocusManager = (CarAppFocusManager) car.getCarManager(
+                    Car.APP_FOCUS_SERVICE);
+        } catch (CarNotConnectedException e) {
+            Log.e(TAG, "Car is not connected!", e);
         }
     };
 
@@ -117,13 +115,8 @@
 
 
     private void initCarApi() {
-        if (mCarApi != null && mCarApi.isConnected()) {
-            mCarApi.disconnect();
-            mCarApi = null;
-        }
-
-        mCarApi = Car.createCar(getContext(), mCarServiceConnection);
-        mCarApi.connect();
+        mCarApi = Car.createCar(getContext(), /* handler= */ null,
+                Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER, mCarServiceLifecycleListener);
     }
 
     @NonNull
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/volume/VolumeTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/volume/VolumeTestFragment.java
index 73e3798..df9aa7b 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/volume/VolumeTestFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/volume/VolumeTestFragment.java
@@ -16,14 +16,12 @@
 package com.google.android.car.kitchensink.volume;
 
 import android.car.Car;
+import android.car.Car.CarServiceLifecycleListener;
 import android.car.media.CarAudioManager;
-import android.content.ComponentName;
 import android.content.Context;
-import android.content.ServiceConnection;
 import android.media.AudioManager;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.IBinder;
 import android.os.Message;
 import android.util.Log;
 import android.util.SparseIntArray;
@@ -112,20 +110,15 @@
         public boolean mHasFocus;
     }
 
-    private final ServiceConnection mCarConnectionCallback =
-            new ServiceConnection() {
-                @Override
-                public void onServiceConnected(ComponentName name, IBinder binder) {
-                    Log.d(TAG, "Connected to Car Service");
-                    mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE);
-                    initVolumeInfo();
-                }
-
-                @Override
-                public void onServiceDisconnected(ComponentName name) {
-                    Log.d(TAG, "Disconnect from Car Service");
-                }
-            };
+    private CarServiceLifecycleListener mCarServiceLifecycleListener = (car, ready) -> {
+        if (!ready) {
+            Log.d(TAG, "Disconnect from Car Service");
+            return;
+        }
+        Log.d(TAG, "Connected to Car Service");
+        mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE);
+        initVolumeInfo();
+    };
 
     @Override
     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@@ -161,8 +154,8 @@
         mBalance = v.findViewById(R.id.balance_bar);
         mBalance.setOnSeekBarChangeListener(seekListener);
 
-        mCar = Car.createCar(getActivity(), mCarConnectionCallback);
-        mCar.connect();
+        mCar = Car.createCar(getActivity(), /* handler= */ null,
+                Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER, mCarServiceLifecycleListener);
         return v;
     }
 
@@ -210,12 +203,4 @@
         }
         mAdapter.refreshVolumes(mVolumeInfos);
     }
-
-    @Override
-    public void onDestroy() {
-        if (mCar != null) {
-            mCar.disconnect();
-        }
-        super.onDestroy();
-    }
 }
diff --git a/tests/carservice_test/src/com/android/car/CarUxRestrictionsManagerServiceTest.java b/tests/carservice_test/src/com/android/car/CarUxRestrictionsManagerServiceTest.java
index aadb6f9..0aa89f9 100644
--- a/tests/carservice_test/src/com/android/car/CarUxRestrictionsManagerServiceTest.java
+++ b/tests/carservice_test/src/com/android/car/CarUxRestrictionsManagerServiceTest.java
@@ -27,14 +27,18 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
+import android.car.VehiclePropertyIds;
 import android.car.drivingstate.CarDrivingStateEvent;
 import android.car.drivingstate.CarUxRestrictions;
 import android.car.drivingstate.CarUxRestrictionsConfiguration;
 import android.car.drivingstate.CarUxRestrictionsConfiguration.Builder;
+import android.car.drivingstate.ICarDrivingStateChangeListener;
 import android.car.hardware.CarPropertyValue;
+import android.car.hardware.property.CarPropertyEvent;
 import android.content.Context;
 import android.content.res.Resources;
 import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
+import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.JsonReader;
 import android.util.JsonWriter;
@@ -46,6 +50,7 @@
 import com.android.car.systeminterface.SystemInterface;
 
 import org.junit.After;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -62,6 +67,7 @@
 import java.nio.file.Files;
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.CountDownLatch;
 
 @RunWith(AndroidJUnit4.class)
 @MediumTest
@@ -220,6 +226,198 @@
         assertTrue(restrictions.toString(), expected.isSameRestrictions(restrictions));
     }
 
+    // This test only involves calling a few methods and should finish very quickly. If it doesn't
+    // finish in 20s, we probably encountered a deadlock.
+    @Test(timeout = 20000)
+    public void testInitService_NoDeadlockWithCarDrivingStateService()
+            throws Exception {
+
+        CarDrivingStateService drivingStateService = new CarDrivingStateService(mSpyContext,
+                mMockCarPropertyService);
+        CarUxRestrictionsManagerService uxRestrictionsService = new CarUxRestrictionsManagerService(
+                mSpyContext, drivingStateService, mMockCarPropertyService);
+
+        CountDownLatch dispatchingStartedSignal = new CountDownLatch(1);
+        CountDownLatch initCompleteSignal = new CountDownLatch(1);
+
+        // A deadlock can exist when the dispatching of a listener is synchronized. For instance,
+        // the CarUxRestrictionsManagerService#init() method registers a callback like this one. The
+        // deadlock risk occurs if:
+        // 1. CarUxRestrictionsManagerService has registered a listener with CarDrivingStateService
+        // 2. A synchronized method of CarUxRestrictionsManagerService starts to run
+        // 3. While the method from (2) is running, a property event occurs on a different thread
+        //    that triggers a drive state event in CarDrivingStateService. If CarDrivingStateService
+        //    handles the property event in a synchronized method, then CarDrivingStateService is
+        //    locked. The listener from (1) will wait until the lock on
+        //    CarUxRestrictionsManagerService is released.
+        // 4. The synchronized method from (2) attempts to access CarDrivingStateService. For
+        //    example, the implementation below attempts to read the restriction mode.
+        //
+        // In the above steps, both CarUxRestrictionsManagerService and CarDrivingStateService are
+        // locked and waiting on each other, hence the deadlock.
+        drivingStateService.registerDrivingStateChangeListener(
+                new ICarDrivingStateChangeListener.Stub() {
+                    @Override
+                    public void onDrivingStateChanged(CarDrivingStateEvent event)
+                            throws RemoteException {
+                        // EVENT 2 [new thread]: this callback is called from within
+                        // handlePropertyEvent(), which might (but shouldn't) lock
+                        // CarDrivingStateService
+
+                        // Notify that the dispatching process has started
+                        dispatchingStartedSignal.countDown();
+
+                        try {
+                            // EVENT 3b [new thread]: Wait until init() has finished. If these
+                            // threads don't have lock dependencies, there is no reason there
+                            // would be an issue with waiting.
+                            //
+                            // In the real world, this wait could represent a long-running
+                            // task, or hitting the below line that attempts to access the
+                            // CarUxRestrictionsManagerService (which might be locked while init
+                            // () is running).
+                            //
+                            // If there is a deadlock while waiting for init to complete, we will
+                            // never progress past this line.
+                            initCompleteSignal.await();
+                        } catch (InterruptedException e) {
+                            Assert.fail("onDrivingStateChanged thread interrupted");
+                        }
+
+                        // Attempt to access CarUxRestrictionsManagerService. If
+                        // CarUxRestrictionsManagerService is locked because it is doing its own
+                        // work, then this will wait.
+                        //
+                        // This line won't execute in the deadlock flow. However, it is an example
+                        // of a real-world piece of code that would serve the same role as the above
+                        uxRestrictionsService.getCurrentUxRestrictions();
+                    }
+                });
+
+        // EVENT 1 [new thread]: handlePropertyEvent() is called, which locks CarDrivingStateService
+        // Ideally CarPropertyService would trigger the change event, but since that is mocked
+        // we manually trigger the event. This event is what eventually triggers the dispatch to
+        // ICarDrivingStateChangeListener that was defined above.
+        Runnable propertyChangeEventRunnable =
+                () -> drivingStateService.handlePropertyEvent(
+                        new CarPropertyEvent(CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE,
+                                new CarPropertyValue<>(
+                                        VehiclePropertyIds.PERF_VEHICLE_SPEED, 0, 100f)));
+        Thread thread = new Thread(propertyChangeEventRunnable);
+        thread.start();
+
+        // Wait until propertyChangeEventRunnable has triggered and the
+        // ICarDrivingStateChangeListener callback declared above started to run.
+        dispatchingStartedSignal.await();
+
+        // EVENT 3a [main thread]: init() is called, which locks CarUxRestrictionsManagerService
+        // If init() is synchronized, thereby locking CarUxRestrictionsManagerService, and it
+        // internally attempts to access CarDrivingStateService, and if CarDrivingStateService has
+        // been locked because of the above listener, then both classes are locked and waiting on
+        // each other, so we would encounter a deadlock.
+        uxRestrictionsService.init();
+
+        // If there is a deadlock in init(), then this will never be called
+        initCompleteSignal.countDown();
+
+        // wait for thread to join to leave in a deterministic state
+        try {
+            thread.join(5000);
+        } catch (InterruptedException e) {
+            Assert.fail("Thread failed to join");
+        }
+    }
+
+    // This test only involves calling a few methods and should finish very quickly. If it doesn't
+    // finish in 20s, we probably encountered a deadlock.
+    @Test(timeout = 20000)
+    public void testSetUxRChangeBroadcastEnabled_NoDeadlockWithCarDrivingStateService()
+            throws Exception {
+
+        CarDrivingStateService drivingStateService = new CarDrivingStateService(mSpyContext,
+                mMockCarPropertyService);
+        CarUxRestrictionsManagerService uxRestrictionService = new CarUxRestrictionsManagerService(
+                mSpyContext, drivingStateService, mMockCarPropertyService);
+
+        CountDownLatch dispatchingStartedSignal = new CountDownLatch(1);
+        CountDownLatch initCompleteSignal = new CountDownLatch(1);
+
+        // See testInitService_NoDeadlockWithCarDrivingStateService for details on why a deadlock
+        // may occur. This test could fail for the same reason, except the callback we register here
+        // is purely to introduce a delay, and the deadlock actually happens inside the callback
+        // that CarUxRestrictionsManagerService#init() registers internally.
+        drivingStateService.registerDrivingStateChangeListener(
+                new ICarDrivingStateChangeListener.Stub() {
+                    @Override
+                    public void onDrivingStateChanged(CarDrivingStateEvent event)
+                            throws RemoteException {
+                        // EVENT 2 [new thread]: this callback is called from within
+                        // handlePropertyEvent(), which might (but shouldn't) lock
+                        // CarDrivingStateService
+
+                        // Notify that the dispatching process has started
+                        dispatchingStartedSignal.countDown();
+
+                        try {
+                            // EVENT 3b [new thread]: Wait until init() has finished. If these
+                            // threads don't have lock dependencies, there is no reason there
+                            // would be an issue with waiting.
+                            //
+                            // In the real world, this wait could represent a long-running
+                            // task, or hitting the line inside
+                            // CarUxRestrictionsManagerService#init()'s internal registration
+                            // that attempts to access the CarUxRestrictionsManagerService (which
+                            // might be locked while init() is running).
+                            //
+                            // If there is a deadlock while waiting for init to complete, we will
+                            // never progress past this line.
+                            initCompleteSignal.await();
+                        } catch (InterruptedException e) {
+                            Assert.fail("onDrivingStateChanged thread interrupted");
+                        }
+                    }
+                });
+
+        // The init() method internally registers a callback to CarDrivingStateService
+        uxRestrictionService.init();
+
+        // EVENT 1 [new thread]: handlePropertyEvent() is called, which locks CarDrivingStateService
+        // Ideally CarPropertyService would trigger the change event, but since that is mocked
+        // we manually trigger the event. This event eventually triggers the dispatch to
+        // ICarDrivingStateChangeListener that was defined above and a dispatch to the registration
+        // that CarUxRestrictionsManagerService internally made to CarDrivingStateService in
+        // CarUxRestrictionsManagerService#init().
+        Runnable propertyChangeEventRunnable =
+                () -> drivingStateService.handlePropertyEvent(
+                        new CarPropertyEvent(CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE,
+                                new CarPropertyValue<>(
+                                        VehiclePropertyIds.PERF_VEHICLE_SPEED, 0, 100f)));
+        Thread thread = new Thread(propertyChangeEventRunnable);
+        thread.start();
+
+        // Wait until propertyChangeEventRunnable has triggered and the
+        // ICarDrivingStateChangeListener callback declared above started to run.
+        dispatchingStartedSignal.await();
+
+        // EVENT 3a [main thread]: a synchronized method is called, which locks
+        // CarUxRestrictionsManagerService
+        //
+        // Any synchronized method that internally accesses CarDrivingStateService could encounter a
+        // deadlock if the above setup locks CarDrivingStateService.
+        uxRestrictionService.setUxRChangeBroadcastEnabled(true);
+
+        // If there is a deadlock in init(), then this will never be called
+        initCompleteSignal.countDown();
+
+        // wait for thread to join to leave in a deterministic state
+        try {
+            thread.join(5000);
+        } catch (InterruptedException e) {
+            Assert.fail("Thread failed to join");
+        }
+    }
+
+
     private CarUxRestrictionsConfiguration createEmptyConfig() {
         return createEmptyConfig(null);
     }
diff --git a/tests/carservice_test/src/com/android/car/MockedCarTestBase.java b/tests/carservice_test/src/com/android/car/MockedCarTestBase.java
index 894c402..5989000 100644
--- a/tests/carservice_test/src/com/android/car/MockedCarTestBase.java
+++ b/tests/carservice_test/src/com/android/car/MockedCarTestBase.java
@@ -261,11 +261,10 @@
         if (mRealCarServiceReleased) {
             return;  // We just want to release it once.
         }
-
         mRealCarServiceReleased = true;  // To make sure it was called once.
 
         Object waitForConnection = new Object();
-        android.car.Car car = android.car.Car.createCar(context, new ServiceConnection() {
+        Car car = android.car.Car.createCar(context, new ServiceConnection() {
             @Override
             public void onServiceConnected(ComponentName name, IBinder service) {
                 synchronized (waitForConnection) {
@@ -287,10 +286,10 @@
         if (car.isConnected()) {
             Log.i(TAG, "Connected to real car service");
             CarTestManagerBinderWrapper binderWrapper =
-                    (CarTestManagerBinderWrapper) car.getCarManager(android.car.Car.TEST_SERVICE);
+                    (CarTestManagerBinderWrapper) car.getCarManager(Car.TEST_SERVICE);
             assertNotNull(binderWrapper);
 
-            CarTestManager mgr = new CarTestManager(binderWrapper.binder);
+            CarTestManager mgr = new CarTestManager(car, binderWrapper.binder);
             mgr.stopCarService(mCarServiceToken);
         }
     }
diff --git a/tests/carservice_unit_test/AndroidManifest.xml b/tests/carservice_unit_test/AndroidManifest.xml
index e5e31bf..5ed59ef 100644
--- a/tests/carservice_unit_test/AndroidManifest.xml
+++ b/tests/carservice_unit_test/AndroidManifest.xml
@@ -21,6 +21,7 @@
     <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
             android:targetPackage="com.android.car.carservice_unittest"
             android:label="Unit Tests for Car APIs"/>
+    <uses-permission android:name="android.car.permission.CAR_ENROLL_TRUST" />
 
     <application android:label="CarServiceUnitTest"
             android:debuggable="true">
diff --git a/tests/carservice_unit_test/src/android/car/CarTest.java b/tests/carservice_unit_test/src/android/car/CarTest.java
index 9ac8d75..d12334d 100644
--- a/tests/carservice_unit_test/src/android/car/CarTest.java
+++ b/tests/carservice_unit_test/src/android/car/CarTest.java
@@ -22,6 +22,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static junit.framework.Assert.fail;
+
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Matchers.anyObject;
 import static org.mockito.Mockito.times;
@@ -177,7 +179,13 @@
         runOnMainSyncSafe(() -> {
             car.getServiceConnectionListener().onServiceConnected(new ComponentName("", ""),
                     mService);
-            car.getServiceConnectionListener().onServiceDisconnected(new ComponentName("", ""));
+            try {
+                car.getServiceConnectionListener().onServiceDisconnected(new ComponentName("", ""));
+            } catch (IllegalStateException e) {
+                // expected
+                return;
+            }
+            fail("onServiceDisconnected should have triggered exception");
         });
     }
 
diff --git a/tests/carservice_unit_test/src/com/android/car/hal/PropertyHalServiceTest.java b/tests/carservice_unit_test/src/com/android/car/hal/PropertyHalServiceTest.java
new file mode 100644
index 0000000..6f7fe18
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/hal/PropertyHalServiceTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.hal;
+
+import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@RunWith(AndroidJUnit4.class)
+public class PropertyHalServiceTest {
+    @Rule
+    public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+    @Mock
+    private VehicleHal mVehicleHal;
+
+    private PropertyHalService mPropertyHalService;
+    private static final int[] UNITS_PROPERTY_ID = {
+            VehicleProperty.DISTANCE_DISPLAY_UNITS,
+            VehicleProperty.FUEL_CONSUMPTION_UNITS_DISTANCE_OVER_VOLUME,
+            VehicleProperty.FUEL_VOLUME_DISPLAY_UNITS,
+            VehicleProperty.TIRE_PRESSURE_DISPLAY_UNITS,
+            VehicleProperty.EV_BATTERY_DISPLAY_UNITS,
+            VehicleProperty.VEHICLE_SPEED_DISPLAY_UNITS};
+
+    @Before
+    public void setUp() {
+        mPropertyHalService = new PropertyHalService(mVehicleHal);
+        mPropertyHalService.init();
+    }
+
+    @After
+    public void tearDown() {
+        mPropertyHalService.release();
+        mPropertyHalService = null;
+    }
+
+    @Test
+    public void checkDisplayUnitsProperty() {
+        for (int propId : UNITS_PROPERTY_ID) {
+            Assert.assertTrue(mPropertyHalService.isDisplayUnitsProperty(propId));
+        }
+    }
+}
diff --git a/tests/carservice_unit_test/src/com/android/car/vms/VmsClientManagerTest.java b/tests/carservice_unit_test/src/com/android/car/vms/VmsClientManagerTest.java
index b90dac4..73217e7 100644
--- a/tests/carservice_unit_test/src/com/android/car/vms/VmsClientManagerTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/vms/VmsClientManagerTest.java
@@ -97,6 +97,8 @@
     private static final String HAL_CLIENT_NAME = "HalClient";
     private static final String UNKNOWN_PACKAGE = "UnknownPackage";
 
+    private static final long MILLIS_BEFORE_REBIND = 100;
+
     @Rule
     public MockitoRule mMockitoRule = MockitoJUnit.rule();
     @Mock
@@ -120,6 +122,12 @@
     private VmsHalService mHal;
 
     @Mock
+    private Handler mHandler;
+
+    @Captor
+    private ArgumentCaptor<Runnable> mRebindCaptor;
+
+    @Mock
     private VmsPublisherService mPublisherService;
 
     @Mock
@@ -152,7 +160,7 @@
 
         when(mResources.getInteger(
                 com.android.car.R.integer.millisecondsBeforeRebindToVmsPublisher)).thenReturn(
-                5);
+                (int) MILLIS_BEFORE_REBIND);
         when(mResources.getStringArray(
                 com.android.car.R.array.vmsPublisherSystemClients)).thenReturn(
                 new String[]{ SYSTEM_CLIENT });
@@ -168,7 +176,7 @@
         mCallingAppUid = UserHandle.getUid(USER_ID, 0);
 
         mClientManager = new VmsClientManager(mContext, mBrokerService, mUserService,
-                mUserManagerHelper, mHal, () -> mCallingAppUid);
+                mUserManagerHelper, mHal, mHandler, () -> mCallingAppUid);
         verify(mHal).setClientManager(mClientManager);
         mClientManager.setPublisherService(mPublisherService);
 
@@ -180,11 +188,10 @@
 
     @After
     public void tearDown() throws Exception {
-        Thread.sleep(10); // Time to allow for delayed rebinds to settle
         verify(mContext, atLeast(0)).getSystemService(eq(Context.USER_SERVICE));
         verify(mContext, atLeast(0)).getResources();
         verify(mContext, atLeast(0)).getPackageManager();
-        verifyNoMoreInteractions(mContext, mBrokerService, mHal, mPublisherService);
+        verifyNoMoreInteractions(mContext, mBrokerService, mHal, mPublisherService, mHandler);
     }
 
     @Test
@@ -467,7 +474,7 @@
         connection.onServiceDisconnected(null);
         verify(mPublisherService).onClientDisconnected(eq(SYSTEM_CLIENT_NAME));
 
-        Thread.sleep(10);
+        verifyAndRunRebindTask();
         verify(mContext).unbindService(connection);
         verifySystemBind(1);
     }
@@ -490,6 +497,8 @@
         binder = createPublisherBinder();
         connection.onServiceConnected(null, binder);
         verifyOnClientConnected(SYSTEM_CLIENT_NAME, binder);
+
+        verifyAndRunRebindTask();
         // No more interactions (verified by tearDown)
     }
 
@@ -507,7 +516,7 @@
         connection.onBindingDied(null);
         verify(mPublisherService).onClientDisconnected(eq(SYSTEM_CLIENT_NAME));
 
-        Thread.sleep(10);
+        verifyAndRunRebindTask();
         verify(mContext).unbindService(connection);
         verifySystemBind(1);
     }
@@ -523,7 +532,7 @@
 
         verifyZeroInteractions(mPublisherService);
 
-        Thread.sleep(10);
+        verifyAndRunRebindTask();
         verify(mContext).unbindService(connection);
         verifySystemBind(1);
     }
@@ -541,7 +550,7 @@
         connection.onServiceDisconnected(null);
         verify(mPublisherService).onClientDisconnected(eq(USER_CLIENT_NAME));
 
-        Thread.sleep(10);
+        verifyAndRunRebindTask();
         verify(mContext).unbindService(connection);
         verifyUserBind(1);
     }
@@ -564,6 +573,8 @@
         binder = createPublisherBinder();
         connection.onServiceConnected(null, binder);
         verifyOnClientConnected(USER_CLIENT_NAME, binder);
+
+        verifyAndRunRebindTask();
         // No more interactions (verified by tearDown)
     }
 
@@ -580,7 +591,7 @@
         connection.onBindingDied(null);
         verify(mPublisherService).onClientDisconnected(eq(USER_CLIENT_NAME));
 
-        Thread.sleep(10);
+        verifyAndRunRebindTask();
         verify(mContext).unbindService(connection);
         verifyUserBind(1);
     }
@@ -596,7 +607,7 @@
 
         verifyZeroInteractions(mPublisherService);
 
-        Thread.sleep(10);
+        verifyAndRunRebindTask();
         verify(mContext).unbindService(connection);
         verifyUserBind(1);
     }
@@ -975,6 +986,11 @@
                 eq(Context.BIND_AUTO_CREATE), any(Handler.class), eq(user));
     }
 
+    private void verifyAndRunRebindTask() {
+        verify(mHandler).postDelayed(mRebindCaptor.capture(), eq(MILLIS_BEFORE_REBIND));
+        mRebindCaptor.getValue().run();
+    }
+
     private void verifyOnClientConnected(String publisherName, IBinder binder) {
         ArgumentCaptor<IVmsPublisherClient> clientCaptor =
                 ArgumentCaptor.forClass(IVmsPublisherClient.class);
diff --git a/user/car-user-lib/src/android/car/userlib/CarUserManagerHelper.java b/user/car-user-lib/src/android/car/userlib/CarUserManagerHelper.java
index 0745945..72332d9 100644
--- a/user/car-user-lib/src/android/car/userlib/CarUserManagerHelper.java
+++ b/user/car-user-lib/src/android/car/userlib/CarUserManagerHelper.java
@@ -238,10 +238,8 @@
         // If an override user is present and a real user, return it
         if (bootUserOverride != BOOT_USER_NOT_FOUND
                 && allUsers.contains(bootUserOverride)) {
-            if (Log.isLoggable(TAG, Log.DEBUG)) {
-                Log.d(TAG, "Boot user id override found for initial user, user id: "
-                        + bootUserOverride);
-            }
+            Log.i(TAG, "Boot user id override found for initial user, user id: "
+                    + bootUserOverride);
             return bootUserOverride;
         }
 
@@ -249,19 +247,15 @@
         int lastActiveUser = getLastActiveUser();
         if (lastActiveUser != UserHandle.USER_SYSTEM
                 && allUsers.contains(lastActiveUser)) {
-            if (Log.isLoggable(TAG, Log.DEBUG)) {
-                Log.d(TAG, "Last active user loaded for initial user, user id: "
-                        + lastActiveUser);
-            }
+            Log.i(TAG, "Last active user loaded for initial user, user id: "
+                    + lastActiveUser);
             return lastActiveUser;
         }
 
         // If all else fails, return the smallest user id
         int returnId = Collections.min(allUsers);
-        if (Log.isLoggable(TAG, Log.DEBUG)) {
-            Log.d(TAG, "Saved ids were invalid. Returning smallest user id, user id: "
-                    + returnId);
-        }
+        Log.i(TAG, "Saved ids were invalid. Returning smallest user id, user id: "
+                + returnId);
         return returnId;
     }