Merge "Limit preferSource package to Maps." into rvc-dev
diff --git a/FrameworkPackageStubs/AndroidManifest.xml b/FrameworkPackageStubs/AndroidManifest.xml
index 912944c..6c25bcd 100644
--- a/FrameworkPackageStubs/AndroidManifest.xml
+++ b/FrameworkPackageStubs/AndroidManifest.xml
@@ -95,6 +95,10 @@
                 <action android:name="android.settings.USER_DICTIONARY_SETTINGS" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
+            <intent-filter android:priority="-1">
+                <action android:name="android.settings.PICTURE_IN_PICTURE_SETTINGS" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
         </activity>
 
         <!-- CDD Core Application Intents Stubs -->
diff --git a/car-internal-lib/src/com/android/internal/car/EventLogTags.logtags b/car-internal-lib/src/com/android/internal/car/EventLogTags.logtags
index 5fec33f..46af12b 100644
--- a/car-internal-lib/src/com/android/internal/car/EventLogTags.logtags
+++ b/car-internal-lib/src/com/android/internal/car/EventLogTags.logtags
@@ -78,6 +78,14 @@
 150112 car_user_svc_switch_user_from_hal_req (request_id|1),(uid|1)
 150113 car_user_svc_set_user_auth_req (uid|1),(user_id|1),(number_associations|1)
 150114 car_user_svc_set_user_auth_resp (number_values|1),(error_message|3)
+150115 car_user_svc_create_user_req (safe_name|3),(user_type|3),(flags|1),(timeout|1)
+150116 car_user_svc_create_user_resp (status|1),(result|1),(error_message|3)
+150117 car_user_svc_create_user_user_created (user_id|1),(safe_name|3),(user_type|3),(flags|1)
+150118 car_user_svc_create_user_user_removed (user_id|1),(reason|3)
+150119 car_user_svc_remove_user_req (user_id|1)
+150120 car_user_svc_remove_user_resp (user_id|1),(result|1)
+150121 car_user_svc_notify_app_lifecycle_listener (uid|1),(event_type|1),(from_user_id|1),(to_user_id|1)
+150122 car_user_svc_notify_internal_lifecycle_listener (listener_name|3),(event_type|1),(from_user_id|1),(to_user_id|1)
 
 150140 car_user_hal_initial_user_info_req (request_id|1),(request_type|1),(timeout|1)
 150141 car_user_hal_initial_user_info_resp (request_id|1),(status|1),(action|1),(user_id|1),(flags|1),(safe_name|3),(user_locales|3)
@@ -92,13 +100,19 @@
 150150 car_user_hal_oem_switch_user_req (request_id|1),(target_user_id|1)
 150151 car_user_hal_create_user_req (request_id|1),(safe_name|3),(flags|1),(timeout|1)
 150152 car_user_hal_create_user_resp (request_id|1),(status|1),(result|1),(error_message|3)
+150153 car_user_hal_remove_user_req (target_user_id|1),(current_user_id|1)
 
 150171 car_user_mgr_add_listener (uid|1)
 150172 car_user_mgr_remove_listener (uid|1)
 150173 car_user_mgr_disconnected (uid|1)
-150174 car_user_mgr_switch_user_request (uid|1),(user_id|1)
-150175 car_user_mgr_switch_user_response (uid|1),(status|1),(error_message|3)
+150174 car_user_mgr_switch_user_req (uid|1),(user_id|1)
+150175 car_user_mgr_switch_user_resp (uid|1),(status|1),(error_message|3)
 150176 car_user_mgr_get_user_auth_req (types|4)
 150177 car_user_mgr_get_user_auth_resp (values|4)
 150178 car_user_mgr_set_user_auth_req (types_and_values_pairs|4)
 150179 car_user_mgr_set_user_auth_resp (values|4)
+150180 car_user_mgr_create_user_req (uid|1),(safe_name|3),(user_type|3),(flags|1)
+150181 car_user_mgr_create_user_resp (uid|1),(status|1),(error_message|3)
+150182 car_user_mgr_remove_user_req (uid|1),(user_id|1)
+150183 car_user_mgr_remove_user_resp (uid|1),(status|1)
+150184 car_user_mgr_notify_lifecycle_listener (number_listeners|1),(event_type|1),(from_user_id|1),(to_user_id|1)
diff --git a/car-lib/Android.bp b/car-lib/Android.bp
index 18a15c0..39db28e 100644
--- a/car-lib/Android.bp
+++ b/car-lib/Android.bp
@@ -139,6 +139,7 @@
 droidstubs {
     name: "android.car-stubs-docs",
     defaults: ["android.car-docs-default"],
+    removed_dex_api_filename: "removed-dex.txt",
     args: "--hide UnavailableSymbol --no-docs --stub-packages android.car* ",
     installable: false,
     check_api: {
@@ -166,6 +167,7 @@
 droidstubs {
     name: "android.car-system-stubs-docs",
     defaults: ["android.car-docs-default"],
+    removed_dex_api_filename: "system-removed-dex.txt",
     args: "--hide UnavailableSymbol --no-docs --stub-packages android.car* " +
         "--show-annotation android.annotation.SystemApi ",
     installable: false,
@@ -194,7 +196,7 @@
 droidstubs {
     name: "android.car-test-stubs-docs",
     defaults: ["android.car-docs-default"],
-    args: "--hide UnavailableSymbol --no-docs --stub-packages android.car* " +
+    args: "--hide HiddenSuperclass --hide UnavailableSymbol --no-docs --stub-packages android.car* " +
         "--show-annotation android.annotation.TestApi ",
     installable: false,
     check_api: {
@@ -217,7 +219,7 @@
         "android.car",
     ],
     api_filename: "api.txt",
-    args: "--hide UnavailableSymbol --no-docs --stub-packages android.car* ",
+    args: "--hide HiddenSuperclass --hide UnavailableSymbol --no-docs --stub-packages android.car* ",
     installable: false,
     product_variables: {
         pdk: {
diff --git a/car-lib/api/system-current.txt b/car-lib/api/system-current.txt
index 4a966d4..c404acd 100644
--- a/car-lib/api/system-current.txt
+++ b/car-lib/api/system-current.txt
@@ -955,6 +955,17 @@
 
 }
 
+package android.car.settings {
+
+  public class CarSettings {
+  }
+
+  public static final class CarSettings.Secure {
+    field public static final String KEY_AUDIO_FOCUS_NAVIGATION_REJECTED_DURING_CALL = "android.car.KEY_AUDIO_FOCUS_NAVIGATION_REJECTED_DURING_CALL";
+  }
+
+}
+
 package android.car.storagemonitoring {
 
   public final class CarStorageMonitoringManager {
diff --git a/car-lib/src/android/car/CarAppFocusManager.java b/car-lib/src/android/car/CarAppFocusManager.java
index 3d4d90d..cc0d10b 100644
--- a/car-lib/src/android/car/CarAppFocusManager.java
+++ b/car-lib/src/android/car/CarAppFocusManager.java
@@ -45,8 +45,11 @@
         /**
          * Application focus has changed. Note that {@link CarAppFocusManager} instance
          * causing the change will not get this notification.
-         * @param appType
-         * @param active
+         *
+         * <p>Note that this call can happen for app focus grant, release, and ownership change.
+         *
+         * @param appType appType where the focus change has happened.
+         * @param active {@code true} if there is an active owner for the focus.
          */
         void onAppFocusChanged(@AppFocusType int appType, boolean active);
     }
diff --git a/car-lib/src/android/car/ICarUserService.aidl b/car-lib/src/android/car/ICarUserService.aidl
index 921c146..b3823b3 100644
--- a/car-lib/src/android/car/ICarUserService.aidl
+++ b/car-lib/src/android/car/ICarUserService.aidl
@@ -17,6 +17,8 @@
 package android.car;
 
 import android.content.pm.UserInfo;
+import android.car.user.UserCreationResult;
+import android.car.user.UserRemovalResult;
 import android.car.user.UserIdentificationAssociationResponse;
 import android.car.user.UserSwitchResult;
 import com.android.internal.infra.AndroidFuture;
@@ -24,10 +26,14 @@
 
 /** @hide */
 interface ICarUserService {
-    UserInfo createDriver(in String name, boolean admin);
-    UserInfo createPassenger(in String name, int driverId);
+    AndroidFuture<UserCreationResult> createDriver(@nullable String name, boolean admin);
+    UserInfo createPassenger(@nullable String name, int driverId);
     void switchDriver(int driverId, in AndroidFuture<UserSwitchResult> receiver);
     void switchUser(int tagerUserId, int timeoutMs, in AndroidFuture<UserSwitchResult> receiver);
+    void setUserSwitchUiCallback(in IResultReceiver callback);
+    void createUser(@nullable String name, String userType, int flags, int timeoutMs,
+      in AndroidFuture<UserCreationResult> receiver);
+    UserRemovalResult removeUser(int userId);
     List<UserInfo> getAllDrivers();
     List<UserInfo> getPassengers(int driverId);
     boolean startPassenger(int passengerId, int zoneId);
@@ -38,5 +44,4 @@
     UserIdentificationAssociationResponse getUserIdentificationAssociation(in int[] types);
     void setUserIdentificationAssociation(int timeoutMs, in int[] types, in int[] values,
       in AndroidFuture<UserIdentificationAssociationResponse> result);
-    void setUserSwitchUiCallback(in IResultReceiver callback);
 }
diff --git a/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java b/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java
index 5272548..cdf0cca 100644
--- a/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java
+++ b/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java
@@ -412,13 +412,13 @@
             startActivityAsUser(intent, mActivityOptions.toBundle(), UserHandle.CURRENT);
             Log.i(TAG, String.format("Activity launched: %s (options: %s, displayId: %d)",
                     mActivityOptions, intent, mActivityOptions.getLaunchDisplayId()));
-        } catch (ActivityNotFoundException ex) {
+        } catch (ActivityNotFoundException e) {
             Log.w(TAG, "Unable to find activity for intent: " + intent);
             return false;
-        } catch (Exception ex) {
+        } catch (RuntimeException e) {
             // Catch all other possible exception to prevent service disruption by misbehaving
             // applications.
-            Log.e(TAG, "Error trying to launch intent: " + intent + ". Ignored", ex);
+            Log.e(TAG, "Error trying to launch intent: " + intent + ". Ignored", e);
             return false;
         }
         return true;
diff --git a/car-lib/src/android/car/content/pm/AppBlockingPackageInfo.java b/car-lib/src/android/car/content/pm/AppBlockingPackageInfo.java
index dbabc50..001c1d2 100644
--- a/car-lib/src/android/car/content/pm/AppBlockingPackageInfo.java
+++ b/car-lib/src/android/car/content/pm/AppBlockingPackageInfo.java
@@ -123,10 +123,13 @@
 
     public static final Parcelable.Creator<AppBlockingPackageInfo> CREATOR =
             new Parcelable.Creator<AppBlockingPackageInfo>() {
+
+                @Override
                 public AppBlockingPackageInfo createFromParcel(Parcel in) {
                     return new AppBlockingPackageInfo(in);
                 }
 
+                @Override
                 public AppBlockingPackageInfo[] newArray(int size) {
                     return new AppBlockingPackageInfo[size];
                 }
diff --git a/car-lib/src/android/car/content/pm/CarPackageManager.java b/car-lib/src/android/car/content/pm/CarPackageManager.java
index 98c5822..6a8c7fb 100644
--- a/car-lib/src/android/car/content/pm/CarPackageManager.java
+++ b/car-lib/src/android/car/content/pm/CarPackageManager.java
@@ -123,6 +123,8 @@
     /**
      * Restarts the requested task. If task with {@code taskId} does not exist, do nothing.
      *
+     * <p>This requires {@code android.permission.REAL_GET_TASKS} permission.
+     *
      * @hide
      */
     public void restartTask(int taskId) {
diff --git a/car-lib/src/android/car/hardware/hvac/CarHvacManager.java b/car-lib/src/android/car/hardware/hvac/CarHvacManager.java
index f6a2d53..971f1fc 100644
--- a/car-lib/src/android/car/hardware/hvac/CarHvacManager.java
+++ b/car-lib/src/android/car/hardware/hvac/CarHvacManager.java
@@ -29,6 +29,8 @@
 import android.util.ArraySet;
 import android.util.Log;
 
+import com.android.internal.annotations.GuardedBy;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
@@ -45,12 +47,15 @@
 @Deprecated
 @SystemApi
 public final class CarHvacManager extends CarManagerBase {
-    private final static boolean DBG = false;
-    private final static String TAG = "CarHvacManager";
+    private static final boolean DBG = false;
+    private static final String TAG = "CarHvacManager";
     private final CarPropertyManager mCarPropertyMgr;
-    private final ArraySet<CarHvacEventCallback> mCallbacks = new ArraySet<>();
     private CarPropertyEventListenerToBase mListenerToBase = null;
 
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
+    private final ArraySet<CarHvacEventCallback> mCallbacks = new ArraySet<>();
 
     /**
      * HVAC property IDs for get/set methods
@@ -252,7 +257,7 @@
     private static class CarPropertyEventListenerToBase implements CarPropertyEventCallback {
         private final WeakReference<CarHvacManager> mManager;
 
-        public CarPropertyEventListenerToBase(CarHvacManager manager) {
+        CarPropertyEventListenerToBase(CarHvacManager manager) {
             mManager = new WeakReference<>(manager);
         }
 
@@ -275,7 +280,7 @@
 
     private void handleOnChangeEvent(CarPropertyValue value) {
         Collection<CarHvacEventCallback> callbacks;
-        synchronized (this) {
+        synchronized (mLock) {
             callbacks = new ArraySet<>(mCallbacks);
         }
         if (!callbacks.isEmpty()) {
@@ -287,7 +292,7 @@
 
     private void handleOnErrorEvent(int propertyId, int zone) {
         Collection<CarHvacEventCallback> callbacks;
-        synchronized (this) {
+        synchronized (mLock) {
             callbacks = new ArraySet<>(mCallbacks);
         }
         if (!callbacks.isEmpty()) {
@@ -315,16 +320,18 @@
      * Implement wrappers for contained CarPropertyManager object
      * @param callback
      */
-    public synchronized void registerCallback(CarHvacEventCallback callback) {
-        if (mCallbacks.isEmpty()) {
-            mListenerToBase = new CarPropertyEventListenerToBase(this);
-        }
-        List<CarPropertyConfig> configs = getPropertyList();
-        for (CarPropertyConfig c : configs) {
+    public void registerCallback(CarHvacEventCallback callback) {
+        synchronized (mLock) {
+            if (mCallbacks.isEmpty()) {
+                mListenerToBase = new CarPropertyEventListenerToBase(this);
+            }
+            List<CarPropertyConfig> configs = getPropertyList();
+            for (CarPropertyConfig c : configs) {
                 // Register each individual propertyId
-            mCarPropertyMgr.registerCallback(mListenerToBase, c.getPropertyId(), 0);
+                mCarPropertyMgr.registerCallback(mListenerToBase, c.getPropertyId(), 0);
+            }
+            mCallbacks.add(callback);
         }
-        mCallbacks.add(callback);
     }
 
     /**
@@ -332,21 +339,23 @@
      * this listener, all listening will be stopped.
      * @param callback
      */
-    public synchronized void unregisterCallback(CarHvacEventCallback callback) {
-        mCallbacks.remove(callback);
-        try {
-            List<CarPropertyConfig> configs = getPropertyList();
-            for (CarPropertyConfig c : configs) {
-                // Register each individual propertyId
-                mCarPropertyMgr.unregisterCallback(mListenerToBase, c.getPropertyId());
+    public void unregisterCallback(CarHvacEventCallback callback) {
+        synchronized (mLock) {
+            mCallbacks.remove(callback);
+            try {
+                List<CarPropertyConfig> configs = getPropertyList();
+                for (CarPropertyConfig c : configs) {
+                    // Register each individual propertyId
+                    mCarPropertyMgr.unregisterCallback(mListenerToBase, c.getPropertyId());
 
+                }
+            } catch (RuntimeException e) {
+                Log.e(TAG, "getPropertyList exception ", e);
             }
-        } catch (Exception e) {
-            Log.e(TAG, "getPropertyList exception ", e);
-        }
-        if (mCallbacks.isEmpty()) {
-            mCarPropertyMgr.unregisterCallback(mListenerToBase);
-            mListenerToBase = null;
+            if (mCallbacks.isEmpty()) {
+                mCarPropertyMgr.unregisterCallback(mListenerToBase);
+                mListenerToBase = null;
+            }
         }
     }
 
@@ -435,7 +444,7 @@
     /** @hide */
     public void onCarDisconnected() {
         // TODO(b/142730482) Fix synchronization to use separate mLock
-        synchronized (this) {
+        synchronized (mLock) {
             mCallbacks.clear();
         }
         mCarPropertyMgr.onCarDisconnected();
diff --git a/car-lib/src/android/car/media/CarAudioManager.java b/car-lib/src/android/car/media/CarAudioManager.java
index 31eb173..9f57617 100644
--- a/car-lib/src/android/car/media/CarAudioManager.java
+++ b/car-lib/src/android/car/media/CarAudioManager.java
@@ -32,8 +32,6 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
-import android.view.Display;
-import android.view.DisplayAddress;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -538,43 +536,6 @@
     }
 
     /**
-     * Get the zone id for the display
-     *
-     * @param  display display to query
-     * @return zone id for display or
-     * CarAudioManager.PRIMARY_AUDIO_ZONE if no match is found.
-     * @hide
-     */
-    @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
-    public int getZoneIdForDisplay(Display display) {
-        DisplayAddress address = display.getAddress();
-        if (address instanceof DisplayAddress.Physical) {
-            DisplayAddress.Physical physicalAddress = (DisplayAddress.Physical) address;
-            if (physicalAddress != null) {
-                return getZoneIdForDisplayPortId(physicalAddress.getPort());
-            }
-        }
-        return PRIMARY_AUDIO_ZONE;
-    }
-
-    /**
-     * Get the zone id for the display port id passed in
-     *
-     * @param  displayPortId display port id to query
-     * @return zone id for display port id or
-     * CarAudioManager.PRIMARY_AUDIO_ZONE if no match is found.
-     * @hide
-     */
-    @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
-    public int getZoneIdForDisplayPortId(byte displayPortId) {
-        try {
-            return mService.getZoneIdForDisplayPortId(displayPortId);
-        } catch (RemoteException e) {
-            return handleRemoteExceptionFromCarService(e, 0);
-        }
-    }
-
-    /**
      * Gets the output device for a given {@link AudioAttributes} usage in zoneId.
      *
      * <p><b>Note:</b> To be used for routing to a specific device. Most applications should
diff --git a/car-lib/src/android/car/media/ICarAudio.aidl b/car-lib/src/android/car/media/ICarAudio.aidl
index 7491a19..a8997ce 100644
--- a/car-lib/src/android/car/media/ICarAudio.aidl
+++ b/car-lib/src/android/car/media/ICarAudio.aidl
@@ -48,8 +48,6 @@
     boolean setZoneIdForUid(int zoneId, int uid);
     boolean clearZoneIdForUid(int uid);
 
-    int getZoneIdForDisplayPortId(byte displayPortId);
-
     String getOutputDeviceAddressForUsage(int zoneId, int usage);
 
     List<AudioDeviceAttributes> getInputDevicesForZoneId(int zoneId);
diff --git a/car-lib/src/android/car/settings/CarSettings.java b/car-lib/src/android/car/settings/CarSettings.java
index 932eec0..3f5a6c3 100644
--- a/car-lib/src/android/car/settings/CarSettings.java
+++ b/car-lib/src/android/car/settings/CarSettings.java
@@ -23,6 +23,7 @@
  *
  * @hide
  */
+@SystemApi
 public class CarSettings {
 
     private CarSettings() {
@@ -73,6 +74,22 @@
                 "android.car.ENABLE_USER_SWITCH_DEVELOPER_MESSAGE";
 
         /**
+         * User id of the last foreground user
+         *
+         * @hide
+         */
+        public static final String LAST_ACTIVE_USER_ID =
+                        "android.car.LAST_ACTIVE_USER_ID";
+
+        /**
+         * User id of the last persistent (i.e, not counting ephemeral guests) foreground user
+         *
+         * @hide
+         */
+        public static final String LAST_ACTIVE_PERSISTENT_USER_ID =
+                        "android.car.LAST_ACTIVE_PERSISTENT_USER_ID";
+
+        /**
          * Defines global runtime overrides to system bar policy.
          *
          * See {@link com.android.systemui.wm.BarControlPolicy} for value format.
diff --git a/car-lib/src/android/car/storagemonitoring/CarStorageMonitoringManager.java b/car-lib/src/android/car/storagemonitoring/CarStorageMonitoringManager.java
index ac59bac..6f993e3 100644
--- a/car-lib/src/android/car/storagemonitoring/CarStorageMonitoringManager.java
+++ b/car-lib/src/android/car/storagemonitoring/CarStorageMonitoringManager.java
@@ -47,9 +47,17 @@
     private final SingleMessageHandler<IoStats> mMessageHandler;
     private final Set<IoStatsListener> mListeners = new HashSet<>();
 
+    /**
+     * Implementers will be notified on every new I/O activity calculated stats.
+     */
     public interface IoStatsListener {
+
+        /**
+         * Invoked when a new periodic snapshot delta of I/O activities is calculated.
+         */
         void onSnapshot(IoStats snapshot);
     }
+
     private static final class ListenerToService extends IIoStatsListener.Stub {
         private final WeakReference<CarStorageMonitoringManager> mManager;
 
@@ -109,7 +117,7 @@
      * It will return either PRE_EOL_INFO_UNKNOWN if the value can't be determined,
      * or one of PRE_EOL_INFO_{NORMAL|WARNING|URGENT} depending on the device state.
      */
-    @RequiresPermission(value=Car.PERMISSION_STORAGE_MONITORING)
+    @RequiresPermission(value = Car.PERMISSION_STORAGE_MONITORING)
     public int getPreEolIndicatorStatus() {
         try {
             return mService.getPreEolIndicatorStatus();
@@ -127,7 +135,7 @@
      *
      * If either or both indicators are not available, they will be reported as UNKNOWN.
      */
-    @RequiresPermission(value=Car.PERMISSION_STORAGE_MONITORING)
+    @RequiresPermission(value = Car.PERMISSION_STORAGE_MONITORING)
     public WearEstimate getWearEstimate() {
         try {
             return mService.getWearEstimate();
@@ -147,7 +155,7 @@
      *
      * If no indicators are available, an empty list will be returned.
      */
-    @RequiresPermission(value=Car.PERMISSION_STORAGE_MONITORING)
+    @RequiresPermission(value = Car.PERMISSION_STORAGE_MONITORING)
     public List<WearEstimateChange> getWearEstimateHistory() {
         try {
             return mService.getWearEstimateHistory();
@@ -166,7 +174,7 @@
      *
      * If the information is not available, an empty list will be returned.
      */
-    @RequiresPermission(value=Car.PERMISSION_STORAGE_MONITORING)
+    @RequiresPermission(value = Car.PERMISSION_STORAGE_MONITORING)
     public List<IoStatsEntry> getBootIoStats() {
         try {
             return mService.getBootIoStats();
@@ -196,7 +204,7 @@
      *
      * <p>If the information is not available, SHUTDOWN_COST_INFO_MISSING will be returned.</p>s
      */
-    @RequiresPermission(value=Car.PERMISSION_STORAGE_MONITORING)
+    @RequiresPermission(value = Car.PERMISSION_STORAGE_MONITORING)
     public long getShutdownDiskWriteAmount() {
         try {
             return mService.getShutdownDiskWriteAmount();
@@ -213,7 +221,7 @@
      *
      * If the information is not available, an empty list will be returned.
      */
-    @RequiresPermission(value=Car.PERMISSION_STORAGE_MONITORING)
+    @RequiresPermission(value = Car.PERMISSION_STORAGE_MONITORING)
     public List<IoStatsEntry> getAggregateIoStats() {
         try {
             return mService.getAggregateIoStats();
@@ -233,7 +241,7 @@
      *
      * If the information is not available, an empty list will be returned.
      */
-    @RequiresPermission(value=Car.PERMISSION_STORAGE_MONITORING)
+    @RequiresPermission(value = Car.PERMISSION_STORAGE_MONITORING)
     public List<IoStats> getIoStatsDeltas() {
         try {
             return mService.getIoStatsDeltas();
@@ -250,7 +258,7 @@
      *
      * The timing of availability of the deltas is configurable by the OEM.
      */
-    @RequiresPermission(value=Car.PERMISSION_STORAGE_MONITORING)
+    @RequiresPermission(value = Car.PERMISSION_STORAGE_MONITORING)
     public void registerListener(IoStatsListener listener) {
         try {
             if (mListeners.isEmpty()) {
@@ -268,7 +276,7 @@
     /**
      * This method removes a registered listener of I/O stats deltas.
      */
-    @RequiresPermission(value=Car.PERMISSION_STORAGE_MONITORING)
+    @RequiresPermission(value = Car.PERMISSION_STORAGE_MONITORING)
     public void unregisterListener(IoStatsListener listener) {
         try {
             if (!mListeners.remove(listener)) {
diff --git a/car-lib/src/android/car/storagemonitoring/WearEstimate.java b/car-lib/src/android/car/storagemonitoring/WearEstimate.java
index ee6aa31..07ef4c7 100644
--- a/car-lib/src/android/car/storagemonitoring/WearEstimate.java
+++ b/car-lib/src/android/car/storagemonitoring/WearEstimate.java
@@ -47,11 +47,10 @@
     public static final WearEstimate UNKNOWN_ESTIMATE = new WearEstimate(UNKNOWN, UNKNOWN);
 
     public static final Parcelable.Creator<WearEstimate> CREATOR =
-        new Parcelable.Creator<WearEstimate>() {
-            public WearEstimate createFromParcel(Parcel in) {
+            new Parcelable.Creator<WearEstimate>() {
+        public WearEstimate createFromParcel(Parcel in) {
                 return new WearEstimate(in);
             }
-
             public WearEstimate[] newArray(int size) {
                 return new WearEstimate[size];
             }
@@ -60,16 +59,16 @@
     /**
      * Wear estimate data for "type A" storage.
      */
-    @IntRange(from=-1, to=100)
+    @IntRange(from = -1, to = 100)
     public final int typeA;
 
     /**
      * Wear estimate data for "type B" storage.
      */
-    @IntRange(from=-1, to=100)
+    @IntRange(from = -1, to = 100)
     public final int typeB;
 
-    private static final int validateWearValue(int value) {
+    private static int validateWearValue(int value) {
         if (value == UNKNOWN) return value;
         if ((value >= 0) && (value <= 100)) return value;
         throw new IllegalArgumentException(value + " is not a valid wear estimate");
@@ -137,7 +136,7 @@
     @Override
     public boolean equals(Object other) {
         if (other instanceof WearEstimate) {
-            WearEstimate wo = (WearEstimate)other;
+            WearEstimate wo = (WearEstimate) other;
             return wo.typeA == typeA && wo.typeB == typeB;
         }
         return false;
@@ -148,7 +147,7 @@
         return Objects.hash(typeA, typeB);
     }
 
-    private static final String wearValueToString(int value) {
+    private static String wearValueToString(int value) {
         if (value == UNKNOWN) return "unknown";
         return value + "%";
     }
diff --git a/car-lib/src/android/car/storagemonitoring/WearEstimateChange.java b/car-lib/src/android/car/storagemonitoring/WearEstimateChange.java
index 2d454b4..a2839d5 100644
--- a/car-lib/src/android/car/storagemonitoring/WearEstimateChange.java
+++ b/car-lib/src/android/car/storagemonitoring/WearEstimateChange.java
@@ -15,15 +15,16 @@
  */
 package android.car.storagemonitoring;
 
+import static java.util.Objects.requireNonNull;
+
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
+
 import java.time.Instant;
 import java.util.Objects;
 
-import static java.util.Objects.requireNonNull;
-
 /**
  * Change in wear-out information.
  *
@@ -35,15 +36,15 @@
 @SystemApi
 public final class WearEstimateChange implements Parcelable {
     public static final Parcelable.Creator<WearEstimateChange> CREATOR =
-        new Parcelable.Creator<WearEstimateChange>() {
-            public WearEstimateChange createFromParcel(Parcel in) {
-                return new WearEstimateChange(in);
-            }
+            new Parcelable.Creator<WearEstimateChange>() {
+        public WearEstimateChange createFromParcel(Parcel in) {
+            return new WearEstimateChange(in);
+        }
 
-            public WearEstimateChange[] newArray(int size) {
+        public WearEstimateChange[] newArray(int size) {
                 return new WearEstimateChange[size];
             }
-        };
+    };
 
     /**
      * The previous wear estimate.
@@ -110,12 +111,12 @@
     @Override
     public boolean equals(Object other) {
         if (other instanceof WearEstimateChange) {
-            WearEstimateChange wo = (WearEstimateChange)other;
-            return wo.isAcceptableDegradation == isAcceptableDegradation &&
-                wo.uptimeAtChange == uptimeAtChange &&
-                wo.dateAtChange.equals(dateAtChange) &&
-                wo.oldEstimate.equals(oldEstimate) &&
-                wo.newEstimate.equals(newEstimate);
+            WearEstimateChange wo = (WearEstimateChange) other;
+            return wo.isAcceptableDegradation == isAcceptableDegradation
+                    && wo.uptimeAtChange == uptimeAtChange
+                    && wo.dateAtChange.equals(dateAtChange)
+                    && wo.oldEstimate.equals(oldEstimate)
+                    && wo.newEstimate.equals(newEstimate);
         }
         return false;
     }
diff --git a/car-lib/src/android/car/user/CarUserManager.java b/car-lib/src/android/car/user/CarUserManager.java
index 048ae0b..a4daa42 100644
--- a/car-lib/src/android/car/user/CarUserManager.java
+++ b/car-lib/src/android/car/user/CarUserManager.java
@@ -20,6 +20,8 @@
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
 import static android.os.Process.myUid;
 
+import static com.android.internal.util.FunctionalUtils.getLambdaName;
+
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -32,6 +34,7 @@
 import android.car.CarManagerBase;
 import android.car.ICarUserService;
 import android.content.pm.UserInfo;
+import android.content.pm.UserInfo.UserInfoFlag;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -56,6 +59,7 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.Executor;
+import java.util.stream.Collectors;
 
 /**
  * API to manage users related to car.
@@ -208,7 +212,7 @@
                 @Override
                 protected void onCompleted(UserSwitchResult result, Throwable err) {
                     if (result != null) {
-                        EventLog.writeEvent(EventLogTags.CAR_USER_MGR_SWITCH_USER_RESPONSE, uid,
+                        EventLog.writeEvent(EventLogTags.CAR_USER_MGR_SWITCH_USER_RESP, uid,
                                 result.getStatus(), result.getErrorMessage());
                     } else {
                         Log.w(TAG, "switchUser(" + targetUserId + ") failed: " + err);
@@ -216,7 +220,7 @@
                     super.onCompleted(result, err);
                 };
             };
-            EventLog.writeEvent(EventLogTags.CAR_USER_MGR_SWITCH_USER_REQUEST, uid, targetUserId);
+            EventLog.writeEvent(EventLogTags.CAR_USER_MGR_SWITCH_USER_REQ, uid, targetUserId);
             mService.switchUser(targetUserId, HAL_TIMEOUT_MS, future);
             return future;
         } catch (RemoteException e) {
@@ -228,6 +232,64 @@
     }
 
     /**
+     * Creates a new Android user.
+     *
+     * @hide
+     */
+    @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS})
+    public AndroidFuture<UserCreationResult> createUser(@Nullable String name,
+            @NonNull String userType, @UserInfoFlag int flags) {
+        int uid = myUid();
+        try {
+            AndroidFuture<UserCreationResult> future = new AndroidFuture<UserCreationResult>() {
+                @Override
+                protected void onCompleted(UserCreationResult result, Throwable err) {
+                    if (result != null) {
+                        EventLog.writeEvent(EventLogTags.CAR_USER_MGR_CREATE_USER_RESP, uid,
+                                result.getStatus(), result.getErrorMessage());
+                    } else {
+                        Log.w(TAG, "createUser(" + userType + "," + UserInfo.flagsToString(flags)
+                                + ") failed: " + err);
+                    }
+                    super.onCompleted(result, err);
+                };
+            };
+            EventLog.writeEvent(EventLogTags.CAR_USER_MGR_CREATE_USER_REQ, uid,
+                    safeName(name), userType, flags);
+            mService.createUser(name, userType, flags, HAL_TIMEOUT_MS, future);
+            return future;
+        } catch (RemoteException e) {
+            AndroidFuture<UserCreationResult> future = new AndroidFuture<>();
+            future.complete(new UserCreationResult(UserCreationResult.STATUS_HAL_INTERNAL_FAILURE,
+                    null, null));
+            return handleRemoteExceptionFromCarService(e, future);
+        }
+    }
+
+     /**
+     * Removes a user.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    public UserRemovalResult removeUser(@UserIdInt int userId) {
+        int uid = myUid();
+        EventLog.writeEvent(EventLogTags.CAR_USER_MGR_REMOVE_USER_REQ, uid, userId);
+        int status = UserRemovalResult.STATUS_HAL_INTERNAL_FAILURE;
+        try {
+            UserRemovalResult result = mService.removeUser(userId);
+            status = result.getStatus();
+            return result;
+        } catch (RemoteException e) {
+            return handleRemoteExceptionFromCarService(e,
+                    new UserRemovalResult(UserRemovalResult.STATUS_HAL_INTERNAL_FAILURE));
+        } finally {
+            EventLog.writeEvent(EventLogTags.CAR_USER_MGR_REMOVE_USER_RESP, uid, status);
+        }
+    }
+
+    /**
      * Adds a listener for {@link UserLifecycleEvent user lifecycle events}.
      *
      * @throws IllegalStateException if the listener was already added.
@@ -261,6 +323,12 @@
 
             if (mListeners == null) {
                 mListeners = new ArrayMap<>(1); // Most likely app will have just one listener
+            } else if (DBG) {
+                Log.d(TAG, "addListener(" + getLambdaName(listener) + "): context " + getContext()
+                        + " already has " + mListeners.size() + " listeners: "
+                        + mListeners.keySet().stream()
+                                .map((l) -> getLambdaName(l))
+                                .collect(Collectors.toList()), new Exception());
             }
             if (DBG) Log.d(TAG, "Adding listener: " + listener);
             mListeners.put(listener, executor);
@@ -443,10 +511,15 @@
                 Log.w(TAG, "No listeners for event " + event);
                 return;
             }
-            for (int i = 0; i < listeners.size(); i++) {
+            int size = listeners.size();
+            EventLog.writeEvent(EventLogTags.CAR_USER_MGR_NOTIFY_LIFECYCLE_LISTENER,
+                    size, eventType, from, to);
+            for (int i = 0; i < size; i++) {
                 UserLifecycleListener listener = listeners.keyAt(i);
                 Executor executor = listeners.valueAt(i);
-                if (DBG) Log.d(TAG, "Calling listener " + listener + " for event " + event);
+                if (DBG) {
+                    Log.d(TAG, "Calling " + getLambdaName(listener) + " for event " + event);
+                }
                 executor.execute(() -> listener.onEvent(event));
             }
         }
@@ -504,7 +577,7 @@
      * @hide
      */
     public boolean isValidUser(@UserIdInt int userId) {
-        List<UserInfo> allUsers = mUserManager.getUsers();
+        List<UserInfo> allUsers = mUserManager.getUsers(/* excludeDying= */ true);
         for (int i = 0; i < allUsers.size(); i++) {
             UserInfo user = allUsers.get(i);
             if (user.id == userId && (userId != UserHandle.USER_SYSTEM
@@ -515,6 +588,13 @@
         return false;
     }
 
+    // TODO(b/150413515): use from UserHelper instead (would require a new make target, otherwise it
+    // would include the whole car-user-lib)
+    @Nullable
+    private static String safeName(@Nullable String name) {
+        return name == null ? name : name.length() + "_chars";
+    }
+
     /**
      * Defines a lifecycle event for an Android user.
      *
diff --git a/car-lib/src/android/car/user/CommonResults.java b/car-lib/src/android/car/user/CommonResults.java
new file mode 100644
index 0000000..9238a0b
--- /dev/null
+++ b/car-lib/src/android/car/user/CommonResults.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 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.user;
+
+/**
+ * Defines status for common result objects.
+ */
+final class CommonResults {
+
+    /**
+     * Operation was is successful for both HAL and Android.
+     */
+    public static final int STATUS_SUCCESSFUL = 1;
+
+    /**
+     * Operation failed on Android.
+     */
+    public static final int STATUS_ANDROID_FAILURE = 2;
+
+    /**
+     * Operation failed on HAL.
+     */
+    public static final int STATUS_HAL_FAILURE = 3;
+
+    /**
+     * Operation failed due to an error communication with HAL (like timeout).
+     */
+    public static final int STATUS_HAL_INTERNAL_FAILURE = 4;
+
+    /**
+     * Operation failed due to invalid request.
+     */
+    public static final int STATUS_INVALID_REQUEST = 5;
+
+    /**
+     * Reference for common status - anything higher than this can be used for custom status
+     */
+    static final int LAST_COMMON_STATUS = 100;
+
+    private CommonResults() {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/car-lib/src/android/car/user/ExperimentalCarUserManager.java b/car-lib/src/android/car/user/ExperimentalCarUserManager.java
index 1396971..fa7eb72 100644
--- a/car-lib/src/android/car/user/ExperimentalCarUserManager.java
+++ b/car-lib/src/android/car/user/ExperimentalCarUserManager.java
@@ -72,19 +72,21 @@
      *
      * @param name The name of the driver to be created.
      * @param admin Whether the created driver will be an admin.
-     * @return user id of the created driver, or {@code INVALID_USER_ID} if the driver could
-     *         not be created.
+     * @return an {@link AndroidFuture} that can be used to track operation's completion and
+     *         retrieve its result (if any).
      *
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
-    @Nullable
-    public int createDriver(@NonNull String name, boolean admin) {
+    public AndroidFuture<UserCreationResult> createDriver(@NonNull String name, boolean admin) {
         try {
-            UserInfo ui = mService.createDriver(name, admin);
-            return ui != null ? ui.id : INVALID_USER_ID;
+            return mService.createDriver(name, admin);
         } catch (RemoteException e) {
-            return handleRemoteExceptionFromCarService(e, null);
+            AndroidFuture<UserCreationResult> future = new AndroidFuture<>();
+            future.complete(new UserCreationResult(UserCreationResult.STATUS_HAL_INTERNAL_FAILURE,
+                    null, null));
+            handleRemoteExceptionFromCarService(e);
+            return future;
         }
     }
 
diff --git a/car-lib/src/android/car/user/UserCreationResult.aidl b/car-lib/src/android/car/user/UserCreationResult.aidl
new file mode 100644
index 0000000..a735202
--- /dev/null
+++ b/car-lib/src/android/car/user/UserCreationResult.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2020 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.user;
+
+parcelable UserCreationResult;
diff --git a/car-lib/src/android/car/user/UserCreationResult.java b/car-lib/src/android/car/user/UserCreationResult.java
new file mode 100644
index 0000000..328908f
--- /dev/null
+++ b/car-lib/src/android/car/user/UserCreationResult.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2020 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.user;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.content.pm.UserInfo;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * User creation results.
+ *
+ * @hide
+ */
+@DataClass(
+        genToString = true,
+        genHiddenConstructor = true,
+        genHiddenConstDefs = true)
+public final class UserCreationResult implements Parcelable {
+
+    /**
+     * {@link Status} called when user creation is successful for both HAL and Android.
+     *
+     * @hide
+     */
+    public static final int STATUS_SUCCESSFUL = CommonResults.STATUS_SUCCESSFUL;
+
+    /**
+     * {@link Status} called when user creation failed on Android - HAL is not even called in this
+     * case.
+     *
+     * @hide
+     */
+    public static final int STATUS_ANDROID_FAILURE = CommonResults.STATUS_ANDROID_FAILURE;
+
+    /**
+     * {@link Status} called when user was created on Android but HAL returned a failure - the
+     * Android user is automatically removed.
+     *
+     * @hide
+     */
+    public static final int STATUS_HAL_FAILURE = CommonResults.STATUS_HAL_FAILURE;
+
+    /**
+     * {@link Status} called when user creation is failed for HAL for some internal error - the
+     * Android user is not automatically removed.
+     *
+     * @hide
+     */
+    public static final int STATUS_HAL_INTERNAL_FAILURE = CommonResults.STATUS_HAL_INTERNAL_FAILURE;
+
+    /**
+     * {@link Status} called when given parameters or environment states are invalid for creating
+     * user - HAL or Android user creation is not requested.
+     */
+    public static final int STATUS_INVALID_REQUEST = CommonResults.STATUS_INVALID_REQUEST;
+
+    /**
+     * Gets the user creation result status.
+     *
+     * @return either {@link UserCreationResult#STATUS_SUCCESSFUL},
+     *         {@link UserCreationResult#STATUS_ANDROID_FAILURE},
+     *         {@link UserCreationResult#STATUS_HAL_FAILURE},
+     *         {@link UserCreationResult#STATUS_HAL_INTERNAL_FAILURE}, or
+     *         {@link UserCreationResult#STATUS_INVALID_REQUEST}.
+     */
+    private final @Status int mStatus;
+
+    /**
+     * Gets the created user.
+     */
+    @Nullable
+    private final UserInfo mUser;
+
+    /**
+     * Gets the error message sent by HAL, if any.
+     */
+    @Nullable
+    private final String mErrorMessage;
+
+    /**
+     * Checks if this result is successful.
+     */
+    public boolean isSuccess() {
+        return mStatus == STATUS_SUCCESSFUL;
+    }
+
+    // TODO(b/158195639): if you change any status constant, you need to manually assign its values
+
+
+
+    // Code below generated by codegen v1.0.15.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/packages/services/Car/car-lib/src/android/car/user/UserCreationResult.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /** @hide */
+    @IntDef(prefix = "STATUS_", value = {
+        STATUS_SUCCESSFUL,
+        STATUS_ANDROID_FAILURE,
+        STATUS_HAL_FAILURE,
+        STATUS_HAL_INTERNAL_FAILURE,
+        STATUS_INVALID_REQUEST
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @DataClass.Generated.Member
+    public @interface Status {}
+
+    /** @hide */
+    @DataClass.Generated.Member
+    public static String statusToString(@Status int value) {
+        switch (value) {
+            case STATUS_SUCCESSFUL:
+                    return "STATUS_SUCCESSFUL";
+            case STATUS_ANDROID_FAILURE:
+                    return "STATUS_ANDROID_FAILURE";
+            case STATUS_HAL_FAILURE:
+                    return "STATUS_HAL_FAILURE";
+            case STATUS_HAL_INTERNAL_FAILURE:
+                    return "STATUS_HAL_INTERNAL_FAILURE";
+            case STATUS_INVALID_REQUEST:
+                    return "STATUS_INVALID_REQUEST";
+            default: return Integer.toHexString(value);
+        }
+    }
+
+    /**
+     * Creates a new UserCreationResult.
+     *
+     * @param status
+     *   Gets the user creation result status.
+     *
+     *   @return either {@link UserCreationResult#STATUS_SUCCESSFUL},
+     *           {@link UserCreationResult#STATUS_ANDROID_FAILURE},
+     *           {@link UserCreationResult#STATUS_HAL_FAILURE},
+     *           {@link UserCreationResult#STATUS_HAL_INTERNAL_FAILURE}, or
+     *           {@link UserCreationResult#STATUS_INVALID_REQUEST}.
+     * @param user
+     *   Gets the created user.
+     * @param errorMessage
+     *   Gets the error message sent by HAL, if any.
+     * @hide
+     */
+    @DataClass.Generated.Member
+    public UserCreationResult(
+            @Status int status,
+            @Nullable UserInfo user,
+            @Nullable String errorMessage) {
+        this.mStatus = status;
+
+        if (!(mStatus == STATUS_SUCCESSFUL)
+                && !(mStatus == STATUS_ANDROID_FAILURE)
+                && !(mStatus == STATUS_HAL_FAILURE)
+                && !(mStatus == STATUS_HAL_INTERNAL_FAILURE)
+                && !(mStatus == STATUS_INVALID_REQUEST)) {
+            throw new java.lang.IllegalArgumentException(
+                    "status was " + mStatus + " but must be one of: "
+                            + "STATUS_SUCCESSFUL(" + STATUS_SUCCESSFUL + "), "
+                            + "STATUS_ANDROID_FAILURE(" + STATUS_ANDROID_FAILURE + "), "
+                            + "STATUS_HAL_FAILURE(" + STATUS_HAL_FAILURE + "), "
+                            + "STATUS_HAL_INTERNAL_FAILURE(" + STATUS_HAL_INTERNAL_FAILURE + "), "
+                            + "STATUS_INVALID_REQUEST(" + STATUS_INVALID_REQUEST + ")");
+        }
+
+        this.mUser = user;
+        this.mErrorMessage = errorMessage;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * Gets the user creation result status.
+     *
+     * @return either {@link UserCreationResult#STATUS_SUCCESSFUL},
+     *         {@link UserCreationResult#STATUS_ANDROID_FAILURE},
+     *         {@link UserCreationResult#STATUS_HAL_FAILURE},
+     *         {@link UserCreationResult#STATUS_HAL_INTERNAL_FAILURE}, or
+     *         {@link UserCreationResult#STATUS_INVALID_REQUEST}.
+     */
+    @DataClass.Generated.Member
+    public @Status int getStatus() {
+        return mStatus;
+    }
+
+    /**
+     * Gets the created user.
+     */
+    @DataClass.Generated.Member
+    public @Nullable UserInfo getUser() {
+        return mUser;
+    }
+
+    /**
+     * Gets the error message sent by HAL, if any.
+     */
+    @DataClass.Generated.Member
+    public @Nullable String getErrorMessage() {
+        return mErrorMessage;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public String toString() {
+        // You can override field toString logic by defining methods like:
+        // String fieldNameToString() { ... }
+
+        return "UserCreationResult { " +
+                "status = " + statusToString(mStatus) + ", " +
+                "user = " + mUser + ", " +
+                "errorMessage = " + mErrorMessage +
+        " }";
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@android.annotation.NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (mUser != null) flg |= 0x2;
+        if (mErrorMessage != null) flg |= 0x4;
+        dest.writeByte(flg);
+        dest.writeInt(mStatus);
+        if (mUser != null) dest.writeTypedObject(mUser, flags);
+        if (mErrorMessage != null) dest.writeString(mErrorMessage);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ UserCreationResult(@android.annotation.NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        int status = in.readInt();
+        UserInfo user = (flg & 0x2) == 0 ? null : (UserInfo) in.readTypedObject(UserInfo.CREATOR);
+        String errorMessage = (flg & 0x4) == 0 ? null : in.readString();
+
+        this.mStatus = status;
+
+        if (!(mStatus == STATUS_SUCCESSFUL)
+                && !(mStatus == STATUS_ANDROID_FAILURE)
+                && !(mStatus == STATUS_HAL_FAILURE)
+                && !(mStatus == STATUS_HAL_INTERNAL_FAILURE)
+                && !(mStatus == STATUS_INVALID_REQUEST)) {
+            throw new java.lang.IllegalArgumentException(
+                    "status was " + mStatus + " but must be one of: "
+                            + "STATUS_SUCCESSFUL(" + STATUS_SUCCESSFUL + "), "
+                            + "STATUS_ANDROID_FAILURE(" + STATUS_ANDROID_FAILURE + "), "
+                            + "STATUS_HAL_FAILURE(" + STATUS_HAL_FAILURE + "), "
+                            + "STATUS_HAL_INTERNAL_FAILURE(" + STATUS_HAL_INTERNAL_FAILURE + "), "
+                            + "STATUS_INVALID_REQUEST(" + STATUS_INVALID_REQUEST + ")");
+        }
+
+        this.mUser = user;
+        this.mErrorMessage = errorMessage;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @android.annotation.NonNull Parcelable.Creator<UserCreationResult> CREATOR
+            = new Parcelable.Creator<UserCreationResult>() {
+        @Override
+        public UserCreationResult[] newArray(int size) {
+            return new UserCreationResult[size];
+        }
+
+        @Override
+        public UserCreationResult createFromParcel(@android.annotation.NonNull android.os.Parcel in) {
+            return new UserCreationResult(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1591401523007L,
+            codegenVersion = "1.0.15",
+            sourceFile = "packages/services/Car/car-lib/src/android/car/user/UserCreationResult.java",
+            inputSignatures = "public static final  int STATUS_SUCCESSFUL\npublic static final  int STATUS_ANDROID_FAILURE\npublic static final  int STATUS_HAL_FAILURE\npublic static final  int STATUS_HAL_INTERNAL_FAILURE\npublic static final  int STATUS_INVALID_REQUEST\nprivate final @android.car.user.UserCreationResult.Status int mStatus\nprivate final @android.annotation.Nullable android.content.pm.UserInfo mUser\nprivate final @android.annotation.Nullable java.lang.String mErrorMessage\npublic  boolean isSuccess()\nclass UserCreationResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/car-lib/src/android/car/user/UserRemovalResult.aidl b/car-lib/src/android/car/user/UserRemovalResult.aidl
new file mode 100644
index 0000000..00a0eba
--- /dev/null
+++ b/car-lib/src/android/car/user/UserRemovalResult.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2020 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.user;
+
+parcelable UserRemovalResult;
diff --git a/car-lib/src/android/car/user/UserRemovalResult.java b/car-lib/src/android/car/user/UserRemovalResult.java
new file mode 100644
index 0000000..dbfd8a6
--- /dev/null
+++ b/car-lib/src/android/car/user/UserRemovalResult.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2020 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.user;
+
+import android.annotation.IntDef;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * User remove result.
+ *
+ * @hide
+ */
+@DataClass(
+        genToString = true,
+        genHiddenConstructor = true,
+        genHiddenConstDefs = true)
+public final class UserRemovalResult implements Parcelable {
+
+    /**
+     * When user remove is successful.
+     *
+     * @hide
+     */
+    public static final int STATUS_SUCCESSFUL = CommonResults.STATUS_SUCCESSFUL;
+
+    /**
+     * When user remove fails for android. Hal user is not removed.
+     *
+     * @hide
+     */
+    public static final int STATUS_ANDROID_FAILURE = CommonResults.STATUS_ANDROID_FAILURE;
+
+    /**
+     * When remove user fails for unknown error.
+     *
+     * @hide
+     */
+    public static final int STATUS_HAL_INTERNAL_FAILURE = CommonResults.STATUS_HAL_INTERNAL_FAILURE;
+
+     /**
+     * When user to remove is same as current user.
+     *
+     * @hide
+     */
+    public static final int STATUS_TARGET_USER_IS_CURRENT_USER =
+            CommonResults.LAST_COMMON_STATUS + 1;
+
+    /**
+     * When user to remove doesn't exits.
+     *
+     * @hide
+     */
+    public static final int STATUS_USER_DOES_NOT_EXIST = CommonResults.LAST_COMMON_STATUS + 2;
+
+    /**
+     * When user to remove is last admin user.
+     *
+     * @hide
+     */
+    public static final int STATUS_TARGET_USER_IS_LAST_ADMIN_USER =
+            CommonResults.LAST_COMMON_STATUS + 3;
+
+    /**
+     * Gets the user switch result status.
+     *
+     * @return either {@link UserRemovalResult#STATUS_SUCCESSFUL},
+     *         {@link UserRemovalResult#STATUS_ANDROID_FAILURE},
+     *         {@link UserRemovalResult#STATUS_HAL_INTERNAL_FAILURE},
+     *         {@link UserRemovalResult#STATUS_TARGET_USER_IS_CURRENT_USER},
+     *         {@link UserRemovalResult#STATUS_USER_DOES_NOT_EXIST}, or
+     *         {@link UserRemovalResult#STATUS_TARGET_USER_IS_LAST_ADMIN_USER}.
+     */
+    private final @Status int mStatus;
+
+    public boolean isSuccess() {
+        return mStatus == STATUS_SUCCESSFUL;
+    }
+
+    // TODO(b/158195639): if you change any status constant, you need to manually assign its values
+    // (rather than using CommonResults) before running codegen to regenerate the class
+
+
+    // Code below generated by codegen v1.0.15.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/packages/services/Car/car-lib/src/android/car/user/UserRemovalResult.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /** @hide */
+    @IntDef(prefix = "STATUS_", value = {
+        STATUS_SUCCESSFUL,
+        STATUS_ANDROID_FAILURE,
+        STATUS_HAL_INTERNAL_FAILURE,
+        STATUS_TARGET_USER_IS_CURRENT_USER,
+        STATUS_USER_DOES_NOT_EXIST,
+        STATUS_TARGET_USER_IS_LAST_ADMIN_USER
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @DataClass.Generated.Member
+    public @interface Status {}
+
+    /** @hide */
+    @DataClass.Generated.Member
+    public static String statusToString(@Status int value) {
+        switch (value) {
+            case STATUS_SUCCESSFUL:
+                    return "STATUS_SUCCESSFUL";
+            case STATUS_ANDROID_FAILURE:
+                    return "STATUS_ANDROID_FAILURE";
+            case STATUS_HAL_INTERNAL_FAILURE:
+                    return "STATUS_HAL_INTERNAL_FAILURE";
+            case STATUS_TARGET_USER_IS_CURRENT_USER:
+                    return "STATUS_TARGET_USER_IS_CURRENT_USER";
+            case STATUS_USER_DOES_NOT_EXIST:
+                    return "STATUS_USER_DOES_NOT_EXIST";
+            case STATUS_TARGET_USER_IS_LAST_ADMIN_USER:
+                    return "STATUS_TARGET_USER_IS_LAST_ADMIN_USER";
+            default: return Integer.toHexString(value);
+        }
+    }
+
+    /**
+     * Creates a new UserRemovalResult.
+     *
+     * @param status
+     *   Gets the user switch result status.
+     *
+     *   @return either {@link UserRemovalResult#STATUS_SUCCESSFUL},
+     *           {@link UserRemovalResult#STATUS_ANDROID_FAILURE},
+     *           {@link UserRemovalResult#STATUS_HAL_INTERNAL_FAILURE},
+     *           {@link UserRemovalResult#STATUS_TARGET_USER_IS_CURRENT_USER},
+     *           {@link UserRemovalResult#STATUS_USER_DOES_NOT_EXIST}, or
+     *           {@link UserRemovalResult#STATUS_TARGET_USER_IS_LAST_ADMIN_USER}.
+     * @hide
+     */
+    @DataClass.Generated.Member
+    public UserRemovalResult(
+            int status) {
+        this.mStatus = status;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * Gets the user switch result status.
+     *
+     * @return either {@link UserRemovalResult#STATUS_SUCCESSFUL},
+     *         {@link UserRemovalResult#STATUS_ANDROID_FAILURE},
+     *         {@link UserRemovalResult#STATUS_HAL_INTERNAL_FAILURE},
+     *         {@link UserRemovalResult#STATUS_TARGET_USER_IS_CURRENT_USER},
+     *         {@link UserRemovalResult#STATUS_USER_DOES_NOT_EXIST}, or
+     *         {@link UserRemovalResult#STATUS_TARGET_USER_IS_LAST_ADMIN_USER}.
+     */
+    @DataClass.Generated.Member
+    public int getStatus() {
+        return mStatus;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public String toString() {
+        // You can override field toString logic by defining methods like:
+        // String fieldNameToString() { ... }
+
+        return "UserRemovalResult { " +
+                "status = " + mStatus +
+        " }";
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@android.annotation.NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        dest.writeInt(mStatus);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ UserRemovalResult(@android.annotation.NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        int status = in.readInt();
+
+        this.mStatus = status;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @android.annotation.NonNull Parcelable.Creator<UserRemovalResult> CREATOR
+            = new Parcelable.Creator<UserRemovalResult>() {
+        @Override
+        public UserRemovalResult[] newArray(int size) {
+            return new UserRemovalResult[size];
+        }
+
+        @Override
+        public UserRemovalResult createFromParcel(@android.annotation.NonNull android.os.Parcel in) {
+            return new UserRemovalResult(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1591259644931L,
+            codegenVersion = "1.0.15",
+            sourceFile = "packages/services/Car/car-lib/src/android/car/user/UserRemovalResult.java",
+            inputSignatures = "public static final  int STATUS_SUCCESSFUL\npublic static final  int STATUS_ANDROID_FAILURE\npublic static final  int STATUS_HAL_INTERNAL_FAILURE\npublic static final  int STATUS_TARGET_USER_IS_CURRENT_USER\npublic static final  int STATUS_USER_DOES_NOT_EXIST\npublic static final  int STATUS_TARGET_USER_IS_LAST_ADMIN_USER\nprivate final  int mStatus\npublic  boolean isSuccess()\nclass UserRemovalResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/car-lib/src/android/car/user/UserSwitchResult.java b/car-lib/src/android/car/user/UserSwitchResult.java
index 5792bf6..55e2dbc 100644
--- a/car-lib/src/android/car/user/UserSwitchResult.java
+++ b/car-lib/src/android/car/user/UserSwitchResult.java
@@ -37,66 +37,51 @@
 public final class UserSwitchResult implements Parcelable {
 
     /**
-     * {@link UserSwitchStatus} called when user switch is successful for both HAL and Android.
-     *
-     * @hide
+     * When user switch is successful for both HAL and Android.
      */
-    public static final int STATUS_SUCCESSFUL = 1;
+    public static final int STATUS_SUCCESSFUL = CommonResults.STATUS_SUCCESSFUL;
 
     /**
-     * {@link UserSwitchStatus} called when user switch is only successful for Hal but not for
-     * Android. Hal user switch rollover message have been sent.
-     *
-     * @hide
+     * When user switch is only successful for Hal but not for Android. Hal user switch rollover
+     * message have been sent.
      */
-    public static final int STATUS_ANDROID_FAILURE = 2;
+    public static final int STATUS_ANDROID_FAILURE = CommonResults.STATUS_ANDROID_FAILURE;
 
     /**
-     * {@link UserSwitchStatus} called when user switch is failed for HAL. User switch for Android
-     * is not called.
-     *
-     * @hide
+     * When user switch fails for HAL. User switch for Android is not called.
      */
-    public static final int STATUS_HAL_FAILURE = 3;
+    public static final int STATUS_HAL_FAILURE = CommonResults.STATUS_HAL_FAILURE;
 
     /**
-     * {@link UserSwitchStatus} called when user switch is failed for HAL for some internal error.
-     * User switch for Android is not called.
-     *
-     * @hide
+     * When user switch fails for HAL for some internal error. User switch for Android is not
+     * called.
      */
-    public static final int STATUS_HAL_INTERNAL_FAILURE = 4;
+    public static final int STATUS_HAL_INTERNAL_FAILURE = CommonResults.STATUS_HAL_INTERNAL_FAILURE;
 
     /**
-     * {@link UserSwitchStatus} called when target user is same as current user.
-     *
-     * @hide
+     * When given parameters or environment states are invalid for switching user. HAL or Android
+     * user switch is not requested.
      */
-    public static final int STATUS_ALREADY_REQUESTED_USER = 5;
+    public static final int STATUS_INVALID_REQUEST = CommonResults.STATUS_INVALID_REQUEST;
 
     /**
-     * {@link UserSwitchStatus} called when another user switch request for the same target user is
-     * in process.
-     *
-     * @hide
+     * When target user is same as current user.
      */
-    public static final int STATUS_TARGET_USER_ALREADY_BEING_SWITCHED_TO = 6;
+    public static final int STATUS_ALREADY_REQUESTED_USER =
+            CommonResults.LAST_COMMON_STATUS + 1;
 
     /**
-     * {@link UserSwitchStatus} called when another user switch request for a new different target
-     * user is received. Previous request is abandoned.
-     *
-     * @hide
+     * When another user switch request for the same target user is in process.
      */
-    public static final int STATUS_TARGET_USER_ABANDONED_DUE_TO_A_NEW_REQUEST = 7;
+    public static final int STATUS_TARGET_USER_ALREADY_BEING_SWITCHED_TO =
+            CommonResults.LAST_COMMON_STATUS + 2;
 
     /**
-     * {@link UserSwitchStatus} called when given parameters or environment states are invalid for
-     * switching user. HAL or Android user switch is not requested.
-     *
-     * @hide
+     * When another user switch request for a new different target user is received. Previous
+     * request is abandoned.
      */
-    public static final int STATUS_INVALID_REQUEST = 8;
+    public static final int STATUS_TARGET_USER_ABANDONED_DUE_TO_A_NEW_REQUEST =
+            CommonResults.LAST_COMMON_STATUS + 3;
 
     /**
      * Gets the user switch result status.
@@ -106,11 +91,11 @@
      *         {@link UserSwitchResult#STATUS_HAL_FAILURE},
      *         {@link UserSwitchResult#STATUS_HAL_INTERNAL_FAILURE},
      *         {@link UserSwitchResult#STATUS_ALREADY_REQUESTED_USER},
-     *         {@link UserSwitchResult#STATUS_TARGET_USER_ALREADY_BEING_SWITCHED_TO}
+     *         {@link UserSwitchResult#STATUS_TARGET_USER_ALREADY_BEING_SWITCHED_TO},
      *         {@link UserSwitchResult#STATUS_TARGET_USER_ABANDONED_DUE_TO_A_NEW_REQUEST}, or
      *         {@link UserSwitchResult#STATUS_INVALID_REQUEST}.
      */
-    private final int mStatus;
+    private final @Status int mStatus;
 
     /**
      * Gets the error message, if any.
@@ -118,6 +103,15 @@
     @Nullable
     private final String mErrorMessage;
 
+    /**
+     * Check if {@link UserSwitchResult} is successful.
+     */
+    public boolean isSuccess() {
+        return mStatus == STATUS_SUCCESSFUL || mStatus == STATUS_ALREADY_REQUESTED_USER;
+    }
+
+    // TODO(b/158195639): if you change any status constant, you need to manually assign its values
+    // (rather than using CommonResults) before running codegen to regenerate the class
 
 
     // Code below generated by codegen v1.0.15.
@@ -183,7 +177,7 @@
      *           {@link UserSwitchResult#STATUS_HAL_FAILURE},
      *           {@link UserSwitchResult#STATUS_HAL_INTERNAL_FAILURE},
      *           {@link UserSwitchResult#STATUS_ALREADY_REQUESTED_USER},
-     *           {@link UserSwitchResult#STATUS_TARGET_USER_ALREADY_BEING_SWITCHED_TO}
+     *           {@link UserSwitchResult#STATUS_TARGET_USER_ALREADY_BEING_SWITCHED_TO},
      *           {@link UserSwitchResult#STATUS_TARGET_USER_ABANDONED_DUE_TO_A_NEW_REQUEST}, or
      *           {@link UserSwitchResult#STATUS_INVALID_REQUEST}.
      * @param errorMessage
@@ -208,7 +202,7 @@
      *         {@link UserSwitchResult#STATUS_HAL_FAILURE},
      *         {@link UserSwitchResult#STATUS_HAL_INTERNAL_FAILURE},
      *         {@link UserSwitchResult#STATUS_ALREADY_REQUESTED_USER},
-     *         {@link UserSwitchResult#STATUS_TARGET_USER_ALREADY_BEING_SWITCHED_TO}
+     *         {@link UserSwitchResult#STATUS_TARGET_USER_ALREADY_BEING_SWITCHED_TO},
      *         {@link UserSwitchResult#STATUS_TARGET_USER_ABANDONED_DUE_TO_A_NEW_REQUEST}, or
      *         {@link UserSwitchResult#STATUS_INVALID_REQUEST}.
      */
@@ -286,10 +280,10 @@
     };
 
     @DataClass.Generated(
-            time = 1589580732431L,
+            time = 1590737883648L,
             codegenVersion = "1.0.15",
             sourceFile = "packages/services/Car/car-lib/src/android/car/user/UserSwitchResult.java",
-            inputSignatures = "public static final  int STATUS_SUCCESSFUL\npublic static final  int STATUS_ANDROID_FAILURE\npublic static final  int STATUS_HAL_FAILURE\npublic static final  int STATUS_HAL_INTERNAL_FAILURE\npublic static final  int STATUS_ALREADY_REQUESTED_USER\npublic static final  int STATUS_TARGET_USER_ALREADY_BEING_SWITCHED_TO\npublic static final  int STATUS_TARGET_USER_ABANDONED_DUE_TO_A_NEW_REQUEST\npublic static final  int STATUS_INVALID_REQUEST\nprivate final  int mStatus\nprivate final @android.annotation.Nullable java.lang.String mErrorMessage\nclass UserSwitchResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)")
+            inputSignatures = "public static final  int STATUS_SUCCESSFUL\npublic static final  int STATUS_ANDROID_FAILURE\npublic static final  int STATUS_HAL_FAILURE\npublic static final  int STATUS_HAL_INTERNAL_FAILURE\npublic static final  int STATUS_ALREADY_REQUESTED_USER\npublic static final  int STATUS_TARGET_USER_ALREADY_BEING_SWITCHED_TO\npublic static final  int STATUS_TARGET_USER_ABANDONED_DUE_TO_A_NEW_REQUEST\npublic static final  int STATUS_INVALID_REQUEST\nprivate final  int mStatus\nprivate final @android.annotation.Nullable java.lang.String mErrorMessage\npublic  boolean isSuccess()\nclass UserSwitchResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/car-lib/src/android/car/vms/VmsOperationRecorder.java b/car-lib/src/android/car/vms/VmsOperationRecorder.java
index f56c475..aa3f09d 100644
--- a/car-lib/src/android/car/vms/VmsOperationRecorder.java
+++ b/car-lib/src/android/car/vms/VmsOperationRecorder.java
@@ -61,68 +61,127 @@
 
     // VMS Client operations.
 
+    /**
+     * Records {@code subscribe} operation with the {@link VmsLayer} layer passed as parameter.
+     */
     public void subscribe(VmsLayer layer) {
         recordOp("subscribe", layer);
     }
 
+    /**
+     * Records {@code unsubscribe} operation with the {@link VmsLayer} layer passed as parameter.
+     */
     public void unsubscribe(VmsLayer layer) {
         recordOp("unsubscribe", layer);
     }
 
+    /**
+     * Records {@code subscribe} operation with the {@link VmsLayer} layer and publisher id passed
+     * both as parameter.
+     */
     public void subscribe(VmsLayer layer, int publisherId) {
         recordOp("subscribe", "publisherId", publisherId, layer);
     }
 
+    /**
+     * Record {@code unsubscribe} operation with the {@link VmsLayer} layer and publisher id passed
+     * both as parameter.
+     */
     public void unsubscribe(VmsLayer layer, int publisherId) {
         recordOp("unsubscribe", "publisherId", publisherId, layer);
     }
 
+    /**
+     * Records {@code startMonitoring} operation.
+     */
     public void startMonitoring() {
         recordOp("startMonitoring");
     }
 
+    /**
+     * Records {@code stopMonitoring} operation.
+     */
     public void stopMonitoring() {
         recordOp("stopMonitoring");
     }
 
+    /**
+     * Records {@code setLayerOffering} operation with the {@link VmsLayerOffering} offering
+     * passed as parameter.
+     */
     public void setLayersOffering(VmsLayersOffering layersOffering) {
         recordOp("setLayersOffering", layersOffering);
     }
 
+    /**
+     * Records {@code getPublisherId} operation with the publisher id passed as parameter.
+     */
     public void getPublisherId(int publisherId) {
         recordOp("getPublisherId", "publisherId", publisherId);
     }
 
     // VMS Service operations.
 
+    /**
+     * Records {@code addSubscription} operation with the {@link VmsLayer} and the sequenceNumber
+     * passed as parameter.
+     */
     public void addSubscription(int sequenceNumber, VmsLayer layer) {
         recordOp("addSubscription", "sequenceNumber", sequenceNumber, layer);
     }
 
+    /**
+     * Records {@code addPromiscuousSubscription} operation with the {@link VmsLayer} and the
+     * sequenceNumber passed as parameter.
+     */
     public void removeSubscription(int sequenceNumber, VmsLayer layer) {
         recordOp("removeSubscription", "sequenceNumber", sequenceNumber, layer);
     }
 
+    /**
+     * Records {@code addPromiscuousSubscription} operation with the sequenceNumber passed as
+     * parameter.
+     */
     public void addPromiscuousSubscription(int sequenceNumber) {
         recordOp("addPromiscuousSubscription", "sequenceNumber", sequenceNumber);
     }
 
+    /**
+     * Records {@code removePromiscuousSubscription} operation with the sequenceNumber passed as
+     * parameter.
+     */
     public void removePromiscuousSubscription(int sequenceNumber) {
         recordOp("removePromiscuousSubscription", "sequenceNumber", sequenceNumber);
     }
 
+    /**
+     * Records {@code addHalSubscription} operation with the {@link VmsLayer} layer and
+     * sequenceNumber both passed as parameter.
+     */
     public void addHalSubscription(int sequenceNumber, VmsLayer layer) {
         recordOp("addHalSubscription", "sequenceNumber", sequenceNumber, layer);
     }
 
+    /**
+     * Records {@code removeHalSubscription} operation with the {@link VmsLayer} layer and
+     * sequenceNumber both passed as parameter.
+     */
     public void removeHalSubscription(int sequenceNumber, VmsLayer layer) {
         recordOp("removeHalSubscription", "sequenceNumber", sequenceNumber, layer);
     }
 
+    /**
+     * Records {@code setPublisherLayersOffering} operation with the {@link VmsLayersOffering}
+     * layersOffering passed as parameter.
+     */
     public void setPublisherLayersOffering(VmsLayersOffering layersOffering) {
         recordOp("setPublisherLayersOffering", layersOffering);
     }
 
+    /**
+     * Records {@code setHalPublisherLayersOffering} operation with the {@link VmsLayersOffering}
+     * layersOffering passed as parameter.
+     */
     public void setHalPublisherLayersOffering(VmsLayersOffering layersOffering) {
         recordOp("setHalPublisherLayersOffering", layersOffering);
     }
diff --git a/car-test-lib/Android.bp b/car-test-lib/Android.bp
index 965ea7b..4522982 100644
--- a/car-test-lib/Android.bp
+++ b/car-test-lib/Android.bp
@@ -43,5 +43,6 @@
     libs: [
         "android.hardware.automotive.vehicle-V2.0-java",
         "mockito-target-extended",
+        "compatibility-device-util-axt",
     ],
 }
diff --git a/car-test-lib/src/android/car/test/mocks/AbstractExtendedMockitoTestCase.java b/car-test-lib/src/android/car/test/mocks/AbstractExtendedMockitoTestCase.java
index 35f0274..c89a337 100644
--- a/car-test-lib/src/android/car/test/mocks/AbstractExtendedMockitoTestCase.java
+++ b/car-test-lib/src/android/car/test/mocks/AbstractExtendedMockitoTestCase.java
@@ -31,6 +31,8 @@
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.Trace;
 import android.os.UserManager;
 import android.provider.Settings;
@@ -59,6 +61,7 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Base class for tests that must use {@link com.android.dx.mockito.inline.extended.ExtendedMockito}
@@ -83,11 +86,11 @@
  */
 public abstract class AbstractExtendedMockitoTestCase {
 
-    private static final boolean TRACE = false;
-
-    private static final boolean VERBOSE = false;
     private static final String TAG = AbstractExtendedMockitoTestCase.class.getSimpleName();
 
+    private static final boolean TRACE = false;
+    private static final boolean VERBOSE = false;
+
     private final List<Class<?>> mStaticSpiedClasses = new ArrayList<>();
 
     // Tracks (S)Log.wtf() calls made during code execution, then used on verifyWtfNeverLogged()
@@ -128,6 +131,7 @@
     @After
     public final void finishSession() {
         beginTrace("finishSession()");
+        completeAllHandlerThreadTasks();
         if (mSession != null) {
             beginTrace("finishMocking()");
             mSession.finishMocking();
@@ -139,6 +143,38 @@
     }
 
     /**
+     * Waits for completion of all pending Handler tasks for all HandlerThread in the process.
+     *
+     * <p>This can prevent pending Handler tasks of one test from affecting another. This does not
+     * work if the message is posted with delay.
+     */
+    protected void completeAllHandlerThreadTasks() {
+        beginTrace("completeAllHandlerThreadTasks");
+        Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
+        ArrayList<HandlerThread> handlerThreads = new ArrayList<>(threadSet.size());
+        Thread currentThread = Thread.currentThread();
+        for (Thread t : threadSet) {
+            if (t != currentThread && t instanceof HandlerThread) {
+                handlerThreads.add((HandlerThread) t);
+            }
+        }
+        ArrayList<SyncRunnable> syncs = new ArrayList<>(handlerThreads.size());
+        Log.i(TAG, "will wait for " + handlerThreads.size() + " HandlerThreads");
+        for (int i = 0; i < handlerThreads.size(); i++) {
+            Handler handler = new Handler(handlerThreads.get(i).getLooper());
+            SyncRunnable sr = new SyncRunnable(() -> { });
+            handler.post(sr);
+            syncs.add(sr);
+        }
+        beginTrace("waitForComplete");
+        for (int i = 0; i < syncs.size(); i++) {
+            syncs.get(i).waitForComplete();
+        }
+        endTrace(); // waitForComplete
+        endTrace(); // completeAllHandlerThreadTasks
+    }
+
+    /**
      * Adds key-value(int) pair in mocked Settings.Global and Settings.Secure
      */
     protected void putSettingsInt(@NonNull String key, int value) {
@@ -167,6 +203,13 @@
     }
 
     /**
+     * Asserts that the giving settings was not set.
+     */
+    protected void assertSettingsNotSet(String key) {
+        mSettings.assertDoesNotContainsKey(key);
+    }
+
+    /**
      * Subclasses can use this method to initialize the Mockito session that's started before every
      * test on {@link #startSession()}.
      *
@@ -486,6 +529,13 @@
         public int getInt(String key) {
             return get(key, null, Integer.class);
         }
+
+        public void assertDoesNotContainsKey(String key) {
+            if (mSettingsMapping.containsKey(key)) {
+                throw new AssertionError("Should not have key " + key + ", but has: "
+                        + mSettingsMapping.get(key));
+            }
+        }
     }
 
     /**
@@ -496,4 +546,33 @@
     @Target({METHOD})
     public static @interface ExpectWtf {
     }
+
+    private static final class SyncRunnable implements Runnable {
+        private final Runnable mTarget;
+        private volatile boolean mComplete = false;
+
+        private SyncRunnable(Runnable target) {
+            mTarget = target;
+        }
+
+        @Override
+        public void run() {
+            mTarget.run();
+            synchronized (this) {
+                mComplete = true;
+                notifyAll();
+            }
+        }
+
+        private void waitForComplete() {
+            synchronized (this) {
+                while (!mComplete) {
+                    try {
+                        wait();
+                    } catch (InterruptedException e) {
+                    }
+                }
+            }
+        }
+    }
 }
diff --git a/car-test-lib/src/android/car/test/mocks/AndroidMockitoHelper.java b/car-test-lib/src/android/car/test/mocks/AndroidMockitoHelper.java
index d05b013..20925e8 100644
--- a/car-test-lib/src/android/car/test/mocks/AndroidMockitoHelper.java
+++ b/car-test-lib/src/android/car/test/mocks/AndroidMockitoHelper.java
@@ -21,9 +21,11 @@
 import static org.mockito.Mockito.when;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.car.test.util.UserTestingHelper;
+import android.car.test.util.UserTestingHelper.UserInfoBuilder;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
@@ -78,7 +80,7 @@
     }
 
     /**
-     * Mocks {@code UserManager.getUserInfo(userId)} to return a {@link UserInfo} with the given
+     * Mocks {@code UserManager#getUserInfo(userId)} to return a {@link UserInfo} with the given
      * {@code flags}.
      */
     @NonNull
@@ -99,7 +101,7 @@
     }
 
     /**
-     * Mocks {@code UserManager.getUserInfo(userId)} when the {@code userId} is the system user's.
+     * Mocks {@code UserManager#getUserInfo(userId)} when the {@code userId} is the system user's.
      */
     @NonNull
     public static void mockUmGetSystemUser(@NonNull UserManager um) {
@@ -109,7 +111,7 @@
     }
 
     /**
-     * Mocks {@code UserManager.getUsers(excludeDying)} to return the given users.
+     * Mocks {@code UserManager#getUsers(excludeDying)} to return the given users.
      */
     public static void mockUmGetUsers(@NonNull UserManager um, @NonNull UserInfo... users) {
         Objects.requireNonNull(um);
@@ -118,7 +120,7 @@
     }
 
     /**
-     * Mocks {@code UserManager.getUsers(excludeDying)} to return simple users with the given ids.
+     * Mocks {@code UserManager#getUsers(excludeDying)} to return simple users with the given ids.
      */
     public static void mockUmGetUsers(@NonNull UserManager um, @NonNull @UserIdInt int... userIds) {
         List<UserInfo> users = UserTestingHelper.newUsers(userIds);
@@ -126,14 +128,22 @@
     }
 
     /**
-     * Mocks a call to {@code UserManager.getUsers()}.
+     * Mocks a call to {@code UserManager#getUsers()}, which includes dying users.
      */
-    public static void mockUmGetUsers(@NonNull UserManager um, @NonNull List<UserInfo> userInfos) {
+    public static void mockUmGetAllUsers(@NonNull UserManager um,
+            @NonNull List<UserInfo> userInfos) {
         when(um.getUsers()).thenReturn(userInfos);
     }
 
     /**
-     * Mocks a call to {@code UserManager.isUserRunning(userId)}.
+     * Mocks a call to {@code UserManager#getUsers(true)}, which excludes dying users.
+     */
+    public static void mockUmGetUsers(@NonNull UserManager um, @NonNull List<UserInfo> userInfos) {
+        when(um.getUsers(/* excludeDying= */ true)).thenReturn(userInfos);
+    }
+
+    /**
+     * Mocks a call to {@code UserManager#isUserRunning(userId)}.
      */
     public static void mockUmIsUserRunning(@NonNull UserManager um, @UserIdInt int userId,
             boolean isRunning) {
@@ -141,6 +151,32 @@
     }
 
     /**
+     * Mocks a successful call to {@code UserManager#createUser(String, String, int)}, returning
+     * a user with the passed arguments.
+     */
+    @NonNull
+    public static UserInfo mockUmCreateUser(@NonNull UserManager um, @Nullable String name,
+            @NonNull String userType, @UserInfoFlag int flags, @UserIdInt int userId) {
+        UserInfo userInfo = new UserInfoBuilder(userId)
+                        .setName(name)
+                        .setType(userType)
+                        .setFlags(flags)
+                        .build();
+        when(um.createUser(name, userType, flags)).thenReturn(userInfo);
+        return userInfo;
+    }
+
+    /**
+     * Mocks a call to {@code UserManager#createUser(String, String, int)} that throws the given
+     * runtime exception.
+     */
+    @NonNull
+    public static void mockUmCreateUser(@NonNull UserManager um, @Nullable String name,
+            @NonNull String userType, @UserInfoFlag int flags, @NonNull RuntimeException e) {
+        when(um.createUser(name, userType, flags)).thenThrow(e);
+    }
+
+    /**
      * Mocks a call to {@link ServiceManager#getService(name)}.
      *
      * <p><b>Note: </b>it must be made inside a
diff --git a/car-test-lib/src/android/car/test/util/UserTestingHelper.java b/car-test-lib/src/android/car/test/util/UserTestingHelper.java
index 92a612c..d1c2f13 100644
--- a/car-test-lib/src/android/car/test/util/UserTestingHelper.java
+++ b/car-test-lib/src/android/car/test/util/UserTestingHelper.java
@@ -15,6 +15,8 @@
  */
 package android.car.test.util;
 
+import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -84,6 +86,27 @@
     }
 
     /**
+     * Sets the property that defines the maximum number of uses allowed in the device.
+     */
+    public static void setMaxSupportedUsers(int max) {
+        runShellCommand("setprop fw.max_users %d", max);
+    }
+
+    /**
+     * Configures the user to use PIN credentials.
+     */
+    public static void setUserLockCredentials(@UserIdInt int userId, int pin) {
+        runShellCommand("locksettings set-pin %s --user %d ", pin, userId);
+    }
+
+    /**
+     * Clears the user credentials using current PIN.
+     */
+    public static void clearUserLockCredentials(@UserIdInt int userId, int pin) {
+        runShellCommand("locksettings clear --old %d --user %d ", pin, userId);
+    }
+
+    /**
      * Builder for {@link UserInfo} objects.
      */
     public static final class UserInfoBuilder {
diff --git a/car-usb-handler/res/values-ur/strings.xml b/car-usb-handler/res/values-ur/strings.xml
index 0fe5553..109dd4c 100644
--- a/car-usb-handler/res/values-ur/strings.xml
+++ b/car-usb-handler/res/values-ur/strings.xml
@@ -19,9 +19,7 @@
     <string name="app_name" msgid="6963366455471441257">"USB ہینڈلر"</string>
     <string name="usb_saved_devices" msgid="2829442070749964872">"محفوظ کردہ آلات"</string>
     <string name="usb_pref_delete_title" msgid="3885061814853467483">"USB آلہ کو ہینڈل کرنے والی اپپ کو ہٹائیں"</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for usb_pref_delete_message (5849493572520646218) -->
-    <skip />
+    <string name="usb_pref_delete_message" msgid="5849493572520646218">"کیا آپ واقعی %1$s اپپ کی ڈیفالٹ ہینڈلنگ کو حذف کرنا چاہتے ہیں؟"</string>
     <string name="usb_pref_delete_yes" msgid="7803356145103146036">"ہاں"</string>
     <string name="usb_pref_delete_cancel" msgid="5999791462730255929">"منسوخ کریں"</string>
     <string name="usb_resolving_handlers" msgid="1943100136172948686">"تعاون یافتہ ہینڈلرز حاصل کر رہے ہیں"</string>
diff --git a/car-usb-handler/src/android/car/usb/handler/AoapInterface.java b/car-usb-handler/src/android/car/usb/handler/AoapInterface.java
index 21aa64e..3b7fbfe 100644
--- a/car-usb-handler/src/android/car/usb/handler/AoapInterface.java
+++ b/car-usb-handler/src/android/car/usb/handler/AoapInterface.java
@@ -22,6 +22,10 @@
 import android.util.Pair;
 
 import java.io.IOException;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 import java.util.HashSet;
 import java.util.Set;
 
@@ -97,7 +101,7 @@
     /**
      * Accessory write timeout.
      */
-    public static final int AOAP_TIMEOUT_MS = 2000;
+    public static final int AOAP_TIMEOUT_MS = 50;
 
     /**
      * Set of VID:PID pairs blacklisted through config_AoapIncompatibleDeviceIds. Only
@@ -107,14 +111,30 @@
 
     private static final String TAG = AoapInterface.class.getSimpleName();
 
+    @Retention(RetentionPolicy.SOURCE)
+    @Target({ ElementType.FIELD, ElementType.PARAMETER })
+    public @interface Direction {}
+
+    @Direction
+    public static final int WRITE = 1;
+    @Direction
+    public static final int READ = 2;
+
+
     public static int getProtocol(UsbDeviceConnection conn) {
         byte[] buffer = new byte[2];
-        int len = conn.controlTransfer(
-                UsbConstants.USB_DIR_IN | UsbConstants.USB_TYPE_VENDOR,
-                ACCESSORY_GET_PROTOCOL, 0, 0, buffer, 2, AOAP_TIMEOUT_MS);
-        if (len != 2) {
+
+        int len = transfer(conn, READ, ACCESSORY_GET_PROTOCOL, 0, buffer, buffer.length);
+        if (len == 0) {
             return -1;
         }
+        if (len < 0) {
+            Log.w(TAG, "getProtocol() failed. Retrying...");
+            len = transfer(conn, READ, ACCESSORY_GET_PROTOCOL, 0, buffer, buffer.length);
+            if (len != buffer.length) {
+                return -1;
+            }
+        }
         return (buffer[1] << 8) | buffer[0];
     }
 
@@ -125,21 +145,22 @@
     public static void sendString(UsbDeviceConnection conn, int index, String string)
             throws IOException {
         byte[] buffer = (string + "\0").getBytes();
-        int len = conn.controlTransfer(
-                UsbConstants.USB_DIR_OUT | UsbConstants.USB_TYPE_VENDOR,
-                ACCESSORY_SEND_STRING, 0, index,
-                buffer, buffer.length, AOAP_TIMEOUT_MS);
+        int len = transfer(conn, WRITE, ACCESSORY_SEND_STRING, index, buffer,
+                buffer.length);
         if (len != buffer.length) {
-            throw new IOException("Failed to send string " + index + ": \"" + string + "\"");
+            Log.w(TAG, "sendString for " + index + ":" + string + " failed. Retrying...");
+            len = transfer(conn, WRITE, ACCESSORY_SEND_STRING, index, buffer,
+                buffer.length);
+            if (len != buffer.length) {
+                throw new IOException("Failed to send string " + index + ": \"" + string + "\"");
+            }
         } else {
             Log.i(TAG, "Sent string " + index + ": \"" + string + "\"");
         }
     }
 
     public static void sendAoapStart(UsbDeviceConnection conn) throws IOException {
-        int len = conn.controlTransfer(
-                UsbConstants.USB_DIR_OUT | UsbConstants.USB_TYPE_VENDOR,
-                ACCESSORY_START, 0, 0, null, 0, AOAP_TIMEOUT_MS);
+        int len = transfer(conn, WRITE, ACCESSORY_START, 0, null, 0);
         if (len < 0) {
             throw new IOException("Control transfer for accessory start failed: " + len);
         }
@@ -181,4 +202,22 @@
         return vid == USB_ACCESSORY_VENDOR_ID
                 && USB_ACCESSORY_MODE_PRODUCT_ID.contains(pid);
     }
+
+    private static int transfer(UsbDeviceConnection conn, @Direction int direction, int string,
+            int index, byte[] buffer, int length) {
+        int directionConstant;
+        switch (direction) {
+            case READ:
+                directionConstant = UsbConstants.USB_DIR_IN;
+                break;
+            case WRITE:
+                directionConstant = UsbConstants.USB_DIR_OUT;
+                break;
+            default:
+                Log.w(TAG, "Unknown direction for transfer: " + direction);
+                return -1;
+        }
+        return conn.controlTransfer(directionConstant | UsbConstants.USB_TYPE_VENDOR, string, 0,
+            index, buffer, length, AOAP_TIMEOUT_MS);
+    }
 }
diff --git a/car-usb-handler/src/android/car/usb/handler/UsbDeviceHandlerResolver.java b/car-usb-handler/src/android/car/usb/handler/UsbDeviceHandlerResolver.java
index 8b2f425..a1be7dd 100644
--- a/car-usb-handler/src/android/car/usb/handler/UsbDeviceHandlerResolver.java
+++ b/car-usb-handler/src/android/car/usb/handler/UsbDeviceHandlerResolver.java
@@ -71,18 +71,16 @@
     private final PackageManager mPackageManager;
     private final UsbDeviceHandlerResolverCallback mDeviceCallback;
     private final Context mContext;
-    private final HandlerThread mHandlerThread;
-    private final UsbDeviceResolverHandler mHandler;
     private final AoapServiceManager mAoapServiceManager;
+    private HandlerThread mHandlerThread;
+    private UsbDeviceResolverHandler mHandler;
 
     public UsbDeviceHandlerResolver(UsbManager manager, Context context,
             UsbDeviceHandlerResolverCallback deviceListener) {
         mUsbManager = manager;
         mContext = context;
         mDeviceCallback = deviceListener;
-        mHandlerThread = new HandlerThread(TAG);
-        mHandlerThread.start();
-        mHandler = new UsbDeviceResolverHandler(mHandlerThread.getLooper());
+        createHandlerThread();
         mPackageManager = context.getPackageManager();
         mAoapServiceManager = new AoapServiceManager(mContext.getApplicationContext());
     }
@@ -104,9 +102,22 @@
     }
 
     /**
+     * Listener for failed {@code startAosp} command.
+     *
+     * <p>If {@code startAosp} fails, the device could be left in a inconsistent state, that's why
+     * we go back to USB enumeration, instead of just repeating the command.
+     */
+    public interface StartAoapFailureListener {
+
+        /** Called if startAoap fails. */
+        void onFailure();
+    }
+
+    /**
      * Dispatches device to component.
      */
-    public boolean dispatch(UsbDevice device, ComponentName component, boolean inAoap) {
+    public boolean dispatch(UsbDevice device, ComponentName component, boolean inAoap,
+            StartAoapFailureListener failureListener) {
         if (LOCAL_LOGD) {
             Log.d(TAG, "dispatch: " + device + " component: " + component + " inAoap: " + inAoap);
         }
@@ -128,10 +139,20 @@
                         packageMatches(activityInfo, intent.getAction(), device, true);
 
                 if (filter != null) {
+                    if (!mHandlerThread.isAlive()) {
+                        // Start a new thread. Used only when startAoap fails, and we need to
+                        // re-enumerate device in order to try again.
+                        createHandlerThread();
+                    }
                     mHandlerThread.getThreadHandler().post(() -> {
                         if (mAoapServiceManager.canSwitchDeviceToAoap(device,
                                 ComponentName.unflattenFromString(filter.mAoapService))) {
-                            requestAoapSwitch(device, filter);
+                            try {
+                                requestAoapSwitch(device, filter);
+                            } catch (IOException e) {
+                                Log.w(TAG, "Start AOAP command failed:" + e);
+                                failureListener.onFailure();
+                            }
                         } else {
                             Log.i(TAG, "Ignore AOAP switch for device " + device
                                     + " handled by " + filter.mAoapService);
@@ -151,6 +172,12 @@
         return true;
     }
 
+    private void createHandlerThread() {
+        mHandlerThread = new HandlerThread(TAG);
+        mHandlerThread.start();
+        mHandler = new UsbDeviceResolverHandler(mHandlerThread.getLooper());
+    }
+
     private static Intent createDeviceAttachedIntent(UsbDevice device) {
         Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED);
         intent.putExtra(UsbManager.EXTRA_DEVICE, device);
@@ -192,7 +219,7 @@
         return settings;
     }
 
-    private void requestAoapSwitch(UsbDevice device, UsbDeviceFilter filter) {
+    private void requestAoapSwitch(UsbDevice device, UsbDeviceFilter filter) throws IOException {
         UsbDeviceConnection connection = UsbUtil.openConnection(mUsbManager, device);
         if (connection == null) {
             Log.e(TAG, "Failed to connect to usb device.");
@@ -209,11 +236,9 @@
                     filter.mAoapVersion,
                     filter.mAoapUri,
                     hashedSerial);
-        } catch (IOException e) {
-            Log.w(TAG, "Failed to switch device into AOAP mode", e);
+        } finally {
+            connection.close();
         }
-
-        connection.close();
     }
 
     private String getHashed(String serial) {
diff --git a/car-usb-handler/src/android/car/usb/handler/UsbHostController.java b/car-usb-handler/src/android/car/usb/handler/UsbHostController.java
index 3fcb67b..0da71ac 100644
--- a/car-usb-handler/src/android/car/usb/handler/UsbHostController.java
+++ b/car-usb-handler/src/android/car/usb/handler/UsbHostController.java
@@ -30,6 +30,7 @@
 import com.android.internal.annotations.GuardedBy;
 
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
 
 /**
@@ -300,6 +301,12 @@
 
         private static final int DEVICE_REMOVE_TIMEOUT_MS = 500;
 
+        // Used to get the device that we are trying to connect to, if mActiveDevice is removed and
+        // startAoap fails afterwards. Used during USB enumeration when retrying to startAoap when
+        // there are multiple devices attached.
+        private int mLastDeviceId = 0;
+        private int mStartAoapRetries = 1;
+
         private UsbHostControllerHandler(Looper looper) {
             super(looper);
         }
@@ -308,6 +315,24 @@
             sendEmptyMessageDelayed(MSG_DEVICE_REMOVED, DEVICE_REMOVE_TIMEOUT_MS);
         }
 
+        private void onFailure() {
+            if (mStartAoapRetries == 0) {
+                Log.w(TAG, "Reached maximum retry count for startAoap. Giving up Aoa handshake.");
+                return;
+            }
+            mStartAoapRetries--;
+
+            Log.d(TAG, "Restarting USB enumeration.");
+            Iterator<UsbDevice> deviceIterator = mUsbManager.getDeviceList().values().iterator();
+            while (deviceIterator.hasNext()) {
+                UsbDevice device = deviceIterator.next();
+                if (mLastDeviceId == device.getDeviceId()) {
+                    processDevice(device);
+                    return;
+                }
+            }
+        }
+
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
@@ -318,8 +343,10 @@
                     UsbHostControllerHandlerDispatchData data =
                             (UsbHostControllerHandlerDispatchData) msg.obj;
                     UsbDevice device = data.getUsbDevice();
+                    mLastDeviceId = device.getDeviceId();
                     UsbDeviceSettings settings = data.getUsbDeviceSettings();
-                    if (!mUsbResolver.dispatch(device, settings.getHandler(), settings.getAoap())) {
+                    if (!mUsbResolver.dispatch(device, settings.getHandler(), settings.getAoap(),
+                            this::onFailure)) {
                         if (data.mRetries > 0) {
                             --data.mRetries;
                             Message nextMessage = Message.obtain(msg);
diff --git a/car_product/build/preinstalled-packages-product-car-base.xml b/car_product/build/preinstalled-packages-product-car-base.xml
index e888eb5..9587c83 100644
--- a/car_product/build/preinstalled-packages-product-car-base.xml
+++ b/car_product/build/preinstalled-packages-product-car-base.xml
@@ -166,6 +166,12 @@
         <install-in user-type="SYSTEM" />
     </install-in-user-type>
 
+    <!-- Failed to pass CTS if not installed for system user -->
+    <install-in-user-type package="com.android.car.ui.sharedlibrary">
+        <install-in user-type="FULL" />
+        <install-in user-type="SYSTEM" />
+    </install-in-user-type>
+
 <!--
   Apps that do need to run on SYSTEM and evaluated by package owner.
   Here the apps will have FULL only.
@@ -260,9 +266,6 @@
     <install-in-user-type package="com.android.car.calendar">
         <install-in user-type="FULL" />
     </install-in-user-type>
-    <install-in-user-type package="com.android.car.ui.sharedlibrary">
-        <install-in user-type="FULL" />
-    </install-in-user-type>
     <install-in-user-type package="com.android.wifi.inprocess.overlay.car">
         <install-in user-type="FULL" />
     </install-in-user-type>
diff --git a/car_product/sepolicy/private/carservice_app.te b/car_product/sepolicy/private/carservice_app.te
index d62cb27..4841891 100644
--- a/car_product/sepolicy/private/carservice_app.te
+++ b/car_product/sepolicy/private/carservice_app.te
@@ -76,3 +76,9 @@
 get_prop(carservice_app, vehicle_hal_prop)
 
 carwatchdog_client_domain(carservice_app)
+
+# For ActivityBlockingActiviy
+allow carservice_app gpu_device:chr_file rw_file_perms;
+allow carservice_app gpu_device:dir r_dir_perms;
+allow carservice_app gpu_service:service_manager find;
+binder_call(carservice_app, gpuservice)
diff --git a/evs/apps/default/ConfigManager.h b/evs/apps/default/ConfigManager.h
index 9c6d1a2..7c2186d 100644
--- a/evs/apps/default/ConfigManager.h
+++ b/evs/apps/default/ConfigManager.h
@@ -16,8 +16,9 @@
 #ifndef CONFIG_MANAGER_H
 #define CONFIG_MANAGER_H
 
-#include <vector>
+#include <cerrno>
 #include <string>
+#include <vector>
 
 #include <system/graphics-base.h>
 
@@ -78,16 +79,27 @@
 
     const std::vector<CameraInfo>& getCameras() const   { return mCameras; };
 
-    bool  setActiveDisplayId(int displayId) {
-        if (displayId < 0 or displayId > mDisplays.size()) {
-            printf("Display %d is invalid.  Current active display is display %d.",
-                   displayId, mActiveDisplayId);
-            return false;
+    int  setActiveDisplayId(int displayId) {
+        if (displayId == -1) {
+            // -1 is reserved for the default display, which is the first
+            // display in config.json's display list
+            printf("Uses a display with id %d", mDisplays[0].port);
+            mActiveDisplayId = mDisplays[0].port;
+            return mActiveDisplayId;
+        } else if (displayId < 0) {
+            printf("Display %d is invalid.", displayId);
+            return -ENOENT;
+        } else {
+            for (auto display : mDisplays) {
+                if (display.port == displayId) {
+                    mActiveDisplayId = displayId;
+                    return mActiveDisplayId;
+                }
+            }
+
+            printf("Display %d does not exist.", displayId);
+            return -ENOENT;
         }
-
-        mActiveDisplayId = displayId;
-
-        return true;
     }
     const std::vector<DisplayInfo>& getDisplays() const { return mDisplays; };
     const DisplayInfo& getActiveDisplay() const { return mDisplays[mActiveDisplayId]; };
diff --git a/evs/apps/default/config.json.readme b/evs/apps/default/config.json.readme
index 7d1ec9d..561adcc 100644
--- a/evs/apps/default/config.json.readme
+++ b/evs/apps/default/config.json.readme
@@ -19,7 +19,7 @@
     "rearExtent" : 40           // The extent of the car body behind the read axel
   },
   "displays" : [                // This configures the dimensions of the surround view display
-    {
+    {                           // The first display will be used as the default display
       "displayPort" : 1         // Display port number, the target display is connected to.
       "frontRange" : 100,       // How far to render the view in front of the front bumper
       "rearRange" : 100         // How far the view extends behind the rear bumper
diff --git a/evs/apps/default/evs_app.cpp b/evs/apps/default/evs_app.cpp
index 9f2f2c8..b30bcc7 100644
--- a/evs/apps/default/evs_app.cpp
+++ b/evs/apps/default/evs_app.cpp
@@ -94,7 +94,7 @@
     bool useVehicleHal = true;
     bool printHelp = false;
     const char* evsServiceName = "default";
-    int displayId = 1;
+    int displayId = -1;
     bool useExternalMemory = false;
     android_pixel_format_t extMemoryFormat = HAL_PIXEL_FORMAT_RGBA_8888;
     for (int i=1; i< argc; i++) {
@@ -133,7 +133,8 @@
         printf("  --test\n\tDo not talk to Vehicle Hal, but simulate 'reverse' instead\n");
         printf("  --hw\n\tBypass EvsManager by connecting directly to EvsEnumeratorHw\n");
         printf("  --mock\n\tConnect directly to EvsEnumeratorHw-Mock\n");
-        printf("  --display\n\tSpecify the display to use\n");
+        printf("  --display\n\tSpecify the display to use.  If this is not set, the first"
+                              "display in config.json's list will be used.\n");
         printf("  --extmem  <format>\n\t"
                "Application allocates buffers to capture camera frames.  "
                "Available format strings are (case insensitive):\n");
@@ -153,7 +154,7 @@
     ConfigManager config;
     if (!config.initialize("/system/etc/automotive/evs/config.json")) {
         LOG(ERROR) << "Missing or improper configuration for the EVS application.  Exiting.";
-        return 1;
+        return EXIT_FAILURE;
     }
 
     // Set thread pool size to one to avoid concurrent events from the HAL.
@@ -171,19 +172,25 @@
     if (pEvs.get() == nullptr) {
         LOG(ERROR) << "getService(" << evsServiceName
                    << ") returned NULL.  Exiting.";
-        return 1;
+        return EXIT_FAILURE;
     }
 
     // Request exclusive access to the EVS display
     LOG(INFO) << "Acquiring EVS Display";
 
     // We'll use an available display device.
+    displayId = config.setActiveDisplayId(displayId);
+    if (displayId < 0) {
+        PLOG(ERROR) << "EVS Display is unknown.  Exiting.";
+        return EXIT_FAILURE;
+    }
+
     android::sp<IEvsDisplay> pDisplay = pEvs->openDisplay_1_1(displayId);
     if (pDisplay.get() == nullptr) {
         LOG(ERROR) << "EVS Display unavailable.  Exiting.";
-        return 1;
+        return EXIT_FAILURE;
     }
-    config.setActiveDisplayId(displayId);
+
     config.useExternalMemory(useExternalMemory);
     config.setExternalMemoryFormat(extMemoryFormat);
 
@@ -194,13 +201,13 @@
         pVnet = IVehicle::getService();
         if (pVnet.get() == nullptr) {
             LOG(ERROR) << "Vehicle HAL getService returned NULL.  Exiting.";
-            return 1;
+            return EXIT_FAILURE;
         } else {
             // Register for vehicle state change callbacks we care about
             // Changes in these values are what will trigger a reconfiguration of the EVS pipeline
             if (!subscribeToVHal(pVnet, pEvsListener, VehicleProperty::GEAR_SELECTION)) {
                 LOG(ERROR) << "Without gear notification, we can't support EVS.  Exiting.";
-                return 1;
+                return EXIT_FAILURE;
             }
             if (!subscribeToVHal(pVnet, pEvsListener, VehicleProperty::TURN_SIGNAL_STATE)) {
                 LOG(WARNING) << "Didn't get turn signal notifications, so we'll ignore those.";
@@ -215,7 +222,7 @@
     EvsStateControl *pStateController = new EvsStateControl(pVnet, pEvs, pDisplay, config);
     if (!pStateController->startUpdateLoop()) {
         LOG(ERROR) << "Initial configuration failed.  Exiting.";
-        return 1;
+        return EXIT_FAILURE;
     }
 
     // Run forever, reacting to events as necessary
@@ -226,5 +233,5 @@
     // One known example is if another process preempts our registration for our service name.
     LOG(ERROR) << "EVS Listener stopped.  Exiting.";
 
-    return 0;
+    return EXIT_SUCCESS;
 }
diff --git a/evs/manager/1.1/HalCamera.cpp b/evs/manager/1.1/HalCamera.cpp
index a90e83b..6e6f77d 100644
--- a/evs/manager/1.1/HalCamera.cpp
+++ b/evs/manager/1.1/HalCamera.cpp
@@ -92,6 +92,10 @@
 
     // Add this virtualCamera to our ownership list via weak pointer
     mClients.emplace_back(virtualCamera);
+
+    // Update statistics
+    mUsageStats->updateNumClients(mClients.size());
+
     return true;
 }
 
@@ -114,6 +118,9 @@
     if (!changeFramesInFlight(0)) {
         LOG(ERROR) << "Error when trying to reduce the in flight buffer count";
     }
+
+    // Update statistics
+    mUsageStats->updateNumClients(mClients.size());
 }
 
 
@@ -352,7 +359,7 @@
             mHwCamera->doneWithFrame_1_1(returnedBuffers);
 
             // Counts a returned buffer
-            mUsageStats->framesReturned();
+            mUsageStats->framesReturned(returnedBuffers);
         }
     }
 
@@ -412,7 +419,7 @@
     }
 
     // Reports the number of received buffers
-    mUsageStats->framesReceived(buffer.size());
+    mUsageStats->framesReceived(buffer);
 
     // Frames are being forwarded to active v1.0 clients and v1.1 clients if we
     // failed to create a timeline.
@@ -437,7 +444,7 @@
         mHwCamera->doneWithFrame_1_1(buffer);
 
         // Reports a returned buffer
-        mUsageStats->framesReturned();
+        mUsageStats->framesReturned(buffer);
     } else {
         // Add an entry for this frame in our tracking list.
         unsigned i;
diff --git a/evs/manager/1.1/stats/CameraUsageStats.cpp b/evs/manager/1.1/stats/CameraUsageStats.cpp
index 11f8229..593577e 100644
--- a/evs/manager/1.1/stats/CameraUsageStats.cpp
+++ b/evs/manager/1.1/stats/CameraUsageStats.cpp
@@ -18,6 +18,15 @@
 
 #include <statslog.h>
 
+#include <android-base/logging.h>
+
+namespace {
+
+    // Length of frame roundtrip history
+    const int kMaxHistoryLength = 100;
+
+}
+
 namespace android {
 namespace automotive {
 namespace evs {
@@ -26,6 +35,50 @@
 
 using ::android::base::Result;
 using ::android::base::StringAppendF;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::automotive::evs::V1_1::BufferDesc;
+
+void CameraUsageStats::updateFrameStatsOnArrival(
+        const hidl_vec<BufferDesc>& bufs) {
+    const auto now = android::uptimeMillis();
+    for (const auto& b : bufs) {
+        auto it = mBufferHistory.find(b.bufferId);
+        if (it == mBufferHistory.end()) {
+            mBufferHistory.emplace(b.bufferId, now);
+        } else {
+            it->second.timestamp = now;
+        }
+    }
+}
+
+
+void CameraUsageStats::updateFrameStatsOnReturn(
+        const hidl_vec<BufferDesc>& bufs) {
+    const auto now = android::uptimeMillis();
+    for (auto& b : bufs) {
+        auto it = mBufferHistory.find(b.bufferId);
+        if (it == mBufferHistory.end()) {
+            LOG(WARNING) << "Buffer " << b.bufferId << " from "
+                         << b.deviceId << " is unknown.";
+        } else {
+            const auto roundtrip = now - it->second.timestamp;
+            it->second.history.emplace(roundtrip);
+            it->second.sum += roundtrip;
+            if (it->second.history.size() > kMaxHistoryLength) {
+                it->second.sum -= it->second.history.front();
+                it->second.history.pop();
+            }
+
+            if (roundtrip > it->second.peak) {
+                it->second.peak = roundtrip;
+            }
+
+            if (mStats.framesFirstRoundtripLatency == 0) {
+                mStats.framesFirstRoundtripLatency = roundtrip;
+            }
+        }
+    }
+}
 
 
 void CameraUsageStats::framesReceived(int n) {
@@ -34,12 +87,30 @@
 }
 
 
+void CameraUsageStats::framesReceived(
+        const hidl_vec<BufferDesc>& bufs) {
+    AutoMutex lock(mMutex);
+    mStats.framesReceived += bufs.size();
+
+    updateFrameStatsOnArrival(bufs);
+}
+
+
 void CameraUsageStats::framesReturned(int n) {
     AutoMutex lock(mMutex);
     mStats.framesReturned += n;
 }
 
 
+void CameraUsageStats::framesReturned(
+        const hidl_vec<BufferDesc>& bufs) {
+    AutoMutex lock(mMutex);
+    mStats.framesReturned += bufs.size();
+
+    updateFrameStatsOnReturn(bufs);
+}
+
+
 void CameraUsageStats::framesIgnored(int n) {
     AutoMutex lock(mMutex);
     mStats.framesIgnored += n;
@@ -58,6 +129,14 @@
 }
 
 
+void CameraUsageStats::updateNumClients(size_t n) {
+    AutoMutex lock(mMutex);
+    if (n > mStats.peakClientsCount) {
+        mStats.peakClientsCount = n;
+    }
+}
+
+
 int64_t CameraUsageStats::getTimeCreated() const {
     AutoMutex lock(mMutex);
     return mTimeCreatedMs;
@@ -76,16 +155,33 @@
 }
 
 
-CameraUsageStatsRecord CameraUsageStats::snapshot() const {
+CameraUsageStatsRecord CameraUsageStats::snapshot() {
     AutoMutex lock(mMutex);
+
+    int32_t sum = 0;
+    int32_t peak = 0;
+    int32_t len = 0;
+    for (auto& [id, rec] : mBufferHistory) {
+        sum += rec.sum;
+        len += rec.history.size();
+        if (peak < rec.peak) {
+            peak = rec.peak;
+        }
+    }
+
+    mStats.framesPeakRoundtripLatency = peak;
+    mStats.framesAvgRoundtripLatency = (double)sum / len;
     return mStats;
 }
 
 
 Result<void> CameraUsageStats::writeStats() const {
     AutoMutex lock(mMutex);
+
+    // Reports the usage statistics before the destruction
+    // EvsUsageStatsReported atom is defined in
+    // frameworks/base/cmds/statsd/src/atoms.proto
     const auto duration = android::uptimeMillis() - mTimeCreatedMs;
-    // TODO(b/156131016): calculates and reports frame roundtrip latencies
     android::util::stats_write(android::util::EVS_USAGE_STATS_REPORTED,
                                mId,
                                mStats.peakClientsCount,
diff --git a/evs/manager/1.1/stats/CameraUsageStats.h b/evs/manager/1.1/stats/CameraUsageStats.h
index 4bc4ea8..7a7224d 100644
--- a/evs/manager/1.1/stats/CameraUsageStats.h
+++ b/evs/manager/1.1/stats/CameraUsageStats.h
@@ -17,8 +17,12 @@
 #ifndef ANDROID_AUTOMOTIVE_EVS_V1_1_CAMERAUSAGESTATS_H
 #define ANDROID_AUTOMOTIVE_EVS_V1_1_CAMERAUSAGESTATS_H
 
+#include <queue>
+#include <unordered_map>
+
 #include <inttypes.h>
 
+#include <android/hardware/automotive/evs/1.1/types.h>
 #include <android-base/result.h>
 #include <android-base/stringprintf.h>
 #include <utils/Mutex.h>
@@ -89,18 +93,46 @@
                 "%sFrames Received: %" PRId64 "\n"
                 "%sFrames Returned: %" PRId64 "\n"
                 "%sFrames Ignored : %" PRId64 "\n"
-                "%sFrames Skipped To Sync: %" PRId64 "\n\n",
+                "%sFrames Skipped To Sync: %" PRId64 "\n"
+                "%sFrames First Roundtrip: %" PRId64 "\n"
+                "%sFrames Peak Roundtrip: %" PRId64 "\n"
+                "%sFrames Average Roundtrip: %f\n"
+                "%sPeak Number of Clients: %" PRId32 "\n\n",
                 indent, ns2ms(timestamp),
                 indent, framesReceived,
                 indent, framesReturned,
                 indent, framesIgnored,
-                indent, framesSkippedToSync);
+                indent, framesSkippedToSync,
+                indent, framesFirstRoundtripLatency,
+                indent, framesPeakRoundtripLatency,
+                indent, framesAvgRoundtripLatency,
+                indent, peakClientsCount);
 
         return buffer;
     }
 };
 
 
+struct BufferRecord {
+    BufferRecord(int64_t timestamp) :
+        timestamp(timestamp),
+        sum(0),
+        peak(0) {}
+
+    // Recent processing time
+    std::queue<int32_t> history;
+
+    // Timestamp on the buffer arrival
+    int64_t timestamp;
+
+    // Sum of processing times
+    int64_t sum;
+
+    // Peak processing time
+    int64_t peak;
+};
+
+
 class CameraUsageStats : public RefBase {
 public:
     CameraUsageStats(int32_t id)
@@ -122,18 +154,34 @@
     // Usage statistics to collect
     CameraUsageStatsRecord mStats GUARDED_BY(mMutex);
 
+    // Frame buffer histories
+    std::unordered_map<int, BufferRecord> mBufferHistory GUARDED_BY(mMutex);
+
 public:
     void framesReceived(int n = 1) EXCLUDES(mMutex);
     void framesReturned(int n = 1) EXCLUDES(mMutex);
+    void framesReceived(
+            const hardware::hidl_vec<::android::hardware::automotive::evs::V1_1::BufferDesc>& bufs
+        ) EXCLUDES(mMutex);
+    void framesReturned(
+            const hardware::hidl_vec<::android::hardware::automotive::evs::V1_1::BufferDesc>& bufs
+        ) EXCLUDES(mMutex);
     void framesIgnored(int n = 1) EXCLUDES(mMutex);
     void framesSkippedToSync(int n = 1) EXCLUDES(mMutex);
     void eventsReceived() EXCLUDES(mMutex);
     int64_t getTimeCreated() const EXCLUDES(mMutex);
     int64_t getFramesReceived() const EXCLUDES(mMutex);
     int64_t getFramesReturned() const EXCLUDES(mMutex);
+    void updateNumClients(size_t n) EXCLUDES(mMutex);
+    void updateFrameStatsOnArrival(
+            const hardware::hidl_vec<::android::hardware::automotive::evs::V1_1::BufferDesc>& bufs
+        ) REQUIRES(mMutex);
+    void updateFrameStatsOnReturn(
+            const hardware::hidl_vec<::android::hardware::automotive::evs::V1_1::BufferDesc>& bufs
+        ) REQUIRES(mMutex);
 
     // Returns the statistics collected so far
-    CameraUsageStatsRecord snapshot() const EXCLUDES(mMutex);
+    CameraUsageStatsRecord snapshot() EXCLUDES(mMutex);
 
     // Reports the usage statistics
     android::base::Result<void> writeStats() const EXCLUDES(mMutex);
diff --git a/evs/manager/1.1/stats/StatsCollector.cpp b/evs/manager/1.1/stats/StatsCollector.cpp
index de85e5a..b57f928 100644
--- a/evs/manager/1.1/stats/StatsCollector.cpp
+++ b/evs/manager/1.1/stats/StatsCollector.cpp
@@ -205,11 +205,6 @@
             PLOG(WARNING) << "Failed to set background scheduling prioirty";
         }
 
-        auto ret = pthread_setname_np(pthread_self(), "EvsCameraUsageCollect");
-        if (ret != 0) {
-            PLOG(WARNING) << "Failed to name a collection thread";
-        }
-
         // Sets a looper for the communication
         mLooper->setLooper(Looper::prepare(/*opts=*/0));
 
@@ -227,6 +222,11 @@
         }
     });
 
+    auto ret = pthread_setname_np(mCollectionThread.native_handle(), "EvsUsageCollect");
+    if (ret != 0) {
+        PLOG(WARNING) << "Failed to name a collection thread";
+    }
+
     return {};
 }
 
diff --git a/service/res/values/config.xml b/service/res/values/config.xml
index 0193408..e40ac97 100644
--- a/service/res/values/config.xml
+++ b/service/res/values/config.xml
@@ -44,6 +44,11 @@
     <!--  Service responsible for displaying information on the car instrument cluster. -->
     <string name="instrumentClusterRendererService" translatable="false">android.car.cluster/.ClusterRenderingService</string>
 
+    <!--  Service responsible for handling the rotary controller input. This service will start
+          on boot or on user switch. Set this string to empty if you don't want to start this
+          service. -->
+    <string name="rotaryService" translatable="false">com.android.car.rotary/com.android.car.rotary.RotaryService</string>
+
     <!--  Whether to enable Activity blocking for safety. When Activity blocking is enabled,
           only whitelisted safe Activities will be allowed while car is not parked. -->
     <bool name="enableActivityBlockingForSafety">true</bool>
diff --git a/service/src/com/android/car/AppFocusService.java b/service/src/com/android/car/AppFocusService.java
index d97bec8..cc1a530 100644
--- a/service/src/com/android/car/AppFocusService.java
+++ b/service/src/com/android/car/AppFocusService.java
@@ -161,7 +161,7 @@
                             ownerInfo.binderInterface, appType);
                     if (DBG) {
                         Log.i(CarLog.TAG_APP_FOCUS, "losing app type "
-                                + appType + "," + ownerInfo.toString());
+                                + appType + "," + ownerInfo);
                     }
                 }
                 updateFocusOwner(appType, info);
@@ -169,20 +169,20 @@
             info.addOwnedAppType(appType);
             mDispatchHandler.requestAppFocusOwnershipGrantDispatch(
                     info.binderInterface, appType);
-            if (mActiveAppTypes.add(appType)) {
-                if (DBG) {
-                    Log.i(CarLog.TAG_APP_FOCUS, "adding active app type " + appType + ","
-                            + info.toString());
-                }
-                for (BinderInterfaceContainer.BinderInterface<IAppFocusListener> client :
-                        mAllChangeClients.getInterfaces()) {
-                    ClientInfo clientInfo = (ClientInfo) client;
-                    // dispatch events only when there is change after filter and the listener
-                    // is not coming from the current caller.
-                    if (clientInfo.getAppTypes().contains(appType)) {
-                        mDispatchHandler.requestAppFocusChangeDispatch(clientInfo.binderInterface,
-                                appType, true);
-                    }
+            mActiveAppTypes.add(appType);
+            if (DBG) {
+                Log.i(CarLog.TAG_APP_FOCUS, "updating active app type " + appType + ","
+                        + info);
+            }
+            // Always dispatch.
+            for (BinderInterfaceContainer.BinderInterface<IAppFocusListener> client :
+                    mAllChangeClients.getInterfaces()) {
+                ClientInfo clientInfo = (ClientInfo) client;
+                // dispatch events only when there is change after filter and the listener
+                // is not coming from the current caller.
+                if (clientInfo.getAppTypes().contains(appType)) {
+                    mDispatchHandler.requestAppFocusChangeDispatch(clientInfo.binderInterface,
+                            appType, true);
                 }
             }
         }
@@ -212,8 +212,7 @@
                 mActiveAppTypes.remove(appType);
                 info.removeOwnedAppType(appType);
                 if (DBG) {
-                    Log.i(CarLog.TAG_APP_FOCUS, "abandoning focus " + appType
-                            + "," + info.toString());
+                    Log.i(CarLog.TAG_APP_FOCUS, "abandoning focus " + appType + "," + info);
                 }
                 for (FocusOwnershipCallback ownershipCallback : mFocusOwnershipCallbacks) {
                     ownershipCallback.onFocusAbandoned(appType, info.mUid, info.mPid);
@@ -270,7 +269,7 @@
             for (BinderInterfaceContainer.BinderInterface<IAppFocusOwnershipCallback> client :
                     mAllOwnershipClients.getInterfaces()) {
                 OwnershipClientInfo clientInfo = (OwnershipClientInfo) client;
-                writer.println(clientInfo.toString());
+                writer.println(clientInfo);
             }
         }
     }
diff --git a/service/src/com/android/car/CarInputService.java b/service/src/com/android/car/CarInputService.java
index 65c616f..222e760 100644
--- a/service/src/com/android/car/CarInputService.java
+++ b/service/src/com/android/car/CarInputService.java
@@ -19,6 +19,7 @@
 import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_PUSH_TO_TALK;
 
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
@@ -32,6 +33,8 @@
 import android.car.input.ICarInputCallback;
 import android.car.input.ICarInputListener;
 import android.car.input.RotaryEvent;
+import android.car.user.CarUserManager;
+import android.car.userlib.UserHelper;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -58,6 +61,7 @@
 import android.view.ViewConfiguration;
 
 import com.android.car.hal.InputHalService;
+import com.android.car.user.CarUserService;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.AssistUtils;
@@ -159,6 +163,7 @@
 
     private final Context mContext;
     private final InputHalService mInputHalService;
+    private final CarUserService mUserService;
     private final TelecomManager mTelecomManager;
     private final AssistUtils mAssistUtils;
     // The ComponentName of the CarInputListener service. Can be changed via resource overlay,
@@ -175,6 +180,8 @@
     // from Settings.Secure for the current user, falling back to the system-wide default
     // long-press delay defined in ViewConfiguration. May be overridden for testing.
     private final IntSupplier mLongPressDelaySupplier;
+    // ComponentName of the RotaryService.
+    private final String mRotaryServiceComponentName;
 
     private final Object mLock = new Object();
 
@@ -270,6 +277,13 @@
         }
     };
 
+    private final CarUserManager.UserLifecycleListener mUserLifecycleListener = event -> {
+        Log.d(CarLog.TAG_INPUT, "CarInputService.onEvent(" + event + ")");
+        if (CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING == event.getEventType()) {
+            updateRotaryServiceSettings(event.getUserId());
+        }
+    };
+
     @Nullable
     private static ComponentName getDefaultInputComponent(Context context) {
         String carInputService = context.getString(R.string.inputService);
@@ -285,8 +299,9 @@
                 UserHandle.USER_CURRENT);
     }
 
-    public CarInputService(Context context, InputHalService inputHalService) {
-        this(context, inputHalService, new Handler(Looper.getMainLooper()),
+    public CarInputService(Context context, InputHalService inputHalService,
+            CarUserService userService) {
+        this(context, inputHalService, userService, new Handler(Looper.getMainLooper()),
                 context.getSystemService(TelecomManager.class), new AssistUtils(context),
                 event ->
                         context.getSystemService(InputManager.class)
@@ -297,14 +312,15 @@
     }
 
     @VisibleForTesting
-    CarInputService(Context context, InputHalService inputHalService, Handler handler,
-            TelecomManager telecomManager, AssistUtils assistUtils,
+    CarInputService(Context context, InputHalService inputHalService, CarUserService userService,
+            Handler handler, TelecomManager telecomManager, AssistUtils assistUtils,
             KeyEventListener mainDisplayHandler, Supplier<String> lastCalledNumberSupplier,
             @Nullable ComponentName customInputServiceComponent,
             IntSupplier longPressDelaySupplier) {
         mContext = context;
         mCaptureController = new InputCaptureClientController(context);
         mInputHalService = inputHalService;
+        mUserService = userService;
         mTelecomManager = telecomManager;
         mAssistUtils = assistUtils;
         mMainDisplayHandler = mainDisplayHandler;
@@ -317,6 +333,8 @@
                         handler, longPressDelaySupplier, this::handleVoiceAssistLongPress);
         mCallKeyTimer =
                 new KeyPressTimer(handler, longPressDelaySupplier, this::handleCallLongPress);
+
+        mRotaryServiceComponentName = mContext.getString(R.string.rotaryService);
     }
 
     @VisibleForTesting
@@ -367,6 +385,9 @@
             mBluetoothAdapter.getProfileProxy(
                     mContext, mBluetoothProfileServiceListener, BluetoothProfile.HEADSET_CLIENT);
         }
+        if (!TextUtils.isEmpty(mRotaryServiceComponentName)) {
+            mUserService.addUserLifecycleListener(mUserLifecycleListener);
+        }
     }
 
     @Override
@@ -385,6 +406,9 @@
                 mBluetoothHeadsetClient = null;
             }
         }
+        if (!TextUtils.isEmpty(mRotaryServiceComponentName)) {
+            mUserService.removeUserLifecycleListener(mUserLifecycleListener);
+        }
     }
 
     @Override
@@ -705,4 +729,19 @@
         intent.setComponent(mCustomInputServiceComponent);
         return mContext.bindService(intent, mInputServiceConnection, Context.BIND_AUTO_CREATE);
     }
+
+    private void updateRotaryServiceSettings(@UserIdInt int userId) {
+        if (UserHelper.isHeadlessSystemUser(userId)) {
+            return;
+        }
+        ContentResolver contentResolver = mContext.getContentResolver();
+        Settings.Secure.putStringForUser(contentResolver,
+                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+                mRotaryServiceComponentName,
+                userId);
+        Settings.Secure.putStringForUser(contentResolver,
+                Settings.Secure.ACCESSIBILITY_ENABLED,
+                "1",
+                userId);
+    }
 }
diff --git a/service/src/com/android/car/CarPowerManagementService.java b/service/src/com/android/car/CarPowerManagementService.java
index 5a2aee2..aef68cd 100644
--- a/service/src/com/android/car/CarPowerManagementService.java
+++ b/service/src/com/android/car/CarPowerManagementService.java
@@ -44,6 +44,7 @@
 import android.os.PowerManager;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -59,6 +60,7 @@
 import com.android.car.user.CarUserService;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.IVoiceInteractionManagerService;
 
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
@@ -137,6 +139,8 @@
     private final CarUserService mUserService;
     private final InitialUserSetter mInitialUserSetter;
 
+    private final IVoiceInteractionManagerService mVoiceInteractionManagerService;
+
     // TODO:  Make this OEM configurable.
     private static final int SHUTDOWN_POLLING_INTERVAL_MS = 2000;
     private static final int SHUTDOWN_EXTEND_MAX_MS = 5000;
@@ -168,13 +172,16 @@
         this(context, context.getResources(), powerHal, systemInterface, UserManager.get(context),
                 carUserService, new InitialUserSetter(context,
                         (u) -> carUserService.setInitialUser(u),
-                        context.getString(R.string.default_guest_name)));
+                        context.getString(R.string.default_guest_name)),
+                IVoiceInteractionManagerService.Stub.asInterface(
+                        ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE)));
     }
 
     @VisibleForTesting
     public CarPowerManagementService(Context context, Resources resources, PowerHalService powerHal,
             SystemInterface systemInterface, UserManager userManager, CarUserService carUserService,
-            InitialUserSetter initialUserSetter) {
+            InitialUserSetter initialUserSetter,
+            IVoiceInteractionManagerService voiceInteractionService) {
         mContext = context;
         mHal = powerHal;
         mSystemInterface = systemInterface;
@@ -194,6 +201,7 @@
         }
         mUserService = carUserService;
         mInitialUserSetter = initialUserSetter;
+        mVoiceInteractionManagerService = voiceInteractionService;
     }
 
     @VisibleForTesting
@@ -408,6 +416,7 @@
 
         mSystemInterface.setDisplayState(true);
         sendPowerManagerEvent(CarPowerStateListener.ON);
+
         mHal.sendOn();
 
         try {
@@ -415,6 +424,8 @@
         } catch (Exception e) {
             Log.e(CarLog.TAG_POWER, "Could not switch user on resume", e);
         }
+
+        setVoiceInteractionDisabled(false);
     }
 
     @VisibleForTesting // Ideally it should not be exposed, but it speeds up the unit tests
@@ -552,6 +563,7 @@
     }
 
     private void handleShutdownPrepare(CpmsState newState) {
+        setVoiceInteractionDisabled(true);
         mSystemInterface.setDisplayState(false);
         // Shutdown on finish if the system doesn't support deep sleep or doesn't allow it.
         synchronized (mLock) {
@@ -619,6 +631,8 @@
                 throw new AssertionError("Should not return from PowerManager.reboot()");
             }
         }
+        setVoiceInteractionDisabled(true);
+
         if (mustShutDown) {
             // shutdown HU
             mSystemInterface.shutdown();
@@ -628,6 +642,14 @@
         mShutdownOnNextSuspend = false;
     }
 
+    private void setVoiceInteractionDisabled(boolean disabled) {
+        try {
+            mVoiceInteractionManagerService.setDisabled(disabled);
+        } catch (RemoteException e) {
+            Log.w(TAG, "setVoiceIntefactionDisabled(" + disabled + ") failed", e);
+        }
+    }
+
     @GuardedBy("mLock")
     private void releaseTimerLocked() {
         if (mTimer != null) {
@@ -737,18 +759,18 @@
             simulateSleepByWaiting();
             nextListenerState = CarPowerStateListener.SHUTDOWN_CANCELLED;
         } else {
-            boolean sleepSucceeded = mSystemInterface.enterDeepSleep();
+            boolean sleepSucceeded = suspendWithRetries();
             if (!sleepSucceeded) {
-                // Suspend failed! VHAL should transition CPMS to shutdown.
-                Log.e(CarLog.TAG_POWER, "Sleep did not succeed. Now attempting to shut down.");
-                mSystemInterface.shutdown();
+                // Suspend failed and we shut down instead.
+                // We either won't get here at all or we will power off very soon.
                 return;
             }
+            // We suspended and have now resumed
             nextListenerState = CarPowerStateListener.SUSPEND_EXIT;
         }
-        // On resume, reset nextWakeup time. If not set again, system will suspend/shutdown forever.
         synchronized (mLock) {
             mIsResuming = true;
+            // Any wakeup time from before is no longer valid.
             mNextWakeupSec = 0;
         }
         Log.i(CarLog.TAG_POWER, "Resuming after suspending");
@@ -1074,6 +1096,37 @@
         }
     }
 
+    // Send the command to enter Suspend to RAM.
+    // If the command is not successful, try again.
+    // If it fails repeatedly, send the command to shut down.
+    // Returns true if we successfully suspended.
+    private boolean suspendWithRetries() {
+        final int maxTries = 3;
+        final long retryIntervalMs = 10;
+        int tryCount = 0;
+
+        while (true) {
+            Log.i(CarLog.TAG_POWER, "Entering Suspend to RAM");
+            boolean suspendSucceeded = mSystemInterface.enterDeepSleep();
+            if (suspendSucceeded) {
+                return true;
+            }
+            tryCount++;
+            if (tryCount >= maxTries) {
+                break;
+            }
+            // We failed to suspend. Block the thread briefly and try again.
+            Log.w(CarLog.TAG_POWER, "Failed to Suspend; will retry later.");
+            try {
+                Thread.sleep(retryIntervalMs);
+            } catch (InterruptedException ignored) { }
+        }
+        // Too many failures trying to suspend. Shut down.
+        Log.w(CarLog.TAG_POWER, "Could not Suspend to RAM. Shutting down.");
+        mSystemInterface.shutdown();
+        return false;
+    }
+
     private static class CpmsState {
         // NOTE: When modifying states below, make sure to update CarPowerStateChanged.State in
         //   frameworks/base/cmds/statsd/src/atoms.proto also.
diff --git a/service/src/com/android/car/CarShellCommand.java b/service/src/com/android/car/CarShellCommand.java
index 7f7818d..0a8c420 100644
--- a/service/src/com/android/car/CarShellCommand.java
+++ b/service/src/com/android/car/CarShellCommand.java
@@ -34,7 +34,9 @@
 import android.car.input.CarInputManager;
 import android.car.input.RotaryEvent;
 import android.car.user.CarUserManager;
+import android.car.user.UserCreationResult;
 import android.car.user.UserIdentificationAssociationResponse;
+import android.car.user.UserRemovalResult;
 import android.car.user.UserSwitchResult;
 import android.car.userlib.HalCallback;
 import android.car.userlib.UserHalHelper;
@@ -45,7 +47,9 @@
 import android.hardware.automotive.vehicle.V2_0.CreateUserStatus;
 import android.hardware.automotive.vehicle.V2_0.InitialUserInfoResponse;
 import android.hardware.automotive.vehicle.V2_0.InitialUserInfoResponseAction;
+import android.hardware.automotive.vehicle.V2_0.RemoveUserRequest;
 import android.hardware.automotive.vehicle.V2_0.SwitchUserMessageType;
+import android.hardware.automotive.vehicle.V2_0.SwitchUserRequest;
 import android.hardware.automotive.vehicle.V2_0.SwitchUserStatus;
 import android.hardware.automotive.vehicle.V2_0.UserFlags;
 import android.hardware.automotive.vehicle.V2_0.UserIdentificationAssociation;
@@ -90,6 +94,7 @@
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 final class CarShellCommand extends ShellCommand {
 
@@ -122,6 +127,7 @@
     private static final String COMMAND_INJECT_ROTARY = "inject-rotary";
     private static final String COMMAND_GET_INITIAL_USER_INFO = "get-initial-user-info";
     private static final String COMMAND_SWITCH_USER = "switch-user";
+    private static final String COMMAND_REMOVE_USER = "remove-user";
     private static final String COMMAND_CREATE_USER = "create-user";
     private static final String COMMAND_GET_INITIAL_USER = "get-initial-user";
     private static final String COMMAND_SET_USER_ID_TO_OCCUPANT_ZONE =
@@ -152,6 +158,8 @@
                 android.Manifest.permission.MANAGE_USERS);
         USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_SWITCH_USER,
                 android.Manifest.permission.MANAGE_USERS);
+        USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_REMOVE_USER,
+                android.Manifest.permission.MANAGE_USERS);
         USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_CREATE_USER,
                 android.Manifest.permission.MANAGE_USERS);
         USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_GET_USER_AUTH_ASSOCIATION,
@@ -371,6 +379,10 @@
         pw.println("\t  The --hal-only option only calls HAL, without switching the user,");
         pw.println("\t  while the --timeout defines how long to wait for the HAL response.");
 
+        pw.printf("\t%s <USER_ID> [--hal-only]\n", COMMAND_REMOVE_USER);
+        pw.println("\t  Removes user with USER_ID using the HAL integration.");
+        pw.println("\t  The --hal-only option only calls HAL, without removing the user,");
+
         pw.printf("\t%s [--hal-only] [--timeout TIMEOUT_MS] [--type TYPE] [--flags FLAGS] [NAME]\n",
                 COMMAND_CREATE_USER);
         pw.println("\t  Creates a new user using the HAL integration.");
@@ -606,6 +618,9 @@
             case COMMAND_SWITCH_USER:
                 switchUser(args, writer);
                 break;
+            case COMMAND_REMOVE_USER:
+                removeUser(args, writer);
+                break;
             case COMMAND_CREATE_USER:
                 createUser(args, writer);
                 break;
@@ -951,12 +966,15 @@
         if (halOnly) {
             CountDownLatch latch = new CountDownLatch(1);
             UserHalService userHal = mHal.getUserHal();
-            UsersInfo usersInfo = generateUsersInfo();
             UserInfo targetUserInfo = new UserInfo();
             targetUserInfo.userId = targetUserId;
             targetUserInfo.flags = getUserHalFlags(targetUserId);
 
-            userHal.switchUser(targetUserInfo, timeout, usersInfo, (status, resp) -> {
+            SwitchUserRequest request = new SwitchUserRequest();
+            request.targetUser = targetUserInfo;
+            request.usersInfo = generateUsersInfo();
+
+            userHal.switchUser(request, timeout, (status, resp) -> {
                 try {
                     Log.d(TAG, "SwitchUserResponse: status=" + status + ", resp=" + resp);
                     writer.printf("Call Status: %s\n",
@@ -976,7 +994,7 @@
                     // Android error. This is to "rollback" the HAL switch.
                     if (status == HalCallback.STATUS_OK
                             && resp.status == SwitchUserStatus.SUCCESS) {
-                        userHal.postSwitchResponse(resp.requestId, targetUserInfo, usersInfo);
+                        userHal.postSwitchResponse(request);
                     }
                 } finally {
                     latch.countDown();
@@ -989,12 +1007,13 @@
         AndroidFuture<UserSwitchResult> future = carUserManager.switchUser(targetUserId);
         UserSwitchResult result = waitForFuture(writer, future, timeout);
         if (result == null) return;
-        writer.printf("UserSwitchResult: status = %s\n",
+        writer.printf("UserSwitchResult: status=%s",
                 UserSwitchResult.statusToString(result.getStatus()));
         String msg = result.getErrorMessage();
-        if (msg != null && !msg.isEmpty()) {
-            writer.printf("UserSwitchResult: Message = %s\n", msg);
+        if (!TextUtils.isEmpty(msg)) {
+            writer.printf(", errorMessage=%s", msg);
         }
+        writer.println();
     }
 
     private void createUser(String[] args, PrintWriter writer) {
@@ -1037,8 +1056,23 @@
                 + ", halOnly=" + halOnly + ", timeout=" + timeout);
 
         if (!halOnly) {
-            // TODO(b/150408921): implement it
-            throw new UnsupportedOperationException("must pass --hal-only for now");
+            CarUserManager carUserManager = getCarUserManager(mContext);
+            AndroidFuture<UserCreationResult> future = carUserManager
+                    .createUser(name, userType, flags);
+
+            UserCreationResult result = waitForFuture(writer, future, timeout);
+            if (result == null) return;
+
+            android.content.pm.UserInfo user = result.getUser();
+            writer.printf("UserCreationResult: status=%s, user=%s",
+                    UserCreationResult.statusToString(result.getStatus()),
+                    user == null ? "N/A" : user.toFullString());
+            String msg = result.getErrorMessage();
+            if (!TextUtils.isEmpty(msg)) {
+                writer.printf(", errorMessage=%s", msg);
+            }
+            writer.println();
+            return;
         }
 
         CountDownLatch latch = new CountDownLatch(1);
@@ -1047,8 +1081,11 @@
         CreateUserRequest request = new CreateUserRequest();
 
         UserManager um = UserManager.get(mContext);
-        android.content.pm.UserInfo newUser =
-                um.createUser(name, userType, flags);
+        android.content.pm.UserInfo newUser = um.createUser(name, userType, flags);
+        if (newUser == null) {
+            writer.printf("Failed to create user");
+            return;
+        }
         writer.printf("New user: %s\n", newUser.toFullString());
         Log.i(TAG, "Created new user: " + newUser.toFullString());
 
@@ -1057,31 +1094,79 @@
 
         request.usersInfo = generateUsersInfo();
 
-        userHal.createUser(request, timeout, (status, resp) -> {
-            Log.d(TAG, "CreateUserResponse: status=" + status + ", resp=" + resp);
-            try {
+        AtomicBoolean halOk = new AtomicBoolean(false);
+        try {
+            userHal.createUser(request, timeout, (status, resp) -> {
+                Log.d(TAG, "CreateUserResponse: status=" + status + ", resp=" + resp);
                 writer.printf("Call Status: %s\n",
                         UserHalHelper.halCallbackStatusToString(status));
-                if (status != HalCallback.STATUS_OK) {
-                    return;
+                if (status == HalCallback.STATUS_OK) {
+                    halOk.set(resp.status == CreateUserStatus.SUCCESS);
+                    writer.printf("Request id: %d\n", resp.requestId);
+                    writer.printf("Create Status: %s\n", CreateUserStatus.toString(resp.status));
+                    String errorMessage = resp.errorMessage;
+                    if (!TextUtils.isEmpty(errorMessage)) {
+                        writer.printf("Error message: %s", errorMessage);
+                    }
                 }
-                writer.printf("Request id: %d\n", resp.requestId);
-                writer.printf("Create Status: %s\n", CreateUserStatus.toString(resp.status));
-                String errorMessage = resp.errorMessage;
-                if (!TextUtils.isEmpty(errorMessage)) {
-                    writer.printf("Error message: %s", errorMessage);
-                }
-
-                if (resp.status == CreateUserStatus.FAILURE) {
-                    Log.i(TAG, "Removing new user due to HAL failure");
-                    boolean removed = um.removeUser(newUser.id);
-                    writer.printf("User removed: %b\n", removed);
-                }
-            } finally {
                 latch.countDown();
+            });
+            waitForHal(writer, latch, timeout);
+        } catch (Exception e) {
+            writer.printf("HAL failed: %s\n", e);
+        } finally {
+            if (!halOk.get()) {
+                writer.printf("Removing user %d due to HAL failure\n", newUser.id);
+                boolean removed = um.removeUser(newUser.id);
+                writer.printf("User removed: %b\n", removed);
             }
-        });
-        waitForHal(writer, latch, timeout);
+        }
+    }
+
+    private void removeUser(String[] args, PrintWriter writer) {
+        if (args.length < 2) {
+            writer.println("Insufficient number of args");
+            return;
+        }
+
+        int userId = Integer.parseInt(args[1]);
+        boolean halOnly = false;
+
+        for (int i = 2; i < args.length; i++) {
+            String arg = args[i];
+            switch (arg) {
+                case "--hal-only":
+                    halOnly = true;
+                    break;
+                default:
+                    writer.println("Invalid option at index " + i + ": " + arg);
+                    return;
+            }
+        }
+
+        Log.d(TAG, "handleRemoveUser(): User to remove=" + userId + ", halOnly=" + halOnly);
+
+        if (halOnly) {
+            UserHalService userHal = mHal.getUserHal();
+            UsersInfo usersInfo = generateUsersInfo();
+            UserInfo userInfo = new UserInfo();
+            userInfo.userId = userId;
+            userInfo.flags = getUserHalFlags(userId);
+
+            RemoveUserRequest request = new RemoveUserRequest();
+            request.removedUserInfo = userInfo;
+            request.usersInfo = usersInfo;
+
+            userHal.removeUser(request);
+            writer.printf("User removal sent for HAL only.\n");
+            return;
+        }
+
+        CarUserManager carUserManager = getCarUserManager(mContext);
+        UserRemovalResult result = carUserManager.removeUser(userId);
+        if (result == null) return;
+        writer.printf("UserRemovalResult: status = %s\n",
+                UserRemovalResult.statusToString(result.getStatus()));
     }
 
     private static <T> T waitForFuture(@NonNull PrintWriter writer,
diff --git a/service/src/com/android/car/ICarImpl.java b/service/src/com/android/car/ICarImpl.java
index 53e76fa..4d78dad 100644
--- a/service/src/com/android/car/ICarImpl.java
+++ b/service/src/com/android/car/ICarImpl.java
@@ -208,7 +208,7 @@
                 mCarUserService);
         mPerUserCarServiceHelper = new PerUserCarServiceHelper(serviceContext, mCarUserService);
         mCarBluetoothService = new CarBluetoothService(serviceContext, mPerUserCarServiceHelper);
-        mCarInputService = new CarInputService(serviceContext, mHal.getInputHal());
+        mCarInputService = new CarInputService(serviceContext, mHal.getInputHal(), mCarUserService);
         mCarProjectionService = new CarProjectionService(
                 serviceContext, null /* handler */, mCarInputService, mCarBluetoothService);
         mGarageModeService = new GarageModeService(mContext);
diff --git a/service/src/com/android/car/audio/CarAudioService.java b/service/src/com/android/car/audio/CarAudioService.java
index b0dd41d..3efbd18 100644
--- a/service/src/com/android/car/audio/CarAudioService.java
+++ b/service/src/com/android/car/audio/CarAudioService.java
@@ -56,7 +56,6 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.SparseIntArray;
-import android.view.DisplayAddress;
 import android.view.KeyEvent;
 
 import com.android.car.CarLocalServices;
@@ -1046,31 +1045,6 @@
         return true;
     }
 
-    /**
-     * Gets the zone id for the display port id.
-     * @param displayPortId display port id to match
-     * @return zone id for the display port id or
-     * CarAudioManager.PRIMARY_AUDIO_ZONE if none are found
-     */
-    @Override
-    public int getZoneIdForDisplayPortId(byte displayPortId) {
-        enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS);
-        requireDynamicRouting();
-        synchronized (mImplLock) {
-            for (int index = 0; index < mCarAudioZones.length; index++) {
-                CarAudioZone zone = mCarAudioZones[index];
-                List<DisplayAddress.Physical> displayAddresses = zone.getPhysicalDisplayAddresses();
-                if (displayAddresses.stream().anyMatch(displayAddress->
-                        displayAddress.getPort() == displayPortId)) {
-                    return index;
-                }
-            }
-
-            // Everything else defaults to primary audio zone
-            return CarAudioManager.PRIMARY_AUDIO_ZONE;
-        }
-    }
-
     @Override
     public void registerVolumeCallback(@NonNull IBinder binder) {
         synchronized (mImplLock) {
diff --git a/service/src/com/android/car/audio/CarAudioZone.java b/service/src/com/android/car/audio/CarAudioZone.java
index a87b4b1..f4a4a33 100644
--- a/service/src/com/android/car/audio/CarAudioZone.java
+++ b/service/src/com/android/car/audio/CarAudioZone.java
@@ -19,7 +19,6 @@
 import android.media.AudioDeviceAttributes;
 import android.media.AudioDeviceInfo;
 import android.util.Log;
-import android.view.DisplayAddress;
 
 import com.android.car.CarLog;
 import com.android.internal.util.Preconditions;
@@ -45,14 +44,12 @@
     private final int mId;
     private final String mName;
     private final List<CarVolumeGroup> mVolumeGroups;
-    private final List<DisplayAddress.Physical> mPhysicalDisplayAddresses;
     private List<AudioDeviceAttributes> mInputAudioDevice;
 
     CarAudioZone(int id, String name) {
         mId = id;
         mName = name;
         mVolumeGroups = new ArrayList<>();
-        mPhysicalDisplayAddresses = new ArrayList<>();
         mInputAudioDevice = new ArrayList<>();
     }
 
@@ -96,23 +93,6 @@
     }
 
     /**
-     * Associates a new display physical port with this audio zone. This can be used to
-     * identify what zone an activity should produce sound in when launching on a particular display
-     * @param physicalDisplayAddress port to associate with this zone
-     */
-    void addPhysicalDisplayAddress(DisplayAddress.Physical physicalDisplayAddress) {
-        mPhysicalDisplayAddresses.add(physicalDisplayAddress);
-    }
-
-    /**
-     * Gets list of ports for displays associated with this audio zone
-     * @return list of Physical ports for displays associated with this audio zone
-     */
-    List<DisplayAddress.Physical> getPhysicalDisplayAddresses() {
-        return mPhysicalDisplayAddresses;
-    }
-
-    /**
      * @return Snapshot of available {@link CarVolumeGroup}s in array.
      */
     CarVolumeGroup[] getVolumeGroups() {
@@ -173,11 +153,6 @@
     void dump(String indent, PrintWriter writer) {
         String internalIndent = indent + "\t";
         writer.printf("%sCarAudioZone(%s:%d) isPrimary? %b\n", indent, mName, mId, isPrimaryZone());
-        for (DisplayAddress.Physical physical: mPhysicalDisplayAddresses) {
-            long port = (long) physical.getPort();
-            writer.printf("%sDisplayAddress.Physical(%d)\n", internalIndent, port);
-        }
-        writer.println();
 
         for (CarVolumeGroup group : mVolumeGroups) {
             group.dump(internalIndent, writer);
diff --git a/service/src/com/android/car/audio/CarAudioZonesHelper.java b/service/src/com/android/car/audio/CarAudioZonesHelper.java
index 3ff25e9..1a4e0ad 100644
--- a/service/src/com/android/car/audio/CarAudioZonesHelper.java
+++ b/service/src/com/android/car/audio/CarAudioZonesHelper.java
@@ -22,7 +22,6 @@
 import android.text.TextUtils;
 import android.util.SparseIntArray;
 import android.util.Xml;
-import android.view.DisplayAddress;
 
 import com.android.car.audio.CarAudioContext.AudioContext;
 import com.android.internal.util.Preconditions;
@@ -55,14 +54,11 @@
     private static final String TAG_VOLUME_GROUP = "group";
     private static final String TAG_AUDIO_DEVICE = "device";
     private static final String TAG_CONTEXT = "context";
-    private static final String TAG_DISPLAYS = "displays";
-    private static final String TAG_DISPLAY = "display";
     private static final String ATTR_VERSION = "version";
     private static final String ATTR_IS_PRIMARY = "isPrimary";
     private static final String ATTR_ZONE_NAME = "name";
     private static final String ATTR_DEVICE_ADDRESS = "address";
     private static final String ATTR_CONTEXT_NAME = "context";
-    private static final String ATTR_PHYSICAL_PORT = "port";
     private static final String ATTR_ZONE_ID = "audioZoneId";
     private static final String ATTR_OCCUPANT_ZONE_ID = "occupantZoneId";
     private static final String TAG_INPUT_DEVICES = "inputDevices";
@@ -132,7 +128,6 @@
     private final Map<String, CarAudioDeviceInfo> mAddressToCarAudioDeviceInfo;
     private final Map<String, AudioDeviceInfo> mAddressToInputAudioDeviceInfo;
     private final InputStream mInputStream;
-    private final Set<Long> mPortIds;
     private final SparseIntArray mZoneIdToOccupantZoneIdMapping;
     private final Set<Integer> mAudioZoneIds;
     private final Set<String> mInputAudioDevices;
@@ -158,7 +153,6 @@
         mAddressToInputAudioDeviceInfo =
                 CarAudioZonesHelper.generateAddressToInputAudioDeviceInfoMap(inputDeviceInfo);
         mNextSecondaryZoneId = CarAudioManager.PRIMARY_AUDIO_ZONE + 1;
-        mPortIds = new HashSet<>();
         mZoneIdToOccupantZoneIdMapping = new SparseIntArray();
         mAudioZoneIds = new HashSet<>();
         mInputAudioDevices = new HashSet<>();
@@ -258,8 +252,6 @@
             // Expect one <volumeGroups> in one audio zone
             if (TAG_VOLUME_GROUPS.equals(parser.getName())) {
                 parseVolumeGroups(parser, zone);
-            } else if (TAG_DISPLAYS.equals(parser.getName())) {
-                parseDisplays(parser, zone);
             } else if (TAG_INPUT_DEVICES.equals(parser.getName())) {
                 parseInputAudioDevices(parser, zone);
             } else {
@@ -364,37 +356,6 @@
         mInputAudioDevices.add(audioDeviceAddress);
     }
 
-    private void parseDisplays(XmlPullParser parser, CarAudioZone zone)
-            throws IOException, XmlPullParserException {
-        while (parser.next() != XmlPullParser.END_TAG) {
-            if (parser.getEventType() != XmlPullParser.START_TAG) continue;
-            if (TAG_DISPLAY.equals(parser.getName())) {
-                zone.addPhysicalDisplayAddress(parsePhysicalDisplayAddress(parser));
-            }
-            skip(parser);
-        }
-    }
-
-    private DisplayAddress.Physical parsePhysicalDisplayAddress(XmlPullParser parser) {
-        String port = parser.getAttributeValue(NAMESPACE, ATTR_PHYSICAL_PORT);
-        long portId;
-        try {
-            portId = Long.parseLong(port);
-        } catch (NumberFormatException e) {
-            throw new IllegalArgumentException(String.format("Port %s is not a number", port), e);
-        }
-        validatePortIsUnique(portId);
-        return DisplayAddress.fromPhysicalDisplayId(portId);
-    }
-
-    private void validatePortIsUnique(Long portId) {
-        if (mPortIds.contains(portId)) {
-            throw new IllegalArgumentException(
-                    String.format("Port Id %d is already associated with a zone", portId));
-        }
-        mPortIds.add(portId);
-    }
-
     private void validateOccupantZoneIdIsUnique(int occupantZoneId) {
         if (mZoneIdToOccupantZoneIdMapping.indexOfValue(occupantZoneId) > -1) {
             throw new IllegalArgumentException(ATTR_OCCUPANT_ZONE_ID + " " + occupantZoneId
diff --git a/service/src/com/android/car/hal/UserHalService.java b/service/src/com/android/car/hal/UserHalService.java
index 33cf970..645f563 100644
--- a/service/src/com/android/car/hal/UserHalService.java
+++ b/service/src/com/android/car/hal/UserHalService.java
@@ -17,6 +17,7 @@
 
 import static android.car.VehiclePropertyIds.CREATE_USER;
 import static android.car.VehiclePropertyIds.INITIAL_USER_INFO;
+import static android.car.VehiclePropertyIds.REMOVE_USER;
 import static android.car.VehiclePropertyIds.SWITCH_USER;
 import static android.car.VehiclePropertyIds.USER_IDENTIFICATION_ASSOCIATION;
 
@@ -34,7 +35,9 @@
 import android.hardware.automotive.vehicle.V2_0.CreateUserResponse;
 import android.hardware.automotive.vehicle.V2_0.CreateUserStatus;
 import android.hardware.automotive.vehicle.V2_0.InitialUserInfoResponse;
+import android.hardware.automotive.vehicle.V2_0.RemoveUserRequest;
 import android.hardware.automotive.vehicle.V2_0.SwitchUserMessageType;
+import android.hardware.automotive.vehicle.V2_0.SwitchUserRequest;
 import android.hardware.automotive.vehicle.V2_0.SwitchUserResponse;
 import android.hardware.automotive.vehicle.V2_0.SwitchUserStatus;
 import android.hardware.automotive.vehicle.V2_0.UserIdentificationAssociationType;
@@ -165,6 +168,9 @@
                     mHandler.sendMessage(obtainMessage(
                             UserHalService::handleOnCreateUserResponse, this, value));
                     break;
+                case REMOVE_USER:
+                    Log.w(TAG, "Received REMOVE_USER HAL event: " + value);
+                    break;
                 case USER_IDENTIFICATION_ASSOCIATION:
                     mHandler.sendMessage(obtainMessage(
                             UserHalService::handleOnUserIdentificationAssociation, this, value));
@@ -270,33 +276,30 @@
     /**
      * Calls HAL to asynchronously switch user.
      *
-     * @param targetInfo target user for user switching
+     * @param request metadata
      * @param timeoutMs how long to wait (in ms) for the property change event.
-     * @param usersInfo current state of Android users.
      * @param callback to handle the response.
      *
      * @throws IllegalStateException if the HAL does not support user management (callers should
      * call {@link #isSupported()} first to avoid this exception).
      */
-    public void switchUser(@NonNull UserInfo targetInfo, int timeoutMs,
-            @NonNull UsersInfo usersInfo, @NonNull HalCallback<SwitchUserResponse> callback) {
-        if (DBG) Log.d(TAG, "switchUser(" + targetInfo + ")");
-
-        Objects.requireNonNull(targetInfo);
+    public void switchUser(@NonNull SwitchUserRequest request, int timeoutMs,
+            @NonNull HalCallback<SwitchUserResponse> callback) {
         Preconditions.checkArgumentPositive(timeoutMs, "timeout must be positive");
-        Objects.requireNonNull(callback);
-        UserHalHelper.checkValid(usersInfo);
+        Objects.requireNonNull(callback, "callback cannot be null");
 
+        if (DBG) Log.d(TAG, "switchUser(" + request + ")");
         VehiclePropValue propRequest;
         int requestId;
         synchronized (mLock) {
             checkSupportedLocked();
             if (hasPendingRequestLocked(SwitchUserResponse.class, callback)) return;
             requestId = getNextRequestId();
+            request.requestId = requestId;
+            request.messageType = SwitchUserMessageType.ANDROID_SWITCH;
+            propRequest = UserHalHelper.toVehiclePropValue(request);
             EventLog.writeEvent(EventLogTags.CAR_USER_HAL_SWITCH_USER_REQ, requestId,
-                    targetInfo.userId, timeoutMs);
-            propRequest = getPropRequestForSwitchUserLocked(requestId,
-                    SwitchUserMessageType.ANDROID_SWITCH, targetInfo, usersInfo);
+                    request.targetUser.userId, timeoutMs);
             addPendingRequestLocked(requestId, SwitchUserResponse.class, callback);
         }
 
@@ -304,6 +307,34 @@
     }
 
     /**
+     * Calls HAL to remove user.
+     *
+     * @throws IllegalStateException if the HAL does not support user management (callers should
+     * call {@link #isSupported()} first to avoid this exception).
+     */
+    public void removeUser(@NonNull RemoveUserRequest request) {
+        Objects.requireNonNull(request, "request cannot be null");
+
+        if (DBG) Log.d(TAG, "removeUser(" + request.removedUserInfo.userId + ")");
+        EventLog.writeEvent(EventLogTags.CAR_USER_HAL_REMOVE_USER_REQ,
+                request.removedUserInfo.userId, request.usersInfo.currentUser.userId);
+
+        VehiclePropValue propRequest;
+        synchronized (mLock) {
+            checkSupportedLocked();
+            request.requestId = getNextRequestId();
+            propRequest = UserHalHelper.toVehiclePropValue(request);
+
+        }
+        try {
+            if (DBG) Log.d(TAG, "Calling hal.set(): " + propRequest);
+            mHal.set(propRequest);
+        } catch (ServiceSpecificException e) {
+            Log.w(TAG, "Failed to set REMOVE USER", e);
+        }
+    }
+
+    /**
      * Calls HAL to indicate an Android user was created.
      *
      * @param request info agout the created user.
@@ -337,23 +368,17 @@
 
     /**
      * Calls HAL after android user switch.
-     *
-     * @param requestId for which switch response is sent.
-     * @param targetInfo target user info.
-     * @param usersInfo current state of Android users.
      */
-    public void postSwitchResponse(int requestId, @NonNull UserInfo targetInfo,
-            @NonNull UsersInfo usersInfo) {
-        EventLog.writeEvent(EventLogTags.CAR_USER_HAL_POST_SWITCH_USER_REQ, requestId,
-                targetInfo.userId, usersInfo.currentUser.userId);
-        if (DBG) Log.d(TAG, "postSwitchResponse(" + targetInfo + ")");
-        UserHalHelper.checkValid(usersInfo);
+    public void postSwitchResponse(@NonNull SwitchUserRequest request) {
+        EventLog.writeEvent(EventLogTags.CAR_USER_HAL_POST_SWITCH_USER_REQ, request.requestId,
+                request.targetUser.userId, request.usersInfo.currentUser.userId);
+        if (DBG) Log.d(TAG, "postSwitchResponse(" + request.targetUser.userId + ")");
 
         VehiclePropValue propRequest;
         synchronized (mLock) {
             checkSupportedLocked();
-            propRequest = getPropRequestForSwitchUserLocked(requestId,
-                    SwitchUserMessageType.ANDROID_POST_SWITCH, targetInfo, usersInfo);
+            request.messageType = SwitchUserMessageType.ANDROID_POST_SWITCH;
+            propRequest = UserHalHelper.toVehiclePropValue(request);
         }
 
         try {
@@ -368,22 +393,18 @@
      * Calls HAL to switch user after legacy Android user switch. Legacy Android user switch means
      * user switch is not requested by {@link CarUserManager} or OEM, and user switch is directly
      * requested by {@link ActivityManager}
-     *
-     * @param targetInfo target user info.
-     * @param usersInfo current state of Android users.
      */
-    public void legacyUserSwitch(@NonNull UserInfo targetInfo, @NonNull UsersInfo usersInfo) {
-        if (DBG) Log.d(TAG, "userSwitchLegacy(" + targetInfo + ")");
-        UserHalHelper.checkValid(usersInfo);
+    public void legacyUserSwitch(@NonNull SwitchUserRequest request) {
+        if (DBG) Log.d(TAG, "userSwitchLegacy(" + request + ")");
 
         VehiclePropValue propRequest;
         synchronized (mLock) {
             checkSupportedLocked();
             int requestId = getNextRequestId();
             EventLog.writeEvent(EventLogTags.CAR_USER_HAL_LEGACY_SWITCH_USER_REQ, requestId,
-                    targetInfo.userId, usersInfo.currentUser.userId);
-            propRequest = getPropRequestForSwitchUserLocked(requestId,
-                    SwitchUserMessageType.LEGACY_ANDROID_SWITCH, targetInfo, usersInfo);
+                    request.targetUser.userId, request.usersInfo.currentUser.userId);
+            request.messageType = SwitchUserMessageType.LEGACY_ANDROID_SWITCH;
+            propRequest = UserHalHelper.toVehiclePropValue(request);
         }
 
         try {
diff --git a/service/src/com/android/car/pm/CarPackageManagerService.java b/service/src/com/android/car/pm/CarPackageManagerService.java
index 1d1bbb6..9f66e49 100644
--- a/service/src/com/android/car/pm/CarPackageManagerService.java
+++ b/service/src/com/android/car/pm/CarPackageManagerService.java
@@ -224,6 +224,11 @@
      */
     @Override
     public void restartTask(int taskId) {
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.REAL_GET_TASKS)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException(
+                    "requires permission " + android.Manifest.permission.REAL_GET_TASKS);
+        }
         mSystemActivityMonitoringService.restartTask(taskId);
     }
 
diff --git a/service/src/com/android/car/stats/CarStatsService.java b/service/src/com/android/car/stats/CarStatsService.java
index 20b451c..bf79ee8 100644
--- a/service/src/com/android/car/stats/CarStatsService.java
+++ b/service/src/com/android/car/stats/CarStatsService.java
@@ -27,7 +27,7 @@
 import com.android.car.CarStatsLog;
 import com.android.car.stats.VmsClientLogger.ConnectionState;
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.ConcurrentUtils;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -102,7 +102,7 @@
         mStatsManager.setPullAtomCallback(
                 CarStatsLog.VMS_CLIENT_STATS,
                 metadata,
-                BackgroundThread.getExecutor(),
+                ConcurrentUtils.DIRECT_EXECUTOR,
                 (atomTag, data) -> pullVmsClientStats(atomTag, data)
         );
     }
diff --git a/service/src/com/android/car/user/CarUserService.java b/service/src/com/android/car/user/CarUserService.java
index d7de501..8e08b28 100644
--- a/service/src/com/android/car/user/CarUserService.java
+++ b/service/src/com/android/car/user/CarUserService.java
@@ -33,7 +33,9 @@
 import android.car.user.CarUserManager.UserLifecycleEvent;
 import android.car.user.CarUserManager.UserLifecycleEventType;
 import android.car.user.CarUserManager.UserLifecycleListener;
+import android.car.user.UserCreationResult;
 import android.car.user.UserIdentificationAssociationResponse;
+import android.car.user.UserRemovalResult;
 import android.car.user.UserSwitchResult;
 import android.car.userlib.CarUserManagerHelper;
 import android.car.userlib.CommonConstants.CarUserServiceConstants;
@@ -45,10 +47,15 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.UserInfo;
+import android.content.pm.UserInfo.UserInfoFlag;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.hardware.automotive.vehicle.V2_0.CreateUserRequest;
+import android.hardware.automotive.vehicle.V2_0.CreateUserStatus;
 import android.hardware.automotive.vehicle.V2_0.InitialUserInfoResponse;
 import android.hardware.automotive.vehicle.V2_0.InitialUserInfoResponseAction;
+import android.hardware.automotive.vehicle.V2_0.RemoveUserRequest;
+import android.hardware.automotive.vehicle.V2_0.SwitchUserRequest;
 import android.hardware.automotive.vehicle.V2_0.SwitchUserStatus;
 import android.hardware.automotive.vehicle.V2_0.UserIdentificationGetRequest;
 import android.hardware.automotive.vehicle.V2_0.UserIdentificationResponse;
@@ -297,6 +304,7 @@
         writer.printf("EnablePassengerSupport: %s\n", mEnablePassengerSupport);
         writer.printf("User HAL timeout: %dms\n",  mHalTimeoutMs);
         writer.printf("Initial user: %s\n", mInitialUser);
+
         writer.println("Relevant overlayable properties");
         Resources res = mContext.getResources();
         writer.printf("%sowner_name=%s\n", indent,
@@ -308,9 +316,18 @@
                     mRequestIdForUserSwitchInProcess);
         writer.printf("System UI package name=%s\n", getSystemUiPackageName());
 
+        writer.println("Relevant Global settings");
+        dumpGlobalProperty(writer, indent, CarSettings.Global.LAST_ACTIVE_USER_ID);
+        dumpGlobalProperty(writer, indent, CarSettings.Global.LAST_ACTIVE_PERSISTENT_USER_ID);
+
         dumpUserMetrics(writer);
     }
 
+    private void dumpGlobalProperty(PrintWriter writer, String indent, String property) {
+        String value = Settings.Global.getString(mContext.getContentResolver(), property);
+        writer.printf("%s%s=%s\n", indent, property, value);
+    }
+
     /**
      * Dumps user metrics.
      */
@@ -328,7 +345,7 @@
     private void handleDumpListeners(@NonNull PrintWriter writer, String indent) {
         CountDownLatch latch = new CountDownLatch(1);
         mHandler.post(() -> {
-            handleDumpUserLifecycleListeners(writer);
+            handleDumpServiceLifecycleListeners(writer);
             handleDumpAppLifecycleListeners(writer, indent);
             latch.countDown();
         });
@@ -344,57 +361,69 @@
         }
     }
 
-    private void handleDumpUserLifecycleListeners(@NonNull PrintWriter writer) {
+    private void handleDumpServiceLifecycleListeners(@NonNull PrintWriter writer) {
         if (mUserLifecycleListeners.isEmpty()) {
-            writer.println("No user lifecycle listeners");
+            writer.println("No lifecycle listeners for internal services");
             return;
         }
-        writer.printf("%d user lifecycle listeners\n", mUserLifecycleListeners.size());
+        int size = mUserLifecycleListeners.size();
+        writer.printf("%d lifecycle listener%s for services\n", size, size == 1 ? "" : "s");
+        String indent = "  ";
         for (UserLifecycleListener listener : mUserLifecycleListeners) {
-            writer.printf("Listener %s\n", listener);
+            writer.printf("%s%s\n", indent, FunctionalUtils.getLambdaName(listener));
         }
     }
 
     private void handleDumpAppLifecycleListeners(@NonNull PrintWriter writer, String indent) {
-        int numberListeners = mAppLifecycleListeners.size();
-        if (numberListeners == 0) {
-            writer.println("No lifecycle listeners");
+        int size = mAppLifecycleListeners.size();
+        if (size == 0) {
+            writer.println("No lifecycle listeners for apps");
             return;
         }
-        writer.printf("%d lifecycle listeners\n", numberListeners);
-        for (int i = 0; i < numberListeners; i++) {
+        writer.printf("%d lifecycle listener%s for apps \n", size, size == 1 ? "" : "s");
+        for (int i = 0; i < size; i++) {
             int uid = mAppLifecycleListeners.keyAt(i);
             IResultReceiver listener = mAppLifecycleListeners.valueAt(i);
-            writer.printf("%suid: %d Listener %s\n", indent, uid, listener);
+            writer.printf("%suid: %d\n", indent, uid);
         }
     }
 
     /**
-     * Creates a driver who is a regular user and is allowed to login to the driving occupant zone.
-     *
-     * @param name The name of the driver to be created.
-     * @param admin Whether the created driver will be an admin.
-     * @return {@link UserInfo} object of the created driver, or {@code null} if the driver could
-     *         not be created.
+     * @see ExperimentalCarUserManager.createDriver
      */
     @Override
-    @Nullable
-    public UserInfo createDriver(@NonNull String name, boolean admin) {
+    public AndroidFuture<UserCreationResult> createDriver(@NonNull String name, boolean admin) {
         checkManageUsersPermission("createDriver");
         Objects.requireNonNull(name, "name cannot be null");
+
+        AndroidFuture<UserCreationResult> future = new AndroidFuture<UserCreationResult>() {
+            @Override
+            protected void onCompleted(UserCreationResult result, Throwable err) {
+                if (result == null) {
+                    Log.w(TAG, "createDriver(" + name + "," + admin + ") failed: " + err);
+                } else {
+                    if (result.getStatus() == UserCreationResult.STATUS_SUCCESSFUL) {
+                        assignDefaultIcon(result.getUser());
+                    }
+                }
+                super.onCompleted(result, err);
+            };
+        };
+        int flags = 0;
         if (admin) {
-            return createNewAdminUser(name);
+            if (!(mUserManager.isAdminUser() || mUserManager.isSystemUser())) {
+                Log.e(TAG_USER, "Only admin users and system user can create other admins.");
+                sendUserCreationResultFailure(future, UserCreationResult.STATUS_INVALID_REQUEST);
+                return future;
+            }
+            flags = UserInfo.FLAG_ADMIN;
         }
-        return mCarUserManagerHelper.createNewNonAdminUser(name);
+        createUser(name, UserInfo.getDefaultUserType(flags), flags, mHalTimeoutMs, future);
+        return future;
     }
 
     /**
-     * Creates a passenger who is a profile of the given driver.
-     *
-     * @param name The name of the passenger to be created.
-     * @param driverId User id of the driver under whom a passenger is created.
-     * @return {@link UserInfo} object of the created passenger, or {@code null} if the passenger
-     *         could not be created.
+     * @see ExperimentalCarUserManager.createPassenger
      */
     @Override
     @Nullable
@@ -410,6 +439,7 @@
             Log.w(TAG_USER, "a guest driver cannot create a passenger");
             return null;
         }
+        // createPassenger doesn't use user HAL because user HAL doesn't support profile user yet.
         UserInfo user = mUserManager.createProfileForUser(name,
                 UserManager.USER_TYPE_PROFILE_MANAGED, /* flags */ 0, driverId);
         if (user == null) {
@@ -424,7 +454,7 @@
     }
 
     /**
-     * @see CarUserManager.switchDriver
+     * @see ExperimentalCarUserManager.switchDriver
      */
     @Override
     public void switchDriver(@UserIdInt int driverId, AndroidFuture<UserSwitchResult> receiver) {
@@ -432,13 +462,13 @@
         if (UserHelper.isHeadlessSystemUser(driverId)) {
             // System user doesn't associate with real person, can not be switched to.
             Log.w(TAG_USER, "switching to system user in headless system user mode is not allowed");
-            sendResult(receiver, UserSwitchResult.STATUS_INVALID_REQUEST);
+            sendUserSwitchResult(receiver, UserSwitchResult.STATUS_INVALID_REQUEST);
             return;
         }
         int userSwitchable = mUserManager.getUserSwitchability();
         if (userSwitchable != UserManager.SWITCHABILITY_STATUS_OK) {
             Log.w(TAG_USER, "current process is not allowed to switch user");
-            sendResult(receiver, UserSwitchResult.STATUS_INVALID_REQUEST);
+            sendUserSwitchResult(receiver, UserSwitchResult.STATUS_INVALID_REQUEST);
             return;
         }
         switchUser(driverId, mHalTimeoutMs, receiver);
@@ -597,7 +627,7 @@
                 timeoutMs);
         Objects.requireNonNull(receiver, "receiver cannot be null");
         checkManageUsersPermission("getInitialInfo");
-        UsersInfo usersInfo = getUsersInfo();
+        UsersInfo usersInfo = UserHalHelper.newUsersInfo(mUserManager);
         mHal.getInitialUserInfo(requestType, timeoutMs, usersInfo, (status, resp) -> {
             Bundle resultData = null;
             if (resp != null) {
@@ -699,7 +729,7 @@
                 mHalTimeoutMs);
         Objects.requireNonNull(callback, "callback cannot be null");
         checkManageUsersPermission("getInitialUserInfo");
-        UsersInfo usersInfo = getUsersInfo();
+        UsersInfo usersInfo = UserHalHelper.newUsersInfo(mUserManager);
         mHal.getInitialUserInfo(requestType, mHalTimeoutMs, usersInfo, callback);
     }
 
@@ -756,7 +786,7 @@
                 Log.d(TAG_USER, "Current user is same as requested target user: " + targetUserId);
             }
             int resultStatus = UserSwitchResult.STATUS_ALREADY_REQUESTED_USER;
-            sendResult(receiver, resultStatus);
+            sendUserSwitchResult(receiver, resultStatus);
             return;
         }
 
@@ -779,7 +809,7 @@
                 }
 
                 int resultStatus = UserSwitchResult.STATUS_TARGET_USER_ALREADY_BEING_SWITCHED_TO;
-                sendResult(receiver, resultStatus);
+                sendUserSwitchResult(receiver, resultStatus);
                 return;
             }
             else {
@@ -788,12 +818,10 @@
             }
         }
 
-        UsersInfo usersInfo = getUsersInfo();
-        android.hardware.automotive.vehicle.V2_0.UserInfo halTargetUser =
-                new android.hardware.automotive.vehicle.V2_0.UserInfo();
-        halTargetUser.userId = targetUser.id;
-        halTargetUser.flags = UserHalHelper.convertFlags(targetUser);
-        mHal.switchUser(halTargetUser, timeoutMs, usersInfo, (status, resp) -> {
+        UsersInfo usersInfo = UserHalHelper.newUsersInfo(mUserManager);
+        SwitchUserRequest request = createUserSwitchRequest(targetUserId, usersInfo);
+
+        mHal.switchUser(request, timeoutMs, (status, resp) -> {
             if (Log.isLoggable(TAG_USER, Log.DEBUG)) {
                 Log.d(TAG, "switch response: status="
                         + UserHalHelper.halCallbackStatusToString(status) + ", resp=" + resp);
@@ -807,7 +835,7 @@
                     Log.w(TAG, "invalid callback status ("
                             + UserHalHelper.halCallbackStatusToString(status) + ") for response "
                             + resp);
-                    sendResult(receiver, resultStatus);
+                    sendUserSwitchResult(receiver, resultStatus);
                     mUserIdForUserSwitchInProcess = UserHandle.USER_NULL;
                     return;
                 }
@@ -825,7 +853,7 @@
                     }
                     resultStatus =
                             UserSwitchResult.STATUS_TARGET_USER_ABANDONED_DUE_TO_A_NEW_REQUEST;
-                    sendResult(receiver, resultStatus);
+                    sendUserSwitchResult(receiver, resultStatus);
                     mUserIdForUserSwitchInProcess = UserHandle.USER_NULL;
                     return;
                 }
@@ -853,24 +881,83 @@
                         // HAL failed to switch user
                         resultStatus = UserSwitchResult.STATUS_HAL_FAILURE;
                         break;
+                    default:
+                        // Shouldn't happen because UserHalService validates the status
+                        Log.wtf(TAG, "Received invalid user switch status from HAL: " + resp);
                 }
 
                 if (mRequestIdForUserSwitchInProcess == 0) {
                     mUserIdForUserSwitchInProcess = UserHandle.USER_NULL;
                 }
             }
-            sendResult(receiver, resultStatus, resp.errorMessage);
+            sendUserSwitchResult(receiver, resultStatus, resp.errorMessage);
         });
     }
 
+    @Override
+    public UserRemovalResult removeUser(@UserIdInt int userId) {
+        checkManageUsersPermission("removeUser");
+        EventLog.writeEvent(EventLogTags.CAR_USER_SVC_REMOVE_USER_REQ, userId);
+        // If the requested user is the current user, return error.
+        if (ActivityManager.getCurrentUser() == userId) {
+            return logAndGetResults(userId,
+                    UserRemovalResult.STATUS_TARGET_USER_IS_CURRENT_USER);
+        }
+
+        // If requested user is the only admin user, return error.
+        UserInfo userInfo = mUserManager.getUserInfo(userId);
+        if (userInfo == null) {
+            return logAndGetResults(userId, UserRemovalResult.STATUS_USER_DOES_NOT_EXIST);
+        }
+
+        android.hardware.automotive.vehicle.V2_0.UserInfo halUser =
+                new android.hardware.automotive.vehicle.V2_0.UserInfo();
+        halUser.userId = userInfo.id;
+        halUser.flags = UserHalHelper.convertFlags(userInfo);
+        UsersInfo usersInfo = UserHalHelper.newUsersInfo(mUserManager);
+
+        // Do not delete last admin user.
+        if (UserHalHelper.isAdmin(halUser.flags)) {
+            int size = usersInfo.existingUsers.size();
+            int totalAdminUsers = 0;
+            for (int i = 0; i < size; i++) {
+                if (UserHalHelper.isAdmin(usersInfo.existingUsers.get(i).flags)) {
+                    totalAdminUsers++;
+                }
+            }
+            if (totalAdminUsers == 1) {
+                return logAndGetResults(userId,
+                        UserRemovalResult.STATUS_TARGET_USER_IS_LAST_ADMIN_USER);
+            }
+        }
+
+        // First remove user from android and then remove from HAL because HAL remove user is one
+        // way call.
+        if (!mUserManager.removeUser(userId)) {
+            return logAndGetResults(userId, UserRemovalResult.STATUS_ANDROID_FAILURE);
+        }
+
+        RemoveUserRequest request = new RemoveUserRequest();
+        request.removedUserInfo = halUser;
+        request.usersInfo = usersInfo;
+        mHal.removeUser(request);
+        return logAndGetResults(userId, UserRemovalResult.STATUS_SUCCESSFUL);
+    }
+
+    private UserRemovalResult logAndGetResults(@UserIdInt int userId,
+            @UserRemovalResult.Status int result) {
+        EventLog.writeEvent(EventLogTags.CAR_USER_SVC_REMOVE_USER_RESP, userId, result);
+        return new UserRemovalResult(result);
+    }
+
     private void sendUserSwitchUiCallback(@UserIdInt int targetUserId) {
         if (mUserSwitchUiReceiver == null) {
             Log.w(TAG_USER, "No User switch UI receiver.");
             return;
         }
 
+        EventLog.writeEvent(EventLogTags.CAR_USER_SVC_SWITCH_USER_UI_REQ, targetUserId);
         try {
-            EventLog.writeEvent(EventLogTags.CAR_USER_SVC_SWITCH_USER_UI_REQ, targetUserId);
             mUserSwitchUiReceiver.send(targetUserId, null);
         } catch (RemoteException e) {
             Log.e(TAG_USER, "Error calling user switch UI receiver.", e);
@@ -878,6 +965,106 @@
     }
 
     @Override
+    public void createUser(@Nullable String name, @NonNull String userType, @UserInfoFlag int flags,
+            int timeoutMs, @NonNull AndroidFuture<UserCreationResult> receiver) {
+        Objects.requireNonNull(userType, "user type cannot be null");
+        Objects.requireNonNull(receiver, "receiver cannot be null");
+        checkManageOrCreateUsersPermission("createUser");
+        EventLog.writeEvent(EventLogTags.CAR_USER_SVC_CREATE_USER_REQ, UserHelper.safeName(name),
+                userType, flags, timeoutMs);
+
+        UserInfo newUser;
+        try {
+            newUser = mUserManager.createUser(name, userType, flags);
+            if (newUser == null) {
+                Log.w(TAG, "um.createUser() returned null for user of type " + userType
+                        + " and flags " + UserInfo.flagsToString(flags));
+                sendUserCreationResultFailure(receiver, UserCreationResult.STATUS_ANDROID_FAILURE);
+                return;
+            }
+            if (Log.isLoggable(TAG_USER, Log.DEBUG)) {
+                Log.d(TAG, "Created user: " + newUser.toFullString());
+            }
+            EventLog.writeEvent(EventLogTags.CAR_USER_SVC_CREATE_USER_USER_CREATED, newUser.id,
+                    UserHelper.safeName(newUser.name), newUser.userType, newUser.flags);
+        } catch (RuntimeException e) {
+            Log.e(TAG_USER, "Error creating user of type " + userType + " and flags"
+                    + UserInfo.flagsToString(flags), e);
+            sendUserCreationResultFailure(receiver, UserCreationResult.STATUS_ANDROID_FAILURE);
+            return;
+        }
+
+        CreateUserRequest request = new CreateUserRequest();
+        request.usersInfo = UserHalHelper.newUsersInfo(mUserManager);
+        if (!TextUtils.isEmpty(name)) {
+            request.newUserName = name;
+        }
+        request.newUserInfo.userId = newUser.id;
+        request.newUserInfo.flags = UserHalHelper.convertFlags(newUser);
+        if (Log.isLoggable(TAG_USER, Log.DEBUG)) {
+            Log.d(TAG, "Create user request: " + request);
+        }
+
+        try {
+            mHal.createUser(request, timeoutMs, (status, resp) -> {
+                int resultStatus = UserCreationResult.STATUS_HAL_INTERNAL_FAILURE;
+                if (Log.isLoggable(TAG_USER, Log.DEBUG)) {
+                    Log.d(TAG, "createUserResponse: status="
+                            + UserHalHelper.halCallbackStatusToString(status) + ", resp=" + resp);
+                }
+                UserInfo user = null; // user returned in the result
+                if (status != HalCallback.STATUS_OK) {
+                    Log.w(TAG, "invalid callback status ("
+                            + UserHalHelper.halCallbackStatusToString(status) + ") for response "
+                            + resp);
+                    EventLog.writeEvent(EventLogTags.CAR_USER_SVC_CREATE_USER_RESP, status,
+                            resultStatus, resp.errorMessage);
+                    removeUser(newUser, "HAL call failed with "
+                            + UserHalHelper.halCallbackStatusToString(status));
+                    sendUserCreationResult(receiver, resultStatus, user, /* errorMsg= */ null);
+                    return;
+                }
+
+                switch (resp.status) {
+                    case CreateUserStatus.SUCCESS:
+                        resultStatus = UserCreationResult.STATUS_SUCCESSFUL;
+                        user = newUser;
+                        break;
+                    case CreateUserStatus.FAILURE:
+                        // HAL failed to switch user
+                        resultStatus = UserCreationResult.STATUS_HAL_FAILURE;
+                        break;
+                    default:
+                        // Shouldn't happen because UserHalService validates the status
+                        Log.wtf(TAG, "Received invalid user switch status from HAL: " + resp);
+                }
+                EventLog.writeEvent(EventLogTags.CAR_USER_SVC_CREATE_USER_RESP, status,
+                        resultStatus, resp.errorMessage);
+                if (user == null) {
+                    removeUser(newUser, "HAL returned "
+                            + UserCreationResult.statusToString(resultStatus));
+                }
+                sendUserCreationResult(receiver, resultStatus, user, resp.errorMessage);
+            });
+        } catch (Exception e) {
+            Log.w(TAG, "mHal.createUser(" + request + ") failed", e);
+            removeUser(newUser, "mHal.createUser() failed");
+            sendUserCreationResultFailure(receiver, UserCreationResult.STATUS_HAL_INTERNAL_FAILURE);
+        }
+    }
+
+    private void removeUser(@NonNull UserInfo user, @NonNull String reason) {
+        EventLog.writeEvent(EventLogTags.CAR_USER_SVC_CREATE_USER_USER_REMOVED, user.id, reason);
+        try {
+            if (!mUserManager.removeUser(user.id)) {
+                Log.w(TAG, "Failed to remove user " + user.toFullString());
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "Failed to remove user " + user.toFullString(), e);
+        }
+    }
+
+    @Override
     public UserIdentificationAssociationResponse getUserIdentificationAssociation(int[] types) {
         Preconditions.checkArgument(!ArrayUtils.isEmpty(types), "must have at least one type");
         checkManageUsersPermission("getUserIdentificationAssociation");
@@ -993,16 +1180,30 @@
         }
     }
 
-    private void sendResult(@NonNull AndroidFuture<UserSwitchResult> receiver,
+    private void sendUserSwitchResult(@NonNull AndroidFuture<UserSwitchResult> receiver,
             @UserSwitchResult.Status int status) {
-        sendResult(receiver, status, /* errorMessage= */ null);
+        sendUserSwitchResult(receiver, status, /* errorMessage= */ null);
     }
 
-    private void sendResult(@NonNull AndroidFuture<UserSwitchResult> receiver,
+    private void sendUserSwitchResult(@NonNull AndroidFuture<UserSwitchResult> receiver,
             @UserSwitchResult.Status int status, @Nullable String errorMessage) {
         receiver.complete(new UserSwitchResult(status, errorMessage));
     }
 
+    private void sendUserCreationResultFailure(@NonNull AndroidFuture<UserCreationResult> receiver,
+            @UserCreationResult.Status int status) {
+        sendUserCreationResult(receiver, status, /* user= */ null, /* errorMessage= */ null);
+    }
+
+    private void sendUserCreationResult(@NonNull AndroidFuture<UserCreationResult> receiver,
+            @UserCreationResult.Status int status, @NonNull UserInfo user,
+            @Nullable String errorMessage) {
+        if (TextUtils.isEmpty(errorMessage)) {
+            errorMessage = null;
+        }
+        receiver.complete(new UserCreationResult(status, user, errorMessage));
+    }
+
     /**
      * Calls activity manager for user switch.
      *
@@ -1045,17 +1246,26 @@
             mRequestIdForUserSwitchInProcess = requestId;
         }
     }
-
     private void postSwitchHalResponse(int requestId, @UserIdInt int targetUserId) {
+        UsersInfo usersInfo = UserHalHelper.newUsersInfo(mUserManager);
+        EventLog.writeEvent(EventLogTags.CAR_USER_SVC_POST_SWITCH_USER_REQ, requestId,
+                targetUserId, usersInfo.currentUser.userId);
+        SwitchUserRequest request = createUserSwitchRequest(targetUserId, usersInfo);
+        request.requestId = requestId;
+        mHal.postSwitchResponse(request);
+    }
+
+    private SwitchUserRequest createUserSwitchRequest(@UserIdInt int targetUserId,
+            @NonNull UsersInfo usersInfo) {
         UserInfo targetUser = mUserManager.getUserInfo(targetUserId);
-        UsersInfo usersInfo = getUsersInfo();
         android.hardware.automotive.vehicle.V2_0.UserInfo halTargetUser =
                 new android.hardware.automotive.vehicle.V2_0.UserInfo();
         halTargetUser.userId = targetUser.id;
         halTargetUser.flags = UserHalHelper.convertFlags(targetUser);
-        EventLog.writeEvent(EventLogTags.CAR_USER_SVC_POST_SWITCH_USER_REQ, requestId,
-                targetUserId, usersInfo.currentUser.userId);
-        mHal.postSwitchResponse(requestId, halTargetUser, usersInfo);
+        SwitchUserRequest request = new SwitchUserRequest();
+        request.targetUser = halTargetUser;
+        request.usersInfo = usersInfo;
+        return request;
     }
 
     /**
@@ -1112,40 +1322,6 @@
         }
     }
 
-    // TODO(b/150413515): use helper to generate UsersInfo
-    private UsersInfo getUsersInfo() {
-        UserInfo currentUser;
-        try {
-            currentUser = mAm.getCurrentUser();
-        } catch (RemoteException e) {
-            // shouldn't happen
-            throw new IllegalStateException("Could not get current user: ", e);
-        }
-        return getUsersInfo(currentUser);
-    }
-
-    // TODO(b/150413515): use helper to generate UsersInfo
-    private UsersInfo getUsersInfo(@NonNull UserInfo currentUser) {
-        List<UserInfo> existingUsers = mUserManager.getUsers();
-        int size = existingUsers.size();
-
-        UsersInfo usersInfo = new UsersInfo();
-        usersInfo.numberUsers = size;
-        usersInfo.currentUser.userId = currentUser.id;
-        usersInfo.currentUser.flags = UserHalHelper.convertFlags(currentUser);
-
-        for (int i = 0; i < size; i++) {
-            UserInfo androidUser = existingUsers.get(i);
-            android.hardware.automotive.vehicle.V2_0.UserInfo halUser =
-                    new android.hardware.automotive.vehicle.V2_0.UserInfo();
-            halUser.userId = androidUser.id;
-            halUser.flags = UserHalHelper.convertFlags(androidUser);
-            usersInfo.existingUsers.add(halUser);
-        }
-
-        return usersInfo;
-    }
-
     private void updateDefaultUserRestriction() {
         // We want to set restrictions on system and guest users only once. These are persisted
         // onto disk, so it's sufficient to do it once + we minimize the number of disk writes.
@@ -1345,8 +1521,10 @@
             handleNotifyAppUserLifecycleListeners(event);
         });
 
-        // Finally, update metrics.
-        mUserMetrics.onEvent(eventType, timestampMs, fromUserId, toUserId);
+        if (timestampMs != 0) {
+            // Finally, update metrics.
+            mUserMetrics.onEvent(eventType, timestampMs, fromUserId, toUserId);
+        }
     }
 
     /**
@@ -1385,23 +1563,27 @@
         }
         int userId = event.getUserId();
         TimingsTraceLog t = new TimingsTraceLog(TAG_USER, Trace.TRACE_TAG_SYSTEM_SERVER);
-        t.traceBegin("notify-app-listeners-user-" + userId + "-event-" + event.getEventType());
+        int eventType = event.getEventType();
+        t.traceBegin("notify-app-listeners-user-" + userId + "-event-" + eventType);
         for (int i = 0; i < listenersSize; i++) {
             int uid = mAppLifecycleListeners.keyAt(i);
+
             IResultReceiver listener = mAppLifecycleListeners.valueAt(i);
             Bundle data = new Bundle();
-            data.putInt(CarUserManager.BUNDLE_PARAM_ACTION, event.getEventType());
+            data.putInt(CarUserManager.BUNDLE_PARAM_ACTION, eventType);
 
-            int fromUid = event.getPreviousUserId();
-            if (fromUid != UserHandle.USER_NULL) {
-                data.putInt(CarUserManager.BUNDLE_PARAM_PREVIOUS_USER_ID, fromUid);
+            int fromUserId = event.getPreviousUserId();
+            if (fromUserId != UserHandle.USER_NULL) {
+                data.putInt(CarUserManager.BUNDLE_PARAM_PREVIOUS_USER_ID, fromUserId);
             }
 
             if (Log.isLoggable(TAG_USER, Log.DEBUG)) {
                 Log.d(TAG_USER, "Notifying listener for uid " + uid);
             }
+            EventLog.writeEvent(EventLogTags.CAR_USER_SVC_NOTIFY_APP_LIFECYCLE_LISTENER,
+                    uid, eventType, fromUserId, userId);
             try {
-                t.traceBegin("notify-app-listener-" + uid);
+                t.traceBegin("notify-app-listener-uid-" + uid);
                 listener.send(userId, data);
             } catch (RemoteException e) {
                 Log.e(TAG_USER, "Error calling lifecycle listener", e);
@@ -1422,10 +1604,13 @@
                     + event);
         }
 
-        t.traceBegin("notify-listeners-user-" + event.getUserId() + "-event-"
-                + event.getEventType());
+        int userId = event.getUserId();
+        int eventType = event.getEventType();
+        t.traceBegin("notify-listeners-user-" + userId + "-event-" + eventType);
         for (UserLifecycleListener listener : mUserLifecycleListeners) {
             String listenerName = FunctionalUtils.getLambdaName(listener);
+            EventLog.writeEvent(EventLogTags.CAR_USER_SVC_NOTIFY_INTERNAL_LIFECYCLE_LISTENER,
+                    listenerName, eventType, event.getPreviousUserId(), userId);
             try {
                 t.traceBegin("notify-listener-" + listenerName);
                 listener.onEvent(event);
@@ -1447,9 +1632,8 @@
         // Switch HAL users if user switch is not requested by CarUserService
         notifyHalLegacySwitch(fromUserId, toUserId);
 
-        if (!UserHelper.isHeadlessSystemUser(toUserId)) {
-            mCarUserManagerHelper.setLastActiveUser(toUserId);
-        }
+        mCarUserManagerHelper.setLastActiveUser(toUserId);
+
         if (mLastPassengerId != UserHandle.USER_NULL) {
             stopPassengerInternal(mLastPassengerId, false);
         }
@@ -1462,18 +1646,19 @@
 
     private void notifyHalLegacySwitch(@UserIdInt int fromUserId, @UserIdInt int toUserId) {
         synchronized (mLockUser) {
-            if (mUserIdForUserSwitchInProcess != UserHandle.USER_NULL) return;
+            if (mUserIdForUserSwitchInProcess != UserHandle.USER_NULL) {
+                if (Log.isLoggable(TAG_USER, Log.DEBUG)) {
+                    Log.d(TAG, "notifyHalLegacySwitch(" + fromUserId + ", " + toUserId
+                            + "): not needed, normal switch for " + mUserIdForUserSwitchInProcess);
+                }
+                return;
+            }
         }
 
         // switch HAL user
-        UserInfo targetUser = mUserManager.getUserInfo(toUserId);
-        android.hardware.automotive.vehicle.V2_0.UserInfo halTargetUser =
-                new android.hardware.automotive.vehicle.V2_0.UserInfo();
-        halTargetUser.userId = targetUser.id;
-        halTargetUser.flags = UserHalHelper.convertFlags(targetUser);
-        UserInfo currentUser = mUserManager.getUserInfo(fromUserId);
-        UsersInfo usersInfo = getUsersInfo(currentUser);
-        mHal.legacyUserSwitch(halTargetUser, usersInfo);
+        UsersInfo usersInfo = UserHalHelper.newUsersInfo(mUserManager, fromUserId);
+        SwitchUserRequest request = createUserSwitchRequest(toUserId, usersInfo);
+        mHal.legacyUserSwitch(request);
     }
 
     /**
@@ -1516,42 +1701,15 @@
     }
 
     /**
-     * Creates a new user on the system, the created user would be granted admin role.
-     *
-     * @param name Name to be given to the newly created user.
-     * @return newly created admin user, {@code null} if it fails to create a user.
-     */
-    @Nullable
-    private UserInfo createNewAdminUser(String name) {
-        if (!(mUserManager.isAdminUser() || mUserManager.isSystemUser())) {
-            // Only admins or system user can create other privileged users.
-            Log.e(TAG_USER, "Only admin users and system user can create other admins.");
-            return null;
-        }
-
-        UserInfo user = mUserManager.createUser(name, UserInfo.FLAG_ADMIN);
-        if (user == null) {
-            // Couldn't create user, most likely because there are too many.
-            Log.w(TAG_USER, "can't create admin user.");
-            return null;
-        }
-        assignDefaultIcon(user);
-
-        return user;
-    }
-
-    /**
      * Assigns a default icon to a user according to the user's id.
      *
      * @param userInfo User whose avatar is set to default icon.
-     * @return Bitmap of the user icon.
      */
-    private Bitmap assignDefaultIcon(UserInfo userInfo) {
+    private void assignDefaultIcon(UserInfo userInfo) {
         int idForIcon = userInfo.isGuest() ? UserHandle.USER_NULL : userInfo.id;
         Bitmap bitmap = UserIcons.convertToBitmap(
                 UserIcons.getDefaultUserIcon(mContext.getResources(), idForIcon, false));
         mUserManager.setUserIcon(userInfo.id, bitmap);
-        return bitmap;
     }
 
     private interface UserFilter {
@@ -1583,6 +1741,12 @@
         checkAtLeastOnePermission(message, android.Manifest.permission.MANAGE_USERS);
     }
 
+    private static void checkManageOrCreateUsersPermission(String message) {
+        checkAtLeastOnePermission(message,
+                android.Manifest.permission.MANAGE_USERS,
+                android.Manifest.permission.CREATE_USERS);
+    }
+
     private static void checkManageUsersOrDumpPermission(String message) {
         checkAtLeastOnePermission(message,
                 android.Manifest.permission.MANAGE_USERS,
diff --git a/surround_view/service-impl/Android.bp b/surround_view/service-impl/Android.bp
index 8ae7bba..80c77a3 100644
--- a/surround_view/service-impl/Android.bp
+++ b/surround_view/service-impl/Android.bp
@@ -14,19 +14,89 @@
 // limitations under the License.
 //
 
-cc_binary {
-    name: "android.automotive.sv.service@1.0-impl",
-    vendor: true,
+cc_library {
+    name : "libobj_reader",
+    vendor : true,
     srcs: [
+        "MtlReader.cpp",
+        "ObjReader.cpp",
+    ],
+    shared_libs : [
+        "libbase",
+    ]
+}
+
+cc_test{
+    name : "obj_reader_tests",
+    test_suites : ["device-tests"],
+    vendor : true,
+    srcs : ["ObjReaderTests.cpp"],
+    shared_libs : [
+        "libobj_reader",
+        "libcutils",
+        "libbase",
+        "libutils",
+    ],
+    required: [
+        "VolvoXC40_low.obj",
+        "VolvoXC40_low.mtl",
+    ],
+}
+
+cc_library{
+    name : "libvhal_handler",
+    vendor : true,
+    srcs : [
+        "VhalHandler.cpp",
+    ],
+    shared_libs : [
+        "android.hardware.automotive.vehicle@2.0",
+        "android.hidl.memory@1.0",
+        "libcutils",
+        "libbase",
+        "libbinder",
+        "libhidlbase",
+        "libhardware",
+        "libhidlmemory",
+        "libui",
+        "libutils",
+    ],
+}
+
+cc_test{
+    name : "vhal_handler_tests",
+    test_suites : ["device-tests"],
+    vendor : true,
+    srcs : ["VhalHandlerTests.cpp"],
+    shared_libs : [
+        "android.hardware.automotive.vehicle@2.0",
+        "libvhal_handler",
+        "libcutils",
+        "libbase",
+        "libbinder",
+        "libhidlbase",
+        "libhardware",
+        "libhidlmemory",
+        "libui",
+        "libutils",
+    ],
+}
+
+cc_binary{
+    name : "android.automotive.sv.service@1.0-impl",
+    vendor : true,
+    srcs : [
         "CoreLibSetupHelper.cpp",
         "SurroundViewService.cpp",
         "SurroundView2dSession.cpp",
         "SurroundView3dSession.cpp",
         "service.cpp",
+
     ],
-    init_rc: ["android.automotive.sv.service@1.0-impl.rc"],
-    shared_libs: [
+    init_rc : ["android.automotive.sv.service@1.0-impl.rc"],
+    shared_libs : [
         "android.hardware.automotive.sv@1.0",
+        "android.hardware.automotive.vehicle@2.0",
         "android.hidl.memory@1.0",
         "libbase",
         "libbinder",
@@ -38,49 +108,43 @@
         "libui",
         "libutils",
     ],
-    required: [
+    required : [
         "cam0.png",
         "cam1.png",
         "cam2.png",
         "cam3.png",
     ],
     // Disable builds except for arm64 and emulator devices
-    enabled: false,
-    arch: {
-        arm64: {
-            enabled: true,
+    enabled : false,
+    arch : {
+        arm64 : {
+            enabled : true,
         },
-        x86: {
-            enabled: true,
+        x86 : {
+            enabled : true,
         },
-        x86_64: {
-            enabled: true,
+        x86_64 : {
+            enabled : true,
         },
     },
-    vintf_fragments: [
+    vintf_fragments : [
         "manifest_android.hardware.automotive.sv@1.0.xml",
     ],
 }
 
-cc_prebuilt_library_shared {
-    name: "libcore_lib_shared",
-    proprietary: true,
-    arch: {
-        arm64: {
-            srcs: ["lib/arm64/libcore_lib_shared.so"]
-        },
-        x86: {
-            srcs: ["lib/x86/libcore_lib_shared.so"]
-        },
-        x86_64: {
-            srcs: ["lib/x86-64/libcore_lib_shared.so"]
-        },
+cc_prebuilt_library_shared{
+    name : "libcore_lib_shared",
+    proprietary : true,
+    arch : {
+        arm64 : {srcs : ["lib/arm64/libcore_lib_shared.so"]},
+        x86 : {srcs : ["lib/x86/libcore_lib_shared.so"]},
+        x86_64 : {srcs : ["lib/x86-64/libcore_lib_shared.so"]},
     },
-    shared_libs: [
-	"libutils",
-	"libcutils",
-	"libbase",
-	"libEGL",
+    shared_libs : [
+        "libutils",
+        "libcutils",
+        "libbase",
+        "libEGL",
         "libGLESv2",
         "libGLESv3",
         "libc",
@@ -91,27 +155,37 @@
     ],
 }
 
+prebuilt_etc{
+    name : "cam0.png",
+    src : "test_data/0.png",
+    sub_dir : "automotive/sv",
+}
+
+prebuilt_etc{
+    name : "cam1.png",
+    src : "test_data/1.png",
+    sub_dir : "automotive/sv",
+}
+
+prebuilt_etc{
+    name : "cam2.png",
+    src : "test_data/2.png",
+    sub_dir : "automotive/sv",
+}
+
 prebuilt_etc {
-    name: "cam0.png",
-    src: "test_data/0.png",
+name:
+    "cam3.png", src : "test_data/3.png", sub_dir : "automotive/sv",
+}
+
+prebuilt_etc {
+    name: "cube.obj",
+    src: "test_data/cube.obj",
     sub_dir: "automotive/sv",
 }
 
 prebuilt_etc {
-    name: "cam1.png",
-    src: "test_data/1.png",
+    name: "cube.mtl",
+    src: "test_data/cube.mtl",
     sub_dir: "automotive/sv",
 }
-
-prebuilt_etc {
-    name: "cam2.png",
-    src: "test_data/2.png",
-    sub_dir: "automotive/sv",
-}
-
-prebuilt_etc {
-    name: "cam3.png",
-    src: "test_data/3.png",
-    sub_dir: "automotive/sv",
-}
-
diff --git a/surround_view/service-impl/MtlReader.cpp b/surround_view/service-impl/MtlReader.cpp
new file mode 100644
index 0000000..d86a15a
--- /dev/null
+++ b/surround_view/service-impl/MtlReader.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2020 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.
+ */
+#include "MtlReader.h"
+
+#include <android-base/logging.h>
+#include <cstdio>
+
+#define LOG_TAG "MtlReader"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace sv {
+namespace V1_0 {
+namespace implementation {
+
+namespace {
+
+constexpr int kCharBufferSize = 128;
+
+void ReadFloat3(FILE* file, float* value) {
+    float temp[3];
+    int res = fscanf(file, "%f %f %f", &temp[0], &temp[1], &temp[2]);
+    3 == res ? std::memcpy(value, temp, 3 * sizeof(float)) : nullptr;
+}
+
+void ReadFloat(FILE* file, float* value) {
+    float temp;
+    int res = fscanf(file, "%f", &temp);
+    *value = res > 0 ? temp : -1;
+}
+
+void ReadInt(FILE* file, int* value) {
+    int temp;
+    int res = fscanf(file, "%d", &temp);
+    *value = res > 0 ? temp : -1;
+}
+
+void ReadString(FILE* file, std::string* value) {
+    char temp[kCharBufferSize];
+    fscanf(file, "%s", temp);
+    *value = temp;
+}
+}  // namespace
+
+bool ReadMtlFromFile(const std::string& mtlFilename,
+                     std::map<std::string, MtlConfigParams>* params) {
+    FILE* file = fopen(mtlFilename.c_str(), "r");
+    if (!file) {
+        LOG(ERROR) << "Failed to open mtl file: " << mtlFilename;
+        return false;
+    }
+
+    std::string currentConfig;
+    while (true) {
+        char lineHeader[kCharBufferSize];
+        // read the first word of the line
+        int res = fscanf(file, "%s", lineHeader);
+
+        if (res == EOF) {
+            break;  // EOF = End Of File. Quit the loop.
+        }
+
+        if (strcmp(lineHeader, "#") == 0) {
+            fgets(lineHeader, sizeof(lineHeader), file);
+            continue;
+        }
+        if (strcmp(lineHeader, "newmtl") == 0) {
+            res = fscanf(file, "%s", lineHeader);
+            if (params->find(lineHeader) != params->end()) {
+                fclose(file);
+                LOG(ERROR) << "Duplicated params of : " << lineHeader[0];
+                return false;
+            }
+            currentConfig = lineHeader;
+            continue;
+        }
+
+        if (strcmp(lineHeader, "Ns") == 0) {
+            ReadFloat(file, &((*params)[currentConfig].ns));
+            continue;
+        }
+        if (strcmp(lineHeader, "Ni") == 0) {
+            ReadFloat(file, &((*params)[currentConfig].ni));
+            continue;
+        }
+        if (strcmp(lineHeader, "d") == 0) {
+            ReadFloat(file, &((*params)[currentConfig].d));
+            continue;
+        }
+        if (strcmp(lineHeader, "Tr") == 0) {
+            ReadFloat(file, &((*params)[currentConfig].tr));
+            continue;
+        }
+        if (strcmp(lineHeader, "Tf") == 0) {
+            ReadFloat3(file, (*params)[currentConfig].tf);
+            continue;
+        }
+        if (strcmp(lineHeader, "illum") == 0) {
+            ReadInt(file, &((*params)[currentConfig].illum));
+            continue;
+        }
+        if (strcmp(lineHeader, "Ka") == 0) {
+            ReadFloat3(file, (*params)[currentConfig].ka);
+            continue;
+        }
+        if (strcmp(lineHeader, "Kd") == 0) {
+            ReadFloat3(file, (*params)[currentConfig].kd);
+            continue;
+        }
+        if (strcmp(lineHeader, "Ks") == 0) {
+            ReadFloat3(file, (*params)[currentConfig].ks);
+            continue;
+        }
+        if (strcmp(lineHeader, "Ke") == 0) {
+            ReadFloat3(file, (*params)[currentConfig].ke);
+            continue;
+        }
+        if (strcmp(lineHeader, "map_bump") == 0) {
+            ReadString(file, &((*params)[currentConfig].mapBump));
+            continue;
+        }
+        if (strcmp(lineHeader, "bump") == 0) {
+            ReadString(file, &((*params)[currentConfig].bump));
+            continue;
+        }
+        if (strcmp(lineHeader, "map_Ka") == 0) {
+            ReadString(file, &((*params)[currentConfig].mapKa));
+            continue;
+        }
+        if (strcmp(lineHeader, "map_Kd") == 0) {
+            ReadString(file, &((*params)[currentConfig].mapKd));
+            continue;
+        }
+        if (strcmp(lineHeader, "map_Ks") == 0) {
+            ReadString(file, &((*params)[currentConfig].mapKs));
+            continue;
+        } else {
+            LOG(WARNING) << "Unknown tag " << lineHeader << ". Skipped";
+            fgets(lineHeader, sizeof(lineHeader), file);
+            continue;
+        }
+    }
+
+    fclose(file);
+    return true;
+}
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace sv
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/surround_view/service-impl/MtlReader.h b/surround_view/service-impl/MtlReader.h
new file mode 100644
index 0000000..3e19876
--- /dev/null
+++ b/surround_view/service-impl/MtlReader.h
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#ifndef SURROUND_VIEW_SERVICE_IMPL_MTLREADER_H_
+#define SURROUND_VIEW_SERVICE_IMPL_MTLREADER_H_
+
+#include <map>
+#include <string>
+
+#include "core_lib.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace sv {
+namespace V1_0 {
+namespace implementation {
+
+// Mtl defined params.
+struct MtlConfigParams {
+    // Ns exponent
+    // Specifies the specular exponent for the current material. This defines
+    // the focus of the specular highlight.
+    // Ns values normally range from 0 to 1000.
+    float ns = -1;
+
+    // optical_density
+    // Specifies the optical density for the surface. This is also known as
+    // index of refraction.
+    //  "optical_density" is the value for the optical density. The values can
+    // range from 0.001 to 10. A value of 1.0 means that light does not bend
+    // as it passes through an object. Increasing the optical_density
+    // increases the amount of bending. Glass has an index of refraction of
+    // about 1.5.  Values of less than 1.0 produce bizarre results and are not
+    // recommended.
+    float ni = -1;
+
+    // d defines the non-transparency of the material to be alpha.
+    // The default is 1.0 (not transparent at all).
+    // The quantities d and Tr are the opposites of each other.
+    float d = -1;
+
+    // The Tr statement specifies the transparency of the material to be alpha.
+    // The default is 0.0 (not transparent at all).
+    // The quantities d and Tr are the opposites of each other,
+    float tr = -1;
+
+    // The Tf statement specifies the transmission filter using RGB values.
+    // "r g b" are the values for the red, green, and blue components of the
+    // atmosphere.  The g and b arguments are optional. If only r is
+    // specified, then g, and b are assumed to be equal to r. The r g b values
+    // are normally in the range of 0.0 to 1.0. Values outside this range
+    // increase or decrease the relectivity accordingly.
+    float tf[3] = {-1, -1, -1};
+
+    // illum_#
+    // The "illum" statement specifies the illumination model to use in the
+    // material.  Illumination models are mathematical equations that represent
+    // various material lighting and shading effects.
+    //
+    // "illum_#"can be a number from 0 to 10. The illumination models are
+    // summarized below;
+    //
+    //  Illumination    Properties that are turned on in the
+    //  model           Property Editor
+    //
+    //  0 Color on and Ambient off
+    //  1 Color on and Ambient on
+    //  2 Highlight on
+    //  3 Reflection on and Ray trace on
+    //  4 Transparency: Glass on
+    //    Reflection: Ray trace on
+    //  5 Reflection: Fresnel on and Ray trace on
+    //  6 Transparency: Refraction on
+    //    Reflection: Fresnel off and Ray trace on
+    //  7 Transparency: Refraction on
+    //    Reflection: Fresnel on and Ray trace on
+    //  8 Reflection on and Ray trace off
+    //  9 Transparency: Glass on
+    //    Reflection: Ray trace off
+    // 10 Casts shadows onto invisible surfaces
+    int illum = -1;
+
+    // The Ka statement specifies the ambient reflectivity using RGB values.
+    // "r g b" are the values for the red, green, and blue components of the
+    // color.  The g and b arguments are optional. If only r is specified,
+    // then g, and b are assumed to be equal to r. The r g b values are
+    // normally in the range of 0.0 to 1.0. Values outside this range increase
+    // or decrease the relectivity accordingly.
+    float ka[3] = {-1, -1, -1};
+
+    // The Kd statement specifies the diffuse reflectivity using RGB values.
+    //  "r g b" are the values for the red, green, and blue components of the
+    // atmosphere.  The g and b arguments are optional.  If only r is
+    // specified, then g, and b are assumed to be equal to r. The r g b values
+    // are normally in the range of 0.0 to 1.0. Values outside this range
+    // increase or decrease the relectivity accordingly.
+    float kd[3] = {-1, -1, -1};
+
+    // The Ks statement specifies the specular reflectivity using RGB values.
+    //  "r g b" are the values for the red, green, and blue components of the
+    // atmosphere. The g and b arguments are optional. If only r is
+    // specified, then g, and b are assumed to be equal to r. The r g b values
+    // are normally in the range of 0.0 to 1.0. Values outside this range
+    // increase or decrease the relectivity accordingly.
+    float ks[3] = {-1, -1, -1};
+
+    // Emissive coeficient. It goes together with ambient, diffuse and specular
+    // and represents the amount of light emitted by the material.
+    float ke[3] = {-1, -1, -1};
+
+    // Specifies that a color texture file or color procedural texture file is
+    // linked to the specular reflectivity of the material. During rendering,
+    // the map_Ks value is multiplied by the Ks value.
+    std::string mapKs;
+
+    // Specifies that a color texture file or a color procedural texture file
+    // is applied to the ambient reflectivity of the material. During
+    // rendering, the "map_Ka" value is multiplied by the "Ka" value.
+    std::string mapKa;
+
+    // Specifies that a color texture file or color procedural texture file is
+    // linked to the diffuse reflectivity of the material. During rendering,
+    // the map_Kd value is multiplied by the Kd value.
+    std::string mapKd;
+
+    // Same as bump
+    std::string mapBump;
+
+    // Specifies that a bump texture file or a bump procedural texture file is
+    // linked to the material.
+    std::string bump;
+
+    MtlConfigParams& operator=(const MtlConfigParams& rhs) {
+        ns = rhs.ns;
+        ni = rhs.ni;
+        d = rhs.d;
+        tr = rhs.tr;
+        std::memcpy(tf, rhs.tf, 3 * sizeof(float));
+        illum = rhs.illum;
+        std::memcpy(ka, rhs.ka, 3 * sizeof(float));
+        std::memcpy(kd, rhs.kd, 3 * sizeof(float));
+        std::memcpy(ks, rhs.ks, 3 * sizeof(float));
+        std::memcpy(ke, rhs.ke, 3 * sizeof(float));
+        mapKs = rhs.mapKs;
+        mapKa = rhs.mapKa;
+        mapKd = rhs.mapKd;
+        mapBump = rhs.mapBump;
+        bump = rhs.bump;
+
+        return *this;
+    }
+};
+
+// Reads mtl file associated with obj file.
+// |filename| is the full path and name of the obj file.
+bool ReadMtlFromFile(const std::string& mtlFilename,
+                     std::map<std::string, MtlConfigParams>* params);
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace sv
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
+
+#endif  // SURROUND_VIEW_SERVICE_IMPL_MTLREADER_H_
diff --git a/surround_view/service-impl/ObjReader.cpp b/surround_view/service-impl/ObjReader.cpp
new file mode 100644
index 0000000..174ef5b
--- /dev/null
+++ b/surround_view/service-impl/ObjReader.cpp
@@ -0,0 +1,304 @@
+/*
+ * Copyright 2020 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.
+ */
+#include "ObjReader.h"
+
+#include <array>
+#include <cstdio>
+#include <filesystem>
+#include <vector>
+
+#include "MtlReader.h"
+#include "core_lib.h"
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace sv {
+namespace V1_0 {
+namespace implementation {
+
+using android_auto::surround_view::CarMaterial;
+using android_auto::surround_view::CarVertex;
+
+namespace {
+
+constexpr int kNumberOfVerticesPerFace = 3;
+constexpr int kNumberOfAxes = 3;
+constexpr int kCharBufferSize = 128;
+
+const std::array<float, 16> kMat4Identity = {
+        /*row 0*/ 1, 0, 0, 0,
+        /*row 1*/ 0, 1, 0, 0,
+        /*row 2*/ 0, 0, 1, 0,
+        /*row 3*/ 0, 0, 0, 1};
+
+// Copies face vertices parsed from obj to car vertices.
+void CopyFaceToCarVertex(const std::vector<std::array<float, kNumberOfAxes>>& currentVertices,
+                         const std::vector<std::array<float, kNumberOfAxes>>& currentTextures,
+                         const std::vector<std::array<float, kNumberOfAxes>>& currentNormals,
+                         int vertexId, int textureId, int normalId, CarVertex* carVertex) {
+    std::memcpy(carVertex->pos.data(), currentVertices[vertexId - 1].data(),
+                currentVertices[vertexId - 1].size() * sizeof(float));
+
+    if (textureId != -1) {
+        std::memcpy(carVertex->tex_coord.data(), currentTextures[textureId - 1].data(),
+                    2 * sizeof(float));
+        // Set texture coodinates as invalid.
+        carVertex->tex_coord = {-1.0, -1.0};
+    }
+
+    std::memcpy(carVertex->normal.data(), currentNormals[normalId - 1].data(),
+                currentNormals[normalId - 1].size() * sizeof(float));
+}
+
+}  // namespace
+
+bool ReadObjFromFile(const std::string& objFilename, std::map<std::string, CarPart>* carPartsMap) {
+    return ReadObjFromFile(objFilename, ReadObjOptions(), carPartsMap);
+}
+
+bool ReadObjFromFile(const std::string& objFilename, const ReadObjOptions& option,
+                     std::map<std::string, CarPart>* carPartsMap) {
+    FILE* file = fopen(objFilename.c_str(), "r");
+    if (!file) {
+        LOG(ERROR) << "Failed to open obj file: " << objFilename;
+        return false;
+    }
+
+    for (int i = 0; i < kNumberOfAxes; ++i) {
+        if (option.coordinateMapping[i] >= kNumberOfAxes || option.coordinateMapping[i] < 0) {
+            fclose(file);
+            LOG(ERROR) << "coordinateMapping index must be less than 3 and greater or equal "
+                          "to 0.";
+            return false;
+        }
+    }
+
+    std::vector<std::array<float, kNumberOfAxes>> currentVertices;
+    std::vector<std::array<float, kNumberOfAxes>> currentNormals;
+    std::vector<std::array<float, kNumberOfAxes>> currentTextures;
+    std::map<std::string, MtlConfigParams> mtlConfigParamsMap;
+    std::string currentGroupName;
+    MtlConfigParams currentMtlConfig;
+
+    while (true) {
+        char lineHeader[kCharBufferSize];
+        // read the first word of the line
+        int res = fscanf(file, "%s", lineHeader);
+
+        if (res == EOF) {
+            break;  // EOF = End Of File. Quit the loop.
+        }
+        if (strcmp(lineHeader, "#") == 0) {
+            fgets(lineHeader, sizeof(lineHeader), file);
+            continue;
+        }
+
+        // TODO(b/156558814): add object type support.
+        // TODO(b/156559272): add document for supported format.
+        // Only single group per line is supported.
+        if (strcmp(lineHeader, "g") == 0) {
+            res = fscanf(file, "%s", lineHeader);
+            currentGroupName = lineHeader;
+            currentMtlConfig = MtlConfigParams();
+
+            if (carPartsMap->find(currentGroupName) != carPartsMap->end()) {
+                LOG(WARNING) << "Duplicate group name: " << currentGroupName
+                             << ". using car part name as: " << currentGroupName << "_dup";
+                currentGroupName.append("_dup");
+            }
+            carPartsMap->emplace(
+                    std::make_pair(currentGroupName,
+                                   CarPart((std::vector<CarVertex>()), CarMaterial(), kMat4Identity,
+                                           std::string(), std::vector<std::string>())));
+            continue;
+        }
+
+        // no "g" case, assign it as default.
+        if (currentGroupName.empty()) {
+            currentGroupName = "default";
+            currentMtlConfig = MtlConfigParams();
+            carPartsMap->emplace(
+                    std::make_pair(currentGroupName,
+                                   CarPart((std::vector<CarVertex>()), CarMaterial(), kMat4Identity,
+                                           std::string(), std::vector<std::string>())));
+        }
+
+        if (strcmp(lineHeader, "usemtl") == 0) {
+            res = fscanf(file, "%s", lineHeader);
+
+            // If material name not found.
+            if (mtlConfigParamsMap.find(lineHeader) == mtlConfigParamsMap.end()) {
+                carPartsMap->at(currentGroupName).material = CarMaterial();
+                LOG(ERROR) << "Material not found: $0" << lineHeader;
+                return false;
+            }
+
+            currentMtlConfig = mtlConfigParamsMap[lineHeader];
+
+            carPartsMap->at(currentGroupName).material.ka = {currentMtlConfig.ka[0],
+                                                             currentMtlConfig.ka[1],
+                                                             currentMtlConfig.ka[2]};
+
+            carPartsMap->at(currentGroupName).material.kd = {currentMtlConfig.kd[0],
+                                                             currentMtlConfig.kd[1],
+                                                             currentMtlConfig.kd[2]};
+
+            carPartsMap->at(currentGroupName).material.ks = {currentMtlConfig.ks[0],
+                                                             currentMtlConfig.ks[1],
+                                                             currentMtlConfig.ks[2]};
+
+            carPartsMap->at(currentGroupName).material.d = currentMtlConfig.d;
+
+            carPartsMap->at(currentGroupName).material.textures.clear();
+
+            continue;
+        }
+
+        if (strcmp(lineHeader, "mtllib") == 0) {
+            res = fscanf(file, "%s", lineHeader);
+            mtlConfigParamsMap.clear();
+            std::string mtlFilename;
+            if (option.mtlFilename.empty()) {
+                mtlFilename = objFilename.substr(0, objFilename.find_last_of("/"));
+                mtlFilename.append("/");
+                mtlFilename.append(lineHeader);
+            } else {
+                mtlFilename = option.mtlFilename;
+            }
+            if (!ReadMtlFromFile(mtlFilename, &mtlConfigParamsMap)) {
+                LOG(ERROR) << "Parse MTL file " << mtlFilename << " failed.";
+                return false;
+            }
+            continue;
+        }
+
+        if (strcmp(lineHeader, "v") == 0) {
+            std::array<float, kNumberOfAxes> pos;
+            fscanf(file, "%f %f %f\n", &pos[option.coordinateMapping[0]],
+                   &pos[option.coordinateMapping[1]], &pos[option.coordinateMapping[2]]);
+            for (int i = 0; i < kNumberOfAxes; ++i) {
+                pos[i] *= option.scales[i];
+                pos[i] += option.offsets[i];
+            }
+            currentVertices.push_back(pos);
+        } else if (strcmp(lineHeader, "vt") == 0) {
+            std::array<float, kNumberOfAxes> texture;
+            fscanf(file, "%f %f %f\n", &texture[0], &texture[1], &texture[2]);
+            currentTextures.push_back(texture);
+        } else if (strcmp(lineHeader, "vn") == 0) {
+            std::array<float, kNumberOfAxes> normal;
+            fscanf(file, "%f %f %f\n", &normal[option.coordinateMapping[0]],
+                   &normal[option.coordinateMapping[1]], &normal[option.coordinateMapping[2]]);
+            currentNormals.push_back(normal);
+        } else if (strcmp(lineHeader, "f") == 0) {
+            int vertexId[kNumberOfVerticesPerFace];
+            int textureId[kNumberOfVerticesPerFace] = {-1, -1, -1};
+            int normalId[kNumberOfVerticesPerFace];
+
+            // Face vertices supported formats:
+            // With texture:     pos/texture/normal
+            // Without texture:  pos//normal
+
+            // Scan first vertex position.
+            int matches = fscanf(file, "%d/", &vertexId[0]);
+
+            if (matches != 1) {
+                LOG(WARNING) << "Face index error. Skipped.";
+                fgets(lineHeader, sizeof(lineHeader), file);
+                continue;
+            }
+
+            // Try scanning first two face 2 vertices with texture format present.
+            bool isTexturePresent = true;
+            matches = fscanf(file, "%d/%d %d/%d/%d", &textureId[0], &normalId[0], &vertexId[1],
+                             &textureId[1], &normalId[1]);
+
+            // If 5 matches not found, try scanning first 2 face vertices without
+            // texture format.
+            if (matches != 5) {
+                matches = fscanf(file, "/%d %d//%d", &normalId[0], &vertexId[1], &normalId[1]);
+
+                // If 3 matches not found return with error.
+                if (matches != 3) {
+                    LOG(WARNING) << "Face format not supported. Skipped.";
+                    fgets(lineHeader, sizeof(lineHeader), file);
+                    continue;
+                }
+
+                isTexturePresent = false;
+            }
+
+            // Copy first two face vertices to car vertices.
+            std::array<CarVertex, kNumberOfVerticesPerFace> carVertices;
+            CopyFaceToCarVertex(currentVertices, currentTextures, currentNormals, vertexId[0],
+                                textureId[0], normalId[0], &carVertices[0]);
+            CopyFaceToCarVertex(currentVertices, currentTextures, currentNormals, vertexId[1],
+                                textureId[1], normalId[1], &carVertices[1]);
+
+            // Add a triangle that the first two vertices make with every subsequent
+            // face vertex 3 and onwards. Note this assumes the face is a convex
+            // polygon.
+            do {
+                if (isTexturePresent) {
+                    matches = fscanf(file, " %d/%d/%d", &vertexId[2], &textureId[2], &normalId[2]);
+                    // Warn if un-expected number of matches.
+                    if (matches != 3 && matches != 0) {
+                        LOG(WARNING) << "Face matches, expected 3, read: " << matches;
+                        break;
+                    }
+                } else {
+                    // Warn if un-expected number of matches.
+                    matches = fscanf(file, " %d//%d", &vertexId[2], &normalId[2]);
+                    if (matches != 2 && matches != 0) {
+                        LOG(WARNING) << "Face matches, expected 2, read: " << matches;
+                        break;
+                    }
+                }
+
+                if (matches == 0) {
+                    break;
+                }
+
+                CopyFaceToCarVertex(currentVertices, currentTextures, currentNormals, vertexId[2],
+                                    textureId[2], normalId[2], &carVertices[2]);
+
+                carPartsMap->at(currentGroupName).vertices.push_back(carVertices[0]);
+                carPartsMap->at(currentGroupName).vertices.push_back(carVertices[1]);
+                carPartsMap->at(currentGroupName).vertices.push_back(carVertices[2]);
+
+                carVertices[1] = carVertices[2];
+            } while (true);
+
+        } else {
+            // LOG(WARNING) << "Unknown tag " << lineHeader << ". Skipped";
+            fgets(lineHeader, sizeof(lineHeader), file);
+            continue;
+        }
+    }
+
+    fclose(file);
+    return true;
+}
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace sv
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/surround_view/service-impl/ObjReader.h b/surround_view/service-impl/ObjReader.h
new file mode 100644
index 0000000..c19be14
--- /dev/null
+++ b/surround_view/service-impl/ObjReader.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#ifndef SURROUND_VIEW_SERVICE_IMPL_OBJREADER_H_
+#define SURROUND_VIEW_SERVICE_IMPL_OBJREADER_H_
+
+#include <map>
+#include <string>
+
+#include "core_lib.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace sv {
+namespace V1_0 {
+namespace implementation {
+
+using android_auto::surround_view::CarPart;
+
+// ReadObjOptions for processing obj's vertex coordinates.
+// Sequence of processing ReadObjOptions:
+// 1. coordinate_mapping
+// 2. scales
+// 3. offsets
+struct ReadObjOptions {
+    // Maps obj coordinates to the output overlay coordinate.
+    // 0 <-> x, 1 <-> y, 2 <-> z
+    // Default is {0, 1, 2}, without coordinate changes.
+    int coordinateMapping[3] = {0, 1, 2};
+
+    // scale of each coordinate (after offsets).
+    float scales[3] = {1.0f, 1.0f, 1.0f};
+
+    // offset of each coordinate (after mapping).
+    float offsets[3] = {0, 0, 0};
+
+    // Optional mtl filename. String name is obj file is used if this is empty.
+    std::string mtlFilename;
+};
+
+// Reads obj file to vector of OverlayVertex.
+// |obj_filename| is the full path and name of the obj file.
+// |car_parts_map| is a map containing all car parts.
+// Now it only supports two face formats:
+// 1. f x/x/x x/x/x x/x/x ...
+// 2. f x//x x//x x//x ...
+// b/
+bool ReadObjFromFile(const std::string& objFilename, std::map<std::string, CarPart>* carPartsMap);
+
+// Reads obj file to vector of OverlayVertex.
+// |obj_filename| is the full path and name of the obj file.
+// |option| provides optional changes on the coordinates.
+// |car_parts_map| is a map containing all car parts.
+bool ReadObjFromFile(const std::string& obFilename, const ReadObjOptions& option,
+                     std::map<std::string, CarPart>* carPartsMap);
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace sv
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
+
+#endif  // SURROUND_VIEW_SERVICE_IMPL_OBJREADER_H_
diff --git a/surround_view/service-impl/ObjReaderTests.cpp b/surround_view/service-impl/ObjReaderTests.cpp
new file mode 100644
index 0000000..9dae171
--- /dev/null
+++ b/surround_view/service-impl/ObjReaderTests.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#define LOG_TAG "ObjReaderTests"
+
+#include "ObjReader.h"
+
+#include "MtlReader.h"
+#include "core_lib.h"
+
+#include <gtest/gtest.h>
+#include <map>
+#include <string>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace sv {
+namespace V1_0 {
+namespace implementation {
+namespace {
+
+TEST(ObjParserTests, ReadCubeSuccess) {
+    std::map<std::string, CarPart> carPartsMap;
+    EXPECT_TRUE(ReadObjFromFile("/etc/automotive/sv/cube.obj", &carPartsMap));
+    EXPECT_NE(carPartsMap.size(), 0);
+}
+
+}  // namespace
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace sv
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/surround_view/service-impl/VhalHandler.cpp b/surround_view/service-impl/VhalHandler.cpp
new file mode 100644
index 0000000..f1294d9
--- /dev/null
+++ b/surround_view/service-impl/VhalHandler.cpp
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2020 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.
+ */
+#define LOG_TAG "VhalHandler"
+
+#include "VhalHandler.h"
+
+#include <chrono>
+#include <cmath>
+#include <condition_variable>
+#include <mutex>
+
+#include <android-base/logging.h>
+#include <android/hardware/automotive/vehicle/2.0/IVehicle.h>
+#include <time.h>
+#include <utils/SystemClock.h>
+#include <utils/Timers.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace sv {
+namespace V1_0 {
+namespace implementation {
+
+using vehicle::V2_0::IVehicle;
+using vehicle::V2_0::StatusCode;
+using vehicle::V2_0::VehiclePropertyType;
+using vehicle::V2_0::VehiclePropValue;
+
+bool VhalHandler::initialize(UpdateMethod updateMethod, int rate) {
+    LOG(DEBUG) << __FUNCTION__;
+    std::scoped_lock<std::mutex> lock(mAccessLock);
+
+    if (mIsInitialized) {
+        LOG(ERROR) << "Vehicle Handler is already initialized.";
+        return false;
+    }
+
+    LOG(INFO) << "Connecting to Vehicle HAL";
+    mVhalServicePtr = IVehicle::getService();
+    if (mVhalServicePtr.get() == nullptr) {
+        LOG(ERROR) << "Vehicle HAL getService failed.";
+        return false;
+    }
+
+    if (rate < 1 || rate > 100) {
+        LOG(ERROR) << "Rate must be in the range [1, 100].";
+        return false;
+    }
+
+    if (mUpdateMethod == UpdateMethod::SUBSCRIBE) {
+        LOG(ERROR) << "Update method Subscribe is not currently implemented.";
+        return false;
+    }
+
+    mUpdateMethod = updateMethod;
+    mRate = rate;
+    mIsInitialized = true;
+    mIsUpdateActive = false;
+
+    return true;
+}
+
+void VhalHandler::pollProperties() {
+    LOG(DEBUG) << "Polling thread started.";
+    while (true) {
+        nsecs_t startTime = elapsedRealtimeNano();
+
+        // Copy properties to read.
+        std::vector<VehiclePropValue> propertiesToRead;
+        int rate;
+        {
+            std::scoped_lock<std::mutex> lock(mAccessLock);
+            if (!mIsUpdateActive) {
+                LOG(DEBUG) << "Exiting polling thread.";
+                break;
+            }
+            propertiesToRead = mPropertiesToRead;
+            rate = mRate;
+        }
+
+        // Make get call for each VHAL property.
+        // Write to back property values, note lock is not needed as only this thread uses it.
+        std::vector<VehiclePropValue> vehiclePropValuesUpdated;
+        for (auto& propertyToRead : propertiesToRead) {
+            // VehiclePropValue vehiclePropValue;
+            mVhalServicePtr->get(propertyToRead,
+                                 [&vehiclePropValuesUpdated](StatusCode status,
+                                                             const VehiclePropValue& propValue) {
+                                     if (status != StatusCode::OK) {
+                                         LOG(ERROR) << "Failed to read vhal property: "
+                                                    << propValue.prop << ", with status code: "
+                                                    << static_cast<int32_t>(status);
+                                     } else {
+                                         vehiclePropValuesUpdated.push_back(propValue);
+                                     }
+                                 });
+        }
+
+        // Update property values by swapping with updated property values.
+        {
+            std::scoped_lock<std::mutex> lock(mAccessLock);
+            std::swap(mPropertyValues, vehiclePropValuesUpdated);
+        }
+
+        std::unique_lock<std::mutex> sleepLock(mPollThreadSleepMutex);
+        // Sleep to generate frames at kTargetFrameRate.
+        // rate is number of updates per seconds,
+        // Target time period between two updates in nano-seconds = (10 ^ 9) / rate.
+        const nsecs_t kTargetRateNs = std::pow(10, 9) / mRate;
+        const nsecs_t now = elapsedRealtimeNano();
+        const nsecs_t workTimeNs = now - startTime;
+        const nsecs_t sleepDurationNs = kTargetRateNs - workTimeNs;
+        if (sleepDurationNs > 0) {
+            // Sleep for sleepDurationNs or until a stop signal is received.
+            mPollThreadCondition.wait_for(sleepLock, std::chrono::nanoseconds(sleepDurationNs),
+                                          [this]() { return mPollStopSleeping; });
+        }
+    }
+}
+
+bool VhalHandler::startPropertiesUpdate() {
+    LOG(DEBUG) << __FUNCTION__;
+    std::scoped_lock<std::mutex> lock(mAccessLock);
+
+    // Check Vhal service is initialized.
+    if (!mIsInitialized) {
+        LOG(ERROR) << "VHAL handler not initialized.";
+        return false;
+    }
+
+    if (mIsUpdateActive) {
+        LOG(ERROR) << "Polling is already started.";
+        return false;
+    }
+
+    mIsUpdateActive = true;
+
+    {
+        std::scoped_lock<std::mutex> sleepLock(mPollThreadSleepMutex);
+        mPollStopSleeping = false;
+    }
+
+    // Start polling thread if updated method is GET.
+    if (mUpdateMethod == UpdateMethod::GET) {
+        mPollingThread = std::thread([this]() { pollProperties(); });
+    }
+
+    return true;
+}
+
+bool VhalHandler::setPropertiesToRead(const std::vector<VehiclePropValue>& propertiesToRead) {
+    LOG(DEBUG) << __FUNCTION__;
+    std::scoped_lock<std::mutex> lock(mAccessLock);
+
+    // Replace property ids to read.
+    mPropertiesToRead = propertiesToRead;
+
+    return true;
+}
+
+bool VhalHandler::getPropertyValues(std::vector<VehiclePropValue>* property_values) {
+    LOG(DEBUG) << __FUNCTION__;
+    std::scoped_lock<std::mutex> lock(mAccessLock);
+
+    // Check Vhal service is initialized.
+    if (!mIsInitialized) {
+        LOG(ERROR) << "VHAL handler not initialized.";
+        return false;
+    }
+
+    // Copy current property values to argument.
+    *property_values = mPropertyValues;
+
+    return true;
+}
+
+bool VhalHandler::stopPropertiesUpdate() {
+    LOG(DEBUG) << __FUNCTION__;
+    {
+        std::scoped_lock<std::mutex> lock(mAccessLock);
+
+        // Check Vhal service is initialized.
+        if (!mIsInitialized) {
+            LOG(ERROR) << "VHAL handler not initialized.";
+            return false;
+        }
+
+        if (!mIsUpdateActive) {
+            LOG(ERROR) << "Polling is already stopped.";
+            return false;
+        }
+
+        mIsUpdateActive = false;
+    }
+
+    // Wake up the polling thread.
+    {
+        std::scoped_lock<std::mutex> sleepLock(mPollThreadSleepMutex);
+        mPollStopSleeping = true;
+    }
+    mPollThreadCondition.notify_one();
+
+    // Wait for polling thread to exit.
+    mPollingThread.join();
+
+    return true;
+}
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace sv
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/surround_view/service-impl/VhalHandler.h b/surround_view/service-impl/VhalHandler.h
new file mode 100644
index 0000000..23941f0
--- /dev/null
+++ b/surround_view/service-impl/VhalHandler.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#ifndef SURROUND_VIEW_SERVICE_IMPL_VHALHANDLER_H_
+#define SURROUND_VIEW_SERVICE_IMPL_VHALHANDLER_H_
+
+#include <mutex>
+#include <thread>
+#include <vector>
+
+#include <android/hardware/automotive/vehicle/2.0/IVehicle.h>
+
+using android::sp;
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace sv {
+namespace V1_0 {
+namespace implementation {
+
+// Vhal handler cache vhal properties needed and updates them at a fixed rate.
+class VhalHandler {
+public:
+    // Enumeration for the method to use for updating the VHAL properties,
+    enum UpdateMethod {
+        // Makes a periodic get call in a polling thread.
+        // Use when VHAL implementation does not support multiple clients in subscribe calls.
+        GET = 0,
+
+        // Subscribes to the VHAL properties, to receive values periodically in a callback.
+        // Use when VHAL implementation support multiple clients in subscribe calls.
+        // NOTE: Currently not implemented.
+        SUBSCRIBE
+    };
+
+    // Empty vhal handler constructor.
+    VhalHandler() : mIsInitialized(false), mUpdateMethod(GET), mRate(0), mIsUpdateActive(false) {}
+
+    // Initializes the VHAL handler.
+    // Valid range of rate is [1, 100] Hz.
+    // For subscribe the rate must be within each properties min and maximum sampling rate.
+    // For get, higher rate may result in excessive binder calls and increased latency.
+    bool initialize(UpdateMethod updateMethod, int rate);
+
+    // List of VHAL properties to read, can include vendor specific VHAL properties.
+    // The updated method determines if properties are updated using get or subscribe calls.
+    bool setPropertiesToRead(const std::vector<vehicle::V2_0::VehiclePropValue>& propertiesToRead);
+
+    // Starts updating the VHAL properties with the specified rate.
+    bool startPropertiesUpdate();
+
+    // Gets the last updated VHAL property values.
+    // property_values is empty if startPropertiesUpdate() has not been called.
+    bool getPropertyValues(std::vector<vehicle::V2_0::VehiclePropValue>* property_values);
+
+    // Stops updating the VHAL properties.
+    // For Get method, waits for the polling thread to exit.
+    bool stopPropertiesUpdate();
+
+private:
+    // Thread function to poll properties.
+    void pollProperties();
+
+    // Pointer to VHAL service.
+    sp<vehicle::V2_0::IVehicle> mVhalServicePtr;
+
+    // Mutex for locking VHAL properties data.
+    std::mutex mAccessLock;
+
+    // Initialized parameters.
+    bool mIsInitialized;
+    UpdateMethod mUpdateMethod;
+    int mRate;
+    bool mIsUpdateActive;
+
+    // GET method related data members.
+    std::thread mPollingThread;
+    std::mutex mPollThreadSleepMutex;
+    std::condition_variable mPollThreadCondition;
+    bool mPollStopSleeping;
+
+    // List of properties to read.
+    std::vector<vehicle::V2_0::VehiclePropValue> mPropertiesToRead;
+
+    // Updated list of property values.
+    std::vector<vehicle::V2_0::VehiclePropValue> mPropertyValues;
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace sv
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
+
+#endif  // SURROUND_VIEW_SERVICE_IMPL_VHALHANDLER_H_
diff --git a/surround_view/service-impl/VhalHandlerTests.cpp b/surround_view/service-impl/VhalHandlerTests.cpp
new file mode 100644
index 0000000..2d0ef40
--- /dev/null
+++ b/surround_view/service-impl/VhalHandlerTests.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#define LOG_TAG "VhalHandlerTests"
+
+#include <android/hardware/automotive/vehicle/2.0/IVehicle.h>
+#include "VhalHandler.h"
+
+#include <gtest/gtest.h>
+#include <time.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace sv {
+namespace V1_0 {
+namespace implementation {
+namespace {
+
+void SetSamplePropertiesToRead(VhalHandler* vhalHandler) {
+    std::vector<vehicle::V2_0::VehiclePropValue> properties_to_read;
+    vehicle::V2_0::VehiclePropValue property_read;
+    property_read.prop = static_cast<int32_t>(vehicle::V2_0::VehicleProperty::INFO_MODEL);
+    properties_to_read.push_back(property_read);
+    ASSERT_TRUE(vhalHandler->setPropertiesToRead(properties_to_read));
+}
+
+TEST(VhalhandlerTests, UninitializedStartFail) {
+    VhalHandler vhalHandler;
+    ASSERT_FALSE(vhalHandler.startPropertiesUpdate());
+}
+
+TEST(VhalhandlerTests, StartStopSuccess) {
+    VhalHandler vhalHandler;
+    ASSERT_TRUE(vhalHandler.initialize(VhalHandler::UpdateMethod::GET, 10));
+    SetSamplePropertiesToRead(&vhalHandler);
+    ASSERT_TRUE(vhalHandler.startPropertiesUpdate());
+    ASSERT_TRUE(vhalHandler.stopPropertiesUpdate());
+}
+
+TEST(VhalhandlerTests, StopTwiceFail) {
+    VhalHandler vhalHandler;
+    ASSERT_TRUE(vhalHandler.initialize(VhalHandler::UpdateMethod::GET, 10));
+    SetSamplePropertiesToRead(&vhalHandler);
+    ASSERT_TRUE(vhalHandler.startPropertiesUpdate());
+    ASSERT_TRUE(vhalHandler.stopPropertiesUpdate());
+    ASSERT_FALSE(vhalHandler.stopPropertiesUpdate());
+}
+
+TEST(VhalhandlerTests, NoStartFail) {
+    VhalHandler vhalHandler;
+    ASSERT_TRUE(vhalHandler.initialize(VhalHandler::UpdateMethod::GET, 10));
+    SetSamplePropertiesToRead(&vhalHandler);
+    ASSERT_FALSE(vhalHandler.stopPropertiesUpdate());
+}
+
+TEST(VhalhandlerTests, StartAgainSuccess) {
+    VhalHandler vhalHandler;
+    ASSERT_TRUE(vhalHandler.initialize(VhalHandler::UpdateMethod::GET, 10));
+    SetSamplePropertiesToRead(&vhalHandler);
+    ASSERT_TRUE(vhalHandler.startPropertiesUpdate());
+    ASSERT_TRUE(vhalHandler.stopPropertiesUpdate());
+    ASSERT_TRUE(vhalHandler.startPropertiesUpdate());
+    ASSERT_TRUE(vhalHandler.stopPropertiesUpdate());
+}
+
+TEST(VhalhandlerTests, GetMethodSuccess) {
+    VhalHandler vhalHandler;
+    ASSERT_TRUE(vhalHandler.initialize(VhalHandler::UpdateMethod::GET, 10));
+
+    SetSamplePropertiesToRead(&vhalHandler);
+
+    ASSERT_TRUE(vhalHandler.startPropertiesUpdate());
+    sleep(1);
+    std::vector<vehicle::V2_0::VehiclePropValue> property_values;
+    EXPECT_TRUE(vhalHandler.getPropertyValues(&property_values));
+    EXPECT_EQ(property_values.size(), 1);
+
+    EXPECT_TRUE(vhalHandler.stopPropertiesUpdate());
+}
+
+}  // namespace
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace sv
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/surround_view/service-impl/lib/arm64/libcore_lib_shared.so b/surround_view/service-impl/lib/arm64/libcore_lib_shared.so
index 2421d41..3988762 100755
--- a/surround_view/service-impl/lib/arm64/libcore_lib_shared.so
+++ b/surround_view/service-impl/lib/arm64/libcore_lib_shared.so
Binary files differ
diff --git a/surround_view/service-impl/lib/x86-64/libcore_lib_shared.so b/surround_view/service-impl/lib/x86-64/libcore_lib_shared.so
index 34d1f6a..e9f94db 100755
--- a/surround_view/service-impl/lib/x86-64/libcore_lib_shared.so
+++ b/surround_view/service-impl/lib/x86-64/libcore_lib_shared.so
Binary files differ
diff --git a/surround_view/service-impl/lib/x86/libcore_lib_shared.so b/surround_view/service-impl/lib/x86/libcore_lib_shared.so
index c6ba2b6..b82d025 100755
--- a/surround_view/service-impl/lib/x86/libcore_lib_shared.so
+++ b/surround_view/service-impl/lib/x86/libcore_lib_shared.so
Binary files differ
diff --git a/surround_view/service-impl/test_data/cube.mtl b/surround_view/service-impl/test_data/cube.mtl
new file mode 100644
index 0000000..2f4e865
--- /dev/null
+++ b/surround_view/service-impl/test_data/cube.mtl
@@ -0,0 +1,5 @@
+newmtl flatwhite
+d 1.0000
+illum 1
+Ka 0.5000 0.5000 0.5000
+Kd 1.0000 1.0000 1.0000
diff --git a/surround_view/service-impl/test_data/cube.obj b/surround_view/service-impl/test_data/cube.obj
new file mode 100644
index 0000000..60ae1b2
--- /dev/null
+++ b/surround_view/service-impl/test_data/cube.obj
@@ -0,0 +1,32 @@
+mtllib cube.mtl
+g cube
+ 
+v 0.0 0.0 0.0
+v 0.0 0.0 1.0
+v 0.0 1.0 0.0
+v 0.0 1.0 1.0
+v 1.0 0.0 0.0
+v 1.0 0.0 1.0
+v 1.0 1.0 0.0
+v 1.0 1.0 1.0
+
+vn 0.0 0.0 1.0
+vn 0.0 0.0 -1.0
+vn 0.0 1.0 0.0
+vn 0.0 -1.0 0.0
+vn 1.0 0.0 0.0
+vn -1.0 0.0 0.0
+usemtl flatwhite 
+f 1//2 7//2 5//2
+f 1//2 3//2 7//2 
+f 1//6 4//6 3//6 
+f 1//6 2//6 4//6 
+f 3//3 8//3 7//3 
+f 3//3 4//3 8//3 
+f 5//5 7//5 8//5 
+f 5//5 8//5 6//5 
+f 1//4 5//4 6//4 
+f 1//4 6//4 2//4 
+f 2//1 6//1 8//1 
+f 2//1 8//1 4//1 
+
diff --git a/tests/CarDeveloperOptions/res/values-b+sr+Latn/strings.xml b/tests/CarDeveloperOptions/res/values-b+sr+Latn/strings.xml
index 8861afb..4c69e86 100644
--- a/tests/CarDeveloperOptions/res/values-b+sr+Latn/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-b+sr+Latn/strings.xml
@@ -3190,7 +3190,7 @@
     <string name="other_sound_settings" msgid="5250376066099818676">"Drugi zvukovi"</string>
     <string name="dial_pad_tones_title" msgid="8877212139988655769">"Tonovi numeričke tastature"</string>
     <string name="screen_locking_sounds_title" msgid="4407110895465866809">"Zvukovi zaključavanja ekrana"</string>
-    <string name="charging_sounds_title" msgid="5070437987230894287">"Menjajući zvuci i vibracija"</string>
+    <string name="charging_sounds_title" msgid="5070437987230894287">"Zvukovi i vibracija punjenja"</string>
     <string name="docking_sounds_title" msgid="2573137471605541366">"Zvukovi montiranja"</string>
     <string name="touch_sounds_title" msgid="165237488496165652">"Zvukovi pri dodiru"</string>
     <string name="vibrate_on_touch_title" msgid="6360155469279157684">"Vibracija pri dodiru"</string>
diff --git a/tests/CarDeveloperOptions/res/values-be/strings.xml b/tests/CarDeveloperOptions/res/values-be/strings.xml
index ec27b37..8c34666 100644
--- a/tests/CarDeveloperOptions/res/values-be/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-be/strings.xml
@@ -3394,10 +3394,10 @@
     <string name="hide_silent_icons_title" msgid="1070905516921542662">"Хаваць значкі стану для апавяшчэнняў без гуку"</string>
     <string name="hide_silent_icons_summary" msgid="2624346914488256888">"Хаваць значкі для апавяшчэнняў без гуку на панэлі стану"</string>
     <string name="notification_badging_title" msgid="6311699476970264712">"Паказваць значкі апавяшчэнняў"</string>
-    <string name="notification_bubbles_title" msgid="9196562435741861317">"Дыялогі"</string>
+    <string name="notification_bubbles_title" msgid="9196562435741861317">"Усплывальныя апавяшчэнні"</string>
     <string name="notification_bubbles_summary" msgid="4624512775901949578">"Адусюль атрымлівайце хуткі доступ да змесціва праграмы з дапамогай зменлівых спалучэнняў клавіш"</string>
     <string name="bubbles_feature_education" msgid="8979109826818881018">"Некаторыя апавяшчэнні і іншае змесціва могуць паказвацца на экране ў выглядзе дыялогаў. Каб адкрыць дыялог, націсніце на яго. Каб закрыць дыялог, перацягніце яго ўніз экрана."</string>
-    <string name="bubbles_app_toggle_title" msgid="6401217027603326439">"Дыялогі"</string>
+    <string name="bubbles_app_toggle_title" msgid="6401217027603326439">"Усплывальныя апавяшчэнні"</string>
     <string name="bubbles_app_toggle_summary" msgid="7707611139796553855">"Дазваляе праграме \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" паказваць некаторыя апавяшчэнні ў выглядзе дыялогаў"</string>
     <string name="bubbles_feature_disabled_dialog_title" msgid="3375452386012079293">"Уключыце дыялогі"</string>
     <string name="bubbles_feature_disabled_dialog_text" msgid="326945485806386477">"Каб уключыць дыялогі для гэтай праграмы, уключыце дыялогі для прылады. Гэта паўплывае на іншыя праграмы, у якіх вы раней уключалі дыялогі."</string>
diff --git a/tests/CarDeveloperOptions/res/values-bn/strings.xml b/tests/CarDeveloperOptions/res/values-bn/strings.xml
index 78b74e4..c86b976 100644
--- a/tests/CarDeveloperOptions/res/values-bn/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-bn/strings.xml
@@ -3041,7 +3041,7 @@
     <string name="connected_devices_dashboard_no_driving_mode_summary" msgid="3524409078596318803">"ব্লুটুথ, এনএফসি"</string>
     <string name="connected_devices_dashboard_no_driving_mode_no_nfc_summary" msgid="7881286613528299400">"ব্লুটুথ"</string>
     <string name="app_and_notification_dashboard_title" msgid="8448096608058843730">"অ্যাপ ও বিজ্ঞপ্তি"</string>
-    <string name="app_and_notification_dashboard_summary" msgid="4165181440955038145">"অ্যাসিস্ট্যান্ট, সাম্প্রতিক অ্যাপ, ডিফল্ট অ্যাপ"</string>
+    <string name="app_and_notification_dashboard_summary" msgid="4165181440955038145">"Assistant, সাম্প্রতিক অ্যাপ, ডিফল্ট অ্যাপ"</string>
     <string name="notification_settings_work_profile" msgid="7190550347842400029">"কাজের প্রোফাইলে অ্যাপের জন্য বিজ্ঞপ্তি অ্যাক্সেস উপলভ্য নয়।"</string>
     <string name="account_dashboard_title" msgid="4734300939532555885">"অ্যাকাউন্ট"</string>
     <string name="account_dashboard_default_summary" msgid="6822549669771936206">"কোনও অ্যাকাউন্ট যোগ করা হয়নি"</string>
@@ -4325,7 +4325,7 @@
     <string name="battery_suggestion_summary" msgid="2669070349482656490"></string>
     <string name="gesture_prevent_ringing_screen_title" msgid="4173494225145223638">"রিং হওয়া বন্ধ করুন"</string>
     <string name="gesture_prevent_ringing_title" msgid="8827963588425673557">"রিং হওয়া বন্ধ করতে পাওয়ার ও ভলিউম বাড়ানোর বোতাম একসাথে প্রেস করুন"</string>
-    <string name="gesture_prevent_ringing_sound_title" msgid="8642330448721033641">"রিং হওয়া আটকানোর শর্টকাট"</string>
+    <string name="gesture_prevent_ringing_sound_title" msgid="8642330448721033641">"রিংয়ের আরওয়াজ আটকানোর শর্টকাট"</string>
     <string name="prevent_ringing_option_vibrate" msgid="6456505293904544108">"ভাইব্রেশন হতে দিন"</string>
     <string name="prevent_ringing_option_mute" msgid="53662688921253613">"মিউট করুন"</string>
     <string name="prevent_ringing_option_none" msgid="1450985763137666231">"যেমন আছে থাক"</string>
diff --git a/tests/CarDeveloperOptions/res/values-bs/strings.xml b/tests/CarDeveloperOptions/res/values-bs/strings.xml
index ba804e6..0bee24c 100644
--- a/tests/CarDeveloperOptions/res/values-bs/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-bs/strings.xml
@@ -3190,9 +3190,9 @@
     <string name="other_sound_settings" msgid="5250376066099818676">"Drugi zvukovi"</string>
     <string name="dial_pad_tones_title" msgid="8877212139988655769">"Tonovi tastature telefona"</string>
     <string name="screen_locking_sounds_title" msgid="4407110895465866809">"Zvukovi zaključavanja ekrana"</string>
-    <string name="charging_sounds_title" msgid="5070437987230894287">"Zvukovi/vibracija pri punjenju"</string>
+    <string name="charging_sounds_title" msgid="5070437987230894287">"Zvukovi/vibracija prilikom punjenja"</string>
     <string name="docking_sounds_title" msgid="2573137471605541366">"Zvukovi priključne stanice"</string>
-    <string name="touch_sounds_title" msgid="165237488496165652">"Zvukovi pri dodiru"</string>
+    <string name="touch_sounds_title" msgid="165237488496165652">"Zvukovi dodira"</string>
     <string name="vibrate_on_touch_title" msgid="6360155469279157684">"Vibracija pri dodiru"</string>
     <string name="vibrate_on_touch_summary" msgid="5504424764028676043">"Haptičke povratne informacije za dodir, tastaturu i drugo"</string>
     <string name="dock_audio_media_title" msgid="1859521680502040781">"Zvučnik priključne stanice reprodukuje zvuk"</string>
@@ -3470,8 +3470,8 @@
     <string name="notification_content_block_summary" msgid="2743896875255591743">"Nikad ne prikazuj obavještenja u nijansi ili na perifernim uređajima"</string>
     <string name="notification_badge_title" msgid="8989086619255666442">"Dozvoli tačku za obavještenja"</string>
     <string name="notification_channel_badge_title" msgid="8228215248332054612">"Prikaži tačku za obavještenja"</string>
-    <string name="app_notification_override_dnd_title" msgid="1757042206738172601">"Zamijeni način rada Ne ometaj"</string>
-    <string name="app_notification_override_dnd_summary" msgid="3152957611171210980">"Dozvolite da se obavještenja nastave pojavljivati kada je uključen način rada Ne ometaj"</string>
+    <string name="app_notification_override_dnd_title" msgid="1757042206738172601">"Zanemari način rada Ne ometaj"</string>
+    <string name="app_notification_override_dnd_summary" msgid="3152957611171210980">"Dozvoli da se obavještenja nastave pojavljivati kada je uključen način rada Ne ometaj"</string>
     <string name="app_notification_visibility_override_title" msgid="2349335170165637672">"Na zaključavanju ekrana"</string>
     <string name="app_notification_row_banned" msgid="2079325338122151677">"Blokirano"</string>
     <string name="app_notification_row_priority" msgid="432299064888787236">"Prioritetna"</string>
@@ -3553,7 +3553,7 @@
     <string name="zen_mode_media" msgid="3701280649874724055">"Reproduciraj zvukove medija"</string>
     <string name="zen_mode_media_list" msgid="509327580522287125">"mediji"</string>
     <string name="zen_mode_system" msgid="597437265986355038">"Dozvoli zvukove dodira"</string>
-    <string name="zen_mode_system_list" msgid="480192458506838077">"zvuci dodira"</string>
+    <string name="zen_mode_system_list" msgid="480192458506838077">"zvukovi dodira"</string>
     <string name="zen_mode_reminders" msgid="7560664194610054038">"Dozvoli podsjetnike"</string>
     <string name="zen_mode_reminders_list" msgid="7347061314032326677">"podsjetnici"</string>
     <string name="zen_mode_events" msgid="5784076928339534984">"Dozvoli događaje"</string>
diff --git a/tests/CarDeveloperOptions/res/values-ca/strings.xml b/tests/CarDeveloperOptions/res/values-ca/strings.xml
index 60f9de3..29423a2 100644
--- a/tests/CarDeveloperOptions/res/values-ca/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-ca/strings.xml
@@ -875,7 +875,7 @@
     <string name="wifi_cellular_data_fallback_title" msgid="5067241930716252665">"Canvia automàticament a dades mòbils"</string>
     <string name="wifi_cellular_data_fallback_summary" msgid="2721467405851519769">"Utilitza dades mòbils quan la Wi-Fi no tingui accés a Internet. És possible que s\'hi apliquin càrrecs per ús de dades."</string>
     <string name="wifi_add_network" msgid="4094957940791876640">"Afegeix una xarxa"</string>
-    <string name="wifi_configure_settings_preference_title" msgid="2678534679408777268">"Preferències de la Wi‑Fi"</string>
+    <string name="wifi_configure_settings_preference_title" msgid="2678534679408777268">"Preferències de Wi‑Fi"</string>
     <string name="wifi_configure_settings_preference_summary_wakeup_on" msgid="5714892572614655675">"La Wi‑Fi es torna a connectar automàticament"</string>
     <string name="wifi_configure_settings_preference_summary_wakeup_off" msgid="286904094152909651">"La Wi‑Fi no es torna a activar automàticament"</string>
     <string name="wifi_access_points" msgid="1647976498906871869">"Xarxes Wi-Fi"</string>
@@ -3548,7 +3548,7 @@
     <string name="zen_mode_screen_off" msgid="84211490206459038">"Quan la pantalla estigui apagada"</string>
     <string name="zen_mode_screen_off_summary" msgid="8592179073243001267">"Permet que les notificacions silenciades pel mode No molestis encenguin la pantalla i facin parpellejar el llum"</string>
     <string name="zen_mode_screen_off_summary_no_led" msgid="7255874108150630145">"Permet que les notificacions silenciades pel mode No molestis encenguin la pantalla"</string>
-    <string name="notification_app_settings_button" msgid="3651180424198580907">"Configuració de les notificacions"</string>
+    <string name="notification_app_settings_button" msgid="3651180424198580907">"Configuració de notificacions"</string>
     <string name="suggestion_button_text" msgid="5783566542423813847">"D\'acord"</string>
     <string name="device_feedback" msgid="4042352891448769818">"Envia suggeriments sobre el dispositiu"</string>
     <string name="restr_pin_enter_admin_pin" msgid="8577847751493521230">"Introdueix el PIN d\'administrador"</string>
diff --git a/tests/CarDeveloperOptions/res/values-es/strings.xml b/tests/CarDeveloperOptions/res/values-es/strings.xml
index eb24e4b..a7a06e2 100644
--- a/tests/CarDeveloperOptions/res/values-es/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-es/strings.xml
@@ -3140,7 +3140,7 @@
     <string name="alarm_volume_option_title" msgid="3184076022438477047">"Volumen de alarma"</string>
     <string name="ring_volume_option_title" msgid="2038924918468372264">"Volumen del tono"</string>
     <string name="notification_volume_option_title" msgid="1358512611511348260">"Volumen de notificaciones"</string>
-    <string name="ringtone_title" msgid="1409086028485922583">"Tono del teléfono"</string>
+    <string name="ringtone_title" msgid="1409086028485922583">"Tono de llamada del teléfono"</string>
     <string name="notification_ringtone_title" msgid="2932960620843976285">"Sonido de notificación predeterminado"</string>
     <string name="notification_unknown_sound_title" msgid="8043718667804838398">"Sonido proporcionado de app"</string>
     <string name="notification_sound_default" msgid="2664544380802426260">"Sonido de notificación predeterminado"</string>
@@ -3422,7 +3422,7 @@
     <string name="notification_content_block_summary" msgid="2743896875255591743">"No mostrar nunca notificaciones en el panel de notificaciones ni en dispositivos periféricos"</string>
     <string name="notification_badge_title" msgid="8989086619255666442">"Permitir burbuja de notificación"</string>
     <string name="notification_channel_badge_title" msgid="8228215248332054612">"Mostrar burbuja de notificación"</string>
-    <string name="app_notification_override_dnd_title" msgid="1757042206738172601">"Omitir No molestar"</string>
+    <string name="app_notification_override_dnd_title" msgid="1757042206738172601">"Ignorar modo No molestar"</string>
     <string name="app_notification_override_dnd_summary" msgid="3152957611171210980">"Permitir notificaciones cuando el modo No molestar esté activado"</string>
     <string name="app_notification_visibility_override_title" msgid="2349335170165637672">"En la pantalla de bloqueo"</string>
     <string name="app_notification_row_banned" msgid="2079325338122151677">"Bloqueada"</string>
diff --git a/tests/CarDeveloperOptions/res/values-fa/arrays.xml b/tests/CarDeveloperOptions/res/values-fa/arrays.xml
index 94410d3..2fcb737 100644
--- a/tests/CarDeveloperOptions/res/values-fa/arrays.xml
+++ b/tests/CarDeveloperOptions/res/values-fa/arrays.xml
@@ -111,7 +111,7 @@
     <item msgid="8247986727324120082">"۲ دقیقه"</item>
     <item msgid="2759776603549270587">"۵ دقیقه"</item>
     <item msgid="167772676068860015">"۱ ساعت"</item>
-    <item msgid="5985477119043628504">"بدون مهلت زمانی"</item>
+    <item msgid="5985477119043628504">"بدون درنگ"</item>
   </string-array>
   <string-array name="bluetooth_max_connected_audio_devices">
     <item msgid="3800257971619063588">"استفاده از پیش‌فرض سیستم: <xliff:g id="DEFAULT_BLUETOOTH_MAX_CONNECTED_AUDIO_DEVICES">%1$d</xliff:g>"</item>
@@ -415,7 +415,7 @@
     <item msgid="8754480102834556765">"آماده سازی..."</item>
     <item msgid="3351334355574270250">"در حال اتصال..."</item>
     <item msgid="8303882153995748352">"متصل"</item>
-    <item msgid="9135049670787351881">"وقفه زمانی"</item>
+    <item msgid="9135049670787351881">"درنگ"</item>
     <item msgid="2124868417182583926">"ناموفق"</item>
   </string-array>
   <string-array name="security_settings_premium_sms_values">
diff --git a/tests/CarDeveloperOptions/res/values-fa/strings.xml b/tests/CarDeveloperOptions/res/values-fa/strings.xml
index c715326..48e902e 100644
--- a/tests/CarDeveloperOptions/res/values-fa/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-fa/strings.xml
@@ -412,7 +412,7 @@
     <string name="face_intro_error_unknown" msgid="3241592604198351134">"چهره بیشتری نمی‌توان اضافه کرد"</string>
     <string name="security_settings_face_enroll_error_dialog_title" msgid="3933492758701563051">"ثبت انجام نشد"</string>
     <string name="security_settings_face_enroll_dialog_ok" msgid="1078348922734845090">"تأیید"</string>
-    <string name="security_settings_face_enroll_error_timeout_dialog_message" msgid="4917894418448325405">"مهلت زمانی ثبت چهره به پایان رسید. دوباره امتحان کنید."</string>
+    <string name="security_settings_face_enroll_error_timeout_dialog_message" msgid="4917894418448325405">"درنگ ثبت چهره به پایان رسید. دوباره امتحان کنید."</string>
     <string name="security_settings_face_enroll_error_generic_dialog_message" msgid="5160473187142616862">"ثبت چهره کار نکرد."</string>
     <string name="security_settings_face_enroll_finish_title" msgid="6800717857394410769">"همه چیز تنظیم شد. خوب به نظر می‌رسد."</string>
     <string name="security_settings_face_enroll_done" msgid="5409739233373490971">"تمام"</string>
@@ -488,7 +488,7 @@
     <string name="security_settings_fingerprint_enroll_touch_dialog_title" msgid="7410398793283818609">"اووه، آن حسگر نیست"</string>
     <string name="security_settings_fingerprint_enroll_touch_dialog_message" msgid="7192100314788868883">"با استفاده از انگشت اشاره، حسگر را در  پشت تلفن لمس کنید."</string>
     <string name="security_settings_fingerprint_enroll_error_dialog_title" msgid="1415709674142168770">"ثبت انجام نشد"</string>
-    <string name="security_settings_fingerprint_enroll_error_timeout_dialog_message" msgid="498951203761192366">"مهلت زمانی ثبت اثر انگشت به پایان رسید. دوباره امتحان کنید."</string>
+    <string name="security_settings_fingerprint_enroll_error_timeout_dialog_message" msgid="498951203761192366">"درنگ ثبت اثر انگشت به پایان رسید. دوباره امتحان کنید."</string>
     <string name="security_settings_fingerprint_enroll_error_generic_dialog_message" msgid="7896295829530444810">"ثبت اثر انگشت کار نمی‌کند. دوباره امتحان کنید یا از انگشت دیگری استفاده کنید."</string>
     <string name="fingerprint_enroll_button_add" msgid="6335782936874996629">"افزودن مورد دیگر"</string>
     <string name="fingerprint_enroll_button_next" msgid="6419214079104413695">"بعدی"</string>
@@ -3112,11 +3112,11 @@
     <string name="keywords_face_settings" msgid="4117345666006836599">"چهره"</string>
     <string name="keywords_fingerprint_settings" msgid="902902368701134163">"اثر انگشت، افزودن اثر انگشت"</string>
     <string name="keywords_display_auto_brightness" msgid="1810596220466483996">"تار کردن صفحه‌نمایش، صفحه لمسی، باتری، روشنایی هوشمند، روشنایی پویا"</string>
-    <string name="keywords_display_adaptive_sleep" msgid="1695357782432822811">"کم‌نور کردن صفحه، خواب، باتری، مهلت زمانی، توجه، نمایشگر، صفحه، بی‌فعالیتی"</string>
+    <string name="keywords_display_adaptive_sleep" msgid="1695357782432822811">"کم‌نور کردن صفحه، خواب، باتری، درنگ، توجه، نمایشگر، صفحه، بی‌فعالیتی"</string>
     <string name="keywords_auto_rotate" msgid="4320791369951647513">"چرخاندن، چرخش، چرخش، پرتره، منظره، جهت، عمودی، افقی"</string>
     <string name="keywords_system_update_settings" msgid="4419971277998986067">"ارتقا دادن، Android"</string>
     <string name="keywords_zen_mode_settings" msgid="4103819458182535493">"«مزاحم نشوید»، زمان‌بندی، اعلان‌ها، مسدود کردن، سکوت، لرزش، خواب، کار، کانونی کردن، صدا، صامت کردن، روز، روز هفته، آخر هفته، شب‌های طول هفته، رویداد"</string>
-    <string name="keywords_screen_timeout" msgid="4328381362313993666">"صفحه نمایش، زمان قفل شدن، مهلت زمانی، صفحه درحالت قفل"</string>
+    <string name="keywords_screen_timeout" msgid="4328381362313993666">"صفحه نمایش، زمان قفل شدن، درنگ، صفحه درحالت قفل"</string>
     <string name="keywords_storage_settings" msgid="6422454520424236476">"حافظه، حافظه پنهان، داده، حذف، پاک کردن، آزاد، فضا"</string>
     <string name="keywords_bluetooth_settings" msgid="1152229891590622822">"متصل، دستگاه، هدفون‌ها، هدست‌ها، بلندگو، بی‌سیم، مرتبط‌سازی، هدفون‌های توگوشی، موسیقی، رسانه"</string>
     <string name="keywords_wallpaper" msgid="7665778626293643625">"پس‌زمینه، صفحه نمایش، صفحه در حالت قفل، طرح زمینه"</string>
diff --git a/tests/CarDeveloperOptions/res/values-fi/strings.xml b/tests/CarDeveloperOptions/res/values-fi/strings.xml
index 61588ac..659ba8a 100644
--- a/tests/CarDeveloperOptions/res/values-fi/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-fi/strings.xml
@@ -246,7 +246,7 @@
     <string name="band_mode_set" msgid="4962130364076526789">"Aseta"</string>
     <string name="band_mode_failed" msgid="8350123391471974137">"Epäonnistui"</string>
     <string name="band_mode_succeeded" msgid="5516613616395402809">"Onnistui"</string>
-    <string name="sdcard_changes_instructions" msgid="4138217393448114001">"Muutokset tulevat voimaan, kun USB-kaapeli kytketään uudelleen."</string>
+    <string name="sdcard_changes_instructions" msgid="4138217393448114001">"Muutokset tulevat voimaan, kun USB-johto kytketään uudelleen."</string>
     <string name="sdcard_settings_screen_mass_storage_text" msgid="7486030250999007641">"Ota USB-massamuisti käyttöön"</string>
     <string name="sdcard_settings_total_bytes_label" msgid="6461741874400909157">"Tavuja yhteensä:"</string>
     <string name="sdcard_settings_not_present_status" product="nosdcard" msgid="5419085128792417589">"USB-tallennustila ei käytössä."</string>
@@ -849,7 +849,7 @@
     <string name="wifi_notify_open_networks" msgid="4782239203624619655">"Avaa verkkoilmoitus"</string>
     <string name="wifi_notify_open_networks_summary" msgid="1383681260705466715">"Ilmoita, kun käytettävissä on laadukkaita julkisia verkkoja."</string>
     <string name="wifi_wakeup" msgid="4963732992164721548">"Laita Wi-Fi päälle automaattisesti"</string>
-    <string name="wifi_wakeup_summary" msgid="1152699417411690">"Wi-Fi laitetaan automaattisesti päälle, kun lähistöllä on kotiverkkosi tai muita laadukkaita tallennettuja verkkoja."</string>
+    <string name="wifi_wakeup_summary" msgid="1152699417411690">"Wi-Fi laitetaan automaattisesti päälle, kun lähistöllä on kotiverkkosi tai muita laadukkaita tallennettuja verkkoja"</string>
     <string name="wifi_wakeup_summary_no_location" msgid="3007457288587966962">"Ei käytettävissä, koska sijainti on poistettu käytöstä. Ota "<annotation id="link">"sijainti"</annotation>" käyttöön."</string>
     <string name="wifi_wakeup_summary_scanning_disabled" msgid="6820040651529910914">"Ei käytettävissä, koska Wi‑Fi-haku on pois päältä."</string>
     <string name="wifi_wakeup_summary_scoring_disabled" msgid="7067018832237903151">"Valitse verkon arviointipalvelu, jotta voit käyttää tätä."</string>
diff --git a/tests/CarDeveloperOptions/res/values-gl/strings.xml b/tests/CarDeveloperOptions/res/values-gl/strings.xml
index d8f5367..51e5d0c 100644
--- a/tests/CarDeveloperOptions/res/values-gl/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-gl/strings.xml
@@ -4477,7 +4477,7 @@
     <string name="contextual_card_dismiss_confirm_message" msgid="2352465079667730312">"Queres eliminar esta suxestión?"</string>
     <string name="contextual_card_removed_message" msgid="4047307820743366876">"Quitouse a suxestión"</string>
     <string name="contextual_card_undo_dismissal_text" msgid="5009245286931852012">"Desfacer"</string>
-    <string name="low_storage_summary" msgid="4562224870189133400">"Queda pouco espazo. Utilizado: <xliff:g id="PERCENTAGE">%1$s</xliff:g>. Libre: <xliff:g id="FREE_SPACE">%2$s</xliff:g>"</string>
+    <string name="low_storage_summary" msgid="4562224870189133400">"Queda pouco espazo. En uso: <xliff:g id="PERCENTAGE">%1$s</xliff:g>. Libre: <xliff:g id="FREE_SPACE">%2$s</xliff:g>"</string>
     <string name="contextual_card_feedback_send" msgid="8698649023854350623">"Enviar comentarios"</string>
     <string name="contextual_card_feedback_confirm_message" msgid="3987973028353264878">"Queres indicarnos a túa opinión sobre esta suxestión?"</string>
     <string name="copyable_slice_toast" msgid="1357518174923789947">"<xliff:g id="COPY_CONTENT">%1$s</xliff:g>: copiouse no portapapeis."</string>
diff --git a/tests/CarDeveloperOptions/res/values-gu/strings.xml b/tests/CarDeveloperOptions/res/values-gu/strings.xml
index 398c56d..49de15b 100644
--- a/tests/CarDeveloperOptions/res/values-gu/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-gu/strings.xml
@@ -3041,7 +3041,7 @@
     <string name="connected_devices_dashboard_no_driving_mode_summary" msgid="3524409078596318803">"બ્લૂટૂથ, NFC"</string>
     <string name="connected_devices_dashboard_no_driving_mode_no_nfc_summary" msgid="7881286613528299400">"બ્લૂટૂથ"</string>
     <string name="app_and_notification_dashboard_title" msgid="8448096608058843730">"ઍપ્લિકેશનો અને નોટિફિકેશન"</string>
-    <string name="app_and_notification_dashboard_summary" msgid="4165181440955038145">"આસિસ્ટંટ, તાજેતરની ઍપ, ડિફૉલ્ટ ઍપ"</string>
+    <string name="app_and_notification_dashboard_summary" msgid="4165181440955038145">"Assistant, તાજેતરની ઍપ, ડિફૉલ્ટ ઍપ"</string>
     <string name="notification_settings_work_profile" msgid="7190550347842400029">"કાર્યાલયની પ્રોફાઇલમાં ઍપ માટે નોટિફિકેશન ઍક્સેસ ઉપલબ્ધ નથી."</string>
     <string name="account_dashboard_title" msgid="4734300939532555885">"એકાઉન્ટ"</string>
     <string name="account_dashboard_default_summary" msgid="6822549669771936206">"કોઈ એકાઉન્ટ ઉમેરવામાં આવ્યાં નથી"</string>
diff --git a/tests/CarDeveloperOptions/res/values-hy/strings.xml b/tests/CarDeveloperOptions/res/values-hy/strings.xml
index 0154721..5d6c540 100644
--- a/tests/CarDeveloperOptions/res/values-hy/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-hy/strings.xml
@@ -849,7 +849,7 @@
     <string name="wifi_notify_open_networks" msgid="4782239203624619655">"Ծանուցումներ բաց ցանցերի մասին"</string>
     <string name="wifi_notify_open_networks_summary" msgid="1383681260705466715">"Տեղեկացնել լավ ազդանշանով բաց ցանցերի հասանելիության մասին"</string>
     <string name="wifi_wakeup" msgid="4963732992164721548">"Ավտոմատ միացնել Wi‑Fi-ը"</string>
-    <string name="wifi_wakeup_summary" msgid="1152699417411690">"Wi‑Fi-ը կրկին կմիանա պահված լավ ազդանշանով պահված ցանցերի, օրինակ, ձեր տան ցանցի մոտակայքում"</string>
+    <string name="wifi_wakeup_summary" msgid="1152699417411690">"Wi‑Fi-ը կրկին կմիանա լավ ազդանշանով պահված ցանցերի, օրինակ, ձեր տան ցանցի մոտակայքում"</string>
     <string name="wifi_wakeup_summary_no_location" msgid="3007457288587966962">"Անհասանելի է, քանի որ տեղորոշումն անջատված է։ Միացրեք "<annotation id="link">"տեղորոշումը"</annotation>"։"</string>
     <string name="wifi_wakeup_summary_scanning_disabled" msgid="6820040651529910914">"Անհասանելի է, քանի որ Wi‑Fi ցանցերի որոնումն անջատված է"</string>
     <string name="wifi_wakeup_summary_scoring_disabled" msgid="7067018832237903151">"Օգտագործելու համար ընտրեք ցանցի վարկանիշի մատակարարը"</string>
diff --git a/tests/CarDeveloperOptions/res/values-in/strings.xml b/tests/CarDeveloperOptions/res/values-in/strings.xml
index 6ada981..307e232 100644
--- a/tests/CarDeveloperOptions/res/values-in/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-in/strings.xml
@@ -2862,7 +2862,7 @@
     <string name="user_summary_managed_profile_not_set_up" msgid="3032986082684011281">"Tidak disiapkan - Profil kerja"</string>
     <string name="user_admin" msgid="805802526361071709">"Admin"</string>
     <string name="user_you" msgid="8212549708652717106">"Anda (<xliff:g id="NAME">%s</xliff:g>)"</string>
-    <string name="user_nickname" msgid="1088216221559125529">"Nama julukan"</string>
+    <string name="user_nickname" msgid="1088216221559125529">"Nama panggilan"</string>
     <string name="user_add_user_type_title" msgid="8672326434351387845">"Tambahkan"</string>
     <string name="user_add_max_count" msgid="4524573950126500416">"Anda dapat menambahkan maksimal <xliff:g id="USER_COUNT">%1$d</xliff:g> pengguna"</string>
     <string name="user_add_user_item_summary" msgid="6114355152711455716">"Pengguna memiliki aplikasi dan konten mereka sendiri"</string>
@@ -3035,7 +3035,7 @@
     <string name="network_dashboard_summary_mobile" msgid="5560545061217580626">"seluler"</string>
     <string name="network_dashboard_summary_data_usage" msgid="4695629715072542102">"penggunaan kuota"</string>
     <string name="network_dashboard_summary_hotspot" msgid="3928610802321995214">"hotspot"</string>
-    <string name="connected_devices_dashboard_title" msgid="7795222675849060444">"Perangkat tersambung"</string>
+    <string name="connected_devices_dashboard_title" msgid="7795222675849060444">"Perangkat terhubung"</string>
     <string name="connected_devices_dashboard_summary" msgid="1072664369515033179">"Bluetooth, mode mengemudi, NFC"</string>
     <string name="connected_devices_dashboard_no_nfc_summary" msgid="2610085597733526722">"Bluetooth, mode mengemudi"</string>
     <string name="connected_devices_dashboard_no_driving_mode_summary" msgid="3524409078596318803">"Bluetooth, NFC"</string>
diff --git a/tests/CarDeveloperOptions/res/values-it/strings.xml b/tests/CarDeveloperOptions/res/values-it/strings.xml
index 5c72158..02abed3 100644
--- a/tests/CarDeveloperOptions/res/values-it/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-it/strings.xml
@@ -4325,7 +4325,7 @@
     <string name="battery_suggestion_summary" msgid="2669070349482656490"></string>
     <string name="gesture_prevent_ringing_screen_title" msgid="4173494225145223638">"Disattiva suoneria"</string>
     <string name="gesture_prevent_ringing_title" msgid="8827963588425673557">"Premi contemporaneamente i tasti di accensione e Volume su per"</string>
-    <string name="gesture_prevent_ringing_sound_title" msgid="8642330448721033641">"Scorciatoia per impedire al telefono di suonare"</string>
+    <string name="gesture_prevent_ringing_sound_title" msgid="8642330448721033641">"Scorciatoia per disattivare la suoneria"</string>
     <string name="prevent_ringing_option_vibrate" msgid="6456505293904544108">"Vibrazione"</string>
     <string name="prevent_ringing_option_mute" msgid="53662688921253613">"Disattiva audio"</string>
     <string name="prevent_ringing_option_none" msgid="1450985763137666231">"Non fare niente"</string>
diff --git a/tests/CarDeveloperOptions/res/values-km/strings.xml b/tests/CarDeveloperOptions/res/values-km/strings.xml
index 29c5ca4..c8b2bdb 100644
--- a/tests/CarDeveloperOptions/res/values-km/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-km/strings.xml
@@ -3136,7 +3136,7 @@
     <string name="sound_settings_example_summary" msgid="2091822107298841827">"កម្រិតសំឡេងរោទ៍ត្រឹម 80%"</string>
     <string name="media_volume_option_title" msgid="3553411883305505682">"កម្រិត​សំឡេង​មេឌៀ"</string>
     <string name="remote_media_volume_option_title" msgid="6355710054191873836">"កម្រិតសំឡេងនៃការបញ្ជូន"</string>
-    <string name="call_volume_option_title" msgid="5028003296631037334">"កម្រិត​សំឡេង​ហៅ"</string>
+    <string name="call_volume_option_title" msgid="5028003296631037334">"កម្រិត​សំឡេង​ហៅទូរសព្ទ"</string>
     <string name="alarm_volume_option_title" msgid="3184076022438477047">"កម្រិត​សំឡេងម៉ោង​រោទ៍"</string>
     <string name="ring_volume_option_title" msgid="2038924918468372264">"កម្រិត​សំឡេង​រោទ៍"</string>
     <string name="notification_volume_option_title" msgid="1358512611511348260">"កម្រិត​សំឡេង​ការ​ជូន​ដំណឹង"</string>
@@ -3153,7 +3153,7 @@
     <string name="docking_sounds_title" msgid="2573137471605541366">"សំឡេង​​ភ្ជាប់"</string>
     <string name="touch_sounds_title" msgid="165237488496165652">"សំឡេង​ប៉ះ"</string>
     <string name="vibrate_on_touch_title" msgid="6360155469279157684">"ការ​ញ័រ​ពេល​ប៉ះ"</string>
-    <string name="vibrate_on_touch_summary" msgid="5504424764028676043">"មតិស្ថាបនា​តាម​ការប៉ះ​សម្រាប់​ការចុច ក្ដារចុច និងច្រើនទៀត"</string>
+    <string name="vibrate_on_touch_summary" msgid="5504424764028676043">"ប្រតិកម្មញ័រពេលចុច ក្ដារចុច និងច្រើនទៀត"</string>
     <string name="dock_audio_media_title" msgid="1859521680502040781">"ភ្ជាប់ការ​ចាក់​តាមអូប៉ាល័រ"</string>
     <string name="dock_audio_media_disabled" msgid="4300752306178486302">"អូឌីយ៉ូ​​ទាំងអស់"</string>
     <string name="dock_audio_media_enabled" msgid="2873275045878628153">"តែ​អូឌីយ៉ូ​​មេឌៀ​ប៉ុណ្ណោះ"</string>
@@ -4310,7 +4310,7 @@
     <string name="change_wifi_state_title" msgid="5140754955787584174">"ការ​គ្រប់គ្រង Wi-Fi"</string>
     <string name="change_wifi_state_app_detail_switch" msgid="6489090744937816260">"អនុញ្ញាត​ឱ្យ​កម្មវិធី​គ្រប់គ្រង Wi-Fi"</string>
     <string name="change_wifi_state_app_detail_summary" msgid="614854822469259860">"អនុញ្ញាត​ឱ្យ​កម្មវិធី​នេះ​បើក ឬបិទ Wi-Fi ស្កេន និងភ្ជាប់បណ្តាញ Wi-Fi បញ្ចូល ឬលុប​បណ្តាញ ឬចាប់ផ្តើមតែហតស្ប៉តមូលដ្ឋាន​ប៉ុណ្ណោះ"</string>
-    <string name="media_output_title" msgid="8710632337456601848">"ចាក់​មេឌៀទៅកាន់"</string>
+    <string name="media_output_title" msgid="8710632337456601848">"ចាក់​មេឌៀនៅលើ"</string>
     <string name="media_output_default_summary" msgid="3159237976830415584">"ឧបករណ៍នេះ"</string>
     <string name="media_output_summary" product="default" msgid="6294261435613551178">"ទូរសព្ទ"</string>
     <string name="media_output_summary" product="tablet" msgid="6672024060360538526">"ថេប្លេត"</string>
diff --git a/tests/CarDeveloperOptions/res/values-kn/arrays.xml b/tests/CarDeveloperOptions/res/values-kn/arrays.xml
index 63d5f6a..2b09f58 100644
--- a/tests/CarDeveloperOptions/res/values-kn/arrays.xml
+++ b/tests/CarDeveloperOptions/res/values-kn/arrays.xml
@@ -223,7 +223,7 @@
     <item msgid="2585253854462134715">"ಒರಟು ಸ್ಥಳ"</item>
     <item msgid="1830619568689922920">"ಉತ್ಕೃಷ್ಟ ಸ್ಥಳ"</item>
     <item msgid="3317274469481923141">"GPS"</item>
-    <item msgid="8931785990160383356">"ಕಂಪನ"</item>
+    <item msgid="8931785990160383356">"ವೈಬ್ರೇಟ್‌"</item>
     <item msgid="8632513128515114092">"ಓದುವ ಸಂಪರ್ಕಗಳು"</item>
     <item msgid="3741042113569620272">"ಸಂಪರ್ಕಗಳನ್ನು ಮಾರ್ಪಡಿಸಿ"</item>
     <item msgid="4204420969709009931">"ಕರೆಯ ಲಾಗ್‌ ಓದಿ"</item>
diff --git a/tests/CarDeveloperOptions/res/values-kn/strings.xml b/tests/CarDeveloperOptions/res/values-kn/strings.xml
index 5d1e489..ad359d0 100644
--- a/tests/CarDeveloperOptions/res/values-kn/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-kn/strings.xml
@@ -3124,7 +3124,7 @@
     <string name="keywords_default_payment_app" msgid="845369409578423996">"ಪಾವತಿ, ಡಿಫಾಲ್ಟ್"</string>
     <string name="keywords_ambient_display" msgid="8835182491798487184">"ಒಳಬರುವ ಅಧಿಸೂಚನೆ"</string>
     <string name="keywords_hotspot_tethering" msgid="1723591462602613867">"usb ಟೆಥರ್, ಬ್ಲೂಟೂತ್ ಟೆಥರ್, ವೈಫೈ ಹಾಟ್‌ಸ್ಪಾಟ್"</string>
-    <string name="keywords_touch_vibration" msgid="2081175517528255224">"ಹ್ಯಾಪ್ಟಿಕ್ಸ್, ಕಂಪನ, ಪರದೆ, ಸಂವೇದನೆ"</string>
+    <string name="keywords_touch_vibration" msgid="2081175517528255224">"ಹ್ಯಾಪ್ಟಿಕ್ಸ್, ವೈಬ್ರೇಟ್‌, ಪರದೆ, ಸಂವೇದನೆ"</string>
     <string name="keywords_ring_vibration" msgid="4210509151866460210">"ಹ್ಯಾಪ್ಟಿಕ್ಸ್, ವೈಬ್ರೇಟ್‌, ಫೋನ್, ಕರೆ, ಸೂಕ್ಷ್ಮತೆ, ರಿಂಗ್"</string>
     <string name="keywords_notification_vibration" msgid="1077515502086745166">"ಹ್ಯಾಪ್ಟಿಕ್ಸ್, ವೈಬ್ರೇಟ್‌, ಸೂಕ್ಷ್ಮತೆ"</string>
     <string name="keywords_battery_saver_sticky" msgid="8733804259716284872">"ಬ್ಯಾಟರಿ ಸೇವರ್, ಸ್ಟಿಕಿ, ತಡೆ ಹಿಡಿ, ಪವರ್ ಸೇವರ್, ಬ್ಯಾಟರಿ"</string>
@@ -3149,7 +3149,7 @@
     <string name="other_sound_settings" msgid="5250376066099818676">"ಇತರ ಧ್ವನಿಗಳು"</string>
     <string name="dial_pad_tones_title" msgid="8877212139988655769">"ಡಯಲ್‌ ಪ್ಯಾಡ್‌ ಟೋನ್‌ಗಳು"</string>
     <string name="screen_locking_sounds_title" msgid="4407110895465866809">"ಸ್ಕ್ರೀನ್ ಲಾಕಿಂಗ್ ಧ್ವನಿಗಳು"</string>
-    <string name="charging_sounds_title" msgid="5070437987230894287">"ಚಾರ್ಜಿಂಗ್ ಧ್ವನಿಗಳು ಮತ್ತು ಕಂಪನ"</string>
+    <string name="charging_sounds_title" msgid="5070437987230894287">"ಚಾರ್ಜಿಂಗ್ ಧ್ವನಿಗಳು ಮತ್ತು ವೈಬ್ರೇಟ್‌"</string>
     <string name="docking_sounds_title" msgid="2573137471605541366">"ಡಾಕಿಂಗ್ ಧ್ವನಿಗಳು"</string>
     <string name="touch_sounds_title" msgid="165237488496165652">"ಸ್ಪರ್ಶ ಧ್ವನಿಗಳು"</string>
     <string name="vibrate_on_touch_title" msgid="6360155469279157684">"ಸ್ಪರ್ಶಿಸಿದಾಗ ವೈಬ್ರೇಷನ್‌"</string>
@@ -4037,7 +4037,7 @@
     <string name="notification_log_details_ashmem" msgid="4272241723105041393">"ಆಶ್ಮೆಮ್"</string>
     <string name="notification_log_details_alerted" msgid="1891749888625061319">"ಅಧಿಸೂಚನೆಯ ಎಚ್ಚರಿಕೆಯನ್ನು ನೀಡಲಾಗಿದೆ"</string>
     <string name="notification_log_details_sound" msgid="4028782443557466322">"ಶಬ್ದ"</string>
-    <string name="notification_log_details_vibrate" msgid="8372400602058888072">"ಕಂಪನ"</string>
+    <string name="notification_log_details_vibrate" msgid="8372400602058888072">"ವೈಬ್ರೇಟ್‌"</string>
     <string name="notification_log_details_vibrate_pattern" msgid="7015554755444260922">"ಪ್ಯಾಟರ್ನ್"</string>
     <string name="notification_log_details_default" msgid="455451833359888182">"ಡಿಫಾಲ್ಟ್"</string>
     <string name="notification_log_details_none" msgid="4294690532744821638">"ಯಾವುದೂ ಇಲ್ಲ"</string>
diff --git a/tests/CarDeveloperOptions/res/values-ky/strings.xml b/tests/CarDeveloperOptions/res/values-ky/strings.xml
index c436ab2..be41ac2 100644
--- a/tests/CarDeveloperOptions/res/values-ky/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-ky/strings.xml
@@ -3035,7 +3035,7 @@
     <string name="network_dashboard_summary_mobile" msgid="5560545061217580626">"мобилдик"</string>
     <string name="network_dashboard_summary_data_usage" msgid="4695629715072542102">"дайын-даректердин өткөрүлүшү"</string>
     <string name="network_dashboard_summary_hotspot" msgid="3928610802321995214">"байланыш түйүнү"</string>
-    <string name="connected_devices_dashboard_title" msgid="7795222675849060444">"Туташкан түзмөктөр"</string>
+    <string name="connected_devices_dashboard_title" msgid="7795222675849060444">"Байланышкан түзмөктөр"</string>
     <string name="connected_devices_dashboard_summary" msgid="1072664369515033179">"Bluetooth, айдоо режими, NFC"</string>
     <string name="connected_devices_dashboard_no_nfc_summary" msgid="2610085597733526722">"Bluetooth, айдоо режими"</string>
     <string name="connected_devices_dashboard_no_driving_mode_summary" msgid="3524409078596318803">"Bluetooth, NFC"</string>
diff --git a/tests/CarDeveloperOptions/res/values-lo/strings.xml b/tests/CarDeveloperOptions/res/values-lo/strings.xml
index 4d48d74..f43a442 100644
--- a/tests/CarDeveloperOptions/res/values-lo/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-lo/strings.xml
@@ -3403,7 +3403,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> ໝວດໝູ່</item>
       <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> ໝວດໝູ່</item>
     </plurals>
-    <string name="no_channels" msgid="8884254729302501652">"This app has not posted any notifications"</string>
+    <string name="no_channels" msgid="8884254729302501652">"ແອັບນີ້ຍັງບໍ່ໄດ້ໂພສການແຈ້ງເຕືອນໃດເທື່ອ"</string>
     <string name="app_settings_link" msgid="8465287765715790984">"ການຕັ້ງຄ່າເພີ່ມເຕີມໃນແອັບ"</string>
     <string name="app_notification_listing_summary_zero" msgid="4047782719487686699">"ເປີດສຳລັບທຸກແອັບ"</string>
     <plurals name="app_notification_listing_summary_others" formatted="false" msgid="1161774065480666519">
diff --git a/tests/CarDeveloperOptions/res/values-mk/strings.xml b/tests/CarDeveloperOptions/res/values-mk/strings.xml
index 56d2a0c..94c0d71 100644
--- a/tests/CarDeveloperOptions/res/values-mk/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-mk/strings.xml
@@ -1608,7 +1608,7 @@
     <string name="master_clear_not_available" msgid="4676613348163652454">"Фабричкото ресетирање не е достапно за овој корисник"</string>
     <string name="master_clear_progress_title" msgid="378953167274114857">"Се брише"</string>
     <string name="master_clear_progress_text" msgid="5418958116008976218">"Почекајте..."</string>
-    <string name="call_settings_title" msgid="5033906789261282752">"Поставки на повик"</string>
+    <string name="call_settings_title" msgid="5033906789261282752">"Поставки за повици"</string>
     <string name="call_settings_summary" msgid="2119161087671450035">"Постави говорна пошта, проследување на повик, повик на чекање, ID на повикувач"</string>
     <string name="tether_settings_title_usb" msgid="4265582654602420357">"Интернет преку USB"</string>
     <string name="tether_settings_title_wifi" msgid="2060965130234484613">"Преносл. точка на пристап"</string>
diff --git a/tests/CarDeveloperOptions/res/values-ml/strings.xml b/tests/CarDeveloperOptions/res/values-ml/strings.xml
index a529cee..2f0c29a 100644
--- a/tests/CarDeveloperOptions/res/values-ml/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-ml/strings.xml
@@ -3105,7 +3105,7 @@
     <string name="keywords_android_version" msgid="4842749998088987740">"android സുരക്ഷാ പാച്ച് നില, ബേസ്‌ബാൻഡ് പതിപ്പ്, കെർണൽ പതിപ്പ്"</string>
     <string name="keywords_dark_ui_mode" msgid="1027966176887770318">"തീം, പ്രകാശം, ഇരുണ്ട മോഡ്"</string>
     <string name="keywords_financial_apps_sms_access" msgid="3236014691838121857">"സാമ്പത്തിക ആപ്പ്, SMS, അനുമതി"</string>
-    <string name="keywords_systemui_theme" msgid="9150908170417305866">"ഇരുണ്ട തീം"</string>
+    <string name="keywords_systemui_theme" msgid="9150908170417305866">"ഡാർക്ക് തീം"</string>
     <string name="keywords_device_feedback" msgid="6948977907405738490">"ബഗ്"</string>
     <string name="keywords_ambient_display_screen" msgid="5873935693887583428">"പാതിമയക്ക ഡിസ്‌പ്ലേ, ലോക്ക് സ്‌ക്രീൻ ഡിസ്‌പ്ലേ"</string>
     <string name="keywords_lock_screen_notif" msgid="4914337222856805463">"ലോക്ക് സ്‌ക്രീൻ അറിയിപ്പ്, അറിയിപ്പുകൾ"</string>
@@ -4310,8 +4310,8 @@
     <string name="change_wifi_state_title" msgid="5140754955787584174">"വൈഫൈ നിയന്ത്രണം"</string>
     <string name="change_wifi_state_app_detail_switch" msgid="6489090744937816260">"വൈഫൈയെ നിയന്ത്രിക്കാൻ ആപ്പിനെ അനുവദിക്കുക"</string>
     <string name="change_wifi_state_app_detail_summary" msgid="614854822469259860">"വൈഫൈ ഓണോ ഓഫോ ആക്കാനോ വൈഫൈ നെറ്റ്‌വർക്കുകൾ സ്കാൻ ചെയ്യാനോ അവയിലേക്ക് കണക്‌റ്റ് ചെയ്യാനോ നെറ്റ്‌വർക്കുകൾ ചേർക്കാനോ നീക്കം ചെയ്യാനോ ഉപകരണം ഉള്ളിടത്ത് മാത്രം പ്രവർത്തിക്കുന്ന ഒരു ഹോട്ട്‌സ്പോട്ട് ആരംഭിക്കാനോ ഈ ആപ്പിനെ അനുവദിക്കുക"</string>
-    <string name="media_output_title" msgid="8710632337456601848">"ഇതിലേക്ക് മീഡിയ പ്ലേ ചെയ്യുക"</string>
-    <string name="media_output_default_summary" msgid="3159237976830415584">"ഈ ഉപകരണം"</string>
+    <string name="media_output_title" msgid="8710632337456601848">"മീഡിയ പ്ലേ ചെയ്യുക:"</string>
+    <string name="media_output_default_summary" msgid="3159237976830415584">"ഈ ഉപകരണത്തിൽ"</string>
     <string name="media_output_summary" product="default" msgid="6294261435613551178">"ഫോൺ"</string>
     <string name="media_output_summary" product="tablet" msgid="6672024060360538526">"ടാബ്‌ലെറ്റ്"</string>
     <string name="media_output_summary" product="device" msgid="5132223072593052660">"ഉപകരണം"</string>
diff --git a/tests/CarDeveloperOptions/res/values-my/strings.xml b/tests/CarDeveloperOptions/res/values-my/strings.xml
index 4ad5b8e..3716e91 100644
--- a/tests/CarDeveloperOptions/res/values-my/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-my/strings.xml
@@ -3332,7 +3332,7 @@
     <string name="lock_screen_notifications_interstitial_title_profile" msgid="4043621508889929254">"ပရိုဖိုင်သတိပေးချက်များ"</string>
     <string name="notifications_title" msgid="8334011924253810654">"အကြောင်းကြားချက်များ"</string>
     <string name="app_notifications_title" msgid="1141791221581312325">"အက်ပ် အကြောင်းကြားချက်များ"</string>
-    <string name="notification_channel_title" msgid="6637705960909690229">"အသိပေးချက် အမျိုးအစား"</string>
+    <string name="notification_channel_title" msgid="6637705960909690229">"အကြောင်းကြားချက် အမျိုးအစား"</string>
     <string name="notification_group_title" msgid="6105337987437608590">"အကြောင်းကြားချက် အုပ်စုအမျိုးအစား"</string>
     <string name="notification_importance_title" msgid="4131979083408000545">"အပြုအမူ"</string>
     <string name="notification_importance_unspecified" msgid="2515778981253707724">"အသံကို ခွင့်ပြုရန်"</string>
diff --git a/tests/CarDeveloperOptions/res/values-or/strings.xml b/tests/CarDeveloperOptions/res/values-or/strings.xml
index bbc3273..17e330b 100644
--- a/tests/CarDeveloperOptions/res/values-or/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-or/strings.xml
@@ -1608,7 +1608,7 @@
     <string name="master_clear_not_available" msgid="4676613348163652454">"ଏହି ଉପଯୋଗକର୍ତ୍ତାଙ୍କ ପାଇଁ ଫ୍ୟାକ୍ଟୋରୀ ରିସେଟ୍‌ ଉପଲବ୍ଧ ନାହିଁ"</string>
     <string name="master_clear_progress_title" msgid="378953167274114857">"ଲିଭାଉଛି"</string>
     <string name="master_clear_progress_text" msgid="5418958116008976218">"ଦୟାକରି ଅପେକ୍ଷା କରନ୍ତୁ..."</string>
-    <string name="call_settings_title" msgid="5033906789261282752">"କଲ୍‌ ସେଟିଙ୍ଗ"</string>
+    <string name="call_settings_title" msgid="5033906789261282752">"କଲ୍‌ ସେଟିଂସ୍"</string>
     <string name="call_settings_summary" msgid="2119161087671450035">"ଭଏସ୍‌ମେଲ୍‌, କଲ୍‌ ଫର୍‌ୱାର୍ଡିଙ୍ଗ, କଲ୍‌ ୱେଟିଙ୍ଗ, କଲର୍‌ ID ସେଟ୍‌ କରନ୍ତୁ"</string>
     <string name="tether_settings_title_usb" msgid="4265582654602420357">"USB ଟିଥରିଂ"</string>
     <string name="tether_settings_title_wifi" msgid="2060965130234484613">"ପୋର୍ଟବଲ୍‌ ହଟସ୍ପଟ୍‌"</string>
@@ -3147,13 +3147,13 @@
     <string name="alarm_ringtone_title" msgid="6411326147408635902">"ଡିଫଲ୍ଟ ଆଲାର୍ମ ସାଉଣ୍ଡ"</string>
     <string name="vibrate_when_ringing_title" msgid="2757996559847126952">"କଲ୍‍ ପାଇଁ ଭାଇବ୍ରେଟ୍‍ କରନ୍ତୁ"</string>
     <string name="other_sound_settings" msgid="5250376066099818676">"ଅନ୍ୟାନ୍ୟ ଶବ୍ଦ"</string>
-    <string name="dial_pad_tones_title" msgid="8877212139988655769">"ଡାୟଲ୍‌ ପ୍ୟାଡ୍‌ ଟୋନ୍‍"</string>
+    <string name="dial_pad_tones_title" msgid="8877212139988655769">"ଡାଏଲ୍‌ ପ୍ୟାଡ୍‌ ଟୋନ୍‍"</string>
     <string name="screen_locking_sounds_title" msgid="4407110895465866809">"ସ୍କ୍ରୀନ୍‌ ଲକ୍‌ କରିବା ସାଉଣ୍ଡ"</string>
     <string name="charging_sounds_title" msgid="5070437987230894287">"ସାଉଣ୍ଡ ଓ ଭାଇବ୍ରେସନ୍ ଚାର୍ଜ ହେଉଛି"</string>
     <string name="docking_sounds_title" msgid="2573137471605541366">"ଡକ୍‌ କରିବା ଧ୍ୱନୀ"</string>
     <string name="touch_sounds_title" msgid="165237488496165652">"ସ୍ପର୍ଶ ଶବ୍ଦ"</string>
-    <string name="vibrate_on_touch_title" msgid="6360155469279157684">"ସ୍ପର୍ଶଜନିତ ଭାଇବ୍ରେଶନ୍"</string>
-    <string name="vibrate_on_touch_summary" msgid="5504424764028676043">"ଟ୍ୟାପ୍‌, କୀ-ବୋର୍ଡ ଓ ଆହୁରି ଅଧିକ ପାଇଁ ସ୍ପର୍ଶ ମାଧ୍ୟମରେ ମତାମତ"</string>
+    <string name="vibrate_on_touch_title" msgid="6360155469279157684">"ସ୍ପର୍ଶ ଭାଇବ୍ରେସନ୍"</string>
+    <string name="vibrate_on_touch_summary" msgid="5504424764028676043">"ଟ୍ୟାପ୍‌, କୀ-ବୋର୍ଡ ଓ ଆହୁରି ଅଧିକ ପାଇଁ ହେପଟିକ୍ ମତାମତ"</string>
     <string name="dock_audio_media_title" msgid="1859521680502040781">"ଡକ୍‌ ସ୍ପିକର୍‌ ବାଜିବ"</string>
     <string name="dock_audio_media_disabled" msgid="4300752306178486302">"ସମସ୍ତ ଅଡିଓ"</string>
     <string name="dock_audio_media_enabled" msgid="2873275045878628153">"କେବଳ ମିଡିଆ ଅଡିଓ"</string>
@@ -3293,7 +3293,7 @@
     <string name="ringtones_install_custom_sound_title" msgid="210551218424553671">"କଷ୍ଟମ୍‌ ସାଉଣ୍ଡ ଯୋଡ଼ିବେ?"</string>
     <string name="ringtones_install_custom_sound_content" msgid="6683649115132255452">"<xliff:g id="FOLDER_NAME">%s</xliff:g> ଫୋଲ୍ଡର୍‌କୁ ଏହି ଫାଇଲ୍‌ କପୀ କରାଯିବ"</string>
     <string name="ringtones_category_preference_title" msgid="4491932700769815470">"ରିଙ୍ଗଟୋନ୍‌"</string>
-    <string name="other_sound_category_preference_title" msgid="2045757472469840859">"ଅନ୍ୟାନ୍ୟ ସାଉଣ୍ଡ ଓ ଭାଇବ୍ରେଶନ୍"</string>
+    <string name="other_sound_category_preference_title" msgid="2045757472469840859">"ଅନ୍ୟ ସାଉଣ୍ଡ ଓ ଭାଇବ୍ରେସନ୍"</string>
     <string name="configure_notification_settings" msgid="291914315140851270">"ବିଜ୍ଞପ୍ତି"</string>
     <string name="recent_notifications" msgid="8125865995065032049">"କିଛି ସମୟ ପୂର୍ବରୁ ବିଜ୍ଞପ୍ତି ପଠାଇଥିବା ଆପ୍"</string>
     <string name="recent_notifications_see_all_title" msgid="4089007770442871469">"ବିଗତ 7 ଦିନରୁ ସବୁଗୁଡ଼ିକୁ ଦେଖନ୍ତୁ"</string>
@@ -3423,7 +3423,7 @@
     <string name="notification_badge_title" msgid="8989086619255666442">"ବିଜ୍ଞପ୍ତି ଡଟ୍‌ର ଅନୁମତି ଦିଅନ୍ତୁ"</string>
     <string name="notification_channel_badge_title" msgid="8228215248332054612">"ବିଜ୍ଞପ୍ତି ଡଟ୍‌ ଦେଖାନ୍ତୁ"</string>
     <string name="app_notification_override_dnd_title" msgid="1757042206738172601">"ବିରକ୍ତ କରନାହିଁ ଓଭରରାଇଡ"</string>
-    <string name="app_notification_override_dnd_summary" msgid="3152957611171210980">"’ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ’ ଅନ୍ ଥିବାବେଳେ ଏହି ବିଜ୍ଞପ୍ତିର ବାଧା ଦେବା ପ୍ରକ୍ରିୟାକୁ ଜାରି ରଖନ୍ତୁ"</string>
+    <string name="app_notification_override_dnd_summary" msgid="3152957611171210980">"\'ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\' ଚାଲୁଥିବା ବେଳେ ଏହି ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକର ବାଧା ଦେବା ପ୍ରକ୍ରିୟାକୁ ଜାରି ରଖନ୍ତୁ"</string>
     <string name="app_notification_visibility_override_title" msgid="2349335170165637672">"ଲକ୍ ସ୍କ୍ରୀନ୍ ଉପରେ"</string>
     <string name="app_notification_row_banned" msgid="2079325338122151677">"ଅବରୋଧିତ"</string>
     <string name="app_notification_row_priority" msgid="432299064888787236">"ପ୍ରାଥମିକତା"</string>
@@ -4310,7 +4310,7 @@
     <string name="change_wifi_state_title" msgid="5140754955787584174">"ୱାଇ-ଫାଇର ନିୟନ୍ତ୍ରଣ"</string>
     <string name="change_wifi_state_app_detail_switch" msgid="6489090744937816260">"ୱାଇ-ଫାଇକୁ ନିୟନ୍ତ୍ରଣ କରିବା ପାଇଁ ଆପ୍‌କୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
     <string name="change_wifi_state_app_detail_summary" msgid="614854822469259860">"ୱାଇ-ଫାଇକୁ ଚାଲୁ କରିବା କିମ୍ବା ବନ୍ଦ କରିବା, ୱାଇ-ଫାଇ ନେଟୱର୍କକୁ ଖୋଜିବା ଓ କନେକ୍ଟ କରିବା, ନେଟୱର୍କକୁ ଯୋଡ଼ିବା କିମ୍ବା କାଢ଼ିଦେବା କିମ୍ବା କେବଳ ସୀମିତ କ୍ଷେତ୍ରରେ କାମ କରୁଥିବା ହଟସ୍ପଟ୍‌କୁ ଚାଲୁ କରିବା ପାଇଁ ଏହି ଆପ୍‌କୁ ଅନୁମତି ଦେବା"</string>
-    <string name="media_output_title" msgid="8710632337456601848">"ମିଡିଆ ଚଲାନ୍ତୁ"</string>
+    <string name="media_output_title" msgid="8710632337456601848">"ଏଥିରେ ମିଡିଆ ଚଲାନ୍ତୁ"</string>
     <string name="media_output_default_summary" msgid="3159237976830415584">"ଏହି ଡିଭାଇସ୍‍"</string>
     <string name="media_output_summary" product="default" msgid="6294261435613551178">"ଫୋନ୍"</string>
     <string name="media_output_summary" product="tablet" msgid="6672024060360538526">"ଟାବଲେଟ୍‌"</string>
@@ -4330,7 +4330,7 @@
     <string name="prevent_ringing_option_mute" msgid="53662688921253613">"ମ୍ୟୁଟ୍ କରନ୍ତୁ"</string>
     <string name="prevent_ringing_option_none" msgid="1450985763137666231">"କିଛି କରନ୍ତୁ ନାହିଁ"</string>
     <string name="prevent_ringing_option_vibrate_summary" msgid="7961818570574683926">"ଚାଲୁ (ଭାଇବ୍ରେଟ୍)"</string>
-    <string name="prevent_ringing_option_mute_summary" msgid="3509459199090688328">"(ମ୍ୟୁଟ୍) ଅନ୍ ଅଛି"</string>
+    <string name="prevent_ringing_option_mute_summary" msgid="3509459199090688328">"ଚାଲୁ (ମ୍ୟୁଟ୍)"</string>
     <string name="prevent_ringing_option_none_summary" msgid="5152618221093037451">"ଅଫ୍"</string>
     <string name="pref_title_network_details" msgid="3971074015034595956">"ନେଟୱାର୍କ୍‌ ବିବରଣୀ"</string>
     <string name="about_phone_device_name_warning" msgid="9088572775969880106">"ଆପଣଙ୍କ ଡିଭାଇସ୍‌ରେ ଥିବା ଆପ୍‌ଗୁଡ଼ିକୁ ଆପଣଙ୍କର ଡିଭାଇସ୍‍ ନାମ ଦେଖାଯାଉଛି। ବ୍ଲୁଟୂଥ୍‍ ଡିଭାଇସ୍‍ ସହ ଯୋଡ଼ି ହେବାବେଳେ କିମ୍ୱା ଏକ ୱାଇ-ଫାଇ ହଟସ୍ପଟ୍ ସେଟ୍ କରିବା ସମୟରେ, ଏହା ଅନ୍ୟ ଲୋକମାନଙ୍କୁ ମଧ୍ୟ ଦେଖାଦେଇପାରେ।"</string>
diff --git a/tests/CarDeveloperOptions/res/values-pt/strings.xml b/tests/CarDeveloperOptions/res/values-pt/strings.xml
index 41b6c14..1f678e4 100644
--- a/tests/CarDeveloperOptions/res/values-pt/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-pt/strings.xml
@@ -3031,7 +3031,7 @@
       <item quantity="one">Mostrar %d item oculto</item>
       <item quantity="other">Mostrar %d itens ocultos</item>
     </plurals>
-    <string name="network_dashboard_title" msgid="8288134139584687806">"Rede e internet"</string>
+    <string name="network_dashboard_title" msgid="8288134139584687806">"Rede e Internet"</string>
     <string name="network_dashboard_summary_mobile" msgid="5560545061217580626">"rede móvel"</string>
     <string name="network_dashboard_summary_data_usage" msgid="4695629715072542102">"uso de dados"</string>
     <string name="network_dashboard_summary_hotspot" msgid="3928610802321995214">"ponto de acesso"</string>
@@ -3423,7 +3423,7 @@
     <string name="notification_badge_title" msgid="8989086619255666442">"Permitir ponto de notificação"</string>
     <string name="notification_channel_badge_title" msgid="8228215248332054612">"Mostrar ponto de notificação"</string>
     <string name="app_notification_override_dnd_title" msgid="1757042206738172601">"Ignorar o Não perturbe"</string>
-    <string name="app_notification_override_dnd_summary" msgid="3152957611171210980">"Permitir que essas notificações sejam mostradas mesmo quando o Não perturbe estiver ativado"</string>
+    <string name="app_notification_override_dnd_summary" msgid="3152957611171210980">"Permitir que as notificações sejam mostradas mesmo quando o Não perturbe estiver ativado"</string>
     <string name="app_notification_visibility_override_title" msgid="2349335170165637672">"Na tela de bloqueio"</string>
     <string name="app_notification_row_banned" msgid="2079325338122151677">"Bloqueadas"</string>
     <string name="app_notification_row_priority" msgid="432299064888787236">"Prioridade"</string>
diff --git a/tests/CarDeveloperOptions/res/values-sk/strings.xml b/tests/CarDeveloperOptions/res/values-sk/strings.xml
index fbac769..0d97f16 100644
--- a/tests/CarDeveloperOptions/res/values-sk/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-sk/strings.xml
@@ -766,7 +766,7 @@
     <string name="bluetooth_pairing_request" msgid="7221745525632573125">"Spárovať so zariadením <xliff:g id="DEVICE_NAME">%1$s</xliff:g>?"</string>
     <string name="bluetooth_pairing_key_msg" msgid="1139230917419961975">"Párovací kód Bluetooth"</string>
     <string name="bluetooth_enter_passkey_msg" msgid="6205151011298670207">"Zadajte párovací kód a potom stlačte tlačidlo Return alebo Enter"</string>
-    <string name="bluetooth_enable_alphanumeric_pin" msgid="9138308197078115672">"Kód PIN obsahuje písmená alebo symboly"</string>
+    <string name="bluetooth_enable_alphanumeric_pin" msgid="9138308197078115672">"PIN obsahuje písmená či symboly"</string>
     <string name="bluetooth_pin_values_hint" msgid="8044671726261326240">"Obvykle 0000 alebo 1234"</string>
     <string name="bluetooth_pin_values_hint_16_digits" msgid="2665983525706661525">"Musí obsahovať 16 číslic"</string>
     <string name="bluetooth_enter_pin_other_device" msgid="1727015949040507621">"Tento kód PIN bude možno treba zadať aj na druhom zariadení."</string>
diff --git a/tests/CarDeveloperOptions/res/values-sq/strings.xml b/tests/CarDeveloperOptions/res/values-sq/strings.xml
index 37c7aa3..ac2c3bc 100644
--- a/tests/CarDeveloperOptions/res/values-sq/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-sq/strings.xml
@@ -3555,7 +3555,7 @@
     <string name="switch_on_text" msgid="7100491749799298324">"Aktivizuar"</string>
     <string name="switch_off_text" msgid="3539551289454353555">"Joaktiv"</string>
     <string name="screen_pinning_title" msgid="578020318289781102">"Gozhdimi i ekranit"</string>
-    <string name="screen_pinning_description" msgid="3814537379086412278">"Kur ky funksion është i aktivizuar, mund të përdorësh gozhdimin e ekranit për të mbajtur pamjen aktuale të ekranit deri sa ta anulosh gozhdimin.\n\nPër të përdorur gozhdimin e ekranit:\n\n1. Sigurohu që gozhdimi i ekranit është i aktivizuar\n\n2. Hap \"Përmbledhja\"\n\n3. Trokit tek ikona e aplikacionit në krye të ekranit dhe më pas trokit te \"Gozhdo\""</string>
+    <string name="screen_pinning_description" msgid="3814537379086412278">"Kur ky cilësim është i aktivizuar, mund të përdorësh gozhdimin e ekranit për të mbajtur pamjen aktuale të ekranit deri sa ta anulosh gozhdimin.\n\nPër të përdorur gozhdimin e ekranit:\n\n1. Sigurohu që gozhdimi i ekranit është i aktivizuar\n\n2. Hap \"Përmbledhja\"\n\n3. Trokit tek ikona e aplikacionit në krye të ekranit dhe më pas trokit te \"Gozhdo\""</string>
     <string name="screen_pinning_unlock_pattern" msgid="1060334707088339444">"Kërko motivin e shkyçjes para anulimit të mbërthimit"</string>
     <string name="screen_pinning_unlock_pin" msgid="1441705536015645023">"Zhgozhdimi kërkon PIN-in"</string>
     <string name="screen_pinning_unlock_password" msgid="1017776884000170841">"Kërko fjalëkalim para heqjes nga gozhdimi"</string>
diff --git a/tests/CarDeveloperOptions/res/values-sr/strings.xml b/tests/CarDeveloperOptions/res/values-sr/strings.xml
index fbee5c3..edf510e 100644
--- a/tests/CarDeveloperOptions/res/values-sr/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-sr/strings.xml
@@ -3190,7 +3190,7 @@
     <string name="other_sound_settings" msgid="5250376066099818676">"Други звукови"</string>
     <string name="dial_pad_tones_title" msgid="8877212139988655769">"Тонови нумеричке тастатуре"</string>
     <string name="screen_locking_sounds_title" msgid="4407110895465866809">"Звукови закључавања екрана"</string>
-    <string name="charging_sounds_title" msgid="5070437987230894287">"Мењајући звуци и вибрација"</string>
+    <string name="charging_sounds_title" msgid="5070437987230894287">"Звукови и вибрација пуњења"</string>
     <string name="docking_sounds_title" msgid="2573137471605541366">"Звукови монтирања"</string>
     <string name="touch_sounds_title" msgid="165237488496165652">"Звукови при додиру"</string>
     <string name="vibrate_on_touch_title" msgid="6360155469279157684">"Вибрација при додиру"</string>
diff --git a/tests/CarDeveloperOptions/res/values-sv/strings.xml b/tests/CarDeveloperOptions/res/values-sv/strings.xml
index 5b2dcd1..68c7e34 100644
--- a/tests/CarDeveloperOptions/res/values-sv/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-sv/strings.xml
@@ -3149,7 +3149,7 @@
     <string name="other_sound_settings" msgid="5250376066099818676">"Andra ljud"</string>
     <string name="dial_pad_tones_title" msgid="8877212139988655769">"Knappsatsljud"</string>
     <string name="screen_locking_sounds_title" msgid="4407110895465866809">"Ljud vid skärmlåsning"</string>
-    <string name="charging_sounds_title" msgid="5070437987230894287">"Laddningsljud- och vibration"</string>
+    <string name="charging_sounds_title" msgid="5070437987230894287">"Laddningsljud och -vibration"</string>
     <string name="docking_sounds_title" msgid="2573137471605541366">"Ljud via dockningsstationen"</string>
     <string name="touch_sounds_title" msgid="165237488496165652">"Ljud vid tryck"</string>
     <string name="vibrate_on_touch_title" msgid="6360155469279157684">"Vibration vid tryck"</string>
diff --git a/tests/CarDeveloperOptions/res/values-sw/strings.xml b/tests/CarDeveloperOptions/res/values-sw/strings.xml
index a07110a..b665336 100644
--- a/tests/CarDeveloperOptions/res/values-sw/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-sw/strings.xml
@@ -2094,7 +2094,7 @@
     <string name="accessibility_vibration_settings_title" msgid="1902649657883159406">"Mtetemo"</string>
     <string name="accessibility_notification_vibration_title" msgid="1005799039440510298">"Mtetemo wa arifa"</string>
     <string name="accessibility_ring_vibration_title" msgid="7943341443551359985">"Mtetemo wa mlio"</string>
-    <string name="accessibility_touch_vibration_title" msgid="285890135612038092">"Mtetemo wa mguso"</string>
+    <string name="accessibility_touch_vibration_title" msgid="285890135612038092">"Mtetemo inapoguswa"</string>
     <string name="accessibility_service_master_switch_title" msgid="2734791644475782924">"Tumia huduma"</string>
     <string name="accessibility_daltonizer_master_switch_title" msgid="4855011639012300777">"Tumia usahihishaji wa rangi"</string>
     <string name="accessibility_caption_master_switch_title" msgid="6373335123229234053">"Tumia manukuu"</string>
@@ -3152,7 +3152,7 @@
     <string name="charging_sounds_title" msgid="5070437987230894287">"Sauti za kuchaji na mtetemo"</string>
     <string name="docking_sounds_title" msgid="2573137471605541366">"Kuambatisha sauti"</string>
     <string name="touch_sounds_title" msgid="165237488496165652">"Sauti inapoguswa"</string>
-    <string name="vibrate_on_touch_title" msgid="6360155469279157684">"Mtetemo wa mguso"</string>
+    <string name="vibrate_on_touch_title" msgid="6360155469279157684">"Mtetemo inapoguswa"</string>
     <string name="vibrate_on_touch_summary" msgid="5504424764028676043">"Majibu unayoweza kuhisi kwa kugusa, kibodi na mengineyo"</string>
     <string name="dock_audio_media_title" msgid="1859521680502040781">"Cheza kutumia spika ya kituo"</string>
     <string name="dock_audio_media_disabled" msgid="4300752306178486302">"Sauti zote"</string>
@@ -3341,11 +3341,11 @@
     <string name="notification_importance_low" msgid="7609797151662295364">"Onyesha chinichini"</string>
     <string name="notification_importance_default" msgid="4091563759103917166">"Toa sauti"</string>
     <string name="notification_importance_high" msgid="7973764540402436656">"Toa sauti na ibukizi kwenye skrini"</string>
-    <string name="notification_importance_high_silent" msgid="3177662759865661155">"Ichomoze kwenye skrini"</string>
+    <string name="notification_importance_high_silent" msgid="3177662759865661155">"Zionekane kwenye skrini"</string>
     <string name="notification_importance_min_title" msgid="705872537330744154">"Punguza"</string>
     <string name="notification_importance_low_title" msgid="2956199021781786232">"Wastani"</string>
     <string name="notification_importance_default_title" msgid="7985549807203332482">"Juu"</string>
-    <string name="notification_importance_high_title" msgid="7258373094258585858">"Ichomoze kwenye skrini"</string>
+    <string name="notification_importance_high_title" msgid="7258373094258585858">"Zionekane kwenye skrini"</string>
     <string name="notification_block_title" msgid="2570364198866886906">"Zuia"</string>
     <string name="notification_silence_title" msgid="6959637402003838093">"Ionyeshe bila kutoa sauti"</string>
     <string name="notification_alert_title" msgid="750683027055192648">"Arifa"</string>
diff --git a/tests/CarDeveloperOptions/res/values-te/strings.xml b/tests/CarDeveloperOptions/res/values-te/strings.xml
index b646142..6e54dda 100644
--- a/tests/CarDeveloperOptions/res/values-te/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-te/strings.xml
@@ -2323,9 +2323,9 @@
     <string name="smart_battery_manager_title" msgid="5744035036663849515">"బ్యాటరీ మేనేజర్"</string>
     <string name="smart_battery_title" msgid="4919670408532804351">"యాప్‌లను ఆటోమేటిక్‌గా నిర్వహించండి"</string>
     <string name="smart_battery_summary" msgid="640027046471198174">"మీరు తరచుగా ఉపయోగించని యాప్‌ల కోసం బ్యాటరీని పరిమితం చేయండి"</string>
-    <string name="smart_battery_footer" product="default" msgid="3971715848890205632">"యాప్‌లు బ్యాటరీ శక్తిని హరిస్తున్నయని బ్యాటరీ మేనేజర్ గుర్తించినప్పుడు, ఈ యాప్‌లను పరిమితం చేసే ఎంపిక మీకు ఉంటుంది. పరిమితం చేయబడిన యాప్‌లు సరిగ్గా పనిచేయకపోవచ్చు మరియు నోటిఫికేషన్‌లు రావడానికి ఆలస్యం కావచ్చు."</string>
-    <string name="smart_battery_footer" product="tablet" msgid="3971715848890205632">"యాప్‌లు బ్యాటరీ శక్తిని హరిస్తున్నయని బ్యాటరీ మేనేజర్ గుర్తించినప్పుడు, ఈ యాప్‌లను పరిమితం చేసే ఎంపిక మీకు ఉంటుంది. పరిమితం చేయబడిన యాప్‌లు సరిగ్గా పనిచేయకపోవచ్చు మరియు నోటిఫికేషన్‌లు రావడానికి ఆలస్యం కావచ్చు."</string>
-    <string name="smart_battery_footer" product="device" msgid="3971715848890205632">"యాప్‌లు బ్యాటరీ శక్తిని హరిస్తున్నయని బ్యాటరీ మేనేజర్ గుర్తించినప్పుడు, ఈ యాప్‌లను పరిమితం చేసే ఎంపిక మీకు ఉంటుంది. పరిమితం చేయబడిన యాప్‌లు సరిగ్గా పనిచేయకపోవచ్చు మరియు నోటిఫికేషన్‌లు రావడానికి ఆలస్యం కావచ్చు."</string>
+    <string name="smart_battery_footer" product="default" msgid="3971715848890205632">"ఏవైనా యాప్‌లు బ్యాటరీని అధికంగా వాడుతున్నాయని బ్యాటరీ మేనేజర్ గుర్తించినప్పుడు, ఆ యాప్‌లను పరిమితం చేసే ఎంపిక మీకు ఉంటుంది. పరిమితం చేయబడిన యాప్‌లు సరిగ్గా పనిచేయకపోవచ్చు, వాటి నోటిఫికేషన్‌లు రావడానికి ఆలస్యం కావచ్చు."</string>
+    <string name="smart_battery_footer" product="tablet" msgid="3971715848890205632">"ఏవైనా యాప్‌లు బ్యాటరీని అధికంగా వాడుతున్నాయని బ్యాటరీ మేనేజర్ గుర్తించినప్పుడు, ఆ యాప్‌లను పరిమితం చేసే ఎంపిక మీకు ఉంటుంది. పరిమితం చేయబడిన యాప్‌లు సరిగ్గా పనిచేయకపోవచ్చు, వాటి నోటిఫికేషన్‌లు రావడానికి ఆలస్యం కావచ్చు."</string>
+    <string name="smart_battery_footer" product="device" msgid="3971715848890205632">"ఏవైనా యాప్‌లు బ్యాటరీని అధికంగా వాడుతున్నాయని బ్యాటరీ మేనేజర్ గుర్తించినప్పుడు, ఆ యాప్‌లను పరిమితం చేసే ఎంపిక మీకు ఉంటుంది. పరిమితం చేయబడిన యాప్‌లు సరిగ్గా పనిచేయకపోవచ్చు, వాటి నోటిఫికేషన్‌లు రావడానికి ఆలస్యం కావచ్చు."</string>
     <string name="restricted_app_title" msgid="4957644700640127606">"నియంత్రించబడిన యాప్‌లు"</string>
     <plurals name="restricted_app_summary" formatted="false" msgid="7609538735465186040">
       <item quantity="other">%1$d యాప్‌ల కోసం బ్యాటరీ వినియోగాన్ని పరిమితం చేయడం</item>
@@ -3041,7 +3041,7 @@
     <string name="connected_devices_dashboard_no_driving_mode_summary" msgid="3524409078596318803">"బ్లూటూత్, NFC"</string>
     <string name="connected_devices_dashboard_no_driving_mode_no_nfc_summary" msgid="7881286613528299400">"బ్లూటూత్"</string>
     <string name="app_and_notification_dashboard_title" msgid="8448096608058843730">"యాప్‌లు &amp; నోటిఫికేషన్‌లు"</string>
-    <string name="app_and_notification_dashboard_summary" msgid="4165181440955038145">"అసిస్టెంట్, ఇటీవలి యాప్‌లు, డిఫాల్ట్ యాప్‌లు"</string>
+    <string name="app_and_notification_dashboard_summary" msgid="4165181440955038145">"Assistant, ఇటీవలి యాప్‌లు, డిఫాల్ట్ యాప్‌లు"</string>
     <string name="notification_settings_work_profile" msgid="7190550347842400029">"కార్యాలయ ప్రొఫైల్‌లో ఉన్న యాప్‌లకు సంబంధించి నోటిఫికేషన్ యాక్సెస్ అందుబాటులో లేదు."</string>
     <string name="account_dashboard_title" msgid="4734300939532555885">"ఖాతాలు"</string>
     <string name="account_dashboard_default_summary" msgid="6822549669771936206">"ఖాతాలు జోడించబడలేదు"</string>
@@ -4473,7 +4473,7 @@
     <string name="hwui_force_dark_title" msgid="3744825212652331461">"ఫోర్స్‌-డార్క్‌ను అధిగ‌మించ‌డం"</string>
     <string name="hwui_force_dark_summary" msgid="2051891908674765817">"ఫోర్స్‌-డార్క్ ఫీచ‌ర్‌ను అధిగ‌మించ‌డం ఎల్ల‌ప్పుడూ ఆన్‌లో ఉండాలి"</string>
     <string name="privacy_dashboard_title" msgid="8764930992456607513">"గోప్యత"</string>
-    <string name="privacy_dashboard_summary" msgid="7916431309860824945">"అనుమతులు, ఖాతా కార్యకలాపం, వ్యక్తిగత డేటా"</string>
+    <string name="privacy_dashboard_summary" msgid="7916431309860824945">"అనుమతులు, ఖాతా యాక్టివిటీ, వ్యక్తిగత డేటా"</string>
     <string name="contextual_card_dismiss_remove" msgid="1750420285615827309">"తీసివేయి"</string>
     <string name="contextual_card_dismiss_keep" msgid="3204450672928193661">"Keep"</string>
     <string name="contextual_card_dismiss_confirm_message" msgid="2352465079667730312">"ఈ సూచనని తీసేయాలా?"</string>
diff --git a/tests/CarDeveloperOptions/res/values-uk/strings.xml b/tests/CarDeveloperOptions/res/values-uk/strings.xml
index 2816cc8..b39d1a4 100644
--- a/tests/CarDeveloperOptions/res/values-uk/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-uk/strings.xml
@@ -1246,7 +1246,7 @@
     <string name="adaptive_sleep_summary_off" msgid="2891586225954973431">"Вимкнено"</string>
     <string name="adaptive_sleep_description" msgid="812673735459170009">"Не дає екрану вимикатись, якщо ви на нього дивитесь."</string>
     <string name="adaptive_sleep_privacy" msgid="5706802215479902623">"Розпізнавання уваги за допомогою передньої камери визначає, чи дивиться користувач на екран. Ця функція працює лише на пристрої, не зберігає зображень і не надсилає їх у Google."</string>
-    <string name="night_display_title" msgid="1305002424893349814">"Нічний режим"</string>
+    <string name="night_display_title" msgid="1305002424893349814">"Нічний екран"</string>
     <string name="night_display_text" msgid="5330502493684652527">"У нічному режимі екран набуває бурштинового відтінку. Це знімає напруження очей при тьмяному освітленні та допомагає легше заснути."</string>
     <string name="night_display_auto_mode_title" msgid="8493573087102481588">"Розклад"</string>
     <string name="night_display_auto_mode_never" msgid="2897444637217807088">"Ніколи"</string>
@@ -4464,7 +4464,7 @@
     <string name="change_wifi_state_title" msgid="5140754955787584174">"Керування Wi-Fi"</string>
     <string name="change_wifi_state_app_detail_switch" msgid="6489090744937816260">"Дозволити додатку керувати Wi-Fi"</string>
     <string name="change_wifi_state_app_detail_summary" msgid="614854822469259860">"Дозволити цьому додатку вмикати чи вимикати Wi-Fi, шукати мережі Wi-Fi та підключатися до них, додавати або видаляти мережі чи запускати лише локальну точку доступу"</string>
-    <string name="media_output_title" msgid="8710632337456601848">"Відтворювати медіа-вміст у додатку"</string>
+    <string name="media_output_title" msgid="8710632337456601848">"Відтворення медіаконтенту"</string>
     <string name="media_output_default_summary" msgid="3159237976830415584">"Цей пристрій"</string>
     <string name="media_output_summary" product="default" msgid="6294261435613551178">"Телефон"</string>
     <string name="media_output_summary" product="tablet" msgid="6672024060360538526">"Планшет"</string>
diff --git a/tests/CarDeveloperOptions/res/values-ur/strings.xml b/tests/CarDeveloperOptions/res/values-ur/strings.xml
index 787d550..4a881ea 100644
--- a/tests/CarDeveloperOptions/res/values-ur/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-ur/strings.xml
@@ -1128,7 +1128,7 @@
     <string name="notification_sound_title" msgid="6812164482799723931">"ڈیفالٹ اطلاع کی آواز"</string>
     <string name="incoming_call_volume_title" msgid="4736570528754310450">"رنگ ٹون"</string>
     <string name="notification_volume_title" msgid="6022562909288085275">"اطلاع"</string>
-    <string name="checkbox_notification_same_as_incoming_call" msgid="7312942422655861175">"اطلاعات کیلئے آنے والی کال کا والیوم استعمال کریں"</string>
+    <string name="checkbox_notification_same_as_incoming_call" msgid="7312942422655861175">"اطلاعات کیلئے اِن کمنگ کال کا والیوم استعمال کریں"</string>
     <string name="home_work_profile_not_supported" msgid="6137073723297076818">"کام کے پروفائلز کا تعاون نہیں کرتا ہے"</string>
     <string name="notification_sound_dialog_title" msgid="6653341809710423276">"ڈیفالٹ اطلاع کی آواز"</string>
     <string name="media_volume_title" msgid="1030438549497800914">"میڈیا"</string>
@@ -3401,7 +3401,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> زمرے</item>
       <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> زمرہ</item>
     </plurals>
-    <string name="no_channels" msgid="8884254729302501652">"اس ایپ نے کوئی اطلاعات شائع نہیں کیا ہے"</string>
+    <string name="no_channels" msgid="8884254729302501652">"اس ایپ نے کوئی اطلاعات شائع نہیں کی ہیں"</string>
     <string name="app_settings_link" msgid="8465287765715790984">"ایپ میں اضافی ترتیبات"</string>
     <string name="app_notification_listing_summary_zero" msgid="4047782719487686699">"سبھی ایپس کے لیے آن ہے"</string>
     <plurals name="app_notification_listing_summary_others" formatted="false" msgid="1161774065480666519">
diff --git a/tests/CarDeveloperOptions/res/values-uz/strings.xml b/tests/CarDeveloperOptions/res/values-uz/strings.xml
index 401fb18..f2fb286 100644
--- a/tests/CarDeveloperOptions/res/values-uz/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-uz/strings.xml
@@ -732,7 +732,7 @@
     <string name="bluetooth_pairing_request" msgid="7221745525632573125">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> qurilmasiga ulansinmi?"</string>
     <string name="bluetooth_pairing_key_msg" msgid="1139230917419961975">"Bluetooth orqali ulanish kodi"</string>
     <string name="bluetooth_enter_passkey_msg" msgid="6205151011298670207">"Ulanish kodini kiriting va keyin “Return” yoki “Enter” tugmasini bosing"</string>
-    <string name="bluetooth_enable_alphanumeric_pin" msgid="9138308197078115672">"PIN-kod harflar va belgilardan iborat bo‘ladi"</string>
+    <string name="bluetooth_enable_alphanumeric_pin" msgid="9138308197078115672">"Harflar yoki maxsus belgilardan iborat PIN kod"</string>
     <string name="bluetooth_pin_values_hint" msgid="8044671726261326240">"Odatda 0000 yoki 1234"</string>
     <string name="bluetooth_pin_values_hint_16_digits" msgid="2665983525706661525">"16 ta raqam bo‘lishi lozim"</string>
     <string name="bluetooth_enter_pin_other_device" msgid="1727015949040507621">"Ushbu PIN kodni boshqa qurilmada ham terish lozim bo‘lishi mumkin."</string>
@@ -2754,7 +2754,7 @@
     <string name="data_usage_metered_no" msgid="1961524615778610008">"Bepul"</string>
     <string name="data_usage_disclaimer" msgid="4683321532922590425">"Aloqa operatorining hisob-kitobi qurilmanikidan farq qilishi mumkin."</string>
     <string name="cryptkeeper_emergency_call" msgid="4625420047524693116">"Favqulodda chaqiruv"</string>
-    <string name="cryptkeeper_return_to_call" msgid="4433942821196822815">"Chaqiruvga qaytish"</string>
+    <string name="cryptkeeper_return_to_call" msgid="4433942821196822815">"Suhbatga qaytish"</string>
     <string name="vpn_name" msgid="3538818658670774080">"Tarmoq nomi"</string>
     <string name="vpn_type" msgid="6389116710008658550">"Turi"</string>
     <string name="vpn_server" msgid="5216559017318406820">"Server manzili"</string>
diff --git a/tests/CarDeveloperOptions/res/values-vi/strings.xml b/tests/CarDeveloperOptions/res/values-vi/strings.xml
index ab20398..1efea2d 100644
--- a/tests/CarDeveloperOptions/res/values-vi/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-vi/strings.xml
@@ -34,7 +34,7 @@
     <string name="radio_info_data_connection_enable" msgid="2554249462719717119">"Bật kết nối dữ liệu"</string>
     <string name="radio_info_data_connection_disable" msgid="2430609627397999371">"Tắt kết nối dữ liệu"</string>
     <string name="volte_provisioned_switch_string" msgid="6326756678226686704">"Đã cấp phép VoLTE"</string>
-    <string name="vt_provisioned_switch_string" msgid="7458479879009293613">"Đã cấp phép gọi điện video"</string>
+    <string name="vt_provisioned_switch_string" msgid="7458479879009293613">"Đã cấp phép gọi video"</string>
     <string name="wfc_provisioned_switch_string" msgid="5446697646596639516">"Đã cấp phép gọi điện qua Wi-Fi"</string>
     <string name="eab_provisioned_switch_string" msgid="3921103790584572430">"Đã cấp phép hiện diện/EAB"</string>
     <string name="cbrs_data_switch_string" msgid="9120919504831536183">"Dữ liệu Cbrs"</string>
@@ -53,7 +53,7 @@
     <string name="radio_info_ims_reg_status_not_registered" msgid="1286050699734226077">"Chưa được đăng ký"</string>
     <string name="radio_info_ims_feature_status_available" msgid="2040629393134756058">"Khả dụng"</string>
     <string name="radio_info_ims_feature_status_unavailable" msgid="3348223769202693596">"Không khả dụng"</string>
-    <string name="radio_info_ims_reg_status" msgid="4771711884059371514">"Đăng ký IMS: <xliff:g id="STATUS">%1$s</xliff:g>\nThoại trên nền LTE: <xliff:g id="AVAILABILITY_0">%2$s</xliff:g>\nThoại qua Wi-Fi: <xliff:g id="AVAILABILITY_1">%3$s</xliff:g>\nGọi điện video: <xliff:g id="AVAILABILITY_2">%4$s</xliff:g>\nGiao diện UT: <xliff:g id="AVAILABILITY_3">%5$s</xliff:g>"</string>
+    <string name="radio_info_ims_reg_status" msgid="4771711884059371514">"Đăng ký IMS: <xliff:g id="STATUS">%1$s</xliff:g>\nThoại trên nền LTE: <xliff:g id="AVAILABILITY_0">%2$s</xliff:g>\nThoại qua Wi-Fi: <xliff:g id="AVAILABILITY_1">%3$s</xliff:g>\nGọi video: <xliff:g id="AVAILABILITY_2">%4$s</xliff:g>\nGiao diện UT: <xliff:g id="AVAILABILITY_3">%5$s</xliff:g>"</string>
     <string name="radioInfo_service_in" msgid="1297020186765943857">"Đang sử dụng"</string>
     <string name="radioInfo_service_out" msgid="8460363463722476510">"Không có dịch vụ"</string>
     <string name="radioInfo_service_emergency" msgid="7674989004735662599">"Chỉ cuộc gọi khẩn cấp"</string>
@@ -3403,8 +3403,8 @@
       <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> loại</item>
       <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> loại</item>
     </plurals>
-    <string name="no_channels" msgid="8884254729302501652">"Ứng dụng chưa đăng bất kỳ thông báo nào"</string>
-    <string name="app_settings_link" msgid="8465287765715790984">"Cài đặt bổ sung trong ứng dụng"</string>
+    <string name="no_channels" msgid="8884254729302501652">"Ứng dụng này chưa đăng bất kỳ thông báo nào"</string>
+    <string name="app_settings_link" msgid="8465287765715790984">"Tùy chọn cài đặt bổ sung trong ứng dụng"</string>
     <string name="app_notification_listing_summary_zero" msgid="4047782719487686699">"Đang bật cho tất cả ứng dụng"</string>
     <plurals name="app_notification_listing_summary_others" formatted="false" msgid="1161774065480666519">
       <item quantity="other">Tắt cho <xliff:g id="COUNT_1">%d</xliff:g> ứng dụng</item>
@@ -3422,7 +3422,7 @@
     <string name="notification_content_block_summary" msgid="2743896875255591743">"Không bao giờ hiển thị thông báo trong ngăn thông báo hoặc trên thiết bị ngoại vi"</string>
     <string name="notification_badge_title" msgid="8989086619255666442">"Cho phép dấu chấm thông báo"</string>
     <string name="notification_channel_badge_title" msgid="8228215248332054612">"Hiển thị dấu chấm thông báo"</string>
-    <string name="app_notification_override_dnd_title" msgid="1757042206738172601">"Ghi đè Không làm phiền"</string>
+    <string name="app_notification_override_dnd_title" msgid="1757042206738172601">"Vô hiệu hóa chế độ Không làm phiền"</string>
     <string name="app_notification_override_dnd_summary" msgid="3152957611171210980">"Cho phép những thông báo này tiếp tục làm gián đoạn khi chế độ Không làm phiền đang bật"</string>
     <string name="app_notification_visibility_override_title" msgid="2349335170165637672">"Trên màn hình khóa"</string>
     <string name="app_notification_row_banned" msgid="2079325338122151677">"Bị chặn"</string>
@@ -3511,8 +3511,8 @@
     <string name="zen_mode_bypassing_apps" msgid="3080739479028713449">"Cho phép ứng dụng ghi đè"</string>
     <string name="zen_mode_bypassing_apps_title" msgid="2115024664615538847">"Ứng dụng ngoại lệ"</string>
     <plurals name="zen_mode_bypassing_apps_subtext" formatted="false" msgid="8723144434730871572">
-      <item quantity="other">Thông báo từ <xliff:g id="NUMBER">%1$d</xliff:g> ứng dụng có thể ghi đè chế độ Không làm phiền</item>
-      <item quantity="one">Thông báo từ 1 ứng dụng có thể ghi đè chế độ Không làm phiền</item>
+      <item quantity="other">Thông báo từ <xliff:g id="NUMBER">%1$d</xliff:g> ứng dụng có thể vô hiệu hóa chế độ Không làm phiền</item>
+      <item quantity="one">Thông báo từ 1 ứng dụng có thể vô hiệu hóa chế độ Không làm phiền</item>
     </plurals>
     <string name="zen_mode_events_list" msgid="8578102701815684873">"sự kiện"</string>
     <string name="zen_mode_all_callers" msgid="4455039040077343838">"bất kỳ ai"</string>
diff --git a/tests/CarDeveloperOptions/res/values-zh-rHK/strings.xml b/tests/CarDeveloperOptions/res/values-zh-rHK/strings.xml
index 51500a7..5e42e3c 100644
--- a/tests/CarDeveloperOptions/res/values-zh-rHK/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-zh-rHK/strings.xml
@@ -3423,7 +3423,7 @@
     <string name="notification_content_block_summary" msgid="2743896875255591743">"永不在通知欄或周邊裝置上顯示通知"</string>
     <string name="notification_badge_title" msgid="8989086619255666442">"允許通知圓點"</string>
     <string name="notification_channel_badge_title" msgid="8228215248332054612">"顯示通知圓點"</string>
-    <string name="app_notification_override_dnd_title" msgid="1757042206738172601">"蓋過「請勿騷擾」設定"</string>
+    <string name="app_notification_override_dnd_title" msgid="1757042206738172601">"忽略「請勿騷擾」設定"</string>
     <string name="app_notification_override_dnd_summary" msgid="3152957611171210980">"「請勿騷擾」模式開啟時繼續接收這些通知"</string>
     <string name="app_notification_visibility_override_title" msgid="2349335170165637672">"在上鎖畫面上"</string>
     <string name="app_notification_row_banned" msgid="2079325338122151677">"已封鎖"</string>
@@ -3512,8 +3512,8 @@
     <string name="zen_mode_bypassing_apps" msgid="3080739479028713449">"允許應用程式取代「請勿騷擾」"</string>
     <string name="zen_mode_bypassing_apps_title" msgid="2115024664615538847">"應用程式例外情況"</string>
     <plurals name="zen_mode_bypassing_apps_subtext" formatted="false" msgid="8723144434730871572">
-      <item quantity="other"><xliff:g id="NUMBER">%1$d</xliff:g> 個應用程式的通知可蓋過「請勿騷擾」設定</item>
-      <item quantity="one">1 個應用程式的通知可蓋過「請勿騷擾」設定</item>
+      <item quantity="other"><xliff:g id="NUMBER">%1$d</xliff:g> 個應用程式的通知可忽略「請勿騷擾」設定</item>
+      <item quantity="one">1 個應用程式的通知可忽略「請勿騷擾」設定</item>
     </plurals>
     <string name="zen_mode_events_list" msgid="8578102701815684873">"活動"</string>
     <string name="zen_mode_all_callers" msgid="4455039040077343838">"任何人"</string>
diff --git a/tests/CarSecurityPermissionTest/src/com/android/car/CarTest.java b/tests/CarSecurityPermissionTest/src/com/android/car/CarPermisisonTest.java
similarity index 97%
rename from tests/CarSecurityPermissionTest/src/com/android/car/CarTest.java
rename to tests/CarSecurityPermissionTest/src/com/android/car/CarPermisisonTest.java
index 90378a8..07f4310 100644
--- a/tests/CarSecurityPermissionTest/src/com/android/car/CarTest.java
+++ b/tests/CarSecurityPermissionTest/src/com/android/car/CarPermisisonTest.java
@@ -29,10 +29,10 @@
 import org.junit.runner.RunWith;
 
 /**
- * This class contains security permission tests for the {@link CarTest}'s system APIs.
+ * This class contains security permission tests for the {@link Car}'s system APIs.
  */
 @RunWith(AndroidJUnit4.class)
-public class CarTest {
+public class CarPermisisonTest {
     private Car mCar = null;
 
     @Before
diff --git a/tests/CarSecurityPermissionTest/src/com/android/car/CarPropertyManagerPublicTest.java b/tests/CarSecurityPermissionTest/src/com/android/car/CarPropertyManagerPublicPermissionTest.java
similarity index 98%
rename from tests/CarSecurityPermissionTest/src/com/android/car/CarPropertyManagerPublicTest.java
rename to tests/CarSecurityPermissionTest/src/com/android/car/CarPropertyManagerPublicPermissionTest.java
index 92226a1..975bc74 100644
--- a/tests/CarSecurityPermissionTest/src/com/android/car/CarPropertyManagerPublicTest.java
+++ b/tests/CarSecurityPermissionTest/src/com/android/car/CarPropertyManagerPublicPermissionTest.java
@@ -40,11 +40,11 @@
  * This class contains security permission tests for the {@link CarPropertyManager}'s public APIs.
  */
 @RunWith(AndroidJUnit4.class)
-public class CarPropertyManagerPublicTest {
+public class CarPropertyManagerPublicPermissionTest {
     private Car mCar = null;
     private CarPropertyManager mPropertyManager;
     private HashSet<Integer> mProps = new HashSet<>();
-    private static final String TAG = CarPropertyManagerPublicTest.class.getSimpleName();
+    private static final String TAG = CarPropertyManagerPublicPermissionTest.class.getSimpleName();
     private static final Integer DUMMY_AREA_ID = VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL;
     // Dummy values for setter test.
     private static final int DUMMY_PROPERTY_VALUE_INTEGER = 1;
diff --git a/tests/CarSecurityPermissionTest/src/com/android/car/CarPublicTest.java b/tests/CarSecurityPermissionTest/src/com/android/car/CarPublicPermissionTest.java
similarity index 97%
rename from tests/CarSecurityPermissionTest/src/com/android/car/CarPublicTest.java
rename to tests/CarSecurityPermissionTest/src/com/android/car/CarPublicPermissionTest.java
index af1867b..16aef37 100644
--- a/tests/CarSecurityPermissionTest/src/com/android/car/CarPublicTest.java
+++ b/tests/CarSecurityPermissionTest/src/com/android/car/CarPublicPermissionTest.java
@@ -29,10 +29,10 @@
 import org.junit.runner.RunWith;
 
 /**
- * This class contains security permission tests for the {@link CarTest}'s public APIs.
+ * This class contains security permission tests for the {@link CarPermisisonTest}'s public APIs.
  */
 @RunWith(AndroidJUnit4.class)
-public class CarPublicTest {
+public class CarPublicPermissionTest {
     private Car mCar = null;
 
     @Before
diff --git a/tests/CarSecurityPermissionTest/src/com/android/car/content/pm/CarPackageManagerPermissionTest.java b/tests/CarSecurityPermissionTest/src/com/android/car/content/pm/CarPackageManagerPermissionTest.java
new file mode 100644
index 0000000..f1e23ba
--- /dev/null
+++ b/tests/CarSecurityPermissionTest/src/com/android/car/content/pm/CarPackageManagerPermissionTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2020 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;
+
+import static org.testng.Assert.assertThrows;
+
+import android.car.Car;
+import android.car.content.pm.CarAppBlockingPolicy;
+import android.car.content.pm.CarPackageManager;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * This class contains security permission tests for {@link CarPackageManager}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class CarPackageManagerPermissionTest {
+    private Car mCar;
+    private CarPackageManager mPm;
+
+    @Before
+    public void setUp() throws Exception {
+        mCar = Car.createCar(InstrumentationRegistry.getInstrumentation().getTargetContext());
+        mPm = (CarPackageManager) mCar.getCarManager(Car.PACKAGE_SERVICE);
+    }
+
+    @After
+    public void tearDown() {
+        mCar.disconnect();
+    }
+
+    @Test
+    public void testRestartTask() {
+        assertThrows(SecurityException.class, () -> mPm.restartTask(0));
+    }
+
+    @Test
+    public void testSetAppBlockingPolicy() {
+        String packageName = "com.android";
+        CarAppBlockingPolicy policy = new CarAppBlockingPolicy(null, null);
+        assertThrows(SecurityException.class, () -> mPm.setAppBlockingPolicy(packageName, policy,
+                0));
+    }
+
+}
diff --git a/tests/CarSecurityPermissionTest/src/com/android/car/input/CarInputManagerTest.java b/tests/CarSecurityPermissionTest/src/com/android/car/input/CarInputManagerPermisisonTest.java
similarity index 97%
rename from tests/CarSecurityPermissionTest/src/com/android/car/input/CarInputManagerTest.java
rename to tests/CarSecurityPermissionTest/src/com/android/car/input/CarInputManagerPermisisonTest.java
index 354446e..9730d13 100644
--- a/tests/CarSecurityPermissionTest/src/com/android/car/input/CarInputManagerTest.java
+++ b/tests/CarSecurityPermissionTest/src/com/android/car/input/CarInputManagerPermisisonTest.java
@@ -35,7 +35,7 @@
  * This class contains security permission tests for the {@link CarInputManager}'s system APIs.
  */
 @RunWith(AndroidJUnit4.class)
-public class CarInputManagerTest {
+public class CarInputManagerPermisisonTest {
     private Car mCar;
 
     private CarInputManager mCarInputManager;
diff --git a/tests/CarSecurityPermissionTest/src/com/android/car/media/CarAudioManagerTest.java b/tests/CarSecurityPermissionTest/src/com/android/car/media/CarAudioManagerPermissionTest.java
similarity index 87%
rename from tests/CarSecurityPermissionTest/src/com/android/car/media/CarAudioManagerTest.java
rename to tests/CarSecurityPermissionTest/src/com/android/car/media/CarAudioManagerPermissionTest.java
index 1bb81ec..1e4391b 100644
--- a/tests/CarSecurityPermissionTest/src/com/android/car/media/CarAudioManagerTest.java
+++ b/tests/CarSecurityPermissionTest/src/com/android/car/media/CarAudioManagerPermissionTest.java
@@ -25,16 +25,12 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.junit.Assume.assumeNotNull;
 import static org.testng.Assert.expectThrows;
 
 import android.car.Car;
 import android.car.media.CarAudioManager;
 import android.content.Context;
-import android.hardware.display.DisplayManager;
 import android.os.Handler;
-import android.view.Display;
-import android.view.DisplayAddress;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.platform.app.InstrumentationRegistry;
@@ -43,15 +39,13 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.Arrays;
 import java.util.Objects;
-import java.util.Optional;
 
 /**
  * This class contains security permission tests for the {@link CarAudioManager}'s system APIs.
  */
 @RunWith(AndroidJUnit4.class)
-public final class CarAudioManagerTest {
+public final class CarAudioManagerPermissionTest {
     private static final int GROUP_ID = 0;
     private static final int UID = 10;
 
@@ -220,22 +214,6 @@
     }
 
     @Test
-    public void getZoneIdForDisplayPermission() {
-        Display display = getPhysicalDisplay();
-        assumeNotNull(display);
-        Exception e = expectThrows(SecurityException.class,
-                () -> mCarAudioManager.getZoneIdForDisplay(display));
-        assertThat(e.getMessage()).contains(PERMISSION_CAR_CONTROL_AUDIO_SETTINGS);
-    }
-
-    @Test
-    public void getZoneIdForDisplayPortIdPermission() {
-        Exception e = expectThrows(SecurityException.class,
-                () -> mCarAudioManager.getZoneIdForDisplayPortId(Byte.MAX_VALUE));
-        assertThat(e.getMessage()).contains(PERMISSION_CAR_CONTROL_AUDIO_SETTINGS);
-    }
-
-    @Test
     public void getOutputDeviceForUsagePermission() {
         Exception e = expectThrows(SecurityException.class,
                 () -> mCarAudioManager.getOutputDeviceForUsage(PRIMARY_AUDIO_ZONE, USAGE_MEDIA));
@@ -255,12 +233,4 @@
                 () -> mCarAudioManager.onCarDisconnected());
         assertThat(e.getMessage()).contains(PERMISSION_CAR_CONTROL_AUDIO_VOLUME);
     }
-
-    private Display getPhysicalDisplay() {
-        DisplayManager displayManager = (DisplayManager) mContext.getSystemService(
-                Context.DISPLAY_SERVICE);
-        Optional<Display> physical = Arrays.stream(displayManager.getDisplays()).filter(
-                display -> (display.getAddress() instanceof DisplayAddress.Physical)).findFirst();
-        return physical.orElse(null);
-    }
 }
diff --git a/tests/CarSecurityPermissionTest/src/com/android/car/media/CarAudioManagerPublicTest.java b/tests/CarSecurityPermissionTest/src/com/android/car/media/CarAudioManagerPublicPermissionTest.java
similarity index 96%
rename from tests/CarSecurityPermissionTest/src/com/android/car/media/CarAudioManagerPublicTest.java
rename to tests/CarSecurityPermissionTest/src/com/android/car/media/CarAudioManagerPublicPermissionTest.java
index 2877043..b9cafc4 100644
--- a/tests/CarSecurityPermissionTest/src/com/android/car/media/CarAudioManagerPublicTest.java
+++ b/tests/CarSecurityPermissionTest/src/com/android/car/media/CarAudioManagerPublicPermissionTest.java
@@ -39,7 +39,7 @@
  * This class contains security permission tests for the {@link CarAudioManager}'s public APIs.
  */
 @RunWith(AndroidJUnit4.class)
-public final class CarAudioManagerPublicTest {
+public final class CarAudioManagerPublicPermissionTest {
     private CarAudioManager mCarAudioManager;
 
     @Before
diff --git a/tests/CarSecurityPermissionTest/src/com/android/car/user/CarUserManagerPermissionTest.java b/tests/CarSecurityPermissionTest/src/com/android/car/user/CarUserManagerPermissionTest.java
index 86a2853..3a5ea1b 100644
--- a/tests/CarSecurityPermissionTest/src/com/android/car/user/CarUserManagerPermissionTest.java
+++ b/tests/CarSecurityPermissionTest/src/com/android/car/user/CarUserManagerPermissionTest.java
@@ -16,6 +16,7 @@
 
 package com.android.car.user;
 
+import static android.Manifest.permission.CREATE_USERS;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
 import static android.Manifest.permission.MANAGE_USERS;
@@ -35,6 +36,7 @@
 import android.car.user.CarUserManager.UserLifecycleListener;
 import android.content.Context;
 import android.os.Handler;
+import android.os.UserManager;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.platform.app.InstrumentationRegistry;
@@ -67,9 +69,21 @@
 
     @Test
     public void testSwitchUserPermission() throws Exception {
-        int target_uid = 100;
+        Exception e = expectThrows(SecurityException.class, () -> mCarUserManager.switchUser(100));
+        assertThat(e.getMessage()).contains(MANAGE_USERS);
+    }
+
+    @Test
+    public void testCreateUserPermission() throws Exception {
         Exception e = expectThrows(SecurityException.class,
-                () -> mCarUserManager.switchUser(target_uid));
+                () -> mCarUserManager.createUser(null, UserManager.USER_TYPE_FULL_SECONDARY, 0));
+        assertThat(e.getMessage()).contains(MANAGE_USERS);
+        assertThat(e.getMessage()).contains(CREATE_USERS);
+    }
+
+    @Test
+    public void testRemoveUserPermission() throws Exception {
+        Exception e = expectThrows(SecurityException.class, () -> mCarUserManager.removeUser(100));
         assertThat(e.getMessage()).contains(MANAGE_USERS);
     }
 
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/audio.xml b/tests/EmbeddedKitchenSinkApp/res/layout/audio.xml
index 7c77b1b..cfc3515 100644
--- a/tests/EmbeddedKitchenSinkApp/res/layout/audio.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/audio.xml
@@ -28,6 +28,50 @@
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:orientation="horizontal"
+                android:layout_weight="1"
+                android:visibility="gone"
+                android:id="@+id/audio_select_device_address_layout">
+                <TextView
+                    android:id="@+id/select_device_title"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/select_device" />
+                <Space
+                    android:layout_width="3dp"
+                    android:layout_height="match_parent" />
+                <Spinner
+                    android:id="@+id/device_address_spinner"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content" />
+                <Space
+                    android:layout_width="3dp"
+                    android:layout_height="match_parent" />
+                <Button
+                    android:id="@+id/button_device_media_play_start"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/play" />
+                <Space
+                    android:layout_width="3dp"
+                    android:layout_height="match_parent" />
+                <Button
+                    android:id="@+id/button_device_media_play_once"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/play_pcm_once" />
+                <Space
+                    android:layout_width="3dp"
+                    android:layout_height="match_parent" />
+                <Button
+                    android:id="@+id/button_device_media_play_stop"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/stop" />
+            </LinearLayout>
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal"
                 android:layout_weight="1" >
                 <TextView
                     android:id="@+id/zone_selection_title"
@@ -72,50 +116,6 @@
             <LinearLayout
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:orientation="horizontal"
-                android:layout_weight="1"
-                android:visibility="gone"
-                android:id="@+id/audio_display_layout">
-                <TextView
-                    android:id="@+id/select_display_title"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:text="@string/select_display" />
-                <Space
-                    android:layout_width="3dp"
-                    android:layout_height="match_parent" />
-                <Spinner
-                    android:id="@+id/display_spinner"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content" />
-                <Space
-                    android:layout_width="3dp"
-                    android:layout_height="match_parent" />
-                <Button
-                    android:id="@+id/button_display_media_play_start"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:text="@string/play" />
-                <Space
-                    android:layout_width="3dp"
-                    android:layout_height="match_parent" />
-                <Button
-                    android:id="@+id/button_display_media_play_once"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:text="@string/play_pcm_once" />
-                <Space
-                    android:layout_width="3dp"
-                    android:layout_height="match_parent" />
-                <Button
-                    android:id="@+id/button_display_media_play_stop"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:text="@string/stop" />
-            </LinearLayout>
-            <LinearLayout
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
                 android:orientation="vertical"
                 android:layout_weight="1" >
                 <LinearLayout
diff --git a/tests/EmbeddedKitchenSinkApp/res/values/strings.xml b/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
index 80e6fc4..162c37d 100644
--- a/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
@@ -137,7 +137,7 @@
     <string name="mock_audio_on" translatable="false">Audio Mocking On</string>
     <string name="mock_audio_off" translatable="false">Audio Mocking Off</string>
     <string name="select_zone_to_hear_on_speaker" translatable="false">Select Zone to hear on speaker</string>
-    <string name="select_display" translatable="false">Select Display</string>
+    <string name="select_device" translatable="false">Select Device</string>
     <string name="activity_current_zone_id" translatable="false">Activity\'s current audio zone id</string>
     <string name="no_zone" translatable="false">none</string>
     <string name="phone_audio_player" translatable="false">Phone Player</string>
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 d4e30b7..bf5865a 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
@@ -24,7 +24,6 @@
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
-import android.hardware.display.DisplayManager;
 import android.media.AudioAttributes;
 import android.media.AudioDeviceInfo;
 import android.media.AudioFocusRequest;
@@ -36,8 +35,6 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.util.Log;
-import android.view.Display;
-import android.view.DisplayAddress;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -82,7 +79,7 @@
     private AudioPlayer mVrPlayer;
     private AudioPlayer mSystemPlayer;
     private AudioPlayer mWavPlayer;
-    private AudioPlayer mMusicPlayerForSelectedDisplay;
+    private AudioPlayer mMusicPlayerForSelectedDeviceAddress;
     private HwAudioSource mHwAudioSource;
     private AudioPlayer[] mAllPlayers;
 
@@ -97,15 +94,14 @@
     private AudioAttributes mVrAudioAttrib;
     private AudioAttributes mRadioAudioAttrib;
     private AudioAttributes mSystemSoundAudioAttrib;
-    private AudioAttributes mMusicAudioAttribForDisplay;
+    private AudioAttributes mMusicAudioAttribForDeviceAddress;
     private CarEmulator mCarEmulator;
     private CarAudioManager mCarAudioManager;
     private Spinner mZoneSpinner;
     ArrayAdapter<Integer> mZoneAdapter;
-    private Spinner mDisplaySpinner;
-    ArrayAdapter<Integer> mDisplayAdapter;
-    private LinearLayout mDisplayLayout;
-    private int mOldZonePosition;
+    private Spinner mDeviceAddressSpinner;
+    ArrayAdapter<CarAudioZoneDeviceInfo> mDeviceAddressAdapter;
+    private LinearLayout mDeviceAddressLayout;
 
     private final Object mLock = new Object();
 
@@ -114,8 +110,6 @@
     private OnAudioFocusChangeListener mMediaWithDelayedFocusListener;
     private TextView mDelayedStatusText;
 
-    private static int sDefaultExtraTestScreenPortId = 1;
-
     private final OnAudioFocusChangeListener mNavFocusListener = (focusChange) -> {
         Log.i(TAG, "Nav focus change:" + focusChange);
     };
@@ -160,9 +154,7 @@
 
                     handleSetUpZoneSelection();
 
-                    if (mCarAudioManager.isDynamicRoutingEnabled()) {
-                        setUpDisplayPlayer();
-                    }
+                    setUpDeviceAddressPlayer();
                 });
     }
 
@@ -173,8 +165,8 @@
         //Zone Spinner
         setUpZoneSpinnerView(view);
 
-        //Display layout
-        setUpDisplayLayoutView(view);
+        // Device Address layout
+        setUpDeviceAddressLayoutView(view);
 
         connectCar();
         initializePlayers();
@@ -293,17 +285,17 @@
             }
         });
 
-        // Manage buttons for audio player for displays
-        view.findViewById(R.id.button_display_media_play_start).setOnClickListener(v -> {
-            startDisplayAudio();
+        // Manage buttons for audio player for device address
+        view.findViewById(R.id.button_device_media_play_start).setOnClickListener(v -> {
+            startDeviceAudio();
         });
-        view.findViewById(R.id.button_display_media_play_once).setOnClickListener(v -> {
-            startDisplayAudio();
+        view.findViewById(R.id.button_device_media_play_once).setOnClickListener(v -> {
+            startDeviceAudio();
             // play only for 1 sec and stop
-            mHandler.postDelayed(() -> mMusicPlayerForSelectedDisplay.stop(), 1000);
+            mHandler.postDelayed(() -> mMusicPlayerForSelectedDeviceAddress.stop(), 1000);
         });
-        view.findViewById(R.id.button_display_media_play_stop)
-                .setOnClickListener(v -> mMusicPlayerForSelectedDisplay.stop());
+        view.findViewById(R.id.button_device_media_play_stop)
+                .setOnClickListener(v -> mMusicPlayerForSelectedDeviceAddress.stop());
 
         view.findViewById(R.id.media_delayed_focus_start)
                 .setOnClickListener(v -> handleDelayedMediaStart());
@@ -347,61 +339,62 @@
 
     private void initializePlayers() {
         mMusicAudioAttrib = new AudioAttributes.Builder()
-            .setUsage(AudioAttributes.USAGE_MEDIA)
-            .build();
+                .setUsage(AudioAttributes.USAGE_MEDIA)
+                .build();
         mNavAudioAttrib = new AudioAttributes.Builder()
-            .setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE)
-            .build();
+                .setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE)
+                .build();
         mPhoneAudioAttrib = new AudioAttributes.Builder()
                 .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
                 .build();
         mVrAudioAttrib = new AudioAttributes.Builder()
-            .setUsage(AudioAttributes.USAGE_ASSISTANT)
-            .build();
+                .setUsage(AudioAttributes.USAGE_ASSISTANT)
+                .build();
         mRadioAudioAttrib = new AudioAttributes.Builder()
-            .setUsage(AudioAttributes.USAGE_MEDIA)
-            .build();
+                .setUsage(AudioAttributes.USAGE_MEDIA)
+                .build();
         mSystemSoundAudioAttrib = new AudioAttributes.Builder()
-            .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
-            .build();
-        // Create a display audio attribute
-        mMusicAudioAttribForDisplay = new AudioAttributes.Builder()
+                .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
+                .build();
+        // Create an audio device address audio attribute
+        mMusicAudioAttribForDeviceAddress = new AudioAttributes.Builder()
                 .setUsage(AudioAttributes.USAGE_MEDIA)
                 .build();
 
-        mMusicPlayerForSelectedDisplay = new AudioPlayer(mContext, R.raw.well_worth_the_wait,
-                mMusicAudioAttribForDisplay);
+
+        mMusicPlayerForSelectedDeviceAddress = new AudioPlayer(mContext, R.raw.well_worth_the_wait,
+                mMusicAudioAttribForDeviceAddress);
         mMusicPlayer = new AudioPlayer(mContext, R.raw.well_worth_the_wait,
-            mMusicAudioAttrib);
+                mMusicAudioAttrib);
         mMusicPlayerWithDelayedFocus = new AudioPlayer(mContext, R.raw.well_worth_the_wait,
                 mMusicAudioAttrib);
         mMusicPlayerShort = new AudioPlayer(mContext, R.raw.ring_classic_01,
-            mMusicAudioAttrib);
+                mMusicAudioAttrib);
         mNavGuidancePlayer = new AudioPlayer(mContext, R.raw.turnright,
-            mNavAudioAttrib);
+                mNavAudioAttrib);
         mPhoneAudioPlayer = new AudioPlayer(mContext, R.raw.free_flight,
                 mPhoneAudioAttrib);
         mVrPlayer = new AudioPlayer(mContext, R.raw.one2six,
-            mVrAudioAttrib);
+                mVrAudioAttrib);
         mSystemPlayer = new AudioPlayer(mContext, R.raw.ring_classic_01,
-            mSystemSoundAudioAttrib);
+                mSystemSoundAudioAttrib);
         mWavPlayer = new AudioPlayer(mContext, R.raw.free_flight,
-            mMusicAudioAttrib);
+                mMusicAudioAttrib);
         final AudioDeviceInfo tuner = findTunerDevice(mContext);
         if (tuner != null) {
             mHwAudioSource = new HwAudioSource.Builder()
-                .setAudioAttributes(mMusicAudioAttrib)
-                .setAudioDeviceInfo(findTunerDevice(mContext))
-                .build();
+                    .setAudioAttributes(mMusicAudioAttrib)
+                    .setAudioDeviceInfo(findTunerDevice(mContext))
+                    .build();
         }
         mAllPlayers = new AudioPlayer[] {
-            mMusicPlayer,
-            mMusicPlayerShort,
-            mNavGuidancePlayer,
-            mVrPlayer,
-            mSystemPlayer,
-            mWavPlayer,
-            mMusicPlayerWithDelayedFocus
+                mMusicPlayer,
+                mMusicPlayerShort,
+                mNavGuidancePlayer,
+                mVrPlayer,
+                mSystemPlayer,
+                mWavPlayer,
+                mMusicPlayerWithDelayedFocus
         };
     }
 
@@ -494,14 +487,14 @@
         if (DBG) Log.d(TAG, "Media With Delayed Focus already stopped");
     }
 
-    private void setUpDisplayLayoutView(View view) {
-        mDisplayLayout = view.findViewById(R.id.audio_display_layout);
+    private void setUpDeviceAddressLayoutView(View view) {
+        mDeviceAddressLayout = view.findViewById(R.id.audio_select_device_address_layout);
 
-        mDisplaySpinner = view.findViewById(R.id.display_spinner);
-        mDisplaySpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+        mDeviceAddressSpinner = view.findViewById(R.id.device_address_spinner);
+        mDeviceAddressSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
             @Override
             public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
-                handleDisplaySelection();
+                handleDeviceAddressSelection();
             }
 
             @Override
@@ -642,72 +635,68 @@
         mAudioManager.abandonAudioFocus(mRadioFocusListener, mRadioAudioAttrib);
     }
 
-    private void setUpDisplayPlayer() {
-        DisplayManager displayManager =  (DisplayManager) mContext.getSystemService(
-                Context.DISPLAY_SERVICE);
-        Display[] displays = displayManager.getDisplays();
-        List<Integer> displayList = new ArrayList<>();
-        for (Display display : displays) {
-            DisplayAddress.Physical physical = (DisplayAddress.Physical) display.getAddress();
-            if (physical != null) {
-                displayList.add((int) physical.getPort());
-                Log.d(TAG, "Found Display Port " + physical.getPort());
-            } else {
-                Log.d(TAG, "Found Display with no physical " + display.getDisplayId());
-            }
+    private void setUpDeviceAddressPlayer() {
+        if (!mCarAudioManager.isDynamicRoutingEnabled()) {
+            mDeviceAddressLayout.setVisibility(View.GONE);
+            return;
         }
-        // If only one display is available add another display for testing
-        if (displayList.size() == 1) {
-            displayList.add(sDefaultExtraTestScreenPortId);
+        mDeviceAddressLayout.setVisibility(View.VISIBLE);
+        List<CarAudioZoneDeviceInfo> deviceList = new ArrayList<>();
+        for (int audioZoneId: mCarAudioManager.getAudioZoneIds()) {
+            AudioDeviceInfo deviceInfo = mCarAudioManager
+                    .getOutputDeviceForUsage(audioZoneId, AudioAttributes.USAGE_MEDIA);
+            CarAudioZoneDeviceInfo carAudioZoneDeviceInfo = new CarAudioZoneDeviceInfo();
+            carAudioZoneDeviceInfo.mDeviceInfo = deviceInfo;
+            carAudioZoneDeviceInfo.mAudioZoneId = audioZoneId;
+            deviceList.add(carAudioZoneDeviceInfo);
+            if (DBG) {
+                Log.d(TAG, "Found device address"
+                        + carAudioZoneDeviceInfo.mDeviceInfo.getAddress()
+                        + " for audio zone id " + audioZoneId);
+            }
+
         }
 
-        //take care of display selection
-        Integer[] displayArray = displayList.stream().toArray(Integer[]::new);
-        mDisplayAdapter = new ArrayAdapter<>(mContext,
-                android.R.layout.simple_spinner_item, displayArray);
-        mDisplayAdapter.setDropDownViewResource(
+        CarAudioZoneDeviceInfo[] deviceArray =
+                deviceList.stream().toArray(CarAudioZoneDeviceInfo[]::new);
+        mDeviceAddressAdapter = new ArrayAdapter<>(mContext,
+                android.R.layout.simple_spinner_item, deviceArray);
+        mDeviceAddressAdapter.setDropDownViewResource(
                 android.R.layout.simple_spinner_dropdown_item);
-        mDisplaySpinner.setAdapter(mDisplayAdapter);
-        createDisplayAudioPlayer();
+        mDeviceAddressSpinner.setAdapter(mDeviceAddressAdapter);
+        createDeviceAddressAudioPlayer();
     }
 
-    private void createDisplayAudioPlayer() {
-        byte selectedDisplayPortId = mDisplayAdapter.getItem(
-                mDisplaySpinner.getSelectedItemPosition()).byteValue();
-        int zoneIdForDisplayId = mCarAudioManager.getZoneIdForDisplayPortId(selectedDisplayPortId);
-        Log.d(TAG, "Setting Bundle to zone " + zoneIdForDisplayId);
+    private void createDeviceAddressAudioPlayer() {
+        CarAudioZoneDeviceInfo carAudioZoneDeviceInfo = mDeviceAddressAdapter.getItem(
+                mDeviceAddressSpinner.getSelectedItemPosition());
+        Log.d(TAG, "Setting Bundle to zone " + carAudioZoneDeviceInfo.mAudioZoneId);
         Bundle bundle = new Bundle();
         bundle.putInt(CarAudioManager.AUDIOFOCUS_EXTRA_REQUEST_ZONE_ID,
-                zoneIdForDisplayId);
-        mMusicAudioAttribForDisplay = new AudioAttributes.Builder()
+                carAudioZoneDeviceInfo.mAudioZoneId);
+        mMusicAudioAttribForDeviceAddress = new AudioAttributes.Builder()
                 .setUsage(AudioAttributes.USAGE_MEDIA)
                 .addBundle(bundle)
                 .build();
 
-        AudioDeviceInfo audioDeviceInfo =
-                mCarAudioManager.getOutputDeviceForUsage(zoneIdForDisplayId,
-                        AudioAttributes.USAGE_MEDIA);
-
-        mMusicPlayerForSelectedDisplay = new AudioPlayer(mContext,
+        mMusicPlayerForSelectedDeviceAddress = new AudioPlayer(mContext,
                 R.raw.well_worth_the_wait,
-                mMusicAudioAttribForDisplay,
-                audioDeviceInfo);
-
-        mDisplayLayout.findViewById(R.id.audio_display_layout)
-                .setVisibility(View.VISIBLE);
+                mMusicAudioAttribForDeviceAddress,
+                carAudioZoneDeviceInfo.mDeviceInfo);
     }
 
-    private void startDisplayAudio() {
-        Log.d(TAG, "Starting display audio");
-        mMusicPlayerForSelectedDisplay.start(true, false,
+    private void startDeviceAudio() {
+        Log.d(TAG, "Starting device address audio");
+        mMusicPlayerForSelectedDeviceAddress.start(true, false,
                 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
     }
 
-    public void handleDisplaySelection() {
-        if (mMusicPlayerForSelectedDisplay != null && mMusicPlayerForSelectedDisplay.isPlaying()) {
-            mMusicPlayerForSelectedDisplay.stop();
+    public void handleDeviceAddressSelection() {
+        if (mMusicPlayerForSelectedDeviceAddress != null
+                && mMusicPlayerForSelectedDeviceAddress.isPlaying()) {
+            mMusicPlayerForSelectedDeviceAddress.stop();
         }
-        createDisplayAudioPlayer();
+        createDeviceAddressAudioPlayer();
     }
 
     /**
@@ -838,4 +827,19 @@
             }
         }
     }
+
+    private class CarAudioZoneDeviceInfo {
+        AudioDeviceInfo mDeviceInfo;
+        int mAudioZoneId;
+
+        @Override
+        public String toString() {
+            StringBuilder builder = new StringBuilder();
+            builder.append("Device Address : ");
+            builder.append(mDeviceInfo.getAddress());
+            builder.append(", Audio Zone Id: ");
+            builder.append(mAudioZoneId);
+            return builder.toString();
+        }
+    }
 }
diff --git a/tests/android_car_api_test/Android.mk b/tests/android_car_api_test/Android.mk
index 7ac74aa..44c38c4 100644
--- a/tests/android_car_api_test/Android.mk
+++ b/tests/android_car_api_test/Android.mk
@@ -41,6 +41,7 @@
         android.car.cluster.navigation \
         android.car.cluster.navigation \
         android.car.testapi \
+        android.car.test.utils \
         compatibility-device-util-axt \
         testng \
         truth-prebuilt \
diff --git a/tests/android_car_api_test/src/android/car/apitest/CarApiTestBase.java b/tests/android_car_api_test/src/android/car/apitest/CarApiTestBase.java
index 4106ccb..b3bf492 100644
--- a/tests/android_car_api_test/src/android/car/apitest/CarApiTestBase.java
+++ b/tests/android_car_api_test/src/android/car/apitest/CarApiTestBase.java
@@ -16,14 +16,22 @@
 
 package android.car.apitest;
 
+import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
+import static com.android.compatibility.common.util.TestUtils.BooleanSupplierWithThrow;
+
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.fail;
+
 import android.car.Car;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.ServiceConnection;
 import android.os.IBinder;
 import android.os.Looper;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.util.Log;
 
 import androidx.test.platform.app.InstrumentationRegistry;
 
@@ -39,6 +47,15 @@
 
     protected static final long DEFAULT_WAIT_TIMEOUT_MS = 1_000;
 
+    /**
+     * Constant used to wait blindly, when there is no condition that can be checked.
+     */
+    private static final int SUSPEND_TIMEOUT_MS = 5_000;
+    /**
+     * How long to sleep (multiple times) while waiting for a condition.
+     */
+    private static final int SMALL_NAP_MS = 100;
+
     protected static final Context sContext = InstrumentationRegistry.getInstrumentation()
             .getTargetContext();
 
@@ -89,4 +106,36 @@
             assertMainThread();
         }
     }
+
+    protected static void suspendToRamAndResume() throws Exception {
+        Log.d(TAG, "Emulate suspend to RAM and resume");
+        PowerManager powerManager = sContext.getSystemService(PowerManager.class);
+        runShellCommand("cmd car_service suspend");
+        // Check for suspend success
+        waitUntil("Suspsend is not successful",
+                SUSPEND_TIMEOUT_MS, () -> !powerManager.isScreenOn());
+
+        // Force turn off garage mode
+        runShellCommand("cmd car_service garage-mode off");
+        runShellCommand("cmd car_service resume");
+    }
+
+    protected static boolean waitUntil(String msg, long timeoutMs,
+            BooleanSupplierWithThrow condition) {
+        long deadline = SystemClock.elapsedRealtime() + timeoutMs;
+        do {
+            try {
+                if (condition.getAsBoolean()) {
+                    return true;
+                }
+            } catch (Exception e) {
+                Log.e(TAG, "Exception in waitUntil: " + msg);
+                throw new RuntimeException(e);
+            }
+            SystemClock.sleep(SMALL_NAP_MS);
+        } while (SystemClock.elapsedRealtime() < deadline);
+
+        fail(msg + " after: " + timeoutMs + "ms");
+        return false;
+    }
 }
diff --git a/tests/android_car_api_test/src/android/car/apitest/CarAppFocusManagerTest.java b/tests/android_car_api_test/src/android/car/apitest/CarAppFocusManagerTest.java
index 4a4681b..8b64620 100644
--- a/tests/android_car_api_test/src/android/car/apitest/CarAppFocusManagerTest.java
+++ b/tests/android_car_api_test/src/android/car/apitest/CarAppFocusManagerTest.java
@@ -54,7 +54,7 @@
         // Request all application focuses and abandon them to ensure no active context is present
         // when test starts.
         int[] activeTypes =  mManager.getActiveAppTypes();
-        FocusOwnershipCallback owner = new FocusOwnershipCallback();
+        FocusOwnershipCallback owner = new FocusOwnershipCallback(/* assertEventThread= */ false);
         for (int i = 0; i < activeTypes.length; i++) {
             mManager.requestAppFocus(activeTypes[i], owner);
             owner.waitForOwnershipGrantAndAssert(DEFAULT_WAIT_TIMEOUT_MS, activeTypes[i]);
@@ -165,9 +165,9 @@
         assertThat(manager1.getActiveAppTypes()).asList().containsExactly(expectedFocus).inOrder();
         assertThat(manager2.getActiveAppTypes()).asList().containsExactly(expectedFocus).inOrder();
         assertThat(change2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
-                APP_FOCUS_TYPE_NAVIGATION, true)).isFalse();
+                APP_FOCUS_TYPE_NAVIGATION, true)).isTrue();
         assertThat(change1.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
-                APP_FOCUS_TYPE_NAVIGATION, true)).isFalse();
+                APP_FOCUS_TYPE_NAVIGATION, true)).isTrue();
 
         assertThat(manager2.requestAppFocus(APP_FOCUS_TYPE_NAVIGATION, owner2))
                 .isEqualTo(CarAppFocusManager.APP_FOCUS_REQUEST_SUCCEEDED);
@@ -337,6 +337,15 @@
         private final Semaphore mLossEventWait = new Semaphore(0);
         private int mLastGrantEvent;
         private final Semaphore mGrantEventWait = new Semaphore(0);
+        private final boolean mAssertEventThread;
+
+        private FocusOwnershipCallback(boolean assertEventThread) {
+            mAssertEventThread = assertEventThread;
+        }
+
+        private FocusOwnershipCallback() {
+            this(true);
+        }
 
         boolean waitForOwnershipLossAndAssert(long timeoutMs, int expectedAppType)
                 throws Exception {
@@ -359,7 +368,9 @@
         @Override
         public void onAppFocusOwnershipLost(int appType) {
             Log.i(TAG, "onAppFocusOwnershipLost " + appType);
-            assertEventThread();
+            if (mAssertEventThread) {
+                assertEventThread();
+            }
             mLastLossEvent = appType;
             mLossEventWait.release();
         }
diff --git a/tests/android_car_api_test/src/android/car/apitest/CarUserManagerTest.java b/tests/android_car_api_test/src/android/car/apitest/CarUserManagerTest.java
index 08ccdd6..421dd44 100644
--- a/tests/android_car_api_test/src/android/car/apitest/CarUserManagerTest.java
+++ b/tests/android_car_api_test/src/android/car/apitest/CarUserManagerTest.java
@@ -15,6 +15,9 @@
  */
 package android.car.apitest;
 
+import static android.car.test.util.UserTestingHelper.clearUserLockCredentials;
+import static android.car.test.util.UserTestingHelper.setMaxSupportedUsers;
+import static android.car.test.util.UserTestingHelper.setUserLockCredentials;
 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_STARTING;
 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_STOPPED;
 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_STOPPING;
@@ -42,6 +45,8 @@
 import android.os.UserManager;
 import android.util.Log;
 
+import androidx.test.filters.FlakyTest;
+
 import com.android.internal.infra.AndroidFuture;
 
 import org.junit.AfterClass;
@@ -49,6 +54,7 @@
 import org.junit.BeforeClass;
 import org.junit.Test;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Executor;
 import java.util.concurrent.TimeUnit;
@@ -58,6 +64,8 @@
 
     private static final String TAG = CarUserManagerTest.class.getSimpleName();
 
+    private static final int PIN = 2345;
+
     private static final int SWITCH_TIMEOUT_MS = 70_000;
     private static final int STOP_TIMEOUT_MS = 300_000;
 
@@ -69,8 +77,11 @@
 
     private static final UserManager sUserManager = UserManager.get(sContext);
 
+    private static final int sMaxNumberUsersBefore = UserManager.getMaxSupportedUsers();
+
+    private static final List<Integer> sCreatedUsers = new ArrayList<>();
+
     private static int sInitialUserId = UserHandle.USER_NULL;
-    private static int sNewUserId = UserHandle.USER_NULL;
 
     private CarUserManager mCarUserManager;
 
@@ -78,25 +89,20 @@
     public static void setupUsers() {
         sInitialUserId = ActivityManager.getCurrentUser();
         Log.i(TAG, "Running test as user " + sInitialUserId);
-
-        sNewUserId = createNewUser("Main", /* isGuest= */ false).id;
+        setMaxSupportedUsers(8); // Total 6 users will be created for all tests
     }
 
     @AfterClass
     public static void cleanupUsers() {
+        setMaxSupportedUsers(sMaxNumberUsersBefore);
         switchUserDirectly(sInitialUserId);
 
-        if (sNewUserId == UserHandle.USER_NULL) {
-            Log.w(TAG, "No need to remove user" + sNewUserId);
-            return;
-        }
-
-        Log.i(TAG, "Switching back to " + sInitialUserId);
-        switchUserDirectly(sInitialUserId);
-
-        Log.i(TAG, "Removing user" + sNewUserId);
-        if (!sUserManager.removeUser(sNewUserId)) {
-            Log.wtf(TAG, "Failed to remove user " + sNewUserId);
+        for (int i = 0; i < sCreatedUsers.size(); i++) {
+            int id = sCreatedUsers.get(i);
+            Log.d(TAG, "removeCreatedUsers: " + id);
+            if (!sUserManager.removeUser(id)) {
+                Log.wtf(TAG, "Failed to remove user " + id);
+            }
         }
     }
 
@@ -107,8 +113,8 @@
 
     @Test
     public void testLifecycleListener() throws Exception {
-        int oldUserId = sInitialUserId;
-        int newUserId = sNewUserId;
+        int oldUserId = ActivityManager.getCurrentUser();
+        int newUserId = createNewUser("Test", /* isGuest= */ false).id;
 
         BlockingUserLifecycleListener startListener = BlockingUserLifecycleListener
                 .forSpecificEvents()
@@ -132,14 +138,16 @@
         // Switch while listener is registered
         switchUser(newUserId);
 
-        List<UserLifecycleEvent> startEvents = startListener.waitForEvents();
+        List<UserLifecycleEvent> startEvents  = startListener.waitForEvents();
         Log.d(TAG, "Received start events: " + startEvents);
 
         // Make sure listener callback was executed in the proper threaqd
         assertWithMessage("not executed on executor").that(executedRef.get()).isTrue();
 
         // Assert user ids
-        for (UserLifecycleEvent event : startEvents) {
+        List<UserLifecycleEvent> expectedEvents = startListener.waitForEvents();
+        Log.d(TAG, "Received expected events: " + expectedEvents);
+        for (UserLifecycleEvent event : expectedEvents) {
             assertWithMessage("wrong userId on %s", event)
                 .that(event.getUserId()).isEqualTo(newUserId);
             assertWithMessage("wrong userHandle on %s", event)
@@ -166,8 +174,8 @@
         Log.d(TAG, "registering stop listener: " + stopListener);
         mCarUserManager.addListener(mExecutor, stopListener);
 
-        // Switch back to the previous user
-        switchUser(oldUserId);
+        // Switch back to the initial user
+        switchUser(sInitialUserId);
 
         if (TEST_STOP) {
             // Must force stop the user, otherwise it can take minutes for its process to finish
@@ -188,8 +196,7 @@
             Log.w(TAG, "NOT testing user stop events");
         }
 
-        // Make sure unregistered listener din't receive any more events
-
+        // Make sure unregistered listener didn't receive any more events
         List<UserLifecycleEvent> allStartEvents = startListener.getAllReceivedEvents();
         Log.d(TAG, "All start events: " + startEvents);
         assertThat(allStartEvents).containsAllIn(startEvents).inOrder();
@@ -198,13 +205,129 @@
         mCarUserManager.removeListener(stopListener);
     }
 
+    /**
+     * Tests resume behavior when current user is ephemeral guest, a new guest user should be
+     * created and switched to.
+     */
+    @Test
+    @FlakyTest // TODO(b/158050171) remove once process is stable on user switching.
+    public void testGuestUserResumeToNewGuestUser() throws Exception {
+        // Create new guest user
+        UserInfo guestUser = createNewUser("Guest", /* isGuestUser= */ true);
+        int guestUserId = guestUser.id;
+
+        // Wait for this user to be active
+        BlockingUserLifecycleListener listener1 = BlockingUserLifecycleListener
+                .forSpecificEvents()
+                .forUser(guestUserId)
+                .setTimeout(SWITCH_TIMEOUT_MS)
+                .addExpectedEvent(USER_LIFECYCLE_EVENT_TYPE_UNLOCKED)
+                .build();
+        mCarUserManager.addListener(Runnable::run, listener1);
+        switchUser(guestUserId);
+        listener1.waitForEvents();
+        mCarUserManager.removeListener(listener1);
+
+        BlockingUserLifecycleListener listener2 = BlockingUserLifecycleListener
+                .forSpecificEvents()
+                .forPreviousUser(guestUserId)
+                .setTimeout(SWITCH_TIMEOUT_MS)
+                .addExpectedEvent(USER_LIFECYCLE_EVENT_TYPE_SWITCHING)
+                .build();
+        // Make sure listener callback was executed in the proper threaqd
+        mCarUserManager.addListener(Runnable::run, listener2);
+
+        // Emulate suspend to RAM
+        suspendToRamAndResume();
+        UserLifecycleEvent event = listener2.waitForEvents().get(0);
+
+        int newGuestId = event.getUserId();
+        sCreatedUsers.add(newGuestId);
+
+        assertWithMessage("wrong user on event %s", event).that(newGuestId)
+                .isNotEqualTo(guestUserId);
+        assertWithMessage("wrong current user").that(newGuestId)
+                .isEqualTo(ActivityManager.getCurrentUser());
+        UserInfo newGuest = sUserManager.getUserInfo(newGuestId);
+        assertWithMessage("new user (%s) is not a guest", newGuest.toFullString())
+                .that(newGuest.isGuest()).isTrue();
+        assertWithMessage("wrong name on new guest(%s)", newGuest.toFullString())
+                .that(newGuest.name).isNotEqualTo(guestUser.name);
+        mCarUserManager.removeListener(listener2);
+    }
+
+    /**
+     * Tests resume behavior when current user is guest guest but with secured lock screen,
+     * resume to same guest user.
+     */
+    @Test
+    @FlakyTest // TODO(b/158050171) remove once process is stable on user switching.
+    public void testSecuredGuestUserResumeToSameUser() throws Exception {
+        // Create new guest user
+        UserInfo guestUser = createNewUser("Guest", /* isGuestUser= */ true);
+        int guestUserId = guestUser.id;
+
+        // Wait for this user to be active
+        BlockingUserLifecycleListener listener = BlockingUserLifecycleListener
+                .forSpecificEvents()
+                .forUser(guestUserId)
+                .setTimeout(SWITCH_TIMEOUT_MS)
+                .addExpectedEvent(USER_LIFECYCLE_EVENT_TYPE_UNLOCKED)
+                .build();
+        mCarUserManager.addListener(Runnable::run, listener);
+
+        switchUser(guestUserId);
+
+        listener.waitForEvents();
+        mCarUserManager.removeListener(listener);
+
+        setUserLockCredentials(guestUserId, PIN);
+        try {
+            // Emulate suspend to RAM
+            suspendToRamAndResume();
+
+            assertWithMessage("not resumed to previous user: %s", guestUser)
+                    .that(ActivityManager.getCurrentUser()).isEqualTo(guestUserId);
+        } finally {
+            clearUserLockCredentials(guestUserId, PIN);
+        }
+
+    }
+
+    /**
+     * Tests resume behavior when current user is persistent user.
+     */
+    @Test
+    @FlakyTest // TODO(b/158050171) remove once process is stable on user switching.
+    public void testPersistentUserResumeToUser() throws Exception {
+        int newUserId = createNewUser("Test", /* isGuest= */ false).id;
+        BlockingUserLifecycleListener listener = BlockingUserLifecycleListener
+                .forSpecificEvents()
+                .forUser(newUserId)
+                .setTimeout(SWITCH_TIMEOUT_MS)
+                .addExpectedEvent(USER_LIFECYCLE_EVENT_TYPE_UNLOCKED)
+                .build();
+        mCarUserManager.addListener(Runnable::run, listener);
+        switchUser(newUserId);
+        listener.waitForEvents();
+
+        // Emulate suspend to RAM
+        suspendToRamAndResume();
+
+        listener.waitForEvents();
+        assertWithMessage("not resumed to previous user: %s", newUserId)
+                .that(ActivityManager.getCurrentUser()).isEqualTo(newUserId);
+
+        mCarUserManager.removeListener(listener);
+    }
+
     @NonNull
     private static UserInfo createNewUser(String name, boolean isGuest) {
         name = "CarUserManagerTest." + name;
         Log.i(TAG, "Creating new user " + name);
         UserInfo newUser = isGuest ? sUserManager.createGuest(sContext, name)
                 : sUserManager.createUser(name, /* flags= */ 0);
-
+        sCreatedUsers.add(newUser.id);
         Log.i(TAG, "Created new user: " + newUser.toFullString());
         return newUser;
     }
diff --git a/tests/carservice_test/res/raw/car_audio_configuration.xml b/tests/carservice_test/res/raw/car_audio_configuration.xml
index b4bb08a..e1d75df 100644
--- a/tests/carservice_test/res/raw/car_audio_configuration.xml
+++ b/tests/carservice_test/res/raw/car_audio_configuration.xml
@@ -21,10 +21,6 @@
                     </device>
                 </group>
             </volumeGroups>
-            <displays>
-                <display port="1"/>
-                <display port="2"/>
-            </displays>
         </zone>
         <zone name="rear seat zone" audioZoneId="2">
             <volumeGroups>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_V1.xml b/tests/carservice_test/res/raw/car_audio_configuration_V1.xml
index 4aaaa6c..26b1c08 100644
--- a/tests/carservice_test/res/raw/car_audio_configuration_V1.xml
+++ b/tests/carservice_test/res/raw/car_audio_configuration_V1.xml
@@ -33,10 +33,6 @@
                     </device>
                 </group>
             </volumeGroups>
-            <displays>
-                <display port="1"/>
-                <display port="2"/>
-            </displays>
         </zone>
         <zone name="rear seat zone">
             <volumeGroups>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_V1_with_non_legacy_contexts.xml b/tests/carservice_test/res/raw/car_audio_configuration_V1_with_non_legacy_contexts.xml
index 03e9a0f..5c39ceb 100644
--- a/tests/carservice_test/res/raw/car_audio_configuration_V1_with_non_legacy_contexts.xml
+++ b/tests/carservice_test/res/raw/car_audio_configuration_V1_with_non_legacy_contexts.xml
@@ -33,10 +33,6 @@
                     </device>
                 </group>
             </volumeGroups>
-            <displays>
-                <display port="1"/>
-                <display port="2"/>
-            </displays>
         </zone>
         <zone name="rear seat zone">
             <volumeGroups>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_duplicate_audio_zone_id.xml b/tests/carservice_test/res/raw/car_audio_configuration_duplicate_audio_zone_id.xml
index 4b5abc5..8561968 100644
--- a/tests/carservice_test/res/raw/car_audio_configuration_duplicate_audio_zone_id.xml
+++ b/tests/carservice_test/res/raw/car_audio_configuration_duplicate_audio_zone_id.xml
@@ -37,10 +37,6 @@
                     </device>
                 </group>
             </volumeGroups>
-            <displays>
-                <display port="1"/>
-                <display port="2"/>
-            </displays>
         </zone>
         <zone name="rear seat zone" audioZoneId="1" occupantZoneId="2">
             <volumeGroups>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_duplicate_occupant_zone_id.xml b/tests/carservice_test/res/raw/car_audio_configuration_duplicate_occupant_zone_id.xml
index 1d15b36..8c1d0e8 100644
--- a/tests/carservice_test/res/raw/car_audio_configuration_duplicate_occupant_zone_id.xml
+++ b/tests/carservice_test/res/raw/car_audio_configuration_duplicate_occupant_zone_id.xml
@@ -37,10 +37,6 @@
                     </device>
                 </group>
             </volumeGroups>
-            <displays>
-                <display port="1"/>
-                <display port="2"/>
-            </displays>
         </zone>
         <zone name="rear seat zone" audioZoneId="2" occupantZoneId="1">
             <volumeGroups>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_duplicate_ports.xml b/tests/carservice_test/res/raw/car_audio_configuration_duplicate_ports.xml
index 288e8fb..dba13a2 100644
--- a/tests/carservice_test/res/raw/car_audio_configuration_duplicate_ports.xml
+++ b/tests/carservice_test/res/raw/car_audio_configuration_duplicate_ports.xml
@@ -17,10 +17,6 @@
                     </device>
                 </group>
             </volumeGroups>
-            <displays>
-                <display port="1"/>
-                <display port="1"/>
-            </displays>
         </zone>
     </zones>
 </carAudioConfiguration>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_empty_occupant_zone_id.xml b/tests/carservice_test/res/raw/car_audio_configuration_empty_occupant_zone_id.xml
index 182e606..972da37 100644
--- a/tests/carservice_test/res/raw/car_audio_configuration_empty_occupant_zone_id.xml
+++ b/tests/carservice_test/res/raw/car_audio_configuration_empty_occupant_zone_id.xml
@@ -37,10 +37,6 @@
                     </device>
                 </group>
             </volumeGroups>
-            <displays>
-                <display port="1"/>
-                <display port="2"/>
-            </displays>
         </zone>
         <zone name="rear seat zone" audioZoneId="2">
             <volumeGroups>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_negative_audio_zone_id.xml b/tests/carservice_test/res/raw/car_audio_configuration_negative_audio_zone_id.xml
index acdb784..060934b 100644
--- a/tests/carservice_test/res/raw/car_audio_configuration_negative_audio_zone_id.xml
+++ b/tests/carservice_test/res/raw/car_audio_configuration_negative_audio_zone_id.xml
@@ -37,10 +37,6 @@
                     </device>
                 </group>
             </volumeGroups>
-            <displays>
-                <display port="1"/>
-                <display port="2"/>
-            </displays>
         </zone>
         <zone name="rear seat zone" audioZoneId="2">
             <volumeGroups>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_negative_occupant_zone_id.xml b/tests/carservice_test/res/raw/car_audio_configuration_negative_occupant_zone_id.xml
index b7d7a62..cffad51 100644
--- a/tests/carservice_test/res/raw/car_audio_configuration_negative_occupant_zone_id.xml
+++ b/tests/carservice_test/res/raw/car_audio_configuration_negative_occupant_zone_id.xml
@@ -37,10 +37,6 @@
                     </device>
                 </group>
             </volumeGroups>
-            <displays>
-                <display port="1"/>
-                <display port="2"/>
-            </displays>
         </zone>
         <zone name="rear seat zone" audioZoneId="2">
             <volumeGroups>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_non_numerical_audio_zone_id.xml b/tests/carservice_test/res/raw/car_audio_configuration_non_numerical_audio_zone_id.xml
index 09168a6..8e1c41b 100644
--- a/tests/carservice_test/res/raw/car_audio_configuration_non_numerical_audio_zone_id.xml
+++ b/tests/carservice_test/res/raw/car_audio_configuration_non_numerical_audio_zone_id.xml
@@ -37,10 +37,6 @@
                     </device>
                 </group>
             </volumeGroups>
-            <displays>
-                <display port="1"/>
-                <display port="2"/>
-            </displays>
         </zone>
         <zone name="rear seat zone" audioZoneId="2">
             <volumeGroups>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_non_numerical_occupant_zone_id.xml b/tests/carservice_test/res/raw/car_audio_configuration_non_numerical_occupant_zone_id.xml
index af57c24..f00b169 100644
--- a/tests/carservice_test/res/raw/car_audio_configuration_non_numerical_occupant_zone_id.xml
+++ b/tests/carservice_test/res/raw/car_audio_configuration_non_numerical_occupant_zone_id.xml
@@ -37,10 +37,6 @@
                     </device>
                 </group>
             </volumeGroups>
-            <displays>
-                <display port="1"/>
-                <display port="2"/>
-            </displays>
         </zone>
         <zone name="rear seat zone" audioZoneId="2">
             <volumeGroups>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_non_numerical_port.xml b/tests/carservice_test/res/raw/car_audio_configuration_non_numerical_port.xml
index f6995bc..9adef24 100644
--- a/tests/carservice_test/res/raw/car_audio_configuration_non_numerical_port.xml
+++ b/tests/carservice_test/res/raw/car_audio_configuration_non_numerical_port.xml
@@ -21,9 +21,6 @@
                     </device>
                 </group>
             </volumeGroups>
-            <displays>
-                <display port="one"/>
-            </displays>
         </zone>
     </zones>
 </carAudioConfiguration>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_non_primary_zone_with_primary_audio_zone_id.xml b/tests/carservice_test/res/raw/car_audio_configuration_non_primary_zone_with_primary_audio_zone_id.xml
index f822469..4364a1d 100644
--- a/tests/carservice_test/res/raw/car_audio_configuration_non_primary_zone_with_primary_audio_zone_id.xml
+++ b/tests/carservice_test/res/raw/car_audio_configuration_non_primary_zone_with_primary_audio_zone_id.xml
@@ -33,10 +33,6 @@
                     </device>
                 </group>
             </volumeGroups>
-            <displays>
-                <display port="1"/>
-                <display port="2"/>
-            </displays>
         </zone>
         <zone name="rear seat zone" audioZoneId="0">
             <volumeGroups>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_output_address_does_not_exist.xml b/tests/carservice_test/res/raw/car_audio_configuration_output_address_does_not_exist.xml
index 83c054c..a359d36 100644
--- a/tests/carservice_test/res/raw/car_audio_configuration_output_address_does_not_exist.xml
+++ b/tests/carservice_test/res/raw/car_audio_configuration_output_address_does_not_exist.xml
@@ -37,10 +37,6 @@
                     </device>
                 </group>
             </volumeGroups>
-            <displays>
-                <display port="1"/>
-                <display port="2"/>
-            </displays>
         </zone>
         <zone name="rear seat zone" audioZoneId="2">
             <volumeGroups>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_primary_zone_with_non_zero_audio_zone_id.xml b/tests/carservice_test/res/raw/car_audio_configuration_primary_zone_with_non_zero_audio_zone_id.xml
index 6f845e7..2d25716 100644
--- a/tests/carservice_test/res/raw/car_audio_configuration_primary_zone_with_non_zero_audio_zone_id.xml
+++ b/tests/carservice_test/res/raw/car_audio_configuration_primary_zone_with_non_zero_audio_zone_id.xml
@@ -37,10 +37,6 @@
                     </device>
                 </group>
             </volumeGroups>
-            <displays>
-                <display port="1"/>
-                <display port="2"/>
-            </displays>
         </zone>
         <zone name="rear seat zone" audioZoneId="2">
             <volumeGroups>
diff --git a/tests/carservice_test/src/com/android/car/audio/CarAudioZonesHelperTest.java b/tests/carservice_test/src/com/android/car/audio/CarAudioZonesHelperTest.java
index a37c0e4..fce0a78 100644
--- a/tests/carservice_test/src/com/android/car/audio/CarAudioZonesHelperTest.java
+++ b/tests/carservice_test/src/com/android/car/audio/CarAudioZonesHelperTest.java
@@ -28,7 +28,6 @@
 import android.media.AudioDeviceAttributes;
 import android.media.AudioDeviceInfo;
 import android.util.SparseIntArray;
-import android.view.DisplayAddress;
 
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -293,56 +292,6 @@
     }
 
     @Test
-    public void loadAudioZones_parsesPhysicalDisplayAddresses() throws Exception {
-        CarAudioZonesHelper cazh = new CarAudioZonesHelper(mCarAudioSettings, mInputStream,
-                mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
-
-        CarAudioZone[] zones = cazh.loadAudioZones();
-
-        CarAudioZone primaryZone = zones[0];
-        List<DisplayAddress.Physical> primaryPhysicals = primaryZone.getPhysicalDisplayAddresses();
-        assertThat(primaryPhysicals).hasSize(2);
-        assertThat(primaryPhysicals.get(0).getPort()).isEqualTo(1);
-        assertThat(primaryPhysicals.get(1).getPort()).isEqualTo(2);
-    }
-
-    @Test
-    public void loadAudioZones_defaultsDisplayAddressesToEmptyList() throws Exception {
-        CarAudioZonesHelper cazh = new CarAudioZonesHelper(mCarAudioSettings, mInputStream,
-                mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
-
-        CarAudioZone[] zones = cazh.loadAudioZones();
-
-        CarAudioZone rseZone = zones[1];
-        List<DisplayAddress.Physical> rsePhysicals = rseZone.getPhysicalDisplayAddresses();
-        assertThat(rsePhysicals).isEmpty();
-    }
-
-    @Test(expected = RuntimeException.class)
-    public void loadAudioZones_throwsOnDuplicatePorts() throws Exception {
-        try (InputStream duplicatePortStream = mContext.getResources().openRawResource(
-                R.raw.car_audio_configuration_duplicate_ports)) {
-            CarAudioZonesHelper cazh =
-                    new CarAudioZonesHelper(mCarAudioSettings, duplicatePortStream,
-                            mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
-
-            cazh.loadAudioZones();
-        }
-    }
-
-    @Test
-    public void loadAudioZones_throwsOnNonNumericalPort() {
-        InputStream duplicatePortStream = mContext.getResources().openRawResource(
-                R.raw.car_audio_configuration_non_numerical_port);
-        CarAudioZonesHelper cazh = new CarAudioZonesHelper(mCarAudioSettings, duplicatePortStream,
-                mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
-
-        IllegalArgumentException exception = expectThrows(IllegalArgumentException.class,
-                cazh::loadAudioZones);
-        assertThat(exception).hasMessageThat().contains("Port one is not a number");
-    }
-
-    @Test
     public void loadAudioZones_passesOnMissingAudioZoneIdForPrimary() throws Exception {
         try (InputStream missingAudioZoneIdStream = mContext.getResources().openRawResource(
                 R.raw.car_audio_configuration_no_audio_zone_id_for_primary_zone)) {
diff --git a/tests/carservice_test/src/com/android/car/watchdog/CarWatchdogServiceTest.java b/tests/carservice_test/src/com/android/car/watchdog/CarWatchdogServiceTest.java
index fb65ff0..28d70e6 100644
--- a/tests/carservice_test/src/com/android/car/watchdog/CarWatchdogServiceTest.java
+++ b/tests/carservice_test/src/com/android/car/watchdog/CarWatchdogServiceTest.java
@@ -17,7 +17,7 @@
 package com.android.car.watchdog;
 
 import static android.car.test.mocks.AndroidMockitoHelper.mockQueryService;
-import static android.car.test.mocks.AndroidMockitoHelper.mockUmGetUsers;
+import static android.car.test.mocks.AndroidMockitoHelper.mockUmGetAllUsers;
 import static android.car.test.mocks.AndroidMockitoHelper.mockUmIsUserRunning;
 import static android.car.test.util.UserTestingHelper.UserInfoBuilder;
 import static android.car.watchdog.CarWatchdogManager.TIMEOUT_CRITICAL;
@@ -100,7 +100,7 @@
         when(mCar.getEventHandler()).thenReturn(mMainHandler);
         when(mServiceBinder.queryLocalInterface(anyString())).thenReturn(mCarWatchdogService);
         when(mMockContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
-        mockUmGetUsers(mUserManager, mUserInfos);
+        mockUmGetAllUsers(mUserManager, mUserInfos);
         mockUmIsUserRunning(mUserManager, 10, true);
         mockUmIsUserRunning(mUserManager, 11, false);
 
diff --git a/tests/carservice_unit_test/src/android/car/userlib/CarUserManagerHelperTest.java b/tests/carservice_unit_test/src/android/car/userlib/CarUserManagerHelperTest.java
index 5fe5670..62cf6d4 100644
--- a/tests/carservice_unit_test/src/android/car/userlib/CarUserManagerHelperTest.java
+++ b/tests/carservice_unit_test/src/android/car/userlib/CarUserManagerHelperTest.java
@@ -16,6 +16,8 @@
 
 package android.car.userlib;
 
+import static android.car.test.mocks.AndroidMockitoHelper.mockUmGetSystemUser;
+import static android.car.test.mocks.AndroidMockitoHelper.mockUmGetUserInfo;
 import static android.car.test.mocks.AndroidMockitoHelper.mockUmGetUsers;
 import static android.car.test.util.UserTestingHelper.newUser;
 import static android.os.UserHandle.USER_SYSTEM;
@@ -31,6 +33,7 @@
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
+import android.car.settings.CarSettings;
 import android.car.test.mocks.AbstractExtendedMockitoTestCase;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -38,7 +41,6 @@
 import android.content.res.Resources;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.provider.Settings;
 import android.sysprop.CarProperties;
 
 import androidx.test.InstrumentationRegistry;
@@ -192,12 +194,31 @@
     }
 
     @Test
-    public void testGetInitialUser_WithNonExistLastActiveUser_ReturnsSmallestUserId() {
-        setLastActiveUser(12);
-        mockGetUsers(USER_SYSTEM, 10, 10 + 1);
+    public void testGetInitialUser_WithNonExistLastActiveUser_ReturnsLastPersistentUser() {
+        setLastActiveUser(120);
+        setLastPersistentActiveUser(110);
+        mockGetUsers(USER_SYSTEM, 100, 110);
 
         assertThat(mCarUserManagerHelper.getInitialUser(/* usesOverrideUserIdProperty= */ true))
-                .isEqualTo(10);
+                .isEqualTo(110);
+        // should have reset last active user
+        assertThat(getSettingsInt(CarSettings.Global.LAST_ACTIVE_USER_ID))
+                .isEqualTo(UserHandle.USER_NULL);
+    }
+
+    @Test
+    public void testGetInitialUser_WithNonExistLastActiveAndPersistentUsers_ReturnsSmallestUser() {
+        setLastActiveUser(120);
+        setLastPersistentActiveUser(120);
+        mockGetUsers(USER_SYSTEM, 100, 110);
+
+        assertThat(mCarUserManagerHelper.getInitialUser(/* usesOverrideUserIdProperty= */ true))
+                .isEqualTo(100);
+        // should have reset both settions
+        assertThat(getSettingsInt(CarSettings.Global.LAST_ACTIVE_USER_ID))
+                .isEqualTo(UserHandle.USER_NULL);
+        assertThat(getSettingsInt(CarSettings.Global.LAST_ACTIVE_PERSISTENT_USER_ID))
+                .isEqualTo(UserHandle.USER_NULL);
     }
 
     @Test
@@ -289,6 +310,65 @@
         assertThat(mCarUserManagerHelper.hasInitialUser()).isFalse();
     }
 
+    @Test
+    public void testSetLastActiveUser_headlessSystem() {
+        mockIsHeadlessSystemUserMode(true);
+        mockUmGetSystemUser(mUserManager);
+
+        mCarUserManagerHelper.setLastActiveUser(UserHandle.USER_SYSTEM);
+
+        assertSettingsNotSet(CarSettings.Global.LAST_ACTIVE_USER_ID);
+        assertSettingsNotSet(CarSettings.Global.LAST_ACTIVE_PERSISTENT_USER_ID);
+    }
+
+    @Test
+    public void testSetLastActiveUser_nonHeadlessSystem() {
+        mockIsHeadlessSystemUserMode(false);
+        mockUmGetSystemUser(mUserManager);
+
+        mCarUserManagerHelper.setLastActiveUser(UserHandle.USER_SYSTEM);
+
+        assertThat(getSettingsInt(CarSettings.Global.LAST_ACTIVE_USER_ID))
+                .isEqualTo(UserHandle.USER_SYSTEM);
+        assertThat(getSettingsInt(CarSettings.Global.LAST_ACTIVE_PERSISTENT_USER_ID))
+                .isEqualTo(UserHandle.USER_SYSTEM);
+    }
+
+    @Test
+    public void testSetLastActiveUser_nonExistingUser() {
+        // Don't need to mock um.getUser(), it will return null by default
+        mCarUserManagerHelper.setLastActiveUser(42);
+
+        assertThat(getSettingsInt(CarSettings.Global.LAST_ACTIVE_USER_ID)).isEqualTo(42);
+        assertSettingsNotSet(CarSettings.Global.LAST_ACTIVE_PERSISTENT_USER_ID);
+    }
+
+    @Test
+    public void testSetLastActiveUser_ephemeralUser() {
+        int persistentUserId = 42;
+        int ephemeralUserid = 108;
+        mockUmGetUserInfo(mUserManager, persistentUserId, NO_FLAGS);
+        mockUmGetUserInfo(mUserManager, ephemeralUserid, UserInfo.FLAG_EPHEMERAL);
+
+        mCarUserManagerHelper.setLastActiveUser(persistentUserId);
+        mCarUserManagerHelper.setLastActiveUser(ephemeralUserid);
+
+        assertThat(getSettingsInt(CarSettings.Global.LAST_ACTIVE_USER_ID))
+                .isEqualTo(ephemeralUserid);
+        assertThat(getSettingsInt(CarSettings.Global.LAST_ACTIVE_PERSISTENT_USER_ID))
+                .isEqualTo(persistentUserId);
+    }
+
+    @Test
+    public void testSetLastActiveUser_nonEphemeralUser() {
+        mockUmGetUserInfo(mUserManager, 42, NO_FLAGS);
+
+        mCarUserManagerHelper.setLastActiveUser(42);
+
+        assertThat(getSettingsInt(CarSettings.Global.LAST_ACTIVE_USER_ID)).isEqualTo(42);
+        assertThat(getSettingsInt(CarSettings.Global.LAST_ACTIVE_PERSISTENT_USER_ID)).isEqualTo(42);
+    }
+
     private void mockGetUsers(@NonNull @UserIdInt int... userIds) {
         mockUmGetUsers(mUserManager, userIds);
     }
@@ -298,7 +378,11 @@
     }
 
     private void setLastActiveUser(@UserIdInt int userId) {
-        putSettingsInt(Settings.Global.LAST_ACTIVE_USER_ID, userId);
+        putSettingsInt(CarSettings.Global.LAST_ACTIVE_USER_ID, userId);
+    }
+
+    private void setLastPersistentActiveUser(@UserIdInt int userId) {
+        putSettingsInt(CarSettings.Global.LAST_ACTIVE_PERSISTENT_USER_ID, userId);
     }
 
     private void setDefaultBootUserOverride(@UserIdInt int userId) {
diff --git a/tests/carservice_unit_test/src/android/car/userlib/UserHalHelperTest.java b/tests/carservice_unit_test/src/android/car/userlib/UserHalHelperTest.java
index 6397c3c..29a1946 100644
--- a/tests/carservice_unit_test/src/android/car/userlib/UserHalHelperTest.java
+++ b/tests/carservice_unit_test/src/android/car/userlib/UserHalHelperTest.java
@@ -17,6 +17,8 @@
 package android.car.userlib;
 
 import static android.car.userlib.UserHalHelper.CREATE_USER_PROPERTY;
+import static android.car.userlib.UserHalHelper.REMOVE_USER_PROPERTY;
+import static android.car.userlib.UserHalHelper.SWITCH_USER_PROPERTY;
 import static android.car.userlib.UserHalHelper.USER_IDENTIFICATION_ASSOCIATION_PROPERTY;
 import static android.hardware.automotive.vehicle.V2_0.UserIdentificationAssociationSetValue.ASSOCIATE_CURRENT_USER;
 import static android.hardware.automotive.vehicle.V2_0.UserIdentificationAssociationSetValue.DISASSOCIATE_ALL_USERS;
@@ -49,6 +51,9 @@
 import android.hardware.automotive.vehicle.V2_0.InitialUserInfoRequestType;
 import android.hardware.automotive.vehicle.V2_0.InitialUserInfoResponse;
 import android.hardware.automotive.vehicle.V2_0.InitialUserInfoResponseAction;
+import android.hardware.automotive.vehicle.V2_0.RemoveUserRequest;
+import android.hardware.automotive.vehicle.V2_0.SwitchUserMessageType;
+import android.hardware.automotive.vehicle.V2_0.SwitchUserRequest;
 import android.hardware.automotive.vehicle.V2_0.UserFlags;
 import android.hardware.automotive.vehicle.V2_0.UserIdentificationAssociation;
 import android.hardware.automotive.vehicle.V2_0.UserIdentificationAssociationType;
@@ -861,6 +866,62 @@
     }
 
     @Test
+    public void testRemoveUserRequestToVehiclePropValue_null() {
+        assertThrows(NullPointerException.class,
+                () -> UserHalHelper.toVehiclePropValue((RemoveUserRequest) null));
+    }
+
+    @Test
+    public void testRemoveUserRequestToVehiclePropValue_emptyRequest() {
+        RemoveUserRequest request = new RemoveUserRequest();
+
+        assertThrows(IllegalArgumentException.class,
+                () -> UserHalHelper.toVehiclePropValue(request));
+    }
+
+    @Test
+    public void testRemoveUserRequestToVehiclePropValue_missingRequestId() {
+        RemoveUserRequest request = new RemoveUserRequest();
+        request.removedUserInfo.userId = 11;
+        request.usersInfo.existingUsers.add(request.removedUserInfo);
+
+        assertThrows(IllegalArgumentException.class,
+                () -> UserHalHelper.toVehiclePropValue(request));
+    }
+
+    @Test
+    public void testRemoveUserRequestToVehiclePropValue_ok() {
+        RemoveUserRequest request = new RemoveUserRequest();
+        request.requestId = 42;
+
+        android.hardware.automotive.vehicle.V2_0.UserInfo user10 =
+                new android.hardware.automotive.vehicle.V2_0.UserInfo();
+        user10.userId = 10;
+        user10.flags = UserFlags.ADMIN;
+
+        // existing users
+        request.usersInfo.numberUsers = 1;
+        request.usersInfo.existingUsers.add(user10);
+
+        // current user
+        request.usersInfo.currentUser = user10;
+        // user to remove
+        request.removedUserInfo = user10;
+
+        VehiclePropValue propValue = UserHalHelper.toVehiclePropValue(request);
+
+        assertWithMessage("wrong prop on %s", propValue).that(propValue.prop)
+                .isEqualTo(REMOVE_USER_PROPERTY);
+        assertWithMessage("wrong int32values on %s", propValue).that(propValue.value.int32Values)
+                .containsExactly(42, // request id
+                        10, UserFlags.ADMIN, // user to remove
+                        10, UserFlags.ADMIN, // current user
+                        1, // number of users
+                        10, UserFlags.ADMIN  // existing user 1
+                        ).inOrder();
+    }
+
+    @Test
     public void testCreateUserRequestToVehiclePropValue_null() {
         assertThrows(NullPointerException.class,
                 () -> UserHalHelper.toVehiclePropValue((CreateUserRequest) null));
@@ -885,6 +946,24 @@
     }
 
     @Test
+    public void testCreateUserRequestToVehiclePropValue_nullNewUserName() {
+        CreateUserRequest request = new CreateUserRequest();
+        request.requestId = 42;
+
+        request.newUserInfo.userId = 10;
+        request.newUserInfo.flags = UserFlags.ADMIN;
+        request.newUserName = null;
+
+        request.usersInfo.numberUsers = 1;
+        request.usersInfo.currentUser.userId = request.newUserInfo.userId;
+        request.usersInfo.currentUser.flags = request.newUserInfo.flags;
+        request.usersInfo.existingUsers.add(request.usersInfo.currentUser);
+
+        assertThrows(IllegalArgumentException.class,
+                () -> UserHalHelper.toVehiclePropValue(request));
+    }
+
+    @Test
     public void testCreateUserRequestToVehiclePropValue_usersInfoDoesNotContainNewUser() {
         CreateUserRequest request = new CreateUserRequest();
         request.requestId = 42;
@@ -966,13 +1045,91 @@
     }
 
     @Test
+    public void testSwitchUserRequestToVehiclePropValue_null() {
+        assertThrows(NullPointerException.class,
+                () -> UserHalHelper.toVehiclePropValue((SwitchUserRequest) null));
+    }
+
+    @Test
+    public void testSwitchUserRequestToVehiclePropValue_emptyRequest() {
+        SwitchUserRequest request = new SwitchUserRequest();
+
+        assertThrows(IllegalArgumentException.class,
+                () -> UserHalHelper.toVehiclePropValue(request));
+    }
+
+    @Test
+    public void testSwitchUserRequestToVehiclePropValue_missingMessageType() {
+        SwitchUserRequest request = new SwitchUserRequest();
+        request.requestId = 42;
+        android.hardware.automotive.vehicle.V2_0.UserInfo user10 =
+                new android.hardware.automotive.vehicle.V2_0.UserInfo();
+        user10.userId = 10;
+        request.usersInfo.numberUsers = 1;
+        request.usersInfo.existingUsers.add(user10);
+        request.usersInfo.currentUser = user10;
+        request.targetUser = user10;
+
+        assertThrows(IllegalArgumentException.class,
+                () -> UserHalHelper.toVehiclePropValue(request));
+    }
+
+    @Test
+    public void testSwitchUserRequestToVehiclePropValue_incorrectMessageType() {
+        SwitchUserRequest request = new SwitchUserRequest();
+        request.requestId = 42;
+        request.messageType = -1;
+        android.hardware.automotive.vehicle.V2_0.UserInfo user10 =
+                new android.hardware.automotive.vehicle.V2_0.UserInfo();
+        user10.userId = 10;
+        request.usersInfo.numberUsers = 1;
+        request.usersInfo.existingUsers.add(user10);
+        request.usersInfo.currentUser = user10;
+        request.targetUser = user10;
+
+        assertThrows(IllegalArgumentException.class,
+                () -> UserHalHelper.toVehiclePropValue(request));
+    }
+
+    @Test
+    public void tesSwitchUserRequestToVehiclePropValue_ok() {
+        SwitchUserRequest request = new SwitchUserRequest();
+        request.requestId = 42;
+        android.hardware.automotive.vehicle.V2_0.UserInfo user10 =
+                new android.hardware.automotive.vehicle.V2_0.UserInfo();
+        user10.userId = 10;
+        user10.flags = UserFlags.ADMIN;
+        // existing users
+        request.usersInfo.numberUsers = 1;
+        request.usersInfo.existingUsers.add(user10);
+        // current user
+        request.usersInfo.currentUser = user10;
+        // user to remove
+        request.targetUser = user10;
+        request.messageType = SwitchUserMessageType.ANDROID_SWITCH;
+
+        VehiclePropValue propValue = UserHalHelper.toVehiclePropValue(request);
+
+        assertWithMessage("wrong prop on %s", propValue).that(propValue.prop)
+                .isEqualTo(SWITCH_USER_PROPERTY);
+        assertWithMessage("wrong int32values on %s", propValue).that(propValue.value.int32Values)
+                .containsExactly(42, // request id
+                        SwitchUserMessageType.ANDROID_SWITCH, // message type
+                        10, UserFlags.ADMIN, // target user
+                        10, UserFlags.ADMIN, // current user
+                        1, // number of users
+                        10, UserFlags.ADMIN  // existing user 1
+                        ).inOrder();
+    }
+
+    @Test
     public void testNewUsersInfo_nullUm() {
-        assertThrows(IllegalArgumentException.class, () -> UserHalHelper.newUsersInfo(null));
+        assertThrows(IllegalArgumentException.class, () -> UserHalHelper.newUsersInfo(null, 100));
     }
 
     @Test
     public void testNewUsersInfo_nullUsers() {
-        UsersInfo usersInfo = UserHalHelper.newUsersInfo(mUm);
+        UsersInfo usersInfo = UserHalHelper.newUsersInfo(mUm, 100);
 
         assertEmptyUsersInfo(usersInfo);
     }
@@ -982,12 +1139,58 @@
         List<UserInfo> users = new ArrayList<>();
         AndroidMockitoHelper.mockUmGetUsers(mUm, users);
 
-        UsersInfo usersInfo = UserHalHelper.newUsersInfo(mUm);
+        UsersInfo usersInfo = UserHalHelper.newUsersInfo(mUm, 100);
 
         assertEmptyUsersInfo(usersInfo);
     }
 
     @Test
+    public void testNewUsersInfo_ok() {
+        UserInfo user100 = new UserInfoBuilder(100).setFlags(UserInfo.FLAG_ADMIN).build();
+        UserInfo user200 = new UserInfoBuilder(200).build();
+
+        AndroidMockitoHelper.mockUmGetUsers(mUm, user100, user200);
+        AndroidMockitoHelper.mockAmGetCurrentUser(300); // just to make sure it's not used
+
+        UsersInfo usersInfo = UserHalHelper.newUsersInfo(mUm, 100);
+
+        assertThat(usersInfo).isNotNull();
+        assertThat(usersInfo.currentUser.userId).isEqualTo(100);
+        assertThat(usersInfo.currentUser.flags).isEqualTo(UserFlags.ADMIN);
+
+        assertThat(usersInfo.numberUsers).isEqualTo(2);
+        assertThat(usersInfo.existingUsers).hasSize(2);
+
+        assertThat(usersInfo.existingUsers.get(0).userId).isEqualTo(100);
+        assertThat(usersInfo.existingUsers.get(0).flags).isEqualTo(UserFlags.ADMIN);
+        assertThat(usersInfo.existingUsers.get(1).userId).isEqualTo(200);
+        assertThat(usersInfo.existingUsers.get(1).flags).isEqualTo(UserFlags.NONE);
+    }
+
+    @Test
+    public void testNewUsersInfo_currentUser_ok() {
+        UserInfo user100 = new UserInfoBuilder(100).setFlags(UserInfo.FLAG_ADMIN).build();
+        UserInfo user200 = new UserInfoBuilder(200).build();
+
+        AndroidMockitoHelper.mockUmGetUsers(mUm, user100, user200);
+        AndroidMockitoHelper.mockAmGetCurrentUser(100);
+
+        UsersInfo usersInfo = UserHalHelper.newUsersInfo(mUm);
+
+        assertThat(usersInfo).isNotNull();
+        assertThat(usersInfo.currentUser.userId).isEqualTo(100);
+        assertThat(usersInfo.currentUser.flags).isEqualTo(UserFlags.ADMIN);
+
+        assertThat(usersInfo.numberUsers).isEqualTo(2);
+        assertThat(usersInfo.existingUsers).hasSize(2);
+
+        assertThat(usersInfo.existingUsers.get(0).userId).isEqualTo(100);
+        assertThat(usersInfo.existingUsers.get(0).flags).isEqualTo(UserFlags.ADMIN);
+        assertThat(usersInfo.existingUsers.get(1).userId).isEqualTo(200);
+        assertThat(usersInfo.existingUsers.get(1).flags).isEqualTo(UserFlags.NONE);
+    }
+
+    @Test
     public void testNewUsersInfo_noCurrentUser() {
         UserInfo user100 = new UserInfoBuilder(100).setFlags(UserInfo.FLAG_ADMIN).build();
         UserInfo user200 = new UserInfoBuilder(200).build();
@@ -1011,29 +1214,6 @@
     }
 
     @Test
-    public void testNewUsersInfo_ok() {
-        UserInfo user100 = new UserInfoBuilder(100).setFlags(UserInfo.FLAG_ADMIN).build();
-        UserInfo user200 = new UserInfoBuilder(200).build();
-
-        AndroidMockitoHelper.mockUmGetUsers(mUm, user100, user200);
-        AndroidMockitoHelper.mockAmGetCurrentUser(100);
-
-        UsersInfo usersInfo = UserHalHelper.newUsersInfo(mUm);
-
-        assertThat(usersInfo).isNotNull();
-        assertThat(usersInfo.currentUser.userId).isEqualTo(100);
-        assertThat(usersInfo.currentUser.flags).isEqualTo(UserFlags.ADMIN);
-
-        assertThat(usersInfo.numberUsers).isEqualTo(2);
-        assertThat(usersInfo.existingUsers).hasSize(2);
-
-        assertThat(usersInfo.existingUsers.get(0).userId).isEqualTo(100);
-        assertThat(usersInfo.existingUsers.get(0).flags).isEqualTo(UserFlags.ADMIN);
-        assertThat(usersInfo.existingUsers.get(1).userId).isEqualTo(200);
-        assertThat(usersInfo.existingUsers.get(1).flags).isEqualTo(UserFlags.NONE);
-    }
-
-    @Test
     public void testCheckValidUsersInfo_null() {
         assertThrows(IllegalArgumentException.class, () -> UserHalHelper.checkValid(null));
     }
diff --git a/tests/carservice_unit_test/src/com/android/car/CarInputServiceTest.java b/tests/carservice_unit_test/src/com/android/car/CarInputServiceTest.java
index 9f360e1..cabab88 100644
--- a/tests/carservice_unit_test/src/com/android/car/CarInputServiceTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/CarInputServiceTest.java
@@ -30,16 +30,25 @@
 import static org.mockito.Mockito.ignoreStubs;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
+import android.annotation.UserIdInt;
+import android.app.IActivityManager;
 import android.car.CarProjectionManager;
 import android.car.input.CarInputHandlingService.InputFilter;
 import android.car.input.ICarInputListener;
+import android.car.testapi.BlockingUserLifecycleListener;
+import android.car.user.CarUserManager;
+import android.car.userlib.CarUserManagerHelper;
 import android.content.ComponentName;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.UserInfo;
+import android.content.res.Resources;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
@@ -48,17 +57,25 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
 import android.service.voice.VoiceInteractionSession;
 import android.telecom.TelecomManager;
+import android.test.mock.MockContentResolver;
 import android.view.KeyEvent;
 
 import androidx.test.core.app.ApplicationProvider;
 
 import com.android.car.hal.InputHalService;
+import com.android.car.hal.UserHalService;
+import com.android.car.user.CarUserService;
 import com.android.internal.app.AssistUtils;
+import com.android.internal.util.test.BroadcastInterceptingContext;
+import com.android.internal.util.test.FakeSettingsProvider;
 
 import com.google.common.collect.Range;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -73,6 +90,9 @@
 
 @RunWith(MockitoJUnitRunner.class)
 public class CarInputServiceTest {
+    // TODO(b/152069895): decrease value once refactored. In fact, it should not even use
+    // runWithScissors(), but only rely on CountdownLatches
+    private static final long DEFAULT_TIMEOUT_MS = 5_000;
 
     @Mock InputHalService mInputHalService;
     @Mock TelecomManager mTelecomManager;
@@ -84,12 +104,54 @@
     @Spy Context mContext = ApplicationProvider.getApplicationContext();
     @Spy Handler mHandler = new Handler(Looper.getMainLooper());
 
+    private MockContext mMockContext;
+    private CarUserService mCarUserService;
     private CarInputService mCarInputService;
 
+    /**
+     * A mock {@link Context}.
+     * This class uses a mock {@link ContentResolver} and {@link android.content.ContentProvider} to
+     * avoid changing real system settings. Besides, to emulate the case where the OEM changes
+     * {@link R.string.rotaryService} to empty in the resource file (e.g., the OEM doesn't want to
+     * start RotaryService), this class allows to return a given String when retrieving {@link
+     * R.string.rotaryService}.
+     */
+    private static class MockContext extends BroadcastInterceptingContext {
+        private final MockContentResolver mContentResolver;
+        private final FakeSettingsProvider mContentProvider;
+        private final Resources mResources;
+
+        MockContext(Context base, String rotaryService) {
+            super(base);
+            FakeSettingsProvider.clearSettingsProvider();
+            mContentResolver = new MockContentResolver(this);
+            mContentProvider = new FakeSettingsProvider();
+            mContentResolver.addProvider(Settings.AUTHORITY, mContentProvider);
+
+            mResources = spy(base.getResources());
+            doReturn(rotaryService).when(mResources).getString(R.string.rotaryService);
+        }
+
+        void release() {
+            FakeSettingsProvider.clearSettingsProvider();
+        }
+
+        @Override
+        public ContentResolver getContentResolver() {
+            return mContentResolver;
+        }
+
+        @Override
+        public Resources getResources() {
+            return mResources;
+        }
+    }
+
     @Before
     public void setUp() {
-        mCarInputService = new CarInputService(mContext, mInputHalService, mHandler,
-                mTelecomManager, mAssistUtils, mDefaultMainListener, mLastCallSupplier,
+        mCarUserService = mock(CarUserService.class);
+        mCarInputService = new CarInputService(mContext, mInputHalService, mCarUserService,
+                mHandler, mTelecomManager, mAssistUtils, mDefaultMainListener, mLastCallSupplier,
                 /* customInputServiceComponent= */ null, mLongPressDelaySupplier);
 
         when(mInputHalService.isKeyInputSupported()).thenReturn(true);
@@ -100,6 +162,69 @@
     }
 
     @Test
+    public void rotaryServiceSettingsUpdated_whenRotaryServiceIsNotEmpty() throws Exception {
+        final String rotaryService = "com.android.car.rotary/com.android.car.rotary.RotaryService";
+        init(rotaryService);
+        assertThat(mMockContext.getString(R.string.rotaryService)).isEqualTo(rotaryService);
+
+        final int userId = 11;
+
+        // By default RotaryService is not enabled.
+        String enabledServices = Settings.Secure.getStringForUser(
+                mMockContext.getContentResolver(),
+                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+                userId);
+        assertThat(enabledServices == null ? "" : enabledServices).doesNotContain(rotaryService);
+
+        String enabled = Settings.Secure.getStringForUser(
+                mMockContext.getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_ENABLED,
+                userId);
+        assertThat(enabled).isNull();
+
+        // Enable RotaryService by sending user switch event.
+        sendUserLifecycleEvent(CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING, userId);
+
+        enabledServices = Settings.Secure.getStringForUser(
+                mMockContext.getContentResolver(),
+                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+                userId);
+        assertThat(enabledServices).contains(rotaryService);
+
+        enabled = Settings.Secure.getStringForUser(
+                mMockContext.getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_ENABLED,
+                userId);
+        assertThat(enabled).isEqualTo("1");
+    }
+
+    @Test
+    public void rotaryServiceSettingsNotUpdated_whenRotaryServiceIsEmpty() throws Exception {
+        final String rotaryService = "";
+        init(rotaryService);
+        assertThat(mMockContext.getString(R.string.rotaryService)).isEqualTo(rotaryService);
+
+        final int userId = 11;
+
+        // By default the Accessibility is disabled.
+        String enabled = Settings.Secure.getStringForUser(
+                mMockContext.getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_ENABLED,
+                userId);
+        assertThat(enabled).isNull();
+
+        sendUserLifecycleEvent(CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING, userId);
+
+        // Sending user switch event shouldn't enable the Accessibility because RotaryService is
+        // empty.
+        enabled = Settings.Secure.getStringForUser(
+                mMockContext.getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_ENABLED,
+                userId);
+        assertThat(enabled).isNull();
+    }
+
+    @Test
     public void ordinaryEvents_onMainDisplay_routedToInputManager() {
         KeyEvent event = send(Key.DOWN, KeyEvent.KEYCODE_ENTER, Display.MAIN);
 
@@ -458,6 +583,54 @@
         assertThat(timeCaptor.getValue()).isIn(Range.closed(then + systemDelay, now + systemDelay));
     }
 
+    @After
+    public void tearDown() {
+        if (mMockContext != null) {
+            mMockContext.release();
+            mMockContext = null;
+        }
+    }
+
+    /**
+     * Initializes {@link #mMockContext}, {@link #mCarUserService}, and {@link #mCarInputService}.
+     */
+    private void init(String rotaryService) {
+        mMockContext = new MockContext(mContext, rotaryService);
+
+        UserManager userManager = mock(UserManager.class);
+        UserInfo userInfo = mock(UserInfo.class);
+        doReturn(userInfo).when(userManager).getUserInfo(anyInt());
+        UserHalService userHal = mock(UserHalService.class);
+        CarUserManagerHelper carUserManagerHelper = mock(CarUserManagerHelper.class);
+        IActivityManager iActivityManager = mock(IActivityManager.class);
+        mCarUserService = new CarUserService(mMockContext, userHal, carUserManagerHelper,
+                userManager, iActivityManager, /* maxRunningUsers= */ 2);
+
+        mCarInputService = new CarInputService(mMockContext, mInputHalService, mCarUserService,
+                mHandler, mTelecomManager, mAssistUtils, mDefaultMainListener, mLastCallSupplier,
+                /* customInputServiceComponent= */ null, mLongPressDelaySupplier);
+        mCarInputService.init();
+    }
+
+    private void sendUserLifecycleEvent(@CarUserManager.UserLifecycleEventType int eventType,
+            @UserIdInt int userId) throws InterruptedException {
+        // Add a blocking listener to ensure CarUserService event notification is completed
+        // before proceeding with test execution.
+        BlockingUserLifecycleListener blockingListener =
+                BlockingUserLifecycleListener.forAnyEvent().build();
+        mCarUserService.addUserLifecycleListener(blockingListener);
+
+        runOnMainThreadAndWaitForIdle(() -> mCarUserService.onUserLifecycleEvent(eventType,
+                /* timestampMs= */ 0, /* fromUserId= */ UserHandle.USER_NULL, userId));
+        blockingListener.waitForAnyEvent();
+    }
+
+    private static void runOnMainThreadAndWaitForIdle(Runnable r) {
+        Handler.getMain().runWithScissors(r, DEFAULT_TIMEOUT_MS);
+        // Run empty runnable to make sure that all posted handlers are done.
+        Handler.getMain().runWithScissors(() -> { }, DEFAULT_TIMEOUT_MS);
+    }
+
     private enum Key {DOWN, UP}
 
     private enum Display {MAIN, INSTRUMENT_CLUSTER}
diff --git a/tests/carservice_unit_test/src/com/android/car/CarPowerManagementServiceTest.java b/tests/carservice_unit_test/src/com/android/car/CarPowerManagementServiceTest.java
index afd6f57..3cbfffc 100644
--- a/tests/carservice_unit_test/src/com/android/car/CarPowerManagementServiceTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/CarPowerManagementServiceTest.java
@@ -62,6 +62,7 @@
 import com.android.car.systeminterface.WakeLockInterface;
 import com.android.car.test.utils.TemporaryDirectory;
 import com.android.car.user.CarUserService;
+import com.android.internal.app.IVoiceInteractionManagerService;
 
 import org.junit.After;
 import org.junit.Before;
@@ -107,6 +108,9 @@
     private CarUserService mUserService;
     @Mock
     private InitialUserSetter mInitialUserSetter;
+    @Mock
+    private IVoiceInteractionManagerService mVoiceInteractionManagerService;
+
 
     @Override
     protected void onSessionBuilder(CustomMockitoSessionBuilder session) {
@@ -150,7 +154,8 @@
                 + ", maxGarageModeRunningDurationInSecs="
                 + mResources.getInteger(R.integer.maxGarageModeRunningDurationInSecs));
         mService = new CarPowerManagementService(mContext, mResources, mPowerHal,
-                mSystemInterface, mUserManager, mUserService, mInitialUserSetter);
+                mSystemInterface, mUserManager, mUserService, mInitialUserSetter,
+                mVoiceInteractionManagerService);
         mService.init();
         mService.setShutdownTimersForTest(0, 0);
         mPowerHal.setSignalListener(mPowerSignalListener);
@@ -180,7 +185,7 @@
                 new PowerState(
                         VehicleApPowerStateReq.SHUTDOWN_PREPARE,
                         VehicleApPowerStateShutdownParam.SHUTDOWN_ONLY));
-        assertStateReceived(PowerHalService.SET_SHUTDOWN_START, WAKE_UP_DELAY);
+        assertStateReceivedForShutdownOrSleepWithPostpone(PowerHalService.SET_SHUTDOWN_START);
         assertThat(mService.garageModeShouldExitImmediately()).isFalse();
         assertThat(mDisplayInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS)).isFalse();
         mPowerSignalListener.waitForShutdown(WAIT_TIMEOUT_MS);
@@ -266,10 +271,10 @@
                 new PowerState(
                         VehicleApPowerStateReq.SHUTDOWN_PREPARE,
                         VehicleApPowerStateShutdownParam.SHUTDOWN_IMMEDIATELY));
-        assertStateReceived(PowerHalService.SET_SHUTDOWN_START, 0);
+        assertStateReceivedForShutdownOrSleepWithPostpone(PowerHalService.SET_SHUTDOWN_START, 0);
         // Cancel the shutdown
         mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.CANCEL_SHUTDOWN, 0));
-        assertStateReceived(PowerHalService.SET_SHUTDOWN_CANCELLED, 0);
+        assertStateReceivedForShutdownOrSleepWithPostpone(PowerHalService.SET_SHUTDOWN_CANCELLED);
         // Go to suspend
         mPowerHal.setCurrentPowerState(
                 new PowerState(
@@ -288,7 +293,7 @@
                 new PowerState(
                         VehicleApPowerStateReq.SHUTDOWN_PREPARE,
                         VehicleApPowerStateShutdownParam.SLEEP_IMMEDIATELY));
-        assertStateReceived(PowerHalService.SET_DEEP_SLEEP_ENTRY, 0);
+        assertStateReceivedForShutdownOrSleepWithPostpone(PowerHalService.SET_DEEP_SLEEP_ENTRY, 0);
         assertThat(mService.garageModeShouldExitImmediately()).isTrue();
         mPowerSignalListener.waitForSleepEntry(WAIT_TIMEOUT_MS);
 
@@ -501,6 +506,7 @@
                 VehicleApPowerStateShutdownParam.CAN_SLEEP));
         assertThat(mDisplayInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS)).isFalse();
         assertStateReceivedForShutdownOrSleepWithPostpone(PowerHalService.SET_DEEP_SLEEP_ENTRY);
+        assertVoiceInteractionDisabled();
         mPowerSignalListener.waitForSleepEntry(WAIT_TIMEOUT_MS);
 
         // Send the finished signal
@@ -530,6 +536,7 @@
         mSystemStateInterface.waitForSleepEntryAndWakeup(WAIT_TIMEOUT_MS);
         // Since we just woke up from shutdown, wake up time will be 0
         assertStateReceived(PowerHalService.SET_DEEP_SLEEP_EXIT, 0);
+        assertVoiceInteractionEnabled();
         assertThat(mDisplayInterface.getDisplayState()).isFalse();
     }
 
@@ -573,6 +580,14 @@
         assertStateReceivedForShutdownOrSleepWithPostpone(lastState, expectedSecondParameter);
     }
 
+    private void assertVoiceInteractionEnabled() throws Exception {
+        verify(mVoiceInteractionManagerService).setDisabled(false);
+    }
+
+    private void assertVoiceInteractionDisabled() throws Exception {
+        verify(mVoiceInteractionManagerService).setDisabled(true);
+    }
+
     private static void waitForSemaphore(Semaphore semaphore, long timeoutMs)
             throws InterruptedException {
         if (!semaphore.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
diff --git a/tests/carservice_unit_test/src/com/android/car/hal/UserHalServiceTest.java b/tests/carservice_unit_test/src/com/android/car/hal/UserHalServiceTest.java
index 2b11bd7..e6a81b6 100644
--- a/tests/carservice_unit_test/src/com/android/car/hal/UserHalServiceTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/hal/UserHalServiceTest.java
@@ -18,6 +18,7 @@
 import static android.car.VehiclePropertyIds.CREATE_USER;
 import static android.car.VehiclePropertyIds.CURRENT_GEAR;
 import static android.car.VehiclePropertyIds.INITIAL_USER_INFO;
+import static android.car.VehiclePropertyIds.REMOVE_USER;
 import static android.car.VehiclePropertyIds.SWITCH_USER;
 import static android.car.VehiclePropertyIds.USER_IDENTIFICATION_ASSOCIATION;
 import static android.car.test.mocks.CarArgumentMatchers.isProperty;
@@ -53,7 +54,9 @@
 import android.hardware.automotive.vehicle.V2_0.CreateUserStatus;
 import android.hardware.automotive.vehicle.V2_0.InitialUserInfoResponse;
 import android.hardware.automotive.vehicle.V2_0.InitialUserInfoResponseAction;
+import android.hardware.automotive.vehicle.V2_0.RemoveUserRequest;
 import android.hardware.automotive.vehicle.V2_0.SwitchUserMessageType;
+import android.hardware.automotive.vehicle.V2_0.SwitchUserRequest;
 import android.hardware.automotive.vehicle.V2_0.SwitchUserResponse;
 import android.hardware.automotive.vehicle.V2_0.SwitchUserStatus;
 import android.hardware.automotive.vehicle.V2_0.UserFlags;
@@ -413,28 +416,28 @@
 
     @Test
     public void testSwitchUser_invalidTimeout() {
-        assertThrows(IllegalArgumentException.class,
-                () -> mUserHalService.switchUser(mUser10, 0, mUsersInfo, noOpCallback()));
-        assertThrows(IllegalArgumentException.class,
-                () -> mUserHalService.switchUser(mUser10, -1, mUsersInfo, noOpCallback()));
+        assertThrows(IllegalArgumentException.class, () -> mUserHalService
+                .switchUser(createUserSwitchRequest(mUser10, mUsersInfo), 0, noOpCallback()));
+        assertThrows(IllegalArgumentException.class, () -> mUserHalService
+                .switchUser(createUserSwitchRequest(mUser10, mUsersInfo), -1, noOpCallback()));
     }
 
     @Test
     public void testSwitchUser_noUsersInfo() {
-        assertThrows(IllegalArgumentException.class,
-                () -> mUserHalService.switchUser(mUser10, TIMEOUT_MS, null, noOpCallback()));
+        assertThrows(IllegalArgumentException.class, () -> mUserHalService
+                .switchUser(createUserSwitchRequest(mUser10, null), TIMEOUT_MS, noOpCallback()));
     }
 
     @Test
     public void testSwitchUser_noCallback() {
-        assertThrows(NullPointerException.class,
-                () -> mUserHalService.switchUser(mUser10, TIMEOUT_MS, mUsersInfo, null));
+        assertThrows(NullPointerException.class, () -> mUserHalService
+                .switchUser(createUserSwitchRequest(mUser10, mUsersInfo), TIMEOUT_MS, null));
     }
 
     @Test
     public void testSwitchUser_noTarget() {
-        assertThrows(NullPointerException.class,
-                () -> mUserHalService.switchUser(null, TIMEOUT_MS, mUsersInfo, noOpCallback()));
+        assertThrows(NullPointerException.class, () -> mUserHalService
+                .switchUser(createUserSwitchRequest(null, mUsersInfo), TIMEOUT_MS, noOpCallback()));
     }
 
     @Test
@@ -443,7 +446,8 @@
 
         GenericHalCallback<SwitchUserResponse> callback = new GenericHalCallback<>(
                 CALLBACK_TIMEOUT_TIMEOUT);
-        mUserHalService.switchUser(mUser10, TIMEOUT_MS, mUsersInfo, callback);
+        mUserHalService.switchUser(createUserSwitchRequest(mUser10, mUsersInfo), TIMEOUT_MS,
+                callback);
 
         callback.assertCalled();
         assertCallbackStatus(callback, HalCallback.STATUS_HAL_SET_TIMEOUT);
@@ -458,7 +462,8 @@
     public void testSwitchUser_halDidNotReply() throws Exception {
         GenericHalCallback<SwitchUserResponse> callback = new GenericHalCallback<>(
                 CALLBACK_TIMEOUT_TIMEOUT);
-        mUserHalService.switchUser(mUser10, TIMEOUT_MS, mUsersInfo, callback);
+        mUserHalService.switchUser(createUserSwitchRequest(mUser10, mUsersInfo), TIMEOUT_MS,
+                callback);
 
         callback.assertCalled();
         assertCallbackStatus(callback, HalCallback.STATUS_HAL_RESPONSE_TIMEOUT);
@@ -475,7 +480,8 @@
 
         GenericHalCallback<SwitchUserResponse> callback = new GenericHalCallback<>(
                 CALLBACK_TIMEOUT_TIMEOUT);
-        mUserHalService.switchUser(mUser10, TIMEOUT_MS, mUsersInfo, callback);
+        mUserHalService.switchUser(createUserSwitchRequest(mUser10, mUsersInfo), TIMEOUT_MS,
+                callback);
 
         callback.assertCalled();
         assertCallbackStatus(callback, HalCallback.STATUS_HAL_RESPONSE_TIMEOUT);
@@ -493,7 +499,8 @@
 
         GenericHalCallback<SwitchUserResponse> callback = new GenericHalCallback<>(
                 CALLBACK_TIMEOUT_SUCCESS);
-        mUserHalService.switchUser(mUser10, TIMEOUT_MS, mUsersInfo, callback);
+        mUserHalService.switchUser(createUserSwitchRequest(mUser10, mUsersInfo), TIMEOUT_MS,
+                callback);
 
         callback.assertCalled();
 
@@ -517,7 +524,8 @@
 
         GenericHalCallback<SwitchUserResponse> callback = new GenericHalCallback<>(
                 CALLBACK_TIMEOUT_SUCCESS);
-        mUserHalService.switchUser(mUser10, TIMEOUT_MS, mUsersInfo, callback);
+        mUserHalService.switchUser(createUserSwitchRequest(mUser10, mUsersInfo), TIMEOUT_MS,
+                callback);
 
         callback.assertCalled();
 
@@ -545,7 +553,8 @@
 
         GenericHalCallback<SwitchUserResponse> callback = new GenericHalCallback<>(
                 CALLBACK_TIMEOUT_SUCCESS);
-        mUserHalService.switchUser(mUser10, TIMEOUT_MS, mUsersInfo, callback);
+        mUserHalService.switchUser(createUserSwitchRequest(mUser10, mUsersInfo), TIMEOUT_MS,
+                callback);
 
         callback.assertCalled();
 
@@ -567,8 +576,10 @@
                 CALLBACK_TIMEOUT_TIMEOUT);
         GenericHalCallback<SwitchUserResponse> callback2 = new GenericHalCallback<>(
                 CALLBACK_TIMEOUT_TIMEOUT);
-        mUserHalService.switchUser(mUser10, TIMEOUT_MS, mUsersInfo, callback1);
-        mUserHalService.switchUser(mUser10, TIMEOUT_MS, mUsersInfo, callback2);
+        mUserHalService.switchUser(createUserSwitchRequest(mUser10, mUsersInfo), TIMEOUT_MS,
+                callback1);
+        mUserHalService.switchUser(createUserSwitchRequest(mUser10, mUsersInfo), TIMEOUT_MS,
+                callback2);
 
         callback1.assertCalled();
         assertCallbackStatus(callback1, HalCallback.STATUS_HAL_RESPONSE_TIMEOUT);
@@ -590,7 +601,8 @@
 
         GenericHalCallback<SwitchUserResponse> callback = new GenericHalCallback<>(
                 CALLBACK_TIMEOUT_SUCCESS);
-        mUserHalService.switchUser(mUser10, TIMEOUT_MS, mUsersInfo, callback);
+        mUserHalService.switchUser(createUserSwitchRequest(mUser10, mUsersInfo), TIMEOUT_MS,
+                callback);
 
         callback.assertCalled();
 
@@ -634,30 +646,113 @@
 
     @Test
     public void testPostSwitchResponse_noUsersInfo() {
-        assertThrows(NullPointerException.class,
-                () -> mUserHalService.postSwitchResponse(42, mUser10, null));
+        SwitchUserRequest request = createUserSwitchRequest(mUser10, null);
+        request.requestId = 42;
+        assertThrows(NullPointerException.class, () -> mUserHalService.postSwitchResponse(request));
     }
 
     @Test
     public void testPostSwitchResponse_HalCalledWithCorrectProp() {
-        mUserHalService.postSwitchResponse(42, mUser10, mUsersInfo);
+        SwitchUserRequest request = createUserSwitchRequest(mUser10, mUsersInfo);
+        request.requestId = 42;
+        mUserHalService.postSwitchResponse(request);
         ArgumentCaptor<VehiclePropValue> propCaptor =
                 ArgumentCaptor.forClass(VehiclePropValue.class);
         verify(mVehicleHal).set(propCaptor.capture());
         VehiclePropValue prop = propCaptor.getValue();
-        assertHalSetSwitchUserRequest(prop, SwitchUserMessageType.ANDROID_POST_SWITCH,
-                mUser10);
+        assertHalSetSwitchUserRequest(prop, SwitchUserMessageType.ANDROID_POST_SWITCH, mUser10);
+    }
+
+    @Test
+    public void testLegacyUserSwitch_nullRequest() {
+        assertThrows(NullPointerException.class, () -> mUserHalService.legacyUserSwitch(null));
+    }
+
+    @Test
+    public void testLegacyUserSwitch_noMessageType() {
+        SwitchUserRequest request = new SwitchUserRequest();
+
+        assertThrows(IllegalArgumentException.class,
+                () -> mUserHalService.legacyUserSwitch(request));
+    }
+
+    @Test
+    public void testLegacyUserSwitch_noTargetUserInfo() {
+        SwitchUserRequest request = new SwitchUserRequest();
+        request.messageType = SwitchUserMessageType.ANDROID_SWITCH;
+
+        assertThrows(IllegalArgumentException.class,
+                () -> mUserHalService.legacyUserSwitch(request));
+    }
+
+    @Test
+    public void testRemoveUser_nullRequest() {
+        RemoveUserRequest request = null;
+
+        assertThrows(NullPointerException.class,
+                () -> mUserHalService.removeUser(request));
+    }
+
+    @Test
+    public void testRemoveUser_noRequestId() {
+        RemoveUserRequest request = new RemoveUserRequest();
+
+        assertThrows(IllegalArgumentException.class,
+                () -> mUserHalService.removeUser(request));
+    }
+
+    @Test
+    public void testRemoveUser_noRemovedUserInfo() {
+        RemoveUserRequest request = new RemoveUserRequest();
+        request.requestId = 1;
+
+        assertThrows(IllegalArgumentException.class,
+                () -> mUserHalService.removeUser(request));
+    }
+
+    @Test
+    public void testRemoveUser_noUsersInfo() {
+        RemoveUserRequest request = new RemoveUserRequest();
+        request.requestId = 1;
+        request.removedUserInfo = mUser10;
+
+        assertThrows(IllegalArgumentException.class,
+                () -> mUserHalService.removeUser(request));
+    }
+
+    @Test
+    public void testRemoveUser_HalCalledWithCorrectProp() {
+        RemoveUserRequest request = new RemoveUserRequest();
+        request.removedUserInfo = mUser10;
+        request.usersInfo = mUsersInfo;
+        ArgumentCaptor<VehiclePropValue> propCaptor =
+                ArgumentCaptor.forClass(VehiclePropValue.class);
+
+        mUserHalService.removeUser(request);
+
+        verify(mVehicleHal).set(propCaptor.capture());
+        assertHalSetRemoveUserRequest(propCaptor.getValue(), mUser10);
     }
 
     @Test
     public void testLegacyUserSwitch_noUsersInfo() {
+        SwitchUserRequest request = new SwitchUserRequest();
+        request.messageType = SwitchUserMessageType.ANDROID_SWITCH;
+        request.targetUser = mUser10;
+
         assertThrows(IllegalArgumentException.class,
-                () -> mUserHalService.legacyUserSwitch(mUser10, null));
+                () -> mUserHalService.legacyUserSwitch(request));
     }
 
     @Test
     public void testLegacyUserSwitch_HalCalledWithCorrectProp() {
-        mUserHalService.legacyUserSwitch(mUser10, mUsersInfo);
+        SwitchUserRequest request = new SwitchUserRequest();
+        request.messageType = SwitchUserMessageType.LEGACY_ANDROID_SWITCH;
+        request.requestId = 1;
+        request.targetUser = mUser10;
+        request.usersInfo = mUsersInfo;
+
+        mUserHalService.legacyUserSwitch(request);
         ArgumentCaptor<VehiclePropValue> propCaptor =
                 ArgumentCaptor.forClass(VehiclePropValue.class);
         verify(mVehicleHal).set(propCaptor.capture());
@@ -1240,6 +1335,15 @@
                 "PropId: 0x" + Integer.toHexString(prop))).when(mVehicleHal).set(isProperty(prop));
     }
 
+    @NonNull
+    private SwitchUserRequest createUserSwitchRequest(@NonNull UserInfo targetUser,
+            @NonNull UsersInfo usersInfo) {
+        SwitchUserRequest request = new SwitchUserRequest();
+        request.targetUser = targetUser;
+        request.usersInfo = usersInfo;
+        return request;
+    }
+
     /**
      * Creates and set expectations for a valid request.
      */
@@ -1313,6 +1417,17 @@
         assertUsersInfo(req, mUsersInfo, 4);
     }
 
+    private void assertHalSetRemoveUserRequest(VehiclePropValue req, UserInfo userInfo) {
+        assertThat(req.prop).isEqualTo(REMOVE_USER);
+        assertWithMessage("wrong request Id on %s", req).that(req.value.int32Values.get(0))
+                .isAtLeast(1);
+        assertWithMessage("user.id mismatch on %s", req).that(req.value.int32Values.get(1))
+                .isEqualTo(userInfo.userId);
+        assertWithMessage("user.flags mismatch on %s", req).that(req.value.int32Values.get(2))
+                .isEqualTo(userInfo.flags);
+        assertUsersInfo(req, mUsersInfo, 3);
+    }
+
     private void assertHalSetCreateUserRequest(VehiclePropValue prop, CreateUserRequest request) {
         assertThat(prop.prop).isEqualTo(CREATE_USER);
         assertWithMessage("wrong request Id on %s", prop).that(prop.value.int32Values.get(0))
diff --git a/tests/carservice_unit_test/src/com/android/car/hardware/power/CarPowerManagerUnitTest.java b/tests/carservice_unit_test/src/com/android/car/hardware/power/CarPowerManagerUnitTest.java
index 6100dbc..ae86874 100644
--- a/tests/carservice_unit_test/src/com/android/car/hardware/power/CarPowerManagerUnitTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/hardware/power/CarPowerManagerUnitTest.java
@@ -41,6 +41,7 @@
 import com.android.car.systeminterface.DisplayInterface;
 import com.android.car.systeminterface.SystemInterface;
 import com.android.car.systeminterface.SystemStateInterface;
+import com.android.internal.app.IVoiceInteractionManagerService;
 
 import org.junit.After;
 import org.junit.Before;
@@ -73,6 +74,8 @@
     private Resources mResources;
     @Mock
     private Car mCar;
+    @Mock
+    private IVoiceInteractionManagerService mVoiceInteractionManagerService;
 
     @Before
     public void setUp() throws Exception {
@@ -204,7 +207,7 @@
                 + ", maxGarageModeRunningDurationInSecs="
                 + mResources.getInteger(R.integer.maxGarageModeRunningDurationInSecs));
         mService = new CarPowerManagementService(mContext, mResources, mPowerHal,
-                mSystemInterface, null, null, null);
+                mSystemInterface, null, null, null, mVoiceInteractionManagerService);
         mService.init();
         mService.setShutdownTimersForTest(0, 0);
         assertStateReceived(MockedPowerHalService.SET_WAIT_FOR_VHAL, 0);
diff --git a/tests/carservice_unit_test/src/com/android/car/user/CarUserManagerUnitTest.java b/tests/carservice_unit_test/src/com/android/car/user/CarUserManagerUnitTest.java
index 13cebbd..1003957 100644
--- a/tests/carservice_unit_test/src/com/android/car/user/CarUserManagerUnitTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/user/CarUserManagerUnitTest.java
@@ -17,7 +17,6 @@
 
 import static android.car.test.mocks.AndroidMockitoHelper.getResult;
 import static android.car.test.mocks.AndroidMockitoHelper.mockUmGetUsers;
-import static android.car.test.util.UserTestingHelper.newUsers;
 import static android.car.testapi.CarMockitoHelper.mockHandleRemoteExceptionFromCarServiceWithDefaultValue;
 import static android.os.UserHandle.USER_SYSTEM;
 
@@ -34,17 +33,22 @@
 import static org.mockito.Mockito.when;
 import static org.testng.Assert.assertThrows;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.car.Car;
 import android.car.ICarUserService;
 import android.car.test.mocks.AbstractExtendedMockitoTestCase;
+import android.car.test.util.UserTestingHelper;
 import android.car.user.CarUserManager;
 import android.car.user.CarUserManager.UserLifecycleListener;
 import android.car.user.CarUserManager.UserSwitchUiCallback;
+import android.car.user.UserCreationResult;
 import android.car.user.UserIdentificationAssociationResponse;
+import android.car.user.UserRemovalResult;
 import android.car.user.UserSwitchResult;
 import android.content.pm.UserInfo;
+import android.content.pm.UserInfo.UserInfoFlag;
 import android.os.RemoteException;
 import android.os.UserManager;
 
@@ -54,8 +58,6 @@
 import org.junit.Test;
 import org.mockito.Mock;
 
-import java.util.List;
-
 public final class CarUserManagerUnitTest extends AbstractExtendedMockitoTestCase {
 
     @Mock
@@ -175,7 +177,7 @@
 
     @Test
     public void testSwitchUser_remoteException() throws Exception {
-        expectServiceSwitchUserSucceeds(11);
+        expectServiceSwitchUserFails(11);
         mockHandleRemoteExceptionFromCarServiceWithDefaultValue(mCar);
 
         AndroidFuture<UserSwitchResult> future = mMgr.switchUser(11);
@@ -187,6 +189,28 @@
     }
 
     @Test
+    public void testRemoveUser_success() throws Exception {
+        int userId = 11;
+        int status = UserRemovalResult.STATUS_SUCCESSFUL;
+        when(mService.removeUser(userId)).thenReturn(new UserRemovalResult(status));
+
+        UserRemovalResult result = mMgr.removeUser(11);
+
+        assertThat(result.getStatus()).isEqualTo(UserRemovalResult.STATUS_SUCCESSFUL);
+    }
+
+    @Test
+    public void testRemoveUser_remoteException() throws Exception {
+        int userId = 11;
+        doThrow(new RemoteException("D'OH!")).when(mService).removeUser(eq(userId));
+        mockHandleRemoteExceptionFromCarServiceWithDefaultValue(mCar);
+
+        UserRemovalResult result = mMgr.removeUser(11);
+
+        assertThat(result.getStatus()).isEqualTo(UserRemovalResult.STATUS_HAL_INTERNAL_FAILURE);
+    }
+
+    @Test
     public void testSetSwitchUserUICallback_success() throws Exception {
         UserSwitchUiCallback callback = (u)-> { };
 
@@ -201,6 +225,42 @@
     }
 
     @Test
+    public void testCreateUser_success() throws Exception {
+        expectServiceCreateUserSucceeds("dude", "sweet", 42, UserCreationResult.STATUS_SUCCESSFUL,
+                108);
+
+        AndroidFuture<UserCreationResult> future = mMgr.createUser("dude", "sweet", 42);
+
+        assertThat(future).isNotNull();
+        UserCreationResult result = getResult(future);
+        assertThat(result.getStatus()).isEqualTo(UserCreationResult.STATUS_SUCCESSFUL);
+        assertThat(result.isSuccess()).isTrue();
+        assertThat(result.getErrorMessage()).isNull();
+
+        UserInfo newUser = result.getUser();
+        assertThat(newUser).isNotNull();
+        assertThat(newUser.id).isEqualTo(108);
+        assertThat(newUser.name).isEqualTo("dude");
+        assertThat(newUser.userType).isEqualTo("sweet");
+        assertThat(newUser.flags).isEqualTo(42);
+    }
+
+    @Test
+    public void testCreateUser_remoteException() throws Exception {
+        expectServiceCreateUserFails("dude", "sweet", 42);
+        mockHandleRemoteExceptionFromCarServiceWithDefaultValue(mCar);
+
+        AndroidFuture<UserCreationResult> future = mMgr.createUser("dude", "sweet", 42);
+
+        assertThat(future).isNotNull();
+        UserCreationResult result = getResult(future);
+        assertThat(result.getStatus()).isEqualTo(UserCreationResult.STATUS_HAL_INTERNAL_FAILURE);
+        assertThat(result.isSuccess()).isFalse();
+        assertThat(result.getErrorMessage()).isNull();
+        assertThat(result.getUser()).isNull();
+    }
+
+    @Test
     public void testGetUserIdentificationAssociation_nullTypes() throws Exception {
         assertThrows(IllegalArgumentException.class,
                 () -> mMgr.getUserIdentificationAssociation(null));
@@ -321,13 +381,32 @@
         }).when(mService).switchUser(eq(userId), anyInt(), notNull());
     }
 
-    private void expectServiceSwitchUserSucceeds(@UserIdInt int userId) throws RemoteException {
+    private void expectServiceSwitchUserFails(@UserIdInt int userId) throws RemoteException {
         doThrow(new RemoteException("D'OH!")).when(mService)
             .switchUser(eq(userId), anyInt(), notNull());
     }
 
+    private void expectServiceCreateUserSucceeds(@Nullable String name,
+            @NonNull String userType, @UserInfoFlag int flags,
+            @UserCreationResult.Status int status, @UserIdInt int userId) throws RemoteException {
+        doAnswer((invocation) -> {
+            @SuppressWarnings("unchecked")
+            AndroidFuture<UserCreationResult> future =
+                    (AndroidFuture<UserCreationResult>) invocation.getArguments()[4];
+            UserInfo newUser = new UserTestingHelper.UserInfoBuilder(108)
+                    .setName(name).setType(userType).setFlags(flags).build();
+            future.complete(new UserCreationResult(status, newUser, /* errorMessage= */ null));
+            return null;
+        }).when(mService).createUser(eq(name), eq(userType), eq(flags), anyInt(), notNull());
+    }
+
+    private void expectServiceCreateUserFails(@Nullable String name,
+            @NonNull String userType, @UserInfoFlag int flags) throws RemoteException {
+        doThrow(new RemoteException("D'OH!")).when(mService)
+                .createUser(eq(name), eq(userType), eq(flags), anyInt(), notNull());
+    }
+
     private void setExistingUsers(int... userIds) {
-        List<UserInfo> users = newUsers(userIds);
-        mockUmGetUsers(mUserManager, users);
+        mockUmGetUsers(mUserManager, userIds);
     }
 }
diff --git a/tests/carservice_unit_test/src/com/android/car/user/CarUserServiceTest.java b/tests/carservice_unit_test/src/com/android/car/user/CarUserServiceTest.java
index 4dc2923..79d3282 100644
--- a/tests/carservice_unit_test/src/com/android/car/user/CarUserServiceTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/user/CarUserServiceTest.java
@@ -17,10 +17,11 @@
 package com.android.car.user;
 
 import static android.car.test.mocks.AndroidMockitoHelper.getResult;
-import static android.car.test.mocks.AndroidMockitoHelper.mockUmGetSystemUser;
+import static android.car.test.mocks.AndroidMockitoHelper.mockUmCreateUser;
 import static android.car.test.mocks.AndroidMockitoHelper.mockUmGetUserInfo;
 import static android.car.test.mocks.AndroidMockitoHelper.mockUmGetUsers;
 import static android.car.test.util.UserTestingHelper.UserInfoBuilder;
+import static android.content.pm.UserInfo.FLAG_ADMIN;
 import static android.content.pm.UserInfo.FLAG_EPHEMERAL;
 import static android.content.pm.UserInfo.FLAG_GUEST;
 
@@ -63,10 +64,13 @@
 import android.car.user.CarUserManager.UserLifecycleEvent;
 import android.car.user.CarUserManager.UserLifecycleEventType;
 import android.car.user.CarUserManager.UserLifecycleListener;
+import android.car.user.UserCreationResult;
 import android.car.user.UserIdentificationAssociationResponse;
+import android.car.user.UserRemovalResult;
 import android.car.user.UserSwitchResult;
 import android.car.userlib.CarUserManagerHelper;
 import android.car.userlib.HalCallback;
+import android.car.userlib.HalCallback.HalCallbackStatus;
 import android.car.userlib.UserHalHelper;
 import android.car.userlib.UserHelper;
 import android.content.Context;
@@ -74,9 +78,14 @@
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
+import android.hardware.automotive.vehicle.V2_0.CreateUserRequest;
+import android.hardware.automotive.vehicle.V2_0.CreateUserResponse;
+import android.hardware.automotive.vehicle.V2_0.CreateUserStatus;
 import android.hardware.automotive.vehicle.V2_0.InitialUserInfoRequestType;
 import android.hardware.automotive.vehicle.V2_0.InitialUserInfoResponse;
 import android.hardware.automotive.vehicle.V2_0.InitialUserInfoResponseAction;
+import android.hardware.automotive.vehicle.V2_0.RemoveUserRequest;
+import android.hardware.automotive.vehicle.V2_0.SwitchUserRequest;
 import android.hardware.automotive.vehicle.V2_0.SwitchUserResponse;
 import android.hardware.automotive.vehicle.V2_0.SwitchUserStatus;
 import android.hardware.automotive.vehicle.V2_0.UserFlags;
@@ -136,6 +145,8 @@
 
     private static final int NON_EXISTING_USER = 55; // must not be on mExistingUsers
 
+    private static final long DEFAULT_LIFECYCLE_TIMESTAMP = 1;
+
     @Mock private Context mMockContext;
     @Mock private Context mApplicationContext;
     @Mock private LocationManager mLocationManager;
@@ -160,6 +171,7 @@
 
     private final int mGetUserInfoRequestType = InitialUserInfoRequestType.COLD_BOOT;
     private final AndroidFuture<UserSwitchResult> mUserSwitchFuture = new AndroidFuture<>();
+    private final AndroidFuture<UserCreationResult> mUserCreationFuture = new AndroidFuture<>();
     private final AndroidFuture<UserIdentificationAssociationResponse> mUserAssociationRespFuture =
             new AndroidFuture<>();
     private final int mAsyncCallTimeoutMs = 100;
@@ -308,21 +320,6 @@
     }
 
     /**
-     * Test that the {@link CarUserService} doesn't update last active user on user switch in
-     * headless system user mode.
-     */
-    @Test
-    public void testLastActiveUserUpdatedOnUserSwitch_headlessSystemUser() throws Exception {
-        mockIsHeadlessSystemUser(mRegularUser.id, true);
-        mockUmGetSystemUser(mMockedUserManager);
-        mockExistingUsers();
-
-        sendUserSwitchingEvent(mAdminUser.id, mRegularUser.id);
-
-        verifyLastActiveUserNotSet();
-    }
-
-    /**
      * Test that the {@link CarUserService} sets default guest restrictions on first boot.
      */
     @Test
@@ -468,33 +465,37 @@
     }
 
     @Test
-    public void testCreateAdminDriver_IfCurrentUserIsAdminUser() {
-        doReturn(true).when(mMockedUserManager).isSystemUser();
-        String userName = "testUser";
-        UserInfo userInfo = new UserInfo();
-        doReturn(userInfo).when(mMockedUserManager).createUser(userName, UserInfo.FLAG_ADMIN);
-        assertEquals(userInfo, mCarUserService.createDriver(userName, true));
+    public void testCreateAdminDriver_IfCurrentUserIsAdminUser() throws Exception {
+        when(mMockedUserManager.isSystemUser()).thenReturn(true);
+        mockUmCreateUser(mMockedUserManager, "testUser", UserManager.USER_TYPE_FULL_SECONDARY,
+                UserInfo.FLAG_ADMIN, 10);
+        mockHalCreateUser(HalCallback.STATUS_OK, CreateUserStatus.SUCCESS);
+
+        AndroidFuture<UserCreationResult> future = mCarUserService.createDriver("testUser", true);
+
+        assertThat(getResult(future).getUser().name).isEqualTo("testUser");
+        assertThat(getResult(future).getUser().id).isEqualTo(10);
     }
 
     @Test
-    public void testCreateAdminDriver_IfCurrentUserIsNotSystemUser() {
-        doReturn(false).when(mMockedUserManager).isSystemUser();
-        assertEquals(null, mCarUserService.createDriver("testUser", true));
+    public void testCreateAdminDriver_IfCurrentUserIsNotSystemUser() throws Exception {
+        when(mMockedUserManager.isSystemUser()).thenReturn(false);
+        AndroidFuture<UserCreationResult> future = mCarUserService.createDriver("testUser", true);
+        assertThat(getResult(future).getStatus())
+                .isEqualTo(UserCreationResult.STATUS_INVALID_REQUEST);
     }
 
     @Test
-    public void testCreateNonAdminDriver() {
-        String userName = "testUser";
-        UserInfo userInfo = new UserInfo();
-        doReturn(userInfo).when(mMockedCarUserManagerHelper).createNewNonAdminUser(userName);
-        assertEquals(userInfo, mCarUserService.createDriver(userName, false));
-    }
+    public void testCreateNonAdminDriver() throws Exception {
+        mockUmCreateUser(mMockedUserManager, "testUser", UserManager.USER_TYPE_FULL_SECONDARY,
+                NO_USER_INFO_FLAGS, 10);
+        mockHalCreateUser(HalCallback.STATUS_OK, CreateUserStatus.SUCCESS);
 
-    @Test
-    public void testCreateNonAdminDriver_IfMaximumUserAlreadyCreated() {
-        String userName = "testUser";
-        doReturn(null).when(mMockedUserManager).createUser(userName, NO_USER_INFO_FLAGS);
-        assertEquals(null, mCarUserService.createDriver(userName, false));
+        AndroidFuture<UserCreationResult> future = mCarUserService.createDriver("testUser", false);
+
+        UserInfo userInfo = getResult(future).getUser();
+        assertThat(userInfo.name).isEqualTo("testUser");
+        assertThat(userInfo.id).isEqualTo(10);
     }
 
     @Test
@@ -664,6 +665,81 @@
     }
 
     @Test
+    public void testRemoveUser_currentUserCannotBeRemoved() throws Exception {
+        mockCurrentUser(mAdminUser);
+
+        UserRemovalResult result = mCarUserService.removeUser(mAdminUser.id);
+
+        assertThat(result.getStatus())
+                .isEqualTo(UserRemovalResult.STATUS_TARGET_USER_IS_CURRENT_USER);
+    }
+
+    @Test
+    public void testRemoveUser_userNotExist() throws Exception {
+        UserRemovalResult result = mCarUserService.removeUser(15);
+
+        assertThat(result.getStatus())
+                .isEqualTo(UserRemovalResult.STATUS_USER_DOES_NOT_EXIST);
+    }
+
+    @Test
+    public void testRemoveUser_lastAdminUser() throws Exception {
+        mockCurrentUser(mRegularUser);
+        mockExistingUsers();
+
+        UserRemovalResult result = mCarUserService.removeUser(mAdminUser.id);
+
+        assertThat(result.getStatus())
+                .isEqualTo(UserRemovalResult.STATUS_TARGET_USER_IS_LAST_ADMIN_USER);
+    }
+
+    @Test
+    public void testRemoveUser_notLastAdminUser_success() throws Exception {
+        // Give admin rights to regular user.
+        UserInfo currentUser = mRegularUser;
+        currentUser.flags = currentUser.flags | FLAG_ADMIN;
+        mockExistingUsersAndCurrentUser(currentUser);
+        int removeUserId = mAdminUser.id;
+        when(mMockedUserManager.removeUser(removeUserId)).thenReturn(true);
+
+        UserRemovalResult result = mCarUserService.removeUser(removeUserId);
+
+        assertThat(result.getStatus()).isEqualTo(UserRemovalResult.STATUS_SUCCESSFUL);
+        assertHalRemove(currentUser.id, removeUserId);
+    }
+
+    @Test
+    public void testRemoveUser_success() throws Exception {
+        mockExistingUsersAndCurrentUser(mAdminUser);
+        int removeUserId = mRegularUser.id;
+        when(mMockedUserManager.removeUser(removeUserId)).thenReturn(true);
+
+        UserRemovalResult result = mCarUserService.removeUser(removeUserId);
+
+        assertThat(result.getStatus()).isEqualTo(UserRemovalResult.STATUS_SUCCESSFUL);
+        assertHalRemove(mAdminUser.id, removeUserId);
+    }
+
+    @Test
+    public void testRemoveUser_androidFailure() throws Exception {
+        mockExistingUsersAndCurrentUser(mAdminUser);
+        int targetUserId = mRegularUser.id;
+        when(mMockedUserManager.removeUser(targetUserId)).thenReturn(false);
+
+        UserRemovalResult result = mCarUserService.removeUser(targetUserId);
+
+        assertThat(result.getStatus()).isEqualTo(UserRemovalResult.STATUS_ANDROID_FAILURE);
+    }
+
+    @Test
+    public void testSwitchUser_nullReceiver() throws Exception {
+        mockExistingUsersAndCurrentUser(mAdminUser);
+
+        assertThrows(NullPointerException.class, () -> mCarUserService
+                .switchUser(mAdminUser.id, mAsyncCallTimeoutMs, null));
+    }
+
+    @Test
     public void testSwitchUser_nonExistingTarget() throws Exception {
         assertThrows(IllegalArgumentException.class, () -> mCarUserService
                 .switchUser(NON_EXISTING_USER, mAsyncCallTimeoutMs, mUserSwitchFuture));
@@ -671,9 +747,10 @@
 
     @Test
     public void testSwitchUser_targetSameAsCurrentUser() throws Exception {
-        mockExistingUsers();
-        mockGetCurrentUser(mAdminUser.id);
+        mockExistingUsersAndCurrentUser(mAdminUser);
+
         mCarUserService.switchUser(mAdminUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture);
+
         assertThat(getUserSwitchResult().getStatus())
                 .isEqualTo(UserSwitchResult.STATUS_ALREADY_REQUESTED_USER);
     }
@@ -961,21 +1038,17 @@
     }
 
     @Test
-    public void testHalUserSwitchOnAndroidSwitch_successfulNoExitingUserSwitch() {
+    public void testLegacyUserSwitch_ok() throws Exception {
         mockExistingUsers();
 
         sendUserSwitchingEvent(mAdminUser.id, mRegularUser.id);
 
-        ArgumentCaptor<android.hardware.automotive.vehicle.V2_0.UserInfo> targetUser =
-                ArgumentCaptor.forClass(android.hardware.automotive.vehicle.V2_0.UserInfo.class);
-        ArgumentCaptor<UsersInfo> usersInfo = ArgumentCaptor.forClass(UsersInfo.class);
-        verify(mUserHal).legacyUserSwitch(targetUser.capture(), usersInfo.capture());
-        assertThat(targetUser.getValue().userId).isEqualTo(mRegularUser.id);
-        assertThat(usersInfo.getValue().currentUser.userId).isEqualTo(mAdminUser.id);
+        verify(mUserHal).legacyUserSwitch(isSwitchUserRequest(0, mAdminUser.id, mRegularUser.id));
     }
 
     @Test
-    public void testHalUserSwitchOnAndroidSwitch_failureExitingUserSwitch() throws Exception {
+    public void testLegacyUserSwitch_notCalledAfterNormalSwitch() throws Exception {
+        // Arrange - emulate normal switch
         mockExistingUsersAndCurrentUser(mAdminUser);
         int requestId = 42;
         mSwitchUserResponse.status = SwitchUserStatus.SUCCESS;
@@ -984,9 +1057,11 @@
         mockAmSwitchUser(mGuestUser, true);
         mCarUserService.switchUser(mGuestUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture);
 
+        // Act - trigger legacy switch
         sendUserSwitchingEvent(mAdminUser.id, mGuestUser.id);
 
-        verify(mUserHal, never()).legacyUserSwitch(any(), any());
+        // Assert
+        verify(mUserHal, never()).legacyUserSwitch(any());
     }
 
     @Test
@@ -1041,6 +1116,164 @@
     }
 
     @Test
+    public void testCreateUser_nullType() throws Exception {
+        assertThrows(NullPointerException.class, () -> mCarUserService
+                .createUser("dude", null, 108, mAsyncCallTimeoutMs, mUserCreationFuture));
+    }
+
+    @Test
+    public void testCreateUser_nullReceiver() throws Exception {
+        assertThrows(NullPointerException.class, () -> mCarUserService
+                .createUser("dude", "TypeONegative", 108, mAsyncCallTimeoutMs, null));
+    }
+
+    @Test
+    public void testCreateUser_umCreateReturnsNull() throws Exception {
+        // No need to mock um.createUser() to return null
+
+        mCarUserService.createUser("dude", "TypeONegative", 108, mAsyncCallTimeoutMs,
+                mUserCreationFuture);
+
+        UserCreationResult result = getUserCreationResult();
+        assertThat(result.getStatus()).isEqualTo(UserCreationResult.STATUS_ANDROID_FAILURE);
+        assertThat(result.getUser()).isNull();
+        assertThat(result.getErrorMessage()).isNull();
+        assertNoHalUserCreation();
+        verifyNoUserRemoved();
+    }
+
+    @Test
+    public void testCreateUser_umCreateThrowsException() throws Exception {
+        mockUmCreateUser(mMockedUserManager, "dude", "TypeONegative", 108,
+                new RuntimeException("D'OH!"));
+
+        mCarUserService.createUser("dude", "TypeONegative", 108, mAsyncCallTimeoutMs,
+                mUserCreationFuture);
+
+        UserCreationResult result = getUserCreationResult();
+        assertThat(result.getStatus()).isEqualTo(UserCreationResult.STATUS_ANDROID_FAILURE);
+        assertThat(result.getUser()).isNull();
+        assertThat(result.getErrorMessage()).isNull();
+        assertNoHalUserCreation();
+        verifyNoUserRemoved();
+    }
+
+    @Test
+    public void testCreateUser_internalHalFailure() throws Exception {
+        mockUmCreateUser(mMockedUserManager, "dude", "TypeONegative", 108, 42);
+        mockHalCreateUser(HalCallback.STATUS_INVALID, /* not_used_status= */ -1);
+
+        mCarUserService.createUser("dude", "TypeONegative", 108, mAsyncCallTimeoutMs,
+                mUserCreationFuture);
+
+        UserCreationResult result = getUserCreationResult();
+        assertThat(result.getStatus()).isEqualTo(UserCreationResult.STATUS_HAL_INTERNAL_FAILURE);
+        assertThat(result.getUser()).isNull();
+        assertThat(result.getErrorMessage()).isNull();
+        verifyUserRemoved(42);
+    }
+
+    @Test
+    public void testCreateUser_halFailure() throws Exception {
+        mockUmCreateUser(mMockedUserManager, "dude", "TypeONegative", 108, 42);
+        mockHalCreateUser(HalCallback.STATUS_OK, CreateUserStatus.FAILURE);
+
+        mCarUserService.createUser("dude", "TypeONegative", 108, mAsyncCallTimeoutMs,
+                mUserCreationFuture);
+
+        UserCreationResult result = getUserCreationResult();
+        assertThat(result.getStatus()).isEqualTo(UserCreationResult.STATUS_HAL_FAILURE);
+        assertThat(result.getUser()).isNull();
+        assertThat(result.getErrorMessage()).isNull();
+
+        verifyUserRemoved(42);
+    }
+
+    @Test
+    public void testCreateUser_halServiceThrowsRuntimeException() throws Exception {
+        mockUmCreateUser(mMockedUserManager, "dude", "TypeONegative", 108, 42);
+        mockHalCreateUserThrowsRuntimeException();
+
+        mCarUserService.createUser("dude", "TypeONegative", 108, mAsyncCallTimeoutMs,
+                mUserCreationFuture);
+
+        UserCreationResult result = getUserCreationResult();
+        assertThat(result.getStatus()).isEqualTo(UserCreationResult.STATUS_HAL_INTERNAL_FAILURE);
+        assertThat(result.getUser()).isNull();
+        assertThat(result.getErrorMessage()).isNull();
+
+        verifyUserRemoved(42);
+    }
+
+    @Test
+    public void testCreateUser_success() throws Exception {
+        mockExistingUsersAndCurrentUser(mAdminUser);
+        int userId = mGuestUser.id;
+        mockUmCreateUser(mMockedUserManager, "dude", UserManager.USER_TYPE_FULL_GUEST,
+                UserInfo.FLAG_EPHEMERAL, userId);
+        ArgumentCaptor<CreateUserRequest> requestCaptor =
+                mockHalCreateUser(HalCallback.STATUS_OK, CreateUserStatus.SUCCESS);
+
+        mCarUserService.createUser("dude", UserManager.USER_TYPE_FULL_GUEST,
+                UserInfo.FLAG_EPHEMERAL, mAsyncCallTimeoutMs, mUserCreationFuture);
+
+        // Assert request
+        CreateUserRequest request = requestCaptor.getValue();
+        Log.d(TAG, "createUser() request: " + request);
+        assertThat(request.newUserName).isEqualTo("dude");
+        assertThat(request.newUserInfo.userId).isEqualTo(userId);
+        assertThat(request.newUserInfo.flags).isEqualTo(UserFlags.GUEST | UserFlags.EPHEMERAL);
+        assertDefaultUsersInfo(request.usersInfo, mAdminUser);
+
+        UserCreationResult result = getUserCreationResult();
+        assertThat(result.getStatus()).isEqualTo(UserCreationResult.STATUS_SUCCESSFUL);
+        assertThat(result.getErrorMessage()).isNull();
+        UserInfo newUser = result.getUser();
+        assertThat(newUser).isNotNull();
+        assertThat(newUser.id).isEqualTo(userId);
+        assertThat(newUser.name).isEqualTo("dude");
+        assertThat(newUser.userType).isEqualTo(UserManager.USER_TYPE_FULL_GUEST);
+        assertThat(newUser.flags).isEqualTo(UserInfo.FLAG_EPHEMERAL);
+
+        verifyNoUserRemoved();
+    }
+
+    @Test
+    public void testCreateUser_success_nullName() throws Exception {
+        String nullName = null;
+        mockExistingUsersAndCurrentUser(mAdminUser);
+        int userId = mGuestUser.id;
+        mockUmCreateUser(mMockedUserManager, nullName, UserManager.USER_TYPE_FULL_GUEST,
+                UserInfo.FLAG_EPHEMERAL, userId);
+        ArgumentCaptor<CreateUserRequest> requestCaptor =
+                mockHalCreateUser(HalCallback.STATUS_OK, CreateUserStatus.SUCCESS);
+
+        mCarUserService.createUser(nullName, UserManager.USER_TYPE_FULL_GUEST,
+                UserInfo.FLAG_EPHEMERAL, mAsyncCallTimeoutMs, mUserCreationFuture);
+
+        // Assert request
+        CreateUserRequest request = requestCaptor.getValue();
+        Log.d(TAG, "createUser() request: " + request);
+        assertThat(request.newUserName).isEmpty();
+        assertThat(request.newUserInfo.userId).isEqualTo(userId);
+        assertThat(request.newUserInfo.flags).isEqualTo(UserFlags.GUEST | UserFlags.EPHEMERAL);
+        assertDefaultUsersInfo(request.usersInfo, mAdminUser);
+
+        UserCreationResult result = getUserCreationResult();
+        assertThat(result.getStatus()).isEqualTo(UserCreationResult.STATUS_SUCCESSFUL);
+        assertThat(result.getErrorMessage()).isNull();
+
+        UserInfo newUser = result.getUser();
+        assertThat(newUser).isNotNull();
+        assertThat(newUser.id).isEqualTo(userId);
+        assertThat(newUser.name).isNull();
+        assertThat(newUser.userType).isEqualTo(UserManager.USER_TYPE_FULL_GUEST);
+        assertThat(newUser.flags).isEqualTo(UserInfo.FLAG_EPHEMERAL);
+
+        verifyNoUserRemoved();
+    }
+
+    @Test
     public void testGetUserInfo_nullReceiver() throws Exception {
         assertThrows(NullPointerException.class, () -> mCarUserService
                 .getInitialUserInfo(mGetUserInfoRequestType, mAsyncCallTimeoutMs, null));
@@ -1341,7 +1574,7 @@
         sendUserSwitchingEvent(mAdminUser.id, mRegularUser.id);
 
         verify(mUserMetrics).onEvent(CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING,
-                0, mAdminUser.id, mRegularUser.id);
+                DEFAULT_LIFECYCLE_TIMESTAMP, mAdminUser.id, mRegularUser.id);
     }
 
     @Test
@@ -1361,6 +1594,11 @@
     }
 
     @NonNull
+    private UserCreationResult getUserCreationResult() throws Exception {
+        return getResult(mUserCreationFuture);
+    }
+
+    @NonNull
     private UserIdentificationAssociationResponse getUserAssociationRespResult()
             throws Exception {
         return getResult(mUserAssociationRespFuture);
@@ -1427,6 +1665,28 @@
         mockHalSwitch(currentUserId, HalCallback.STATUS_OK, response, androidTargetUser);
     }
 
+    @NonNull
+    private ArgumentCaptor<CreateUserRequest> mockHalCreateUser(
+            @HalCallbackStatus int callbackStatus, int responseStatus) {
+        CreateUserResponse response = new CreateUserResponse();
+        response.status = responseStatus;
+        ArgumentCaptor<CreateUserRequest> captor = ArgumentCaptor.forClass(CreateUserRequest.class);
+        doAnswer((invocation) -> {
+            Log.d(TAG, "Answering " + invocation + " with " + response);
+            @SuppressWarnings("unchecked")
+            HalCallback<CreateUserResponse> callback =
+                    (HalCallback<CreateUserResponse>) invocation.getArguments()[2];
+            callback.onResponse(callbackStatus, response);
+            return null;
+        }).when(mUserHal).createUser(captor.capture(), eq(mAsyncCallTimeoutMs), notNull());
+        return captor;
+    }
+
+    private void mockHalCreateUserThrowsRuntimeException() {
+        doThrow(new RuntimeException("D'OH!"))
+                .when(mUserHal).createUser(any(), eq(mAsyncCallTimeoutMs), notNull());
+    }
+
     private void mockCallerUid(int uid, boolean returnCorrectUid) throws Exception {
         String packageName = "packageName";
         String className = "className";
@@ -1448,18 +1708,20 @@
         halTargetUser.userId = androidTargetUser.id;
         halTargetUser.flags = UserHalHelper.convertFlags(androidTargetUser);
         UsersInfo usersInfo = newUsersInfo(currentUserId);
+        SwitchUserRequest request = new SwitchUserRequest();
+        request.targetUser = halTargetUser;
+        request.usersInfo = usersInfo;
 
         BlockingAnswer<Void> blockingAnswer = BlockingAnswer.forVoidReturn(10_000, (invocation) -> {
             Log.d(TAG, "Answering " + invocation + " with " + response);
             @SuppressWarnings("unchecked")
             HalCallback<SwitchUserResponse> callback = (HalCallback<SwitchUserResponse>) invocation
-                    .getArguments()[3];
+                    .getArguments()[2];
             callback.onResponse(HalCallback.STATUS_OK, response);
         });
-        doAnswer(blockingAnswer).when(mUserHal).switchUser(eq(halTargetUser),
-                eq(mAsyncCallTimeoutMs), eq(usersInfo), notNull());
+        doAnswer(blockingAnswer).when(mUserHal).switchUser(eq(request), eq(mAsyncCallTimeoutMs),
+                notNull());
         return blockingAnswer;
-
     }
 
     private void mockHalSwitch(@UserIdInt int currentUserId,
@@ -1470,15 +1732,18 @@
         halTargetUser.userId = androidTargetUser.id;
         halTargetUser.flags = UserHalHelper.convertFlags(androidTargetUser);
         UsersInfo usersInfo = newUsersInfo(currentUserId);
+        SwitchUserRequest request = new SwitchUserRequest();
+        request.targetUser = halTargetUser;
+        request.usersInfo = usersInfo;
+
         doAnswer((invocation) -> {
             Log.d(TAG, "Answering " + invocation + " with " + response);
             @SuppressWarnings("unchecked")
             HalCallback<SwitchUserResponse> callback =
-                    (HalCallback<SwitchUserResponse>) invocation.getArguments()[3];
+                    (HalCallback<SwitchUserResponse>) invocation.getArguments()[2];
             callback.onResponse(callbackStatus, response);
             return null;
-        }).when(mUserHal).switchUser(eq(halTargetUser), eq(mAsyncCallTimeoutMs), eq(usersInfo),
-                notNull());
+        }).when(mUserHal).switchUser(eq(request), eq(mAsyncCallTimeoutMs), notNull());
     }
 
     private void mockHalGetUserIdentificationAssociation(@NonNull UserInfo user,
@@ -1569,7 +1834,6 @@
         for (int i = 0; i < actual.numberUsers; i++) {
             assertSameUser(actual.existingUsers.get(i), mExistingUsers.get(i));
         }
-
     }
 
     private void assertSameUser(android.hardware.automotive.vehicle.V2_0.UserInfo halUser,
@@ -1581,6 +1845,14 @@
             .that(halUser.flags).isEqualTo(UserHalHelper.convertFlags(androidUser));
     }
 
+    private void verifyUserRemoved(@UserIdInt int userId) {
+        verify(mMockedUserManager).removeUser(userId);
+    }
+
+    private void verifyNoUserRemoved() {
+        verify(mMockedUserManager, never()).removeUser(anyInt());
+    }
+
     @NonNull
     private UsersInfo newUsersInfo(@UserIdInt int currentUserId) {
         UsersInfo infos = new UsersInfo();
@@ -1666,34 +1938,34 @@
     }
 
     private void assertNoPostSwitch() {
-        verify(mUserHal, never()).postSwitchResponse(anyInt(), any(), any());
+        verify(mUserHal, never()).postSwitchResponse(any());
     }
 
     private void assertPostSwitch(int requestId, int currentId, int targetId) {
-        // verify post switch response
-        ArgumentCaptor<android.hardware.automotive.vehicle.V2_0.UserInfo> targetUser =
-                ArgumentCaptor.forClass(android.hardware.automotive.vehicle.V2_0.UserInfo.class);
-        ArgumentCaptor<UsersInfo> usersInfo = ArgumentCaptor.forClass(UsersInfo.class);
-        verify(mUserHal).postSwitchResponse(eq(requestId), targetUser.capture(),
-                usersInfo.capture());
-        assertThat(targetUser.getValue().userId).isEqualTo(targetId);
-        assertThat(usersInfo.getValue().currentUser.userId).isEqualTo(currentId);
+        verify(mUserHal).postSwitchResponse(isSwitchUserRequest(requestId, currentId, targetId));
     }
 
     private void assertHalSwitch(int currentId, int targetId) {
-        verify(mUserHal).switchUser(isHalUser(targetId), eq(mAsyncCallTimeoutMs),
-                isHalCurrentUser(currentId), any());
+        verify(mUserHal).switchUser(isSwitchUserRequest(0, currentId, targetId),
+                eq(mAsyncCallTimeoutMs), any());
+    }
+
+    private void assertNoHalUserCreation() {
+        verify(mUserHal, never()).createUser(any(), eq(mAsyncCallTimeoutMs), any());
+    }
+
+    private void assertHalRemove(int currentId, int removeUserId) {
+        ArgumentCaptor<RemoveUserRequest> request =
+                ArgumentCaptor.forClass(RemoveUserRequest.class);
+        verify(mUserHal).removeUser(request.capture());
+        assertThat(request.getValue().removedUserInfo.userId).isEqualTo(removeUserId);
+        assertThat(request.getValue().usersInfo.currentUser.userId).isEqualTo(currentId);
     }
 
     @NonNull
-    private static android.hardware.automotive.vehicle.V2_0.UserInfo isHalUser(
-            @UserIdInt int userId) {
-        return argThat(new UserInfoMatcher(userId));
-    }
-
-    @NonNull
-    private static UsersInfo isHalCurrentUser(@UserIdInt int userId) {
-        return argThat(new UsersInfoCurrentUserIdMatcher(userId));
+    private static SwitchUserRequest isSwitchUserRequest(int requestId,
+            @UserIdInt int currentUserId, @UserIdInt int targetUserId) {
+        return argThat(new SwitchUserRequestMatcher(requestId, currentUserId, targetUserId));
     }
 
     static final class FakeCarOccupantZoneService {
@@ -1740,7 +2012,8 @@
 
     private void sendUserLifecycleEvent(@UserIdInt int fromUserId, @UserIdInt int toUserId,
             @UserLifecycleEventType int eventType) {
-        mCarUserService.onUserLifecycleEvent(eventType, /* timestampMs= */ 0, fromUserId, toUserId);
+        mCarUserService.onUserLifecycleEvent(eventType, DEFAULT_LIFECYCLE_TIMESTAMP, fromUserId,
+                toUserId);
     }
 
     private void sendUserUnlockedEvent(@UserIdInt int userId) {
@@ -1818,53 +2091,46 @@
         }
     }
 
-    private static final class UserInfoMatcher
-            implements ArgumentMatcher<android.hardware.automotive.vehicle.V2_0.UserInfo> {
-
-        private static final String MY_TAG =
-                android.hardware.automotive.vehicle.V2_0.UserInfo.class.getSimpleName();
-
-        private final @UserIdInt int mUserId;
-
-        private UserInfoMatcher(@UserIdInt int userId) {
-            mUserId = userId;
-        }
-
-        @Override
-        public boolean matches(android.hardware.automotive.vehicle.V2_0.UserInfo argument) {
-            if (argument == null) {
-                Log.w(MY_TAG, "null argument");
-                return false;
-            }
-            if (argument.userId != mUserId) {
-                Log.w(MY_TAG, "wrong user id on " + argument + "; expected " + mUserId);
-                return false;
-            }
-            Log.d(MY_TAG, "Good News, Everyone! " + argument + " matches " + this);
-            return true;
-        }
-    }
-
-    private static final class UsersInfoCurrentUserIdMatcher implements ArgumentMatcher<UsersInfo> {
-
+    private static final class SwitchUserRequestMatcher
+            implements ArgumentMatcher<SwitchUserRequest> {
         private static final String MY_TAG = UsersInfo.class.getSimpleName();
 
-        private final @UserIdInt int mUserId;
+        private final int mRequestId;
+        private final @UserIdInt int mCurrentUserId;
+        private final @UserIdInt int mTargetUserId;
 
-        private UsersInfoCurrentUserIdMatcher(@UserIdInt int userId) {
-            mUserId = userId;
+
+        private SwitchUserRequestMatcher(int requestId, @UserIdInt int currentUserId,
+                @UserIdInt int targetUserId) {
+            mCurrentUserId = currentUserId;
+            mTargetUserId = targetUserId;
+            mRequestId = requestId;
         }
 
         @Override
-        public boolean matches(UsersInfo argument) {
+        public boolean matches(SwitchUserRequest argument) {
             if (argument == null) {
                 Log.w(MY_TAG, "null argument");
                 return false;
             }
-            if (argument.currentUser.userId != mUserId) {
-                Log.w(MY_TAG, "wrong user id on " + argument + "; expected " + mUserId);
+            if (argument.usersInfo.currentUser.userId != mCurrentUserId) {
+                Log.w(MY_TAG,
+                        "wrong current user id on " + argument + "; expected " + mCurrentUserId);
                 return false;
             }
+
+            if (argument.targetUser.userId != mTargetUserId) {
+                Log.w(MY_TAG,
+                        "wrong target user id on " + argument + "; expected " + mTargetUserId);
+                return false;
+            }
+
+            if (argument.requestId != mRequestId) {
+                Log.w(MY_TAG,
+                        "wrong request Id on " + argument + "; expected " + mTargetUserId);
+                return false;
+            }
+
             Log.d(MY_TAG, "Good News, Everyone! " + argument + " matches " + this);
             return true;
         }
diff --git a/tests/carservice_unit_test/src/com/android/car/user/ExperimentalCarUserManagerUnitTest.java b/tests/carservice_unit_test/src/com/android/car/user/ExperimentalCarUserManagerUnitTest.java
index 8b4dc55..2869d5d 100644
--- a/tests/carservice_unit_test/src/com/android/car/user/ExperimentalCarUserManagerUnitTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/user/ExperimentalCarUserManagerUnitTest.java
@@ -20,6 +20,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.notNull;
 import static org.mockito.Mockito.doAnswer;
@@ -33,6 +34,7 @@
 import android.car.test.util.UserTestingHelper;
 import android.car.user.CarUserManager;
 import android.car.user.ExperimentalCarUserManager;
+import android.car.user.UserCreationResult;
 import android.car.user.UserSwitchResult;
 import android.content.pm.UserInfo;
 import android.os.RemoteException;
@@ -66,23 +68,41 @@
 
     @Test
     public void testCreateDriver_Success_Admin() throws Exception {
-        expectCreateDriverSucceed(10);
-        int userId = mManager.createDriver("test driver", true);
-        assertThat(userId).isEqualTo(10);
+        String name = "test driver";
+        int userId = 10;
+        expectCreateDriverSucceed(name, userId);
+
+        AndroidFuture<UserCreationResult> future = mManager.createDriver(name, true);
+
+        UserCreationResult result = getResult(future);
+        assertThat(result.getErrorMessage()).isNull();
+        assertThat(result.getStatus()).isEqualTo(UserCreationResult.STATUS_SUCCESSFUL);
+        assertThat(result.getUser().id).isEqualTo(userId);
     }
 
     @Test
     public void testCreateDriver_Success_NonAdmin() throws Exception {
-        expectCreateDriverSucceed(10);
-        int userId = mManager.createDriver("test driver", false);
-        assertThat(userId).isEqualTo(10);
+        String name = "test driver";
+        int userId = 10;
+        expectCreateDriverSucceed(name, userId);
+
+        AndroidFuture<UserCreationResult> future = mManager.createDriver(name, false);
+
+        UserCreationResult result = getResult(future);
+        assertThat(result.getErrorMessage()).isNull();
+        assertThat(result.getStatus()).isEqualTo(UserCreationResult.STATUS_SUCCESSFUL);
+        assertThat(result.getUser().id).isEqualTo(userId);
     }
 
     @Test
     public void testCreateDriver_Error() throws Exception {
         expectCreateDriverFail();
-        int userId = mManager.createDriver("test driver", false);
-        assertThat(userId).isEqualTo(UserHandle.USER_NULL);
+
+        AndroidFuture<UserCreationResult> future = mManager.createDriver("test driver", false);
+
+        assertThat(future).isNotNull();
+        UserCreationResult result = getResult(future);
+        assertThat(result.getStatus()).isEqualTo(UserSwitchResult.STATUS_HAL_INTERNAL_FAILURE);
     }
 
     @Test
@@ -165,13 +185,16 @@
         assertThat(success).isFalse();
     }
 
-    private void expectCreateDriverSucceed(@UserIdInt int userId) throws Exception {
-        UserInfo userInfo = UserTestingHelper.newUser(userId);
-        when(mService.createDriver(eq("test driver"), anyBoolean())).thenReturn(userInfo);
+    private void expectCreateDriverSucceed(String name, @UserIdInt int userId) throws Exception {
+        AndroidFuture<UserCreationResult> future = new AndroidFuture<>();
+        future.complete(new UserCreationResult(UserCreationResult.STATUS_SUCCESSFUL,
+                UserTestingHelper.newUser(userId), null));
+        when(mService.createDriver(eq(name), anyBoolean())).thenReturn(future);
     }
 
     private void expectCreateDriverFail() throws Exception {
-        when(mService.createDriver(eq("test driver"), anyBoolean())).thenReturn(null);
+        doThrow(new RemoteException("D'OH!")).when(mService)
+            .createDriver(anyString(), anyBoolean());
     }
 
     private void expectCreatePassengerSucceed() throws Exception {
diff --git a/tests/carservice_unit_test/src/com/android/car/user/UserCreationResultTest.java b/tests/carservice_unit_test/src/com/android/car/user/UserCreationResultTest.java
new file mode 100644
index 0000000..7aba561
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/user/UserCreationResultTest.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 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.user;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.car.user.UserCreationResult;
+
+import org.junit.Test;
+
+public final class UserCreationResultTest {
+
+    @Test
+    public void testIsSuccess() {
+        assertThat(new UserCreationResult(UserCreationResult.STATUS_SUCCESSFUL, null, null)
+                .isSuccess()).isTrue();
+        assertThat(new UserCreationResult(UserCreationResult.STATUS_HAL_FAILURE, null, null)
+                .isSuccess()).isFalse();
+        assertThat(
+                new UserCreationResult(UserCreationResult.STATUS_HAL_INTERNAL_FAILURE, null, null)
+                        .isSuccess()).isFalse();
+        assertThat(new UserCreationResult(UserCreationResult.STATUS_ANDROID_FAILURE, null, null)
+                .isSuccess()).isFalse();
+    }
+}
diff --git a/tests/carservice_unit_test/src/com/android/car/user/UserSwitchResultTest.java b/tests/carservice_unit_test/src/com/android/car/user/UserSwitchResultTest.java
new file mode 100644
index 0000000..385bb4e
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/user/UserSwitchResultTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 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.user;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.car.user.UserSwitchResult;
+
+import org.junit.Test;
+
+public final class UserSwitchResultTest {
+
+    @Test
+    public void testIUserSwitchResult_checkStatusAndMessage() {
+        String msg = "Test Message";
+        UserSwitchResult result =
+                new UserSwitchResult(UserSwitchResult.STATUS_SUCCESSFUL, msg);
+        assertThat(result.getStatus()).isEqualTo(UserSwitchResult.STATUS_SUCCESSFUL);
+        assertThat(result.getErrorMessage()).isEqualTo(msg);
+    }
+
+    @Test
+    public void testIUserSwitchResult_isSuccess_failure() {
+        UserSwitchResult result =
+                new UserSwitchResult(UserSwitchResult.STATUS_ANDROID_FAILURE, null);
+        assertThat(result.isSuccess()).isFalse();
+    }
+
+    @Test
+    public void testIUserSwitchResult_isSuccess_success() {
+        UserSwitchResult result =
+                new UserSwitchResult(UserSwitchResult.STATUS_SUCCESSFUL, null);
+        assertThat(result.isSuccess()).isTrue();
+    }
+
+    @Test
+    public void testIUserSwitchResult_isSuccess_requestedState() {
+        UserSwitchResult result =
+                new UserSwitchResult(UserSwitchResult.STATUS_ALREADY_REQUESTED_USER, null);
+        assertThat(result.isSuccess()).isTrue();
+    }
+}
diff --git a/user/car-user-lib/Android.bp b/user/car-user-lib/Android.bp
index a9a5fc1..12bc950 100644
--- a/user/car-user-lib/Android.bp
+++ b/user/car-user-lib/Android.bp
@@ -18,6 +18,7 @@
         "src/**/*.java",
     ],
     static_libs: [
+        "android.car.settings",
         "android.hardware.automotive.vehicle-V2.0-java",
     ],
     product_variables: {
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 4079e28..15a7d1a 100644
--- a/user/car-user-lib/src/android/car/userlib/CarUserManagerHelper.java
+++ b/user/car-user-lib/src/android/car/userlib/CarUserManagerHelper.java
@@ -17,10 +17,12 @@
 package android.car.userlib;
 
 import android.Manifest;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
+import android.car.settings.CarSettings;
 import android.content.Context;
 import android.content.pm.UserInfo;
 import android.graphics.Bitmap;
@@ -30,7 +32,6 @@
 import android.sysprop.CarProperties;
 import android.util.Log;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.UserIcons;
 
 import com.google.android.collect.Sets;
@@ -57,6 +58,7 @@
 public final class CarUserManagerHelper {
     private static final String TAG = "CarUserManagerHelper";
 
+    private static final boolean DEBUG = false;
     private static final int BOOT_USER_NOT_FOUND = -1;
 
     /**
@@ -97,14 +99,41 @@
      * Sets the last active user.
      */
     public void setLastActiveUser(@UserIdInt int userId) {
-        Settings.Global.putInt(
-                mContext.getContentResolver(), Settings.Global.LAST_ACTIVE_USER_ID, userId);
+        if (UserHelper.isHeadlessSystemUser(userId)) {
+            if (DEBUG) Log.d(TAG, "setLastActiveUser(): ignoring headless system user " + userId);
+            return;
+        }
+        setUserIdGlobalProperty(CarSettings.Global.LAST_ACTIVE_USER_ID, userId);
+
+        // TODO(b/155918094): change method to receive a UserInfo instead
+        UserInfo user = mUserManager.getUserInfo(userId);
+        if (user == null) {
+            Log.w(TAG, "setLastActiveUser(): user " + userId + " doesn't exist");
+            return;
+        }
+        if (!user.isEphemeral()) {
+            setUserIdGlobalProperty(CarSettings.Global.LAST_ACTIVE_PERSISTENT_USER_ID, userId);
+        }
     }
 
-    private int getLastActiveUser() {
-        return Settings.Global.getInt(
-            mContext.getContentResolver(), Settings.Global.LAST_ACTIVE_USER_ID,
-            /* default user id= */ UserHandle.USER_SYSTEM);
+    private void setUserIdGlobalProperty(@NonNull String name, @UserIdInt int userId) {
+        if (DEBUG) Log.d(TAG, "setting global property " + name + " to " + userId);
+
+        Settings.Global.putInt(mContext.getContentResolver(), name, userId);
+    }
+
+    private int getUserIdGlobalProperty(@NonNull String name) {
+        int userId = Settings.Global.getInt(mContext.getContentResolver(), name,
+                UserHandle.USER_NULL);
+        if (DEBUG) Log.d(TAG, "getting global property " + name + ": " + userId);
+
+        return userId;
+    }
+
+    private void resetUserIdGlobalProperty(@NonNull String name) {
+        if (DEBUG) Log.d(TAG, "resetting global property " + name);
+
+        Settings.Global.putInt(mContext.getContentResolver(), name, UserHandle.USER_NULL);
     }
 
     /**
@@ -125,7 +154,6 @@
      * @return user id of the initial user to boot into on the device, or
      * {@link UserHandle#USER_NULL} if there is no user available.
      */
-    @VisibleForTesting
     int getInitialUser(boolean usesOverrideUserIdProperty) {
 
         List<Integer> allUsers = userInfoListToUserIdList(getAllUsers());
@@ -148,18 +176,29 @@
         }
 
         // If the last active user is not the SYSTEM user and is a real user, return it
-        int lastActiveUser = getLastActiveUser();
-        if (lastActiveUser != UserHandle.USER_SYSTEM
-                && allUsers.contains(lastActiveUser)) {
-            Log.i(TAG, "Last active user loaded for initial user, user id: "
-                    + lastActiveUser);
+        int lastActiveUser = getUserIdGlobalProperty(CarSettings.Global.LAST_ACTIVE_USER_ID);
+        if (allUsers.contains(lastActiveUser)) {
+            Log.i(TAG, "Last active user loaded for initial user: " + lastActiveUser);
             return lastActiveUser;
         }
+        resetUserIdGlobalProperty(CarSettings.Global.LAST_ACTIVE_USER_ID);
+
+        int lastPersistentUser = getUserIdGlobalProperty(
+                CarSettings.Global.LAST_ACTIVE_PERSISTENT_USER_ID);
+        if (allUsers.contains(lastPersistentUser)) {
+            Log.i(TAG, "Last active, persistent user loaded for initial user: "
+                    + lastPersistentUser);
+            return lastPersistentUser;
+        }
+        resetUserIdGlobalProperty(CarSettings.Global.LAST_ACTIVE_PERSISTENT_USER_ID);
 
         // If all else fails, return the smallest user id
         int returnId = Collections.min(allUsers);
-        Log.i(TAG, "Saved ids were invalid. Returning smallest user id, user id: "
-                + returnId);
+        // TODO(b/158101909): the smallest user id is not always the initial user; a better approach
+        // would be looking for the first ADMIN user, or keep track of all last active users (not
+        // just the very last)
+        Log.w(TAG, "Last active user (" + lastActiveUser + ") not found. Returning smallest user id"
+                + " instead: " + returnId);
         return returnId;
     }
 
diff --git a/user/car-user-lib/src/android/car/userlib/UserHalHelper.java b/user/car-user-lib/src/android/car/userlib/UserHalHelper.java
index a9ddf0b..67a83c3 100644
--- a/user/car-user-lib/src/android/car/userlib/UserHalHelper.java
+++ b/user/car-user-lib/src/android/car/userlib/UserHalHelper.java
@@ -27,6 +27,8 @@
 import android.hardware.automotive.vehicle.V2_0.InitialUserInfoRequestType;
 import android.hardware.automotive.vehicle.V2_0.InitialUserInfoResponse;
 import android.hardware.automotive.vehicle.V2_0.InitialUserInfoResponseAction;
+import android.hardware.automotive.vehicle.V2_0.RemoveUserRequest;
+import android.hardware.automotive.vehicle.V2_0.SwitchUserRequest;
 import android.hardware.automotive.vehicle.V2_0.UserFlags;
 import android.hardware.automotive.vehicle.V2_0.UserIdentificationAssociation;
 import android.hardware.automotive.vehicle.V2_0.UserIdentificationAssociationSetValue;
@@ -60,9 +62,12 @@
     private static final boolean DEBUG = false;
 
     public static final int INITIAL_USER_INFO_PROPERTY = 299896583;
+    public static final int SWITCH_USER_PROPERTY = 299896584;
     public static final int CREATE_USER_PROPERTY = 299896585;
+    public static final int REMOVE_USER_PROPERTY = 299896586;
     public static final int USER_IDENTIFICATION_ASSOCIATION_PROPERTY = 299896587;
 
+
     private static final String STRING_SEPARATOR = "\\|\\|";
 
     /**
@@ -486,6 +491,8 @@
         Objects.requireNonNull(request, "request cannot be null");
         checkArgument(request.requestId > 0, "invalid requestId on %s", request);
         checkValid(request.usersInfo);
+        checkArgument(request.newUserName != null, "newUserName cannot be null (should be empty "
+                + "instead) on %s", request);
 
         boolean hasNewUser = false;
         int newUserFlags = UserFlags.NONE;
@@ -513,10 +520,62 @@
     }
 
     /**
-     * Creates a {@link UsersInfo} instance populated with the current users.
+     * Creates a generic {@link VehiclePropValue} (that can be sent to HAL) from a
+     * {@link SwitchUserRequest}.
+     *
+     * @throws IllegalArgumentException if the request doesn't have the proper format.
+     */
+    @NonNull
+    public static VehiclePropValue toVehiclePropValue(@NonNull SwitchUserRequest request) {
+        Objects.requireNonNull(request, "request cannot be null");
+        checkArgument(request.messageType > 0, "invalid messageType on %s", request);
+        android.hardware.automotive.vehicle.V2_0.UserInfo targetInfo = request.targetUser;
+        UsersInfo usersInfo = request.usersInfo;
+        Objects.requireNonNull(targetInfo);
+        checkValid(usersInfo);
+
+        VehiclePropValue propValue = createPropRequest(SWITCH_USER_PROPERTY, request.requestId,
+                request.messageType);
+        addUserInfo(propValue, targetInfo);
+        addUsersInfo(propValue, usersInfo);
+        return propValue;
+    }
+
+    /**
+     * Creates a generic {@link VehiclePropValue} (that can be sent to HAL) from a
+     * {@link RemoveUserRequest}.
+     *
+     * @throws IllegalArgumentException if the request doesn't have the proper format.
+     */
+    @NonNull
+    public static VehiclePropValue toVehiclePropValue(@NonNull RemoveUserRequest request) {
+        checkArgument(request.requestId > 0, "invalid requestId on %s", request);
+        android.hardware.automotive.vehicle.V2_0.UserInfo removedUserInfo = request.removedUserInfo;
+        Objects.requireNonNull(removedUserInfo);
+        UsersInfo usersInfo = request.usersInfo;
+        checkValid(usersInfo);
+
+        VehiclePropValue propValue = createPropRequest(REMOVE_USER_PROPERTY, request.requestId);
+        addUserInfo(propValue, removedUserInfo);
+        addUsersInfo(propValue, usersInfo);
+        return propValue;
+    }
+
+    /**
+     * Creates a {@link UsersInfo} instance populated with the current users, using
+     * {@link ActivityManager#getCurrentUser()} as the current user.
      */
     @NonNull
     public static UsersInfo newUsersInfo(@NonNull UserManager um) {
+        return newUsersInfo(um, ActivityManager.getCurrentUser());
+    }
+
+    /**
+     * Creates a {@link UsersInfo} instance populated with the current users, using
+     * {@code userId} as the current user.
+     */
+    @NonNull
+    public static UsersInfo newUsersInfo(@NonNull UserManager um, @UserIdInt int userId) {
         Preconditions.checkArgument(um != null, "UserManager cannot be null");
 
         List<UserInfo> users = um.getUsers(/*excludeDying= */ true);
@@ -527,7 +586,7 @@
         }
 
         UsersInfo usersInfo = new UsersInfo();
-        usersInfo.currentUser.userId = ActivityManager.getCurrentUser();
+        usersInfo.currentUser.userId = userId;
         UserInfo currentUser = null;
         usersInfo.numberUsers = users.size();