Merge "Updating getSuggestedAudioUsage" into rvc-dev
diff --git a/car-bugreportd/OWNERS b/car-bugreportd/OWNERS
new file mode 100644
index 0000000..335fc46
--- /dev/null
+++ b/car-bugreportd/OWNERS
@@ -0,0 +1,2 @@
+sgurun@google.com
+zhomart@google.com
diff --git a/car-bugreportd/README.md b/car-bugreportd/README.md
new file mode 100644
index 0000000..c6fae4c
--- /dev/null
+++ b/car-bugreportd/README.md
@@ -0,0 +1,9 @@
+# car-bugreportd
+
+Android Automotive OS only service. Please use `CarBugreportManager` API.
+
+It takes bugreport, appends car specific files and then proxies them to
+`CarBugreportManagerService`.
+
+To start, set the value of the system property `ctl.start` to `car-bugreportd`,
+e.g. `SystemProperties.set("ctl.start", "car-bugreportd");`
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 e5b8db8..9473474 100644
--- a/car-internal-lib/src/com/android/internal/car/EventLogTags.logtags
+++ b/car-internal-lib/src/com/android/internal/car/EventLogTags.logtags
@@ -74,6 +74,7 @@
 150108 car_user_svc_post_switch_user_req (target_user_id|1),(current_user_id|1)
 150109 car_user_svc_get_user_auth_req (uid|1),(user_id|1),(number_types|1)
 150110 car_user_svc_get_user_auth_resp (number_values|1)
+150111 car_user_svc_switch_user_ui_req (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)
@@ -82,6 +83,7 @@
 150144 car_user_hal_post_switch_user_req (request_id|1),(target_user_id|1),(current_user_id|1)
 150145 car_user_hal_get_user_auth_req (int32values|4)
 150146 car_user_hal_get_user_auth_resp (int32values|4),(error_message|3)
+150147 car_user_hal_legacy_switch_user_req (request_id|1),(target_user_id|1),(current_user_id|1)
 
 150171 car_user_mgr_add_listener (uid|1)
 150172 car_user_mgr_remove_listener (uid|1)
diff --git a/car-lib/native/include/CarPowerManager.h b/car-lib/native/include/CarPowerManager.h
index 57bbd50..b02c886 100644
--- a/car-lib/native/include/CarPowerManager.h
+++ b/car-lib/native/include/CarPowerManager.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef CAR_POWER_MANAGER
-#define CAR_POWER_MANAGER
+#ifndef CAR_LIB_NATIVE_INCLUDE_CARPOWERMANAGER_H_
+#define CAR_LIB_NATIVE_INCLUDE_CARPOWERMANAGER_H_
 
 #include <binder/Status.h>
 #include <utils/RefBase.h>
@@ -38,6 +38,7 @@
     //  NOTE:  The entries in this enum must match the ones in CarPowerStateListener located in
     //      packages/services/Car/car-lib/src/android/car/hardware/power/CarPowerManager.java
     enum class State {
+        kInvalid = 0,
         kWaitForVhal = 1,
         kSuspendEnter = 2,
         kSuspendExit = 3,
@@ -46,8 +47,7 @@
         kShutdownPrepare = 7,
         kShutdownCancelled = 8,
 
-
-        kFirst = kWaitForVhal,
+        kFirst = kInvalid,
         kLast = kShutdownCancelled,
     };
 
@@ -74,7 +74,7 @@
 private:
     class CarPowerStateListener final : public BnCarPowerStateListener {
     public:
-        explicit CarPowerStateListener(CarPowerManager* parent) : mParent(parent) {};
+        explicit CarPowerStateListener(CarPowerManager* parent) : mParent(parent) {}
 
         Status onStateChanged(int state) override {
             sp<CarPowerManager> parent = mParent;
@@ -102,9 +102,9 @@
     sp<CarPowerStateListener> mListenerToService;
 };
 
-} // namespace power
-} // namespace hardware
-} // namespace car
-} // namespace android
+}  // namespace power
+}  // namespace hardware
+}  // namespace car
+}  // namespace android
 
-#endif // CAR_POWER_MANAGER
+#endif  // CAR_LIB_NATIVE_INCLUDE_CARPOWERMANAGER_H_
diff --git a/car-lib/src/android/car/Car.java b/car-lib/src/android/car/Car.java
index 3160e9e..099d56a 100644
--- a/car-lib/src/android/car/Car.java
+++ b/car-lib/src/android/car/Car.java
@@ -988,6 +988,10 @@
 
     /**
      * A factory method that creates Car instance for all Car API access.
+     *
+     * <p>Instance created with this should be disconnected from car service by calling
+     * {@link #disconnect()} before the passed {code Context} is released.
+     *
      * @param context App's Context. This should not be null. If you are passing
      *                {@link ContextWrapper}, make sure that its base Context is non-null as well.
      *                Otherwise it will throw {@link java.lang.NullPointerException}.
@@ -1020,6 +1024,9 @@
      * A factory method that creates Car instance for all Car API access using main thread {@code
      * Looper}.
      *
+     * <p>Instance created with this should be disconnected from car service by calling
+     * {@link #disconnect()} before the passed {code Context} is released.
+     *
      * @see #createCar(Context, ServiceConnection, Handler)
      *
      * @deprecated use {@link #createCar(Context, Handler)} instead.
@@ -1032,6 +1039,9 @@
     /**
      * Creates new {@link Car} object which connected synchronously to Car Service and ready to use.
      *
+     * <p>Instance created with this should be disconnected from car service by calling
+     * {@link #disconnect()} before the passed {code Context} is released.
+     *
      * @param context application's context
      *
      * @return Car object if operation succeeded, otherwise null.
@@ -1044,6 +1054,9 @@
     /**
      * Creates new {@link Car} object which connected synchronously to Car Service and ready to use.
      *
+     * <p>Instance created with this should be disconnected from car service by calling
+     * {@link #disconnect()} before the passed {code Context} is released.
+     *
      * @param context App's Context. This should not be null. If you are passing
      *                {@link ContextWrapper}, make sure that its base Context is non-null as well.
      *                Otherwise it will throw {@link java.lang.NullPointerException}.
@@ -1111,6 +1124,9 @@
     /**
      * Creates new {@link Car} object with {@link CarServiceLifecycleListener}.
      *
+     * <p>Instance created with this should be disconnected from car service by calling
+     * {@link #disconnect()} before the passed {code Context} is released.
+     *
      * <p> If car service is ready inside this call and if the caller is running in the main thread,
      * {@link CarServiceLifecycleListener#onLifecycleChanged(Car, boolean)} will be called
      * with ready set to be true. Otherwise,
diff --git a/car-lib/src/android/car/ICarUserService.aidl b/car-lib/src/android/car/ICarUserService.aidl
index 9e49790..2eccdbb 100644
--- a/car-lib/src/android/car/ICarUserService.aidl
+++ b/car-lib/src/android/car/ICarUserService.aidl
@@ -37,4 +37,5 @@
     oneway void resetLifecycleListenerForUid();
     oneway void getInitialUserInfo(int requestType, int timeoutMs, in IResultReceiver receiver);
     GetUserIdentificationAssociationResponse getUserIdentificationAssociation(in int[] types);
+    oneway void setUserSwitchUiCallback(in IResultReceiver callback);
 }
diff --git a/car-lib/src/android/car/content/pm/CarAppBlockingPolicy.java b/car-lib/src/android/car/content/pm/CarAppBlockingPolicy.java
index 3ada352..3027d02 100644
--- a/car-lib/src/android/car/content/pm/CarAppBlockingPolicy.java
+++ b/car-lib/src/android/car/content/pm/CarAppBlockingPolicy.java
@@ -85,16 +85,19 @@
         payloadParcel.recycle();
     }
 
-    public static final Parcelable.Creator<CarAppBlockingPolicy> CREATOR
-            = new Parcelable.Creator<CarAppBlockingPolicy>() {
-        public CarAppBlockingPolicy createFromParcel(Parcel in) {
-            return new CarAppBlockingPolicy(in);
-        }
+    public static final Parcelable.Creator<CarAppBlockingPolicy> CREATOR =
+            new Parcelable.Creator<CarAppBlockingPolicy>() {
 
-        public CarAppBlockingPolicy[] newArray(int size) {
-            return new CarAppBlockingPolicy[size];
-        }
-    };
+                @Override
+                public CarAppBlockingPolicy createFromParcel(Parcel in) {
+                    return new CarAppBlockingPolicy(in);
+                }
+
+                @Override
+                public CarAppBlockingPolicy[] newArray(int size) {
+                    return new CarAppBlockingPolicy[size];
+                }
+            };
 
     @Override
     public int hashCode() {
diff --git a/car-lib/src/android/car/hardware/power/CarPowerManager.java b/car-lib/src/android/car/hardware/power/CarPowerManager.java
index 7c2a323..d9ff33d 100644
--- a/car-lib/src/android/car/hardware/power/CarPowerManager.java
+++ b/car-lib/src/android/car/hardware/power/CarPowerManager.java
@@ -16,6 +16,7 @@
 
 package android.car.hardware.power;
 
+import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.car.Car;
 import android.car.CarManagerBase;
@@ -56,10 +57,15 @@
     public interface CarPowerStateListener {
         /**
          * onStateChanged() states.  These definitions must match the ones located in the native
-         * CarPowerManager:  packages/services/Car/car-lib/native/CarPowerManager/CarPowerManager.h
+         * CarPowerManager:  packages/services/Car/car-lib/native/include/CarPowerManager.h
          */
 
         /**
+         * The current power state is unavailable, unknown, or invalid
+         * @hide
+         */
+        int INVALID = 0;
+        /**
          * Android is up, but vendor is controlling the audio / display
          * @hide
          */
@@ -162,6 +168,20 @@
     }
 
     /**
+     * Returns the current power state
+     * @return One of the values defined in {@link CarPowerStateListener}
+     * @hide
+     */
+    @RequiresPermission(Car.PERMISSION_CAR_POWER)
+    public int getPowerState() {
+        try {
+            return mService.getPowerState();
+        } catch (RemoteException e) {
+            return handleRemoteExceptionFromCarService(e, CarPowerStateListener.INVALID);
+        }
+    }
+
+    /**
      * Sets a listener to receive power state changes. Only one listener may be set at a
      * time for an instance of CarPowerManager.
      * The listener is assumed to completely handle the 'onStateChanged' before returning.
diff --git a/car-lib/src/android/car/hardware/power/ICarPower.aidl b/car-lib/src/android/car/hardware/power/ICarPower.aidl
index 581e736..505b075 100644
--- a/car-lib/src/android/car/hardware/power/ICarPower.aidl
+++ b/car-lib/src/android/car/hardware/power/ICarPower.aidl
@@ -31,4 +31,6 @@
     void scheduleNextWakeupTime(int seconds) = 4;
 
     void registerListenerWithCompletion(in ICarPowerStateListener listener) = 5;
+
+    int getPowerState() = 6;
 }
diff --git a/car-lib/src/android/car/navigation/CarNavigationInstrumentCluster.java b/car-lib/src/android/car/navigation/CarNavigationInstrumentCluster.java
index b41c7de..444f82a 100644
--- a/car-lib/src/android/car/navigation/CarNavigationInstrumentCluster.java
+++ b/car-lib/src/android/car/navigation/CarNavigationInstrumentCluster.java
@@ -57,25 +57,31 @@
 
     private final Bundle mExtra;
 
-    public static final Parcelable.Creator<CarNavigationInstrumentCluster> CREATOR
-            = new Parcelable.Creator<CarNavigationInstrumentCluster>() {
-        public CarNavigationInstrumentCluster createFromParcel(Parcel in) {
-            return new CarNavigationInstrumentCluster(in);
-        }
+    public static final Parcelable.Creator<CarNavigationInstrumentCluster> CREATOR =
+                new Parcelable.Creator<CarNavigationInstrumentCluster>() {
+            public CarNavigationInstrumentCluster createFromParcel(Parcel in) {
+                return new CarNavigationInstrumentCluster(in);
+            }
 
-        public CarNavigationInstrumentCluster[] newArray(int size) {
-            return new CarNavigationInstrumentCluster[size];
-        }
-    };
+            public CarNavigationInstrumentCluster[] newArray(int size) {
+                return new CarNavigationInstrumentCluster[size];
+            }
+        };
 
+    /**
+     * Creates a new {@link CarNavigationInstrumentCluster}.
+     */
     public static CarNavigationInstrumentCluster createCluster(int minIntervalMillis) {
         return new CarNavigationInstrumentCluster(minIntervalMillis, CLUSTER_TYPE_IMAGE_CODES_ONLY,
                 0, 0, 0);
     }
 
-    public static CarNavigationInstrumentCluster createCustomImageCluster(int minIntervalMs,
+    /**
+     * Creates a new {@link CarNavigationInstrumentCluster}.
+     */
+    public static CarNavigationInstrumentCluster createCustomImageCluster(int minIntervalMillis,
             int imageWidth, int imageHeight, int imageColorDepthBits) {
-        return new CarNavigationInstrumentCluster(minIntervalMs,
+        return new CarNavigationInstrumentCluster(minIntervalMillis,
                 CLUSTER_TYPE_CUSTOM_IMAGES_SUPPORTED,
                 imageWidth, imageHeight, imageColorDepthBits);
     }
@@ -108,7 +114,9 @@
      * Contains extra information about instrument cluster.
      * @hide
      */
-    public Bundle getExtra() { return mExtra; }
+    public Bundle getExtra() {
+        return mExtra;
+    }
 
     /**
      * If instrument cluster is image, number of bits of colour depth it supports (8, 16, or 32).
@@ -127,10 +135,9 @@
 
     /**
      * Whether cluster support custom image or not.
-     * @return
      */
     public boolean supportsCustomImages() {
-      return mType == CLUSTER_TYPE_CUSTOM_IMAGES_SUPPORTED;
+        return mType == CLUSTER_TYPE_CUSTOM_IMAGES_SUPPORTED;
     }
 
     private CarNavigationInstrumentCluster(
@@ -174,12 +181,12 @@
     /** Converts to string for debug purpose */
     @Override
     public String toString() {
-        return CarNavigationInstrumentCluster.class.getSimpleName() + "{ " +
-                "minIntervalMillis: " + mMinIntervalMillis + ", " +
-                "type: " + mType + ", " +
-                "imageWidth: " + mImageWidth + ", " +
-                "imageHeight: " + mImageHeight + ", " +
-                "imageColourDepthBits: " + mImageColorDepthBits +
-                "extra: " + mExtra + " }";
+        return CarNavigationInstrumentCluster.class.getSimpleName() + "{ "
+                + "minIntervalMillis: " + mMinIntervalMillis + ", "
+                + "type: " + mType + ", "
+                + "imageWidth: " + mImageWidth + ", "
+                + "imageHeight: " + mImageHeight + ", "
+                + "imageColourDepthBits: " + mImageColorDepthBits
+                + "extra: " + mExtra + " }";
     }
 }
diff --git a/car-lib/src/android/car/storagemonitoring/IoStats.java b/car-lib/src/android/car/storagemonitoring/IoStats.java
index d620169..5154196 100644
--- a/car-lib/src/android/car/storagemonitoring/IoStats.java
+++ b/car-lib/src/android/car/storagemonitoring/IoStats.java
@@ -70,7 +70,7 @@
         mUptimeTimestamp = in.getInt("uptime");
         JSONArray statsArray = in.getJSONArray("stats");
         mStats = new ArrayList<>();
-        for(int i = 0; i < statsArray.length(); ++i) {
+        for (int i = 0; i < statsArray.length(); ++i) {
             mStats.add(new IoStatsEntry(statsArray.getJSONObject(i)));
         }
     }
@@ -113,6 +113,11 @@
         return Objects.hash(mStats, mUptimeTimestamp);
     }
 
+    /**
+     * Returns user's stats ({@link IoStatsEntry}).
+     *
+     * @param uid Android's user id
+     */
     public IoStatsEntry getUserIdStats(int uid) {
         for (IoStatsEntry stats : getStats()) {
             if (stats.uid == uid) {
@@ -123,6 +128,10 @@
         return null;
     }
 
+    /**
+     * Returns the following foreground total metrics: bytes written and read, bytes read from and
+     * written to storage, and number of sync calls.
+     */
     public IoStatsEntry.Metrics getForegroundTotals() {
         long bytesRead = 0;
         long bytesWritten = 0;
@@ -145,6 +154,10 @@
                 fsyncCalls);
     }
 
+    /**
+     * Returns the following background total metrics: bytes written and read, bytes read from and
+     * written to storage, and number of sync calls.
+     */
     public IoStatsEntry.Metrics getBackgroundTotals() {
         long bytesRead = 0;
         long bytesWritten = 0;
@@ -167,6 +180,10 @@
             fsyncCalls);
     }
 
+    /**
+     * Returns the sum of all foreground and background metrics (bytes written, bytes read from
+     * storage, bytes written to storage and number of sync calls).
+     */
     public IoStatsEntry.Metrics getTotals() {
         IoStatsEntry.Metrics foreground = getForegroundTotals();
         IoStatsEntry.Metrics background = getBackgroundTotals();
@@ -181,9 +198,9 @@
     @Override
     public boolean equals(Object other) {
         if (other instanceof IoStats) {
-            IoStats delta = (IoStats)other;
-            return delta.getTimestamp() == getTimestamp() &&
-                delta.getStats().equals(getStats());
+            IoStats delta = (IoStats) other;
+            return delta.getTimestamp() == getTimestamp()
+                && delta.getStats().equals(getStats());
         }
         return false;
     }
diff --git a/car-lib/src/android/car/user/CarUserManager.java b/car-lib/src/android/car/user/CarUserManager.java
index 8d7bfa1..784ee48 100644
--- a/car-lib/src/android/car/user/CarUserManager.java
+++ b/car-lib/src/android/car/user/CarUserManager.java
@@ -338,6 +338,40 @@
     }
 
     /**
+     * Sets a callback to be notified before user switch. It should only be used by Car System UI.
+     *
+     * @hide
+     */
+    public void setUserSwitchUiCallback(@NonNull UserSwitchUiCallback callback) {
+        Preconditions.checkArgument(callback != null, "Null callback");
+        UserSwitchUiCallbackReceiver userSwitchUiCallbackReceiver =
+                new UserSwitchUiCallbackReceiver(callback);
+        try {
+            mService.setUserSwitchUiCallback(userSwitchUiCallbackReceiver);
+        } catch (RemoteException e) {
+            handleRemoteExceptionFromCarService(e);
+        }
+    }
+
+    /**
+     * {@code IResultReceiver} used to receive user switch UI Callback.
+     */
+    // TODO(b/154958003): use mReceiver instead as now there are two binder objects
+    private final class UserSwitchUiCallbackReceiver extends IResultReceiver.Stub {
+
+        private final UserSwitchUiCallback mUserSwitchUiCallback;
+
+        UserSwitchUiCallbackReceiver(UserSwitchUiCallback callback) {
+            mUserSwitchUiCallback = callback;
+        }
+
+        @Override
+        public void send(int userId, Bundle unused) throws RemoteException {
+            mUserSwitchUiCallback.showUserSwitchDialog(userId);
+        }
+    }
+
+    /**
      * {@code IResultReceiver} used to receive lifecycle events and dispatch to the proper listener.
      */
     private class LifecycleResultReceiver extends IResultReceiver.Stub {
@@ -560,4 +594,20 @@
          */
         void onEvent(@NonNull UserLifecycleEvent event);
     }
+
+    /**
+     * Callback for notifying user switch before switch started.
+     *
+     * <p> It should only be user by Car System UI. The purpose of this callback is notify the
+     * Car System UI to display the user switch UI.
+     *
+     * @hide
+     */
+    public interface UserSwitchUiCallback {
+
+        /**
+         * Called to notify that user switch dialog should be shown now.
+         */
+        void showUserSwitchDialog(@UserIdInt int userId);
+    }
 }
diff --git a/car_product/build/car.mk b/car_product/build/car.mk
index 64d6bec..bc61fca 100644
--- a/car_product/build/car.mk
+++ b/car_product/build/car.mk
@@ -16,8 +16,8 @@
 
 # Common make file for all car builds
 
-BOARD_PLAT_PUBLIC_SEPOLICY_DIR += packages/services/Car/car_product/sepolicy/public
-BOARD_PLAT_PRIVATE_SEPOLICY_DIR += packages/services/Car/car_product/sepolicy/private
+PRODUCT_PUBLIC_SEPOLICY_DIRS += packages/services/Car/car_product/sepolicy/public
+PRODUCT_PRIVATE_SEPOLICY_DIRS += packages/services/Car/car_product/sepolicy/private
 
 PRODUCT_PACKAGES += \
     Bluetooth \
diff --git a/car_product/build/preinstalled-packages-product-car-base.xml b/car_product/build/preinstalled-packages-product-car-base.xml
index b15007f..e888eb5 100644
--- a/car_product/build/preinstalled-packages-product-car-base.xml
+++ b/car_product/build/preinstalled-packages-product-car-base.xml
@@ -66,12 +66,6 @@
         <install-in user-type="SYSTEM" />
     </install-in-user-type>
 
-    <!-- Mainline Wi-fi stack, it's needed for all users -->
-    <install-in-user-type package="com.android.wifi">
-        <install-in user-type="SYSTEM" />
-        <install-in user-type="FULL" />
-    </install-in-user-type>
-
     <!-- Provides Settings. Secure for SYSTEM, which are used in places such as SUW -->
     <install-in-user-type package="com.android.providers.settings">
         <install-in user-type="FULL" />
@@ -124,18 +118,6 @@
         <install-in user-type="SYSTEM" />
     </install-in-user-type>
 
-    <!-- Uses system user id -->
-    <install-in-user-type package="com.android.incremental.nativeadb">
-        <install-in user-type="FULL" />
-        <install-in user-type="SYSTEM" />
-    </install-in-user-type>
-
-    <!-- Uses system user id -->
-    <install-in-user-type package="com.android.networkstack.inprocess">
-        <install-in user-type="FULL" />
-        <install-in user-type="SYSTEM" />
-    </install-in-user-type>
-
     <!-- Required StorageManagerService to bind to the ExternalStorageService -->
     <install-in-user-type package="com.android.providers.media.module">
         <install-in user-type="FULL" />
@@ -191,9 +173,6 @@
     <install-in-user-type package="com.android.htmlviewer">
         <install-in user-type="FULL" />
     </install-in-user-type>
-    <install-in-user-type package="com.android.protips">
-        <install-in user-type="FULL" />
-    </install-in-user-type>
     <install-in-user-type package="com.android.inputdevices">
         <install-in user-type="FULL" />
     </install-in-user-type>
@@ -239,9 +218,6 @@
     <install-in-user-type package="com.android.bluetoothmidiservice">
         <install-in user-type="FULL" />
     </install-in-user-type>
-    <install-in-user-type package="com.android.smspush">
-        <install-in user-type="FULL" />
-    </install-in-user-type>
     <install-in-user-type package="com.android.statementservice">
         <install-in user-type="FULL" />
     </install-in-user-type>
@@ -299,9 +275,6 @@
     <install-in-user-type package="com.android.storagemanager">
         <install-in user-type="FULL" />
     </install-in-user-type>
-    <install-in-user-type package="android.autoinstalls.config.google.car">
-        <install-in user-type="FULL" />
-    </install-in-user-type>
     <install-in-user-type package="com.android.carrierconfig">
         <install-in user-type="FULL" />
     </install-in-user-type>
@@ -323,4 +296,7 @@
     <install-in-user-type package="com.android.externalstorage">
         <install-in user-type="FULL" />
     </install-in-user-type>
+    <install-in-user-type package="com.android.hotspot2.osulogin">
+        <install-in user-type="FULL" />
+    </install-in-user-type>
 </config>
diff --git a/car_product/occupant_awareness/OccupantAwareness.mk b/car_product/occupant_awareness/OccupantAwareness.mk
index 43d904f..3d27ed8 100644
--- a/car_product/occupant_awareness/OccupantAwareness.mk
+++ b/car_product/occupant_awareness/OccupantAwareness.mk
@@ -1,7 +1,5 @@
 # Occupant Awareness SELinux policy variable definitions
 LOCAL_PATH:= $(call my-dir)
 
-BOARD_PLAT_PUBLIC_SEPOLICY_DIR += $(LOCAL_PATH)/sepolicy/public
-BOARD_PLAT_PRIVATE_SEPOLICY_DIR += $(LOCAL_PATH)/sepolicy/private
-
-BOARD_SEPOLICY_DIRS += $(LOCAL_PATH)/sepolicy
+PRODUCT_PUBLIC_SEPOLICY_DIRS += $(LOCAL_PATH)/sepolicy/public
+PRODUCT_PRIVATE_SEPOLICY_DIRS += $(LOCAL_PATH)/sepolicy/private
diff --git a/computepipe/products/computepipe.mk b/computepipe/products/computepipe.mk
index d5fe7f7..44c93f9 100644
--- a/computepipe/products/computepipe.mk
+++ b/computepipe/products/computepipe.mk
@@ -29,7 +29,7 @@
 
 
 # Selinux public policies for computepipe services
-BOARD_PLAT_PUBLIC_SEPOLICY_DIR += packages/services/Car/computepipe/sepolicy/public
+PRODUCT_PUBLIC_SEPOLICY_DIRS += packages/services/Car/computepipe/sepolicy/public
 
 # Selinux private policies for computepipe services
-BOARD_PLAT_PRIVATE_SEPOLICY_DIR += packages/services/Car/computepipe/sepolicy/private
+PRODUCT_PRIVATE_SEPOLICY_DIRS += packages/services/Car/computepipe/sepolicy/private
diff --git a/computepipe/proto/InputConfig.proto b/computepipe/proto/InputConfig.proto
index 0381e1a..2d82706 100644
--- a/computepipe/proto/InputConfig.proto
+++ b/computepipe/proto/InputConfig.proto
@@ -69,6 +69,26 @@
   optional VideoFileConfig video_config = 8;
 
   optional int32 stream_id = 9;
+
+  enum PixelLayout {
+    UNKNOWN = 0;
+
+    // one byte for R, then one byte for G, then one byte for B for each pixel.
+    RGB24 = 1;
+
+    // one byte for R, one byte for G, one byte for B, one byte for alpha or
+    // unused.
+    RGBA32 = 2;
+
+    // Grayscale, one byte per pixel.
+    GRAY8 = 3;
+
+    // Grayscale, one uint16 per pixel.
+    GRAY16 = 4;
+  }
+
+  // Represent pixel layout of image expected by graph.
+  optional PixelLayout pixel_layout = 10 [default = RGB24];
 }
 
 // A graph could require streams from multiple cameras simultaneously, so each possible input
diff --git a/computepipe/runner/client_interface/PipeOptionsConverter.cpp b/computepipe/runner/client_interface/PipeOptionsConverter.cpp
index c53da46..12b2b3e 100644
--- a/computepipe/runner/client_interface/PipeOptionsConverter.cpp
+++ b/computepipe/runner/client_interface/PipeOptionsConverter.cpp
@@ -41,66 +41,66 @@
 
 namespace {
 
-PipeInputConfigInputType ConvertInputType(proto::InputStreamConfig_InputType type) {
+PipeInputConfigInputType ConvertInputType(proto::InputStreamConfig::InputType type) {
     switch (type) {
-        case proto::InputStreamConfig_InputType_CAMERA:
+        case proto::InputStreamConfig::CAMERA:
             return PipeInputConfigInputType::CAMERA;
-        case proto::InputStreamConfig_InputType_VIDEO_FILE:
+        case proto::InputStreamConfig::VIDEO_FILE:
             return PipeInputConfigInputType::VIDEO_FILE;
-        case proto::InputStreamConfig_InputType_IMAGE_FILES:
+        case proto::InputStreamConfig::IMAGE_FILES:
             return PipeInputConfigInputType::IMAGE_FILES;
     }
 }
 
-PipeInputConfigCameraType ConvertCameraType(proto::CameraConfig_CameraType type) {
+PipeInputConfigCameraType ConvertCameraType(proto::CameraConfig::CameraType type) {
     switch (type) {
-        case proto::CameraConfig_CameraType_DRIVER_VIEW_CAMERA:
+        case proto::CameraConfig::DRIVER_VIEW_CAMERA:
             return PipeInputConfigCameraType::DRIVER_VIEW_CAMERA;
-        case proto::CameraConfig_CameraType_OCCUPANT_VIEW_CAMERA:
+        case proto::CameraConfig::OCCUPANT_VIEW_CAMERA:
             return PipeInputConfigCameraType::OCCUPANT_VIEW_CAMERA;
-        case proto::CameraConfig_CameraType_EXTERNAL_CAMERA:
+        case proto::CameraConfig::EXTERNAL_CAMERA:
             return PipeInputConfigCameraType::EXTERNAL_CAMERA;
-        case proto::CameraConfig_CameraType_SURROUND_VIEW_CAMERA:
+        case proto::CameraConfig::SURROUND_VIEW_CAMERA:
             return PipeInputConfigCameraType::SURROUND_VIEW_CAMERA;
     }
 }
 
-PipeInputConfigImageFileType ConvertImageFileType(proto::ImageFileConfig_ImageFileType type) {
+PipeInputConfigImageFileType ConvertImageFileType(proto::ImageFileConfig::ImageFileType type) {
     switch (type) {
-        case proto::ImageFileConfig_ImageFileType_JPEG:
+        case proto::ImageFileConfig::JPEG:
             return PipeInputConfigImageFileType::JPEG;
-        case proto::ImageFileConfig_ImageFileType_PNG:
+        case proto::ImageFileConfig::PNG:
             return PipeInputConfigImageFileType::PNG;
     }
 }
 
-PipeInputConfigVideoFileType ConvertVideoFileType(proto::VideoFileConfig_VideoFileType type) {
+PipeInputConfigVideoFileType ConvertVideoFileType(proto::VideoFileConfig::VideoFileType type) {
     switch (type) {
-        case proto::VideoFileConfig_VideoFileType_MPEG:
+        case proto::VideoFileConfig::MPEG:
             return PipeInputConfigVideoFileType::MPEG;
     }
 }
 
-PipeInputConfigFormatType ConvertInputFormat(proto::InputStreamConfig_FormatType type) {
+PipeInputConfigFormatType ConvertInputFormat(proto::InputStreamConfig::FormatType type) {
     switch (type) {
-        case proto::InputStreamConfig_FormatType_RGB:
+        case proto::InputStreamConfig::RGB:
             return PipeInputConfigFormatType::RGB;
-        case proto::InputStreamConfig_FormatType_NIR:
+        case proto::InputStreamConfig::NIR:
             return PipeInputConfigFormatType::NIR;
-        case proto::InputStreamConfig_FormatType_NIR_DEPTH:
+        case proto::InputStreamConfig::NIR_DEPTH:
             return PipeInputConfigFormatType::NIR_DEPTH;
     }
 }
 
-PipeOffloadConfigOffloadType ConvertOffloadType(proto::OffloadOption_OffloadType type) {
+PipeOffloadConfigOffloadType ConvertOffloadType(proto::OffloadOption::OffloadType type) {
     switch (type) {
-        case proto::OffloadOption_OffloadType_CPU:
+        case proto::OffloadOption::CPU:
             return PipeOffloadConfigOffloadType::CPU;
-        case proto::OffloadOption_OffloadType_GPU:
+        case proto::OffloadOption::GPU:
             return PipeOffloadConfigOffloadType::GPU;
-        case proto::OffloadOption_OffloadType_NEURAL_ENGINE:
+        case proto::OffloadOption::NEURAL_ENGINE:
             return PipeOffloadConfigOffloadType::NEURAL_ENGINE;
-        case proto::OffloadOption_OffloadType_CV_ENGINE:
+        case proto::OffloadOption::CV_ENGINE:
             return PipeOffloadConfigOffloadType::CV_ENGINE;
     }
 }
@@ -117,15 +117,15 @@
 }
 
 PipeTerminationConfigTerminationType ConvertTerminationType(
-    proto::TerminationOption_TerminationType type) {
+    proto::TerminationOption::TerminationType type) {
     switch (type) {
-        case proto::TerminationOption_TerminationType_CLIENT_STOP:
+        case proto::TerminationOption::CLIENT_STOP:
             return PipeTerminationConfigTerminationType::CLIENT_STOP;
-        case proto::TerminationOption_TerminationType_MIN_PACKET_COUNT:
+        case proto::TerminationOption::MIN_PACKET_COUNT:
             return PipeTerminationConfigTerminationType::MIN_PACKET_COUNT;
-        case proto::TerminationOption_TerminationType_MAX_RUN_TIME:
+        case proto::TerminationOption::MAX_RUN_TIME:
             return PipeTerminationConfigTerminationType::MAX_RUN_TIME;
-        case proto::TerminationOption_TerminationType_EVENT:
+        case proto::TerminationOption::EVENT:
             return PipeTerminationConfigTerminationType::EVENT;
     }
 }
@@ -141,7 +141,8 @@
         aidlInputDesc.height = inputStreamConfig.height();
         aidlInputDesc.stride = inputStreamConfig.stride();
         aidlInputDesc.camDesc.camId = inputStreamConfig.cam_config().cam_id();
-        aidlInputDesc.camDesc.type = ConvertCameraType(inputStreamConfig.cam_config().camera_type());
+        aidlInputDesc.camDesc.type =
+                ConvertCameraType(inputStreamConfig.cam_config().camera_type());
         aidlInputDesc.imageDesc.fileType =
             ConvertImageFileType(inputStreamConfig.image_config().file_type());
         aidlInputDesc.imageDesc.filePath = inputStreamConfig.image_config().image_dir();
diff --git a/service/src/com/android/car/CarPowerManagementService.java b/service/src/com/android/car/CarPowerManagementService.java
index b2331ca..d130c43 100644
--- a/service/src/com/android/car/CarPowerManagementService.java
+++ b/service/src/com/android/car/CarPowerManagementService.java
@@ -878,6 +878,7 @@
 
     @Override
     public void scheduleNextWakeupTime(int seconds) {
+        ICarImpl.assertPermission(mContext, Car.PERMISSION_CAR_POWER);
         if (seconds < 0) {
             Log.w(CarLog.TAG_POWER, "Next wake up time is negative. Ignoring!");
             return;
@@ -899,6 +900,15 @@
         }
     }
 
+    @Override
+    public int getPowerState() {
+        ICarImpl.assertPermission(mContext, Car.PERMISSION_CAR_POWER);
+        synchronized (mLock) {
+            return (mCurrentState == null) ? CarPowerStateListener.INVALID
+                    : mCurrentState.mCarPowerStateListenerState;
+        }
+    }
+
     private void finishedImpl(IBinder binder) {
         boolean allAreComplete = false;
         synchronized (mLock) {
diff --git a/service/src/com/android/car/hal/HalClient.java b/service/src/com/android/car/hal/HalClient.java
index 5f5febc..bb96957 100644
--- a/service/src/com/android/car/hal/HalClient.java
+++ b/service/src/com/android/car/hal/HalClient.java
@@ -32,6 +32,7 @@
 import android.util.Log;
 
 import com.android.car.CarLog;
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
@@ -41,7 +42,11 @@
  * Vehicle HAL client. Interacts directly with Vehicle HAL interface {@link IVehicle}. Contains
  * some logic for retriable properties, redirects Vehicle notifications into given looper thread.
  */
-class  HalClient {
+final class HalClient {
+
+    private static final String TAG = CarLog.TAG_HAL;
+    private static final boolean DEBUG = false;
+
     /**
      * If call to vehicle HAL returns StatusCode.TRY_AGAIN, than {@link HalClient} will retry to
      * invoke that method again for this amount of milliseconds.
@@ -51,8 +56,9 @@
     private static final int SLEEP_BETWEEN_RETRIABLE_INVOKES_MS = 50;
 
     private final IVehicle mVehicle;
-
     private final IVehicleCallback mInternalCallback;
+    private final int mWaitCapMs;
+    private final int mSleepMs;
 
     /**
      * Create HalClient object
@@ -62,9 +68,18 @@
      * @param callback to propagate notifications from Vehicle HAL in the provided looper thread
      */
     HalClient(IVehicle vehicle, Looper looper, IVehicleCallback callback) {
+        this(vehicle, looper, callback, WAIT_CAP_FOR_RETRIABLE_RESULT_MS,
+                SLEEP_BETWEEN_RETRIABLE_INVOKES_MS);
+    }
+
+    @VisibleForTesting
+    HalClient(IVehicle vehicle, Looper looper, IVehicleCallback callback,
+            int waitCapMs, int sleepMs) {
         mVehicle = vehicle;
         Handler handler = new CallbackHandler(looper, callback);
         mInternalCallback = new VehicleCallback(handler);
+        mWaitCapMs = waitCapMs;
+        mSleepMs = sleepMs;
     }
 
     ArrayList<VehiclePropConfig> getAllPropConfigs() throws RemoteException {
@@ -84,45 +99,44 @@
             try {
                 return mVehicle.set(propValue);
             } catch (RemoteException e) {
-                Log.e(CarLog.TAG_HAL, "Failed to set value", e);
+                Log.e(TAG, getValueErrorMessage("set", propValue), e);
                 return StatusCode.TRY_AGAIN;
             }
-        }, WAIT_CAP_FOR_RETRIABLE_RESULT_MS, SLEEP_BETWEEN_RETRIABLE_INVOKES_MS);
+        }, mWaitCapMs, mSleepMs);
 
         if (StatusCode.INVALID_ARG == status) {
-            throw new IllegalArgumentException(
-                    String.format("Failed to set value for: 0x%s, areaId: 0x%s",
-                            Integer.toHexString(propValue.prop),
-                            Integer.toHexString(propValue.areaId)));
+            throw new IllegalArgumentException(getValueErrorMessage("set", propValue));
         }
 
         if (StatusCode.OK != status) {
-            Log.i(CarLog.TAG_HAL, String.format(
-                    "Failed to set property: 0x%s, areaId: 0x%s, code: %d",
-                    Integer.toHexString(propValue.prop),
-                    Integer.toHexString(propValue.areaId),
-                    status));
+            Log.e(TAG, getPropertyErrorMessage("set", propValue, status));
             throw new ServiceSpecificException(status,
                     "Failed to set property: 0x" + Integer.toHexString(propValue.prop)
                             + " in areaId: 0x" + Integer.toHexString(propValue.areaId));
         }
     }
 
+    private String getValueErrorMessage(String action, VehiclePropValue propValue) {
+        return String.format("Failed to %s value for: 0x%s, areaId: 0x%s", action,
+                Integer.toHexString(propValue.prop), Integer.toHexString(propValue.areaId));
+    }
+
+    private String getPropertyErrorMessage(String action, VehiclePropValue propValue, int status) {
+        return String.format("Failed to %s property: 0x%s, areaId: 0x%s, code: %d (%s)", action,
+                Integer.toHexString(propValue.prop), Integer.toHexString(propValue.areaId),
+                status, StatusCode.toString(status));
+    }
+
     VehiclePropValue getValue(VehiclePropValue requestedPropValue) {
         final ObjectWrapper<VehiclePropValue> valueWrapper = new ObjectWrapper<>();
         int status = invokeRetriable(() -> {
             ValueResult res = internalGet(requestedPropValue);
             valueWrapper.object = res.propValue;
             return res.status;
-        }, WAIT_CAP_FOR_RETRIABLE_RESULT_MS, SLEEP_BETWEEN_RETRIABLE_INVOKES_MS);
+        }, mWaitCapMs, mSleepMs);
 
-        int propId = requestedPropValue.prop;
-        int areaId = requestedPropValue.areaId;
         if (StatusCode.INVALID_ARG == status) {
-            throw new IllegalArgumentException(
-                    String.format("Failed to get value for: 0x%s, areaId: 0x%s",
-                            Integer.toHexString(propId),
-                            Integer.toHexString(areaId)));
+            throw new IllegalArgumentException(getValueErrorMessage("get", requestedPropValue));
         }
 
         if (StatusCode.OK != status || valueWrapper.object == null) {
@@ -131,11 +145,7 @@
             if (StatusCode.OK == status) {
                 status = StatusCode.NOT_AVAILABLE;
             }
-            Log.i(CarLog.TAG_HAL, String.format(
-                    "Failed to get property: 0x%s, areaId: 0x%s, code: %d",
-                    Integer.toHexString(requestedPropValue.prop),
-                    Integer.toHexString(requestedPropValue.areaId),
-                    status));
+            Log.e(TAG, getPropertyErrorMessage("get", requestedPropValue, status));
             throw new ServiceSpecificException(status,
                     "Failed to get property: 0x" + Integer.toHexString(requestedPropValue.prop)
                             + " in areaId: 0x" + Integer.toHexString(requestedPropValue.areaId));
@@ -153,7 +163,7 @@
                         result.propValue = propValue;
                     });
         } catch (RemoteException e) {
-            Log.e(CarLog.TAG_HAL, "Failed to get value from vehicle HAL", e);
+            Log.e(TAG, getValueErrorMessage("get", requestedPropValue), e);
             result.status = StatusCode.TRY_AGAIN;
         }
 
@@ -169,28 +179,35 @@
         int status = callback.action();
         long startTime = elapsedRealtime();
         while (StatusCode.TRY_AGAIN == status && (elapsedRealtime() - startTime) < timeoutMs) {
+            if (DEBUG) {
+                Log.d(TAG, "Status before sleeping " + sleepMs + "ms: "
+                        + StatusCode.toString(status));
+            }
             try {
                 Thread.sleep(sleepMs);
             } catch (InterruptedException e) {
-                Log.e(CarLog.TAG_HAL, "Thread was interrupted while waiting for vehicle HAL.", e);
+                Thread.currentThread().interrupt();
+                Log.e(TAG, "Thread was interrupted while waiting for vehicle HAL.", e);
                 break;
             }
 
             status = callback.action();
+            if (DEBUG) Log.d(TAG, "Status after waking up: " + StatusCode.toString(status));
         }
+        if (DEBUG) Log.d(TAG, "Returning status: " + StatusCode.toString(status));
         return status;
     }
 
-    private static class ObjectWrapper<T> {
+    private static final class ObjectWrapper<T> {
         T object;
     }
 
-    private static class ValueResult {
+    private static final class ValueResult {
         int status;
         VehiclePropValue propValue;
     }
 
-    private static class PropertySetError {
+    private static final class PropertySetError {
         final int errorCode;
         final int propId;
         final int areaId;
@@ -218,7 +235,7 @@
         public void handleMessage(Message msg) {
             IVehicleCallback callback = mCallback.get();
             if (callback == null) {
-                Log.i(CarLog.TAG_HAL, "handleMessage null callback");
+                Log.i(TAG, "handleMessage null callback");
                 return;
             }
 
@@ -235,16 +252,16 @@
                         callback.onPropertySetError(obj.errorCode, obj.propId, obj.areaId);
                         break;
                     default:
-                        Log.e(CarLog.TAG_HAL, "Unexpected message: " + msg.what);
+                        Log.e(TAG, "Unexpected message: " + msg.what);
                 }
             } catch (RemoteException e) {
-                Log.e(CarLog.TAG_HAL, "Message failed: " + msg.what);
+                Log.e(TAG, "Message failed: " + msg.what);
             }
         }
     }
 
-    private static class VehicleCallback extends IVehicleCallback.Stub {
-        private Handler mHandler;
+    private static final class VehicleCallback extends IVehicleCallback.Stub {
+        private final Handler mHandler;
 
         VehicleCallback(Handler handler) {
             mHandler = handler;
diff --git a/service/src/com/android/car/hal/UserHalService.java b/service/src/com/android/car/hal/UserHalService.java
index 7b81e0a..ea5a2e8 100644
--- a/service/src/com/android/car/hal/UserHalService.java
+++ b/service/src/com/android/car/hal/UserHalService.java
@@ -23,7 +23,9 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityManager;
 import android.car.hardware.property.CarPropertyManager;
+import android.car.user.CarUserManager;
 import android.car.userlib.HalCallback;
 import android.car.userlib.UserHalHelper;
 import android.hardware.automotive.vehicle.V2_0.InitialUserInfoResponse;
@@ -272,11 +274,8 @@
             requestId = mNextRequestId++;
             EventLog.writeEvent(EventLogTags.CAR_USER_HAL_SWITCH_USER_REQ, requestId,
                     targetInfo.userId, timeoutMs);
-            propRequest = UserHalHelper.createPropRequest(requestId,
-                        SwitchUserMessageType.ANDROID_SWITCH, SWITCH_USER);
-            propRequest.value.int32Values.add(targetInfo.userId);
-            propRequest.value.int32Values.add(targetInfo.flags);
-            UserHalHelper.addUsersInfo(propRequest, usersInfo);
+            propRequest = getPropRequestForSwitchUserLocked(requestId,
+                    SwitchUserMessageType.ANDROID_SWITCH, targetInfo, usersInfo);
             addPendingRequestLocked(requestId, SwitchUserResponse.class, callback);
         }
 
@@ -313,11 +312,8 @@
         VehiclePropValue propRequest;
         synchronized (mLock) {
             checkSupportedLocked();
-            propRequest = UserHalHelper.createPropRequest(requestId,
-                    SwitchUserMessageType.ANDROID_POST_SWITCH, SWITCH_USER);
-            propRequest.value.int32Values.add(targetInfo.userId);
-            propRequest.value.int32Values.add(targetInfo.flags);
-            UserHalHelper.addUsersInfo(propRequest, usersInfo);
+            propRequest = getPropRequestForSwitchUserLocked(requestId,
+                    SwitchUserMessageType.ANDROID_POST_SWITCH, targetInfo, usersInfo);
         }
 
         try {
@@ -329,6 +325,47 @@
     }
 
     /**
+     * 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 + ")");
+        Objects.requireNonNull(usersInfo);
+        // TODO(b/150413515): use helper method to check usersInfo is valid
+
+        VehiclePropValue propRequest;
+        synchronized (mLock) {
+            checkSupportedLocked();
+            int requestId = mNextRequestId++;
+            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);
+        }
+
+        try {
+            if (DBG) Log.d(TAG, "Calling hal.set(): " + propRequest);
+            mHal.set(propRequest);
+        } catch (ServiceSpecificException e) {
+            Log.w(TAG, "Failed to set LEGACY ANDROID SWITCH", e);
+        }
+    }
+
+    private VehiclePropValue getPropRequestForSwitchUserLocked(int requestId, int requestType,
+            @NonNull UserInfo targetInfo, @NonNull UsersInfo usersInfo) {
+        VehiclePropValue propRequest =
+                UserHalHelper.createPropRequest(requestId, requestType, SWITCH_USER);
+        propRequest.value.int32Values.add(targetInfo.userId);
+        propRequest.value.int32Values.add(targetInfo.flags);
+        UserHalHelper.addUsersInfo(propRequest, usersInfo);
+        return propRequest;
+    }
+
+    /**
      * Calls HAL to get the value of the user identifications associated with the given user.
      *
      * @return HAL response or {@code null} if it was invalid (for example, mismatch on the
diff --git a/service/src/com/android/car/user/CarUserService.java b/service/src/com/android/car/user/CarUserService.java
index f193815..2ae56cb 100644
--- a/service/src/com/android/car/user/CarUserService.java
+++ b/service/src/com/android/car/user/CarUserService.java
@@ -36,6 +36,7 @@
 import android.car.user.GetUserIdentificationAssociationResponse;
 import android.car.user.UserSwitchResult;
 import android.car.userlib.CarUserManagerHelper;
+import android.car.userlib.CommonConstants.CarUserServiceConstants;
 import android.car.userlib.HalCallback;
 import android.car.userlib.UserHalHelper;
 import android.content.Context;
@@ -103,13 +104,14 @@
     private static final String TAG = TAG_USER;
 
     /** {@code int} extra used to represent a user id in a {@link IResultReceiver} response. */
-    public static final String BUNDLE_USER_ID = "user.id";
+    public static final String BUNDLE_USER_ID = CarUserServiceConstants.BUNDLE_USER_ID;
     /** {@code int} extra used to represent user flags in a {@link IResultReceiver} response. */
-    public static final String BUNDLE_USER_FLAGS = "user.flags";
+    public static final String BUNDLE_USER_FLAGS = CarUserServiceConstants.BUNDLE_USER_FLAGS;
     /** {@code String} extra used to represent a user name in a {@link IResultReceiver} response. */
-    public static final String BUNDLE_USER_NAME = "user.name";
+    public static final String BUNDLE_USER_NAME = CarUserServiceConstants.BUNDLE_USER_NAME;
     /** {@code int} extra used to represent the info action {@link IResultReceiver} response. */
-    public static final String BUNDLE_INITIAL_INFO_ACTION = "initial_info.action";
+    public static final String BUNDLE_INITIAL_INFO_ACTION =
+            CarUserServiceConstants.BUNDLE_INITIAL_INFO_ACTION;
 
     private final Context mContext;
     private final CarUserManagerHelper mCarUserManagerHelper;
@@ -178,6 +180,8 @@
 
     private UserMetrics mUserMetrics;
 
+    private IResultReceiver mUserSwitchUiReceiver;
+
     /** Interface for callbaks related to passenger activities. */
     public interface PassengerCallback {
         /** Called when passenger is started at a certain zone. */
@@ -248,6 +252,7 @@
         writer.println("*CarUserService*");
         String indent = "  ";
         handleDumpListeners(writer, indent);
+        writer.printf("User switch UI receiver %s\n", mUserSwitchUiReceiver);
         synchronized (mLockUser) {
             writer.println("User0Unlocked: " + mUser0Unlocked);
             writer.println("BackgroundUsersToRestart: " + mBackgroundUsersToRestart);
@@ -820,6 +825,7 @@
                         try {
                             switched = mAm.switchUser(targetUserId);
                             if (switched) {
+                                sendUserSwitchUiCallback(targetUserId);
                                 resultStatus = UserSwitchResult.STATUS_SUCCESSFUL;
                                 mRequestIdForUserSwitchInProcess = resp.requestId;
                             } else {
@@ -846,6 +852,20 @@
         });
     }
 
+    private void sendUserSwitchUiCallback(@UserIdInt int targetUserId) {
+        if (mUserSwitchUiReceiver == null) {
+            Log.w(TAG_USER, "No User switch UI receiver.");
+            return;
+        }
+
+        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);
+        }
+    }
+
     @Override
     public GetUserIdentificationAssociationResponse getUserIdentificationAssociation(int[] types) {
         Preconditions.checkArgument(!ArrayUtils.isEmpty(types), "must have at least one type");
@@ -930,6 +950,19 @@
         return mHal.isSupported();
     }
 
+    /**
+     * Sets a callback which is invoked before user switch.
+     *
+     * <p>
+     * This method should only be called by the Car System UI. The purpose of this call is to notify
+     * Car System UI to show the user switch UI before the user switch.
+     */
+    @Override
+    public void setUserSwitchUiCallback(@NonNull IResultReceiver receiver) {
+        // TODO(b/154958003): check UID, only carSysUI should be allowed to set it.
+        mUserSwitchUiReceiver = receiver;
+    }
+
     // TODO(b/144120654): use helper to generate UsersInfo
     private UsersInfo getUsersInfo() {
         UserInfo currentUser;
@@ -939,6 +972,11 @@
             // shouldn't happen
             throw new IllegalStateException("Could not get current user: ", e);
         }
+        return getUsersInfo(currentUser);
+    }
+
+    // TODO(b/144120654): use helper to generate UsersInfo
+    private UsersInfo getUsersInfo(@NonNull UserInfo currentUser) {
         List<UserInfo> existingUsers = mUserManager.getUsers();
         int size = existingUsers.size();
 
@@ -1150,7 +1188,7 @@
 
         // Handle special cases first...
         if (eventType == CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING) {
-            onUserSwitching(userId);
+            onUserSwitching(fromUserId, toUserId);
         } else if (eventType == CarUserManager.USER_LIFECYCLE_EVENT_TYPE_UNLOCKED) {
             onUserUnlocked(userId);
         }
@@ -1257,24 +1295,43 @@
         t.traceEnd(); // notify-listeners-user-USERID-event-EVENT_TYPE
     }
 
-    private void onUserSwitching(@UserIdInt int userId) {
-        Log.i(TAG_USER, "onSwitchUser() callback for user " + userId);
+    private void onUserSwitching(@UserIdInt int fromUserId, @UserIdInt int toUserId) {
+        Log.i(TAG_USER, "onSwitchUser() callback for user " + toUserId);
         TimingsTraceLog t = new TimingsTraceLog(TAG_USER, Trace.TRACE_TAG_SYSTEM_SERVER);
-        t.traceBegin("onUserSwitching-" + userId);
+        t.traceBegin("onUserSwitching-" + toUserId);
 
-        if (!isSystemUser(userId)) {
-            mCarUserManagerHelper.setLastActiveUser(userId);
+        // Switch HAL users if user switch is not requested by CarUserService
+        notifyHalLegacySwitch(fromUserId, toUserId);
+
+        if (!isSystemUser(toUserId)) {
+            mCarUserManagerHelper.setLastActiveUser(toUserId);
         }
         if (mLastPassengerId != UserHandle.USER_NULL) {
             stopPassengerInternal(mLastPassengerId, false);
         }
         if (mEnablePassengerSupport && isPassengerDisplayAvailable()) {
             setupPassengerUser();
-            startFirstPassenger(userId);
+            startFirstPassenger(toUserId);
         }
         t.traceEnd();
     }
 
+    private void notifyHalLegacySwitch(@UserIdInt int fromUserId, @UserIdInt int toUserId) {
+        synchronized (mLockUser) {
+            if (mUserIdForUserSwitchInProcess != UserHandle.USER_NULL) 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);
+    }
+
     /**
      * Runs the given runnable when user 0 is unlocked. If user 0 is already unlocked, it is
      * run inside this call.
diff --git a/tests/BugReportApp/res/layout/bug_info_view.xml b/tests/BugReportApp/res/layout/bug_info_view.xml
index 199b368..1841f9c 100644
--- a/tests/BugReportApp/res/layout/bug_info_view.xml
+++ b/tests/BugReportApp/res/layout/bug_info_view.xml
@@ -30,8 +30,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_gravity="left"
-            android:textColor="@*android:color/car_yellow_500"
-            android:textSize="@dimen/bug_report_default_text_size" />
+            style="@style/TextAppearance.BugReportUi.Title" />
 
         <LinearLayout
             android:layout_width="wrap_content"
@@ -42,12 +41,12 @@
                 android:layout_height="wrap_content"
                 android:layout_marginRight="@dimen/bug_report_horizontal_layout_children_margin"
                 android:text="@string/bugreport_info_status"
-                android:textSize="@dimen/bug_report_default_text_size" />
+                style="@style/TextAppearance.BugReportUi.Body" />
             <TextView
                 android:id="@+id/bug_info_row_status"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:textSize="@dimen/bug_report_default_text_size" />
+                style="@style/TextAppearance.BugReportUi.Body" />
         </LinearLayout>
 
         <TextView
@@ -55,7 +54,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:visibility="gone"
-            android:textSize="@dimen/bug_report_default_text_size" />
+            style="@style/TextAppearance.BugReportUi.Body" />
 
         <LinearLayout
             android:layout_width="wrap_content"
@@ -68,8 +67,8 @@
                 android:layout_marginTop="@dimen/bug_report_user_action_button_padding"
                 android:layout_marginRight="@dimen/bug_report_horizontal_layout_children_margin"
                 android:visibility="gone"
-                android:textSize="@dimen/bug_report_button_text_size"
-                android:text="@string/bugreport_add_audio_button_text" />
+                android:text="@string/bugreport_add_audio_button_text"
+                style="@style/Widget.BugReportUi.InfoActionButton" />
             <Button
                 android:id="@+id/bug_info_upload_button"
                 android:layout_width="wrap_content"
@@ -77,16 +76,16 @@
                 android:layout_marginTop="@dimen/bug_report_user_action_button_padding"
                 android:layout_marginRight="@dimen/bug_report_horizontal_layout_children_margin"
                 android:visibility="gone"
-                android:textSize="@dimen/bug_report_button_text_size"
-                android:text="@string/bugreport_upload_button_text" />
+                android:text="@string/bugreport_upload_button_text"
+                style="@style/Widget.BugReportUi.InfoActionButton" />
             <Button
                 android:id="@+id/bug_info_move_button"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_marginTop="@dimen/bug_report_user_action_button_padding"
                 android:visibility="gone"
-                android:textSize="@dimen/bug_report_button_text_size"
-                android:text="@string/bugreport_move_button_text" />
+                android:text="@string/bugreport_move_button_text"
+                style="@style/Widget.BugReportUi.InfoActionButton" />
         </LinearLayout>
     </LinearLayout>
 
diff --git a/tests/BugReportApp/res/layout/bug_report_info_activity.xml b/tests/BugReportApp/res/layout/bug_report_info_activity.xml
index 1cbe9d4..4cc6fbf 100644
--- a/tests/BugReportApp/res/layout/bug_report_info_activity.xml
+++ b/tests/BugReportApp/res/layout/bug_report_info_activity.xml
@@ -32,13 +32,13 @@
             android:layout_height="wrap_content"
             android:layout_width="wrap_content"
             android:text="@string/bugreport_info_quit"
-            style="?android:attr/borderlessButtonStyle" />
+            style="@style/android:Widget.DeviceDefault.Button.Borderless.Colored" />
         <Button
             android:id="@+id/start_bug_report_button"
             android:layout_height="wrap_content"
             android:layout_width="wrap_content"
             android:text="@string/bugreport_info_start"
-            style="?android:attr/borderlessButtonStyle" />
+            style="@style/android:Widget.DeviceDefault.Button.Borderless.Colored" />
         <TextView
             android:id="@+id/version_text_view"
             android:layout_height="wrap_content"
@@ -46,7 +46,8 @@
             android:layout_weight="1"
             android:textSize="14sp"
             android:gravity="right"
-            android:text="" />
+            android:text=""
+            style="@style/android:TextAppearance.DeviceDefault" />
     </LinearLayout>
 
     <androidx.recyclerview.widget.RecyclerView
diff --git a/tests/BugReportApp/res/values/dimens.xml b/tests/BugReportApp/res/values/dimens.xml
index 7f7967f..cb41f89 100644
--- a/tests/BugReportApp/res/values/dimens.xml
+++ b/tests/BugReportApp/res/values/dimens.xml
@@ -20,6 +20,7 @@
     <dimen name="bug_report_padding">30dp</dimen>
 
     <dimen name="bug_report_default_text_size">28dp</dimen>
+    <dimen name="bug_report_small_text_size">24dp</dimen>
 
     <!-- VoiceRecordingView dimensions -->
     <dimen name="bug_report_voice_recording_margin_top">20dp</dimen>
diff --git a/tests/BugReportApp/res/values/styles.xml b/tests/BugReportApp/res/values/styles.xml
index 6d8dd96..9b146a2 100644
--- a/tests/BugReportApp/res/values/styles.xml
+++ b/tests/BugReportApp/res/values/styles.xml
@@ -22,4 +22,17 @@
     <item name="android:clickable">true</item>
     <item name="android:background">@drawable/item_background</item>
   </style>
+
+  <style name="Widget.BugReportUi.InfoActionButton" parent="android:Widget.DeviceDefault.Button">
+    <item name="android:textSize">@dimen/bug_report_button_text_size</item>
+  </style>
+
+  <style name="TextAppearance.BugReportUi.Title" parent="android:TextAppearance.DeviceDefault">
+    <item name="android:textSize">@dimen/bug_report_default_text_size</item>
+  </style>
+
+  <style name="TextAppearance.BugReportUi.Body" parent="android:TextAppearance.DeviceDefault">
+    <item name="android:textColor">@*android:color/car_body1</item>
+    <item name="android:textSize">@dimen/bug_report_small_text_size</item>
+  </style>
 </resources>
diff --git a/tests/CarSecurityPermissionTest/src/com/android/car/input/CarInputManagerTest.java b/tests/CarSecurityPermissionTest/src/com/android/car/input/CarInputManagerTest.java
new file mode 100644
index 0000000..354446e
--- /dev/null
+++ b/tests/CarSecurityPermissionTest/src/com/android/car/input/CarInputManagerTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.input;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.car.Car;
+import android.car.input.CarInputManager;
+
+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;
+import org.mockito.Mock;
+
+/**
+ * This class contains security permission tests for the {@link CarInputManager}'s system APIs.
+ */
+@RunWith(AndroidJUnit4.class)
+public class CarInputManagerTest {
+    private Car mCar;
+
+    private CarInputManager mCarInputManager;
+
+    @Mock
+    private CarInputManager.CarInputCaptureCallback mMockedCallback;
+
+    @Before
+    public void setUp() throws Exception {
+        mCar = Car.createCar(
+                InstrumentationRegistry.getInstrumentation().getTargetContext());
+        assertThat(mCar).isNotNull();
+        mCarInputManager = (CarInputManager) mCar.getCarManager(Car.CAR_INPUT_SERVICE);
+        assertThat(mCarInputManager).isNotNull();
+    }
+
+    @After
+    public void tearDown() {
+        mCar.disconnect();
+    }
+
+    @Test
+    public void testEnableFeaturePermission() throws Exception {
+        assertThrows(SecurityException.class, () -> mCarInputManager.requestInputEventCapture(
+                mMockedCallback,
+                CarInputManager.TARGET_DISPLAY_TYPE_MAIN,
+                new int[]{CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION}, 0));
+    }
+}
+
diff --git a/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml b/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
index 66e68eb..ec480c4 100644
--- a/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
+++ b/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
@@ -62,6 +62,7 @@
     <uses-permission android:name="android.permission.ACTIVITY_EMBEDDING"/>
     <uses-permission android:name="android.permission.BLUETOOTH"/>
     <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
+    <uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED"/>
     <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
     <uses-permission android:name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
diff --git a/tests/android_car_api_test/AndroidManifest.xml b/tests/android_car_api_test/AndroidManifest.xml
index 9f22d11..bd8fb8c 100644
--- a/tests/android_car_api_test/AndroidManifest.xml
+++ b/tests/android_car_api_test/AndroidManifest.xml
@@ -35,9 +35,9 @@
         </activity>
         <service android:name=".CarProjectionManagerTest$TestService"
                  android:exported="true" />
-        <activity android:name=".CarActivityViewDisplayIdTest$ActivityInActivityView"/>
-        <activity android:name=".CarActivityViewDisplayIdTest$ActivityViewTestActivity"/>
-        <activity android:name=".CarActivityViewDisplayIdTest$MultiProcessActivityViewTestActivity"
+        <activity android:name=".CarActivityViewDisplayIdTestBase$ActivityInActivityView"/>
+        <activity android:name=".CarActivityViewDisplayIdTestBase$ActivityViewTestActivity"/>
+        <activity android:name=".CarActivityViewDisplayIdTestBase$MultiProcessActivityViewTestActivity"
                   android:exported="true" android:process=":activity_view_test"/>
     </application>
 </manifest>
diff --git a/tests/android_car_api_test/src/android/car/apitest/CarActivityViewDisplayIdCrashTest.java b/tests/android_car_api_test/src/android/car/apitest/CarActivityViewDisplayIdCrashTest.java
new file mode 100644
index 0000000..f275fe6
--- /dev/null
+++ b/tests/android_car_api_test/src/android/car/apitest/CarActivityViewDisplayIdCrashTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.apitest;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.ActivityManager;
+import android.content.Intent;
+import android.os.Process;
+import android.os.SystemClock;
+
+import androidx.test.filters.MediumTest;
+
+import org.junit.Test;
+
+import java.util.List;
+
+/**
+ * The test contained in this class kills a test activity, making the system unstable for a while.
+ * That said, this class must have only one test method.
+ */
+@MediumTest
+public final class CarActivityViewDisplayIdCrashTest extends CarActivityViewDisplayIdTestBase {
+
+    private static final int INVALID_PID = -1;
+
+    @Test
+    public void testCleanUpAfterClientIsCrashed() throws Exception {
+        Intent intent = new Intent(getContext(),
+                CarActivityViewDisplayIdTest.MultiProcessActivityViewTestActivity.class);
+        getContext().startActivity(intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+        int pidOfTestActivity = waitForTestActivityReady();
+        int displayIdOfTestActivity = waitForActivityViewDisplayReady(ACTIVITY_VIEW_TEST_PKG_NAME);
+
+        assertThat(getMappedPhysicalDisplayOfVirtualDisplay(displayIdOfTestActivity))
+            .isEqualTo(DEFAULT_DISPLAY);
+
+        Process.killProcess(pidOfTestActivity);
+
+        assertThat(waitForMappedPhysicalDisplayOfVirtualDisplayCleared(displayIdOfTestActivity))
+            .isEqualTo(INVALID_DISPLAY);
+    }
+
+    private int waitForMappedPhysicalDisplayOfVirtualDisplayCleared(int displayId) {
+        // Initialized with a random number which is not DEFAULT_DISPLAY nor INVALID_DISPLAY.
+        int physicalDisplayId = 999;
+        for (int i = 0; i < TEST_TIMEOUT_MS / TEST_POLL_MS; ++i) {
+            physicalDisplayId = getMappedPhysicalDisplayOfVirtualDisplay(displayId);
+            if (physicalDisplayId == INVALID_DISPLAY) {
+                return physicalDisplayId;
+            }
+            SystemClock.sleep(TEST_POLL_MS);
+        }
+        return physicalDisplayId;
+    }
+
+    private int waitForTestActivityReady() {
+        for (int i = 0; i < TEST_TIMEOUT_MS / TEST_POLL_MS; ++i) {
+            List<ActivityManager.RunningAppProcessInfo> appProcesses =
+                    mActivityManager.getRunningAppProcesses();
+            for (ActivityManager.RunningAppProcessInfo info : appProcesses) {
+                if (info.processName.equals(ACTIVITY_VIEW_TEST_PROCESS_NAME) && info.importance
+                        == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
+                    return info.pid;
+                }
+            }
+            SystemClock.sleep(TEST_POLL_MS);
+        }
+        return INVALID_PID;
+    }
+}
diff --git a/tests/android_car_api_test/src/android/car/apitest/CarActivityViewDisplayIdTest.java b/tests/android_car_api_test/src/android/car/apitest/CarActivityViewDisplayIdTest.java
index c096bb8..93f2530 100644
--- a/tests/android_car_api_test/src/android/car/apitest/CarActivityViewDisplayIdTest.java
+++ b/tests/android_car_api_test/src/android/car/apitest/CarActivityViewDisplayIdTest.java
@@ -16,84 +16,30 @@
 
 package android.car.apitest;
 
-import static android.app.ActivityManager.RunningAppProcessInfo;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assume.assumeTrue;
 import static org.testng.Assert.assertThrows;
 
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.ActivityOptions;
-import android.app.ActivityView;
-import android.app.Instrumentation;
-import android.car.Car;
-import android.car.app.CarActivityView;
-import android.car.drivingstate.CarUxRestrictionsManager;
-import android.content.Context;
-import android.content.Intent;
-import android.hardware.display.DisplayManager;
-import android.os.Bundle;
-import android.os.Process;
-import android.os.SystemClock;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.view.Display;
-import android.view.ViewGroup;
-
 import androidx.test.filters.FlakyTest;
+import androidx.test.filters.MediumTest;
 
-import org.junit.Before;
-import org.junit.FixMethodOrder;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
-import org.junit.runners.MethodSorters;
-
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
 
 /**
  * Build/Install/Run:
  *  atest AndroidCarApiTest:CarActivityViewDisplayIdTest
  */
 @RunWith(JUnit4.class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @MediumTest
-public class CarActivityViewDisplayIdTest extends CarApiTestBase {
+public class CarActivityViewDisplayIdTest extends CarActivityViewDisplayIdTestBase {
     private static final String CAR_LAUNCHER_PKG_NAME = "com.android.car.carlauncher";
-    private static final String ACTIVITY_VIEW_TEST_PKG_NAME = "android.car.apitest";
-    private static final String ACTIVITY_VIEW_TEST_PROCESS_NAME =
-            ACTIVITY_VIEW_TEST_PKG_NAME + ":activity_view_test";
-    private static final String ACTIVITY_VIEW_DISPLAY_NAME = "TaskVirtualDisplay";
     private static final int NONEXISTENT_DISPLAY_ID = Integer.MAX_VALUE;
-    private static final int TEST_TIMEOUT_SEC = 5;
-    private static final int TEST_TIMEOUT_MS = TEST_TIMEOUT_SEC * 1000;
-    private static final int TEST_POLL_MS = 50;
-    private static final int INVALID_PID = -1;
-
-    private DisplayManager mDisplayManager;
-    private ActivityManager mActivityManager;
-    private CarUxRestrictionsManager mCarUxRestrictionsManager;
-
-    @Before
-    public void setUp() throws Exception {
-        mDisplayManager = getContext().getSystemService(DisplayManager.class);
-        mActivityManager = getContext().getSystemService(ActivityManager.class);
-        mCarUxRestrictionsManager = (CarUxRestrictionsManager)
-                getCar().getCarManager(Car.CAR_UX_RESTRICTION_SERVICE);
-    }
-
-    private int getMappedPhysicalDisplayOfVirtualDisplay(int displayId) {
-        return mCarUxRestrictionsManager.getMappedPhysicalDisplayOfVirtualDisplay(displayId);
-    }
 
     @Test
     @FlakyTest
@@ -191,181 +137,4 @@
                         displayIdOfCarLauncher, DEFAULT_DISPLAY + 1));
 
     }
-
-    // The test name starts with 'testz' to run it at the last among the tests, since killing
-    // TestActivity forcefully causes the system unstable for a while.
-    @Test
-    @FlakyTest
-    public void testzCleanUpAfterClientIsCrashed() throws Exception {
-        Intent intent = new Intent(getContext(), MultiProcessActivityViewTestActivity.class);
-        getContext().startActivity(intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
-        int pidOfTestActivity = waitForTestActivityReady();
-        int displayIdOfTestActivity = waitForActivityViewDisplayReady(ACTIVITY_VIEW_TEST_PKG_NAME);
-
-        assertThat(getMappedPhysicalDisplayOfVirtualDisplay(displayIdOfTestActivity))
-                .isEqualTo(DEFAULT_DISPLAY);
-
-        Process.killProcess(pidOfTestActivity);
-
-        assertThat(waitForMappedPhysicalDisplayOfVirtualDisplayCleared(displayIdOfTestActivity))
-                .isEqualTo(INVALID_DISPLAY);
-    }
-
-    private int waitForActivityViewDisplayReady(String packageName) {
-        for (int i = 0; i < TEST_TIMEOUT_MS / TEST_POLL_MS; ++i) {
-            for (Display display : mDisplayManager.getDisplays()) {
-                if (display.getName().contains(ACTIVITY_VIEW_DISPLAY_NAME)
-                        && display.getOwnerPackageName().equals(packageName)
-                        && display.getState() == Display.STATE_ON) {
-                    return display.getDisplayId();
-                }
-            }
-            SystemClock.sleep(TEST_POLL_MS);
-        }
-        return INVALID_DISPLAY;
-    }
-
-    private int waitForMappedPhysicalDisplayOfVirtualDisplayCleared(int displayId) {
-        // Initialized with a random number which is not DEFAULT_DISPLAY nor INVALID_DISPLAY.
-        int physicalDisplayId = 999;
-        for (int i = 0; i < TEST_TIMEOUT_MS / TEST_POLL_MS; ++i) {
-            physicalDisplayId = getMappedPhysicalDisplayOfVirtualDisplay(displayId);
-            if (physicalDisplayId == INVALID_DISPLAY) {
-                return physicalDisplayId;
-            }
-            SystemClock.sleep(TEST_POLL_MS);
-        }
-        return physicalDisplayId;
-    }
-
-    private int waitForTestActivityReady() {
-        for (int i = 0; i < TEST_TIMEOUT_MS / TEST_POLL_MS; ++i) {
-            List<RunningAppProcessInfo> appProcesses = mActivityManager.getRunningAppProcesses();
-            for (RunningAppProcessInfo info : appProcesses) {
-                if (info.processName.equals(ACTIVITY_VIEW_TEST_PROCESS_NAME)
-                        && info.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
-                    return info.pid;
-                }
-            }
-            SystemClock.sleep(TEST_POLL_MS);
-        }
-        return INVALID_PID;
-    }
-
-    private static class TestActivity extends Activity {
-        private final CountDownLatch mResumed = new CountDownLatch(1);
-
-        @Override
-        protected void onPostResume() {
-            super.onPostResume();
-            mResumed.countDown();
-        }
-
-        void waitForResumeStateChange() throws Exception {
-            waitForLatch(mResumed);
-        }
-    }
-
-    private static void waitForLatch(CountDownLatch latch) throws Exception {
-        boolean result = latch.await(TEST_TIMEOUT_SEC, TimeUnit.SECONDS);
-        if (!result) {
-            throw new TimeoutException("Timed out waiting for task stack change notification");
-        }
-    }
-
-    /**
-     * Starts the provided activity and returns the started instance.
-     */
-    private TestActivity startTestActivity(Class<?> activityClass, int displayId) throws Exception {
-        Instrumentation.ActivityMonitor monitor = new Instrumentation.ActivityMonitor(
-                activityClass.getName(), null, false);
-        getInstrumentation().addMonitor(monitor);
-
-        Context context = getContext();
-        Intent intent = new Intent(context, activityClass).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        ActivityOptions options = ActivityOptions.makeBasic();
-        if (displayId != DEFAULT_DISPLAY) {
-            intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
-            options.setLaunchDisplayId(displayId);
-        }
-        context.startActivity(intent, options.toBundle());
-
-        TestActivity activity = (TestActivity) monitor.waitForActivityWithTimeout(TEST_TIMEOUT_MS);
-        if (activity == null) {
-            throw new TimeoutException("Timed out waiting for Activity");
-        }
-        activity.waitForResumeStateChange();
-        return activity;
-    }
-
-    public static class ActivityViewTestActivity extends TestActivity {
-        private static final class ActivityViewStateCallback extends ActivityView.StateCallback {
-            private final CountDownLatch mActivityViewReadyLatch = new CountDownLatch(1);
-            private final CountDownLatch mActivityViewDestroyedLatch = new CountDownLatch(1);
-
-            @Override
-            public void onActivityViewReady(ActivityView view) {
-                mActivityViewReadyLatch.countDown();
-            }
-
-            @Override
-            public void onActivityViewDestroyed(ActivityView view) {
-                mActivityViewDestroyedLatch.countDown();
-            }
-        }
-
-        private CarActivityView mActivityView;
-        private final ActivityViewStateCallback mCallback = new ActivityViewStateCallback();
-
-        @Override
-        public void onCreate(Bundle savedInstanceState) {
-            super.onCreate(savedInstanceState);
-
-            mActivityView = new CarActivityView(this, /*attrs=*/null , /*defStyle=*/0 ,
-                    /*singleTaskInstance=*/true);
-            mActivityView.setCallback(mCallback);
-            setContentView(mActivityView);
-
-            ViewGroup.LayoutParams layoutParams = mActivityView.getLayoutParams();
-            layoutParams.width = MATCH_PARENT;
-            layoutParams.height = MATCH_PARENT;
-            mActivityView.requestLayout();
-        }
-
-        @Override
-        protected void onStop() {
-            super.onStop();
-            // Moved the release of the view from onDestroy to onStop since onDestroy was called
-            // in non-deterministic timing.
-            mActivityView.release();
-        }
-
-        ActivityView getActivityView() {
-            return mActivityView;
-        }
-
-        void waitForActivityViewReady() throws Exception {
-            waitForLatch(mCallback.mActivityViewReadyLatch);
-        }
-
-        void waitForActivityViewDestroyed() throws Exception {
-            waitForLatch(mCallback.mActivityViewDestroyedLatch);
-        }
-    }
-
-    public static final class MultiProcessActivityViewTestActivity extends
-            ActivityViewTestActivity {
-    }
-
-    private ActivityViewTestActivity startActivityViewTestActivity(int displayId) throws Exception {
-        return (ActivityViewTestActivity) startTestActivity(ActivityViewTestActivity.class,
-                displayId);
-    }
-
-    // Activity that has {@link android.R.attr#resizeableActivity} attribute set to {@code true}
-    public static class ActivityInActivityView extends TestActivity {}
-
-    private ActivityInActivityView startActivityInActivityView(int displayId) throws Exception {
-        return (ActivityInActivityView) startTestActivity(ActivityInActivityView.class, displayId);
-    }
 }
diff --git a/tests/android_car_api_test/src/android/car/apitest/CarActivityViewDisplayIdTestBase.java b/tests/android_car_api_test/src/android/car/apitest/CarActivityViewDisplayIdTestBase.java
new file mode 100644
index 0000000..e1782cb
--- /dev/null
+++ b/tests/android_car_api_test/src/android/car/apitest/CarActivityViewDisplayIdTestBase.java
@@ -0,0 +1,213 @@
+/*
+ * 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.apitest;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.ActivityView;
+import android.app.Instrumentation;
+import android.car.Car;
+import android.car.app.CarActivityView;
+import android.car.drivingstate.CarUxRestrictionsManager;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.display.DisplayManager;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.view.Display;
+import android.view.ViewGroup;
+
+import org.junit.Before;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Base class for all CarActivityViewDisplayId tests.
+ */
+abstract class CarActivityViewDisplayIdTestBase extends CarApiTestBase {
+    protected static final String ACTIVITY_VIEW_TEST_PKG_NAME = "android.car.apitest";
+
+    protected static final String ACTIVITY_VIEW_TEST_PROCESS_NAME =
+            ACTIVITY_VIEW_TEST_PKG_NAME + ":activity_view_test";
+
+    protected static final String ACTIVITY_VIEW_DISPLAY_NAME = "TaskVirtualDisplay";
+
+    protected static final int TEST_POLL_MS = 50;
+    protected static final int TEST_TIMEOUT_SEC = 5;
+    protected static final int TEST_TIMEOUT_MS = TEST_TIMEOUT_SEC * 1000;
+
+    protected DisplayManager mDisplayManager;
+    protected ActivityManager mActivityManager;
+    protected CarUxRestrictionsManager mCarUxRestrictionsManager;
+
+    @Before
+    public void setUp() throws Exception {
+        mDisplayManager = getContext().getSystemService(DisplayManager.class);
+        mActivityManager = getContext().getSystemService(ActivityManager.class);
+        mCarUxRestrictionsManager = (CarUxRestrictionsManager)
+                getCar().getCarManager(Car.CAR_UX_RESTRICTION_SERVICE);
+    }
+
+    protected int waitForActivityViewDisplayReady(String packageName) {
+        for (int i = 0; i < TEST_TIMEOUT_MS / TEST_POLL_MS; ++i) {
+            for (Display display : mDisplayManager.getDisplays()) {
+                if (display.getName().contains(ACTIVITY_VIEW_DISPLAY_NAME)
+                        && display.getOwnerPackageName().equals(packageName)
+                        && display.getState() == Display.STATE_ON) {
+                    return display.getDisplayId();
+                }
+            }
+            SystemClock.sleep(TEST_POLL_MS);
+        }
+        return INVALID_DISPLAY;
+    }
+
+    protected int getMappedPhysicalDisplayOfVirtualDisplay(int displayId) {
+        return mCarUxRestrictionsManager.getMappedPhysicalDisplayOfVirtualDisplay(displayId);
+    }
+
+    public static class ActivityViewTestActivity extends CarActivityViewDisplayIdTest.TestActivity {
+        private static final class ActivityViewStateCallback extends ActivityView.StateCallback {
+            private final CountDownLatch mActivityViewReadyLatch = new CountDownLatch(1);
+            private final CountDownLatch mActivityViewDestroyedLatch = new CountDownLatch(1);
+
+            @Override
+            public void onActivityViewReady(ActivityView view) {
+                mActivityViewReadyLatch.countDown();
+            }
+
+            @Override
+            public void onActivityViewDestroyed(ActivityView view) {
+                mActivityViewDestroyedLatch.countDown();
+            }
+        }
+
+        private CarActivityView mActivityView;
+        private final ActivityViewStateCallback mCallback = new ActivityViewStateCallback();
+
+        @Override
+        public void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+
+            mActivityView = new CarActivityView(this, /*attrs=*/null , /*defStyle=*/0 ,
+                    /*singleTaskInstance=*/true);
+            mActivityView.setCallback(mCallback);
+            setContentView(mActivityView);
+
+            ViewGroup.LayoutParams layoutParams = mActivityView.getLayoutParams();
+            layoutParams.width = MATCH_PARENT;
+            layoutParams.height = MATCH_PARENT;
+            mActivityView.requestLayout();
+        }
+
+        @Override
+        protected void onStop() {
+            super.onStop();
+            // Moved the release of the view from onDestroy to onStop since onDestroy was called
+            // in non-deterministic timing.
+            mActivityView.release();
+        }
+
+        ActivityView getActivityView() {
+            return mActivityView;
+        }
+
+        void waitForActivityViewReady() throws Exception {
+            waitForLatch(mCallback.mActivityViewReadyLatch);
+        }
+
+        void waitForActivityViewDestroyed() throws Exception {
+            waitForLatch(mCallback.mActivityViewDestroyedLatch);
+        }
+    }
+
+    protected ActivityViewTestActivity startActivityViewTestActivity(int displayId)
+            throws Exception {
+        return (ActivityViewTestActivity) startTestActivity(ActivityViewTestActivity.class,
+                displayId);
+    }
+
+    /** Test activity representing a multi-process activity. */
+    public static final class MultiProcessActivityViewTestActivity extends
+            ActivityViewTestActivity {
+    }
+
+    /**
+     * Test activity that has {@link android.R.attr#resizeableActivity} attribute set to
+     * {@code true}.
+     */
+    public static final class ActivityInActivityView extends TestActivity {}
+
+    /**
+     * Starts the provided activity and returns the started instance.
+     */
+    protected TestActivity startTestActivity(Class<?> activityClass, int displayId)
+            throws Exception {
+        Instrumentation.ActivityMonitor monitor = new Instrumentation.ActivityMonitor(
+                activityClass.getName(), null, false);
+        getInstrumentation().addMonitor(monitor);
+
+        Context context = getContext();
+        Intent intent = new Intent(context, activityClass).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        ActivityOptions options = ActivityOptions.makeBasic();
+        if (displayId != DEFAULT_DISPLAY) {
+            intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+            options.setLaunchDisplayId(displayId);
+        }
+        context.startActivity(intent, options.toBundle());
+
+        TestActivity activity = (TestActivity) monitor.waitForActivityWithTimeout(TEST_TIMEOUT_MS);
+        if (activity == null) {
+            throw new TimeoutException("Timed out waiting " + TEST_TIMEOUT_MS + " milliseconds "
+                    + " waiting for Activity");
+        }
+
+        activity.waitForResumeStateChange();
+        return activity;
+    }
+
+    private static void waitForLatch(CountDownLatch latch) throws Exception {
+        boolean result = latch.await(TEST_TIMEOUT_SEC, TimeUnit.SECONDS);
+        if (!result) {
+            throw new TimeoutException("Timed out waiting " + TEST_TIMEOUT_SEC + " seconds waiting"
+                    + " for task stack change notification");
+        }
+    }
+
+    protected static class TestActivity extends Activity {
+        private final CountDownLatch mResumed = new CountDownLatch(1);
+
+        @Override
+        protected void onPostResume() {
+            super.onPostResume();
+            mResumed.countDown();
+        }
+
+        void waitForResumeStateChange() throws Exception {
+            waitForLatch(mResumed);
+        }
+    }
+}
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 f52bf1c..a37c0e4 100644
--- a/tests/carservice_test/src/com/android/car/audio/CarAudioZonesHelperTest.java
+++ b/tests/carservice_test/src/com/android/car/audio/CarAudioZonesHelperTest.java
@@ -19,10 +19,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 import static org.testng.Assert.expectThrows;
@@ -60,6 +56,8 @@
     private Context mContext;
     private CarAudioSettings mCarAudioSettings;
 
+    private static final String PRIMARY_ZONE_NAME = "primary zone";
+
     private static final String BUS_0_ADDRESS = "bus0_media_out";
     private static final String BUS_1_ADDRESS = "bus1_navigation_out";
     private static final String BUS_3_ADDRESS = "bus3_call_ring_out";
@@ -140,9 +138,9 @@
         CarAudioZonesHelper cazh = new CarAudioZonesHelper(mCarAudioSettings, mInputStream,
                 mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
 
-        CarAudioZone[] zones = cazh.loadAudioZones();
+        List<CarAudioZone> zoneList = Arrays.asList(cazh.loadAudioZones());
 
-        assertThat(zones.length).isEqualTo(2);
+        assertThat(zoneList).hasSize(2);
     }
 
     @Test
@@ -168,7 +166,7 @@
         List<Integer> zoneIds = getListOfZoneIds(zones);
         assertThat(zoneIds.size()).isEqualTo(2);
         assertThat(zoneIds)
-                .containsAllOf(CarAudioManager.PRIMARY_AUDIO_ZONE, SECONDARY_ZONE_ID).inOrder();
+                .containsExactly(CarAudioManager.PRIMARY_AUDIO_ZONE, SECONDARY_ZONE_ID).inOrder();
     }
 
     @Test
@@ -178,7 +176,7 @@
 
         CarAudioZone[] zones = cazh.loadAudioZones();
 
-        assertEquals(2, zones.length);
+        assertThat(zones.length).isEqualTo(2);
 
         SparseIntArray audioZoneIdToOccupantZoneIdMapping =
                 cazh.getCarAudioZoneIdToOccupantZoneIdMapping();
@@ -196,7 +194,7 @@
         CarAudioZone[] zones = cazh.loadAudioZones();
 
         CarAudioZone primaryZone = zones[0];
-        assertEquals("primary zone", primaryZone.getName());
+        assertThat(primaryZone.getName()).isEqualTo(PRIMARY_ZONE_NAME);
     }
 
     @Test
@@ -207,10 +205,10 @@
         CarAudioZone[] zones = cazh.loadAudioZones();
 
         CarAudioZone primaryZone = zones[0];
-        assertTrue(primaryZone.isPrimaryZone());
+        assertThat(primaryZone.isPrimaryZone()).isTrue();
 
         CarAudioZone rseZone = zones[1];
-        assertFalse(rseZone.isPrimaryZone());
+        assertThat(rseZone.isPrimaryZone()).isFalse();
     }
 
     @Test
@@ -221,7 +219,7 @@
         CarAudioZone[] zones = cazh.loadAudioZones();
 
         CarAudioZone primaryZone = zones[0];
-        assertEquals(2, primaryZone.getVolumeGroupCount());
+        assertThat(primaryZone.getVolumeGroupCount()).isEqualTo(2);
     }
 
     @Test
@@ -234,9 +232,7 @@
         CarAudioZone primaryZone = zones[0];
         CarVolumeGroup volumeGroup = primaryZone.getVolumeGroups()[0];
         List<String> addresses = volumeGroup.getAddresses();
-        assertEquals(2, addresses.size());
-        assertEquals(BUS_0_ADDRESS, addresses.get(0));
-        assertEquals(BUS_3_ADDRESS, addresses.get(1));
+        assertThat(addresses).containsExactly(BUS_0_ADDRESS, BUS_3_ADDRESS);
     }
 
     @Test
@@ -248,14 +244,17 @@
 
         CarAudioZone primaryZone = zones[0];
         CarVolumeGroup volumeGroup = primaryZone.getVolumeGroups()[0];
-        int[] expectedContextForBus0 = {CarAudioContext.MUSIC};
-        assertArrayEquals(expectedContextForBus0, volumeGroup.getContextsForAddress(BUS_0_ADDRESS));
+        assertThat(volumeGroup.getContextsForAddress(BUS_0_ADDRESS)).asList()
+                .containsExactly(CarAudioContext.MUSIC);
 
-        int[] expectedContextForBus100 = CarAudioContext.CONTEXTS;
         CarAudioZone rearSeatEntertainmentZone = zones[1];
         CarVolumeGroup rseVolumeGroup = rearSeatEntertainmentZone.getVolumeGroups()[0];
-        int[] contextForBus100 = rseVolumeGroup.getContextsForAddress(BUS_100_ADDRESS);
-        assertArrayEquals(expectedContextForBus100, contextForBus100);
+        List<Integer> contextForBus100List =
+                Arrays.stream(rseVolumeGroup.getContextsForAddress(BUS_100_ADDRESS))
+                        .boxed().collect(Collectors.toList());
+        List<Integer> contextsList =
+                Arrays.stream(CarAudioContext.CONTEXTS).boxed().collect(Collectors.toList());
+        assertThat(contextForBus100List).containsExactlyElementsIn(contextsList);
     }
 
     @Test
@@ -285,7 +284,7 @@
 
         CarAudioZonesHelper cazh =
                 new CarAudioZonesHelper(mCarAudioSettings, v1NonLegacyContextStream,
-                mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+                        mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
 
         IllegalArgumentException exception = expectThrows(IllegalArgumentException.class,
                 cazh::loadAudioZones);
@@ -302,9 +301,9 @@
 
         CarAudioZone primaryZone = zones[0];
         List<DisplayAddress.Physical> primaryPhysicals = primaryZone.getPhysicalDisplayAddresses();
-        assertEquals(2, primaryPhysicals.size());
-        assertEquals(1, (long) primaryPhysicals.get(0).getPort());
-        assertEquals(2, (long) primaryPhysicals.get(1).getPort());
+        assertThat(primaryPhysicals).hasSize(2);
+        assertThat(primaryPhysicals.get(0).getPort()).isEqualTo(1);
+        assertThat(primaryPhysicals.get(1).getPort()).isEqualTo(2);
     }
 
     @Test
@@ -316,7 +315,7 @@
 
         CarAudioZone rseZone = zones[1];
         List<DisplayAddress.Physical> rsePhysicals = rseZone.getPhysicalDisplayAddresses();
-        assertTrue(rsePhysicals.isEmpty());
+        assertThat(rsePhysicals).isEmpty();
     }
 
     @Test(expected = RuntimeException.class)
@@ -325,7 +324,7 @@
                 R.raw.car_audio_configuration_duplicate_ports)) {
             CarAudioZonesHelper cazh =
                     new CarAudioZonesHelper(mCarAudioSettings, duplicatePortStream,
-                    mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+                            mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
 
             cazh.loadAudioZones();
         }
@@ -349,7 +348,7 @@
                 R.raw.car_audio_configuration_no_audio_zone_id_for_primary_zone)) {
             CarAudioZonesHelper cazh =
                     new CarAudioZonesHelper(mCarAudioSettings, missingAudioZoneIdStream,
-                    mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+                            mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
 
             CarAudioZone[] zones = cazh.loadAudioZones();
 
@@ -380,7 +379,7 @@
                 R.raw.car_audio_configuration_V1_with_occupant_zone_id)) {
             CarAudioZonesHelper cazh =
                     new CarAudioZonesHelper(mCarAudioSettings, versionOneOccupantIdStream,
-                    mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+                            mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
             IllegalArgumentException thrown =
                     expectThrows(IllegalArgumentException.class,
                             () -> cazh.loadAudioZones());
@@ -394,7 +393,7 @@
                 R.raw.car_audio_configuration_with_input_devices)) {
             CarAudioZonesHelper cazh =
                     new CarAudioZonesHelper(mCarAudioSettings, inputDevicesStream,
-                    mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+                            mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
 
             CarAudioZone[] zones = cazh.loadAudioZones();
 
@@ -406,7 +405,7 @@
             List<String> primaryZoneInputAddresses =
                     primaryZoneInputDevices.stream().map(a -> a.getAddress()).collect(
                             Collectors.toList());
-            assertThat(primaryZoneInputAddresses).containsAllOf(PRIMARY_ZONE_FM_TUNER_ADDRESS,
+            assertThat(primaryZoneInputAddresses).containsExactly(PRIMARY_ZONE_FM_TUNER_ADDRESS,
                     PRIMARY_ZONE_MICROPHONE_ADDRESS).inOrder();
 
             CarAudioZone secondaryZone = zones[1];
@@ -415,7 +414,7 @@
             List<String> secondaryZoneInputAddresses =
                     secondaryZoneInputDevices.stream().map(a -> a.getAddress()).collect(
                             Collectors.toList());
-            assertThat(secondaryZoneInputAddresses).containsAllOf(
+            assertThat(secondaryZoneInputAddresses).containsExactly(
                     SECONDARY_ZONE_BUS_1000_INPUT_ADDRESS,
                     SECONDARY_ZONE_BACK_MICROPHONE_ADDRESS).inOrder();
         }
@@ -441,7 +440,7 @@
                 R.raw.car_audio_configuration_duplicate_audio_zone_id)) {
             CarAudioZonesHelper cazh =
                     new CarAudioZonesHelper(mCarAudioSettings, duplicateAudioZoneIdStream,
-                    mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+                            mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
             IllegalArgumentException thrown =
                     expectThrows(IllegalArgumentException.class,
                             () -> cazh.loadAudioZones());
@@ -455,7 +454,7 @@
                 R.raw.car_audio_configuration_empty_input_device)) {
             CarAudioZonesHelper cazh =
                     new CarAudioZonesHelper(mCarAudioSettings, inputDevicesStream,
-                    mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+                            mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
 
             IllegalArgumentException thrown =
                     expectThrows(IllegalArgumentException.class,
@@ -470,7 +469,7 @@
                 R.raw.car_audio_configuration_non_numerical_audio_zone_id)) {
             CarAudioZonesHelper cazh =
                     new CarAudioZonesHelper(mCarAudioSettings, nonNumericalStream,
-                    mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+                            mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
             IllegalArgumentException thrown =
                     expectThrows(IllegalArgumentException.class,
                             () -> cazh.loadAudioZones());
@@ -484,7 +483,7 @@
                 R.raw.car_audio_configuration_negative_audio_zone_id)) {
             CarAudioZonesHelper cazh =
                     new CarAudioZonesHelper(mCarAudioSettings, negativeAudioZoneIdStream,
-                    mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+                            mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
             IllegalArgumentException thrown =
                     expectThrows(IllegalArgumentException.class,
                             () -> cazh.loadAudioZones());
@@ -498,7 +497,7 @@
                 R.raw.car_audio_configuration_missing_address)) {
             CarAudioZonesHelper cazh =
                     new CarAudioZonesHelper(mCarAudioSettings, inputDevicesStream,
-                    mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+                            mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
 
             NullPointerException thrown =
                     expectThrows(NullPointerException.class,
@@ -513,7 +512,7 @@
                 R.raw.car_audio_configuration_non_numerical_occupant_zone_id)) {
             CarAudioZonesHelper cazh =
                     new CarAudioZonesHelper(mCarAudioSettings, nonNumericalStream,
-                    mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+                            mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
             IllegalArgumentException thrown =
                     expectThrows(IllegalArgumentException.class,
                             () -> cazh.loadAudioZones());
@@ -541,7 +540,7 @@
                 R.raw.car_audio_configuration_non_existent_input_device)) {
             CarAudioZonesHelper cazh =
                     new CarAudioZonesHelper(mCarAudioSettings, inputDevicesStream,
-                    mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+                            mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
 
             IllegalArgumentException thrown =
                     expectThrows(IllegalArgumentException.class,
@@ -556,7 +555,7 @@
                 R.raw.car_audio_configuration_empty_occupant_zone_id)) {
             CarAudioZonesHelper cazh =
                     new CarAudioZonesHelper(mCarAudioSettings, emptyOccupantZoneIdStream,
-                    mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+                            mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
             IllegalArgumentException thrown =
                     expectThrows(IllegalArgumentException.class,
                             () -> cazh.loadAudioZones());
@@ -570,7 +569,7 @@
                 R.raw.car_audio_configuration_primary_zone_with_non_zero_audio_zone_id)) {
             CarAudioZonesHelper cazh =
                     new CarAudioZonesHelper(mCarAudioSettings, nonZeroForPrimaryStream,
-                    mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+                            mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
             IllegalArgumentException thrown =
                     expectThrows(IllegalArgumentException.class,
                             () -> cazh.loadAudioZones());
@@ -588,7 +587,7 @@
             IllegalArgumentException thrown =
                     expectThrows(IllegalArgumentException.class,
                             () -> cazh.loadAudioZones());
-            assertThat(thrown).hasMessageThat().contains("for primary zone");
+            assertThat(thrown).hasMessageThat().contains(PRIMARY_ZONE_NAME);
         }
     }
 
@@ -598,7 +597,7 @@
                 R.raw.car_audio_configuration_repeat_input_device)) {
             CarAudioZonesHelper cazh =
                     new CarAudioZonesHelper(mCarAudioSettings, inputDevicesStream,
-                    mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+                            mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
 
             IllegalArgumentException thrown =
                     expectThrows(IllegalArgumentException.class,
@@ -613,7 +612,7 @@
                 R.raw.car_audio_configuration_output_address_does_not_exist)) {
             CarAudioZonesHelper cazh =
                     new CarAudioZonesHelper(mCarAudioSettings, outputDevicesStream,
-                    mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+                            mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
 
             IllegalStateException thrown =
                     expectThrows(IllegalStateException.class,
diff --git a/tests/carservice_unit_test/src/com/android/car/hal/HalClientUnitTest.java b/tests/carservice_unit_test/src/com/android/car/hal/HalClientUnitTest.java
new file mode 100644
index 0000000..8b18119
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/hal/HalClientUnitTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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.hal;
+
+import static android.car.test.mocks.CarArgumentMatchers.isProperty;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.expectThrows;
+
+import android.car.test.mocks.AbstractExtendedMockitoTestCase;
+import android.hardware.automotive.vehicle.V2_0.IVehicle;
+import android.hardware.automotive.vehicle.V2_0.IVehicleCallback;
+import android.hardware.automotive.vehicle.V2_0.StatusCode;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+public final class HalClientUnitTest extends AbstractExtendedMockitoTestCase {
+
+    private static final int WAIT_CAP_FOR_RETRIABLE_RESULT_MS = 100;
+    private static final int SLEEP_BETWEEN_RETRIABLE_INVOKES_MS = 50;
+
+    private static final int PROP = 42;
+    private static final int AREA_ID = 108;
+
+    private final VehiclePropValue mProp = new VehiclePropValue();
+
+    @Mock IVehicle mIVehicle;
+    @Mock IVehicleCallback mIVehicleCallback;
+
+    private HalClient mClient;
+
+    @Before
+    public void setFixtures() {
+        mClient = new HalClient(mIVehicle, Looper.getMainLooper(), mIVehicleCallback,
+                WAIT_CAP_FOR_RETRIABLE_RESULT_MS, SLEEP_BETWEEN_RETRIABLE_INVOKES_MS);
+        mProp.prop = PROP;
+        mProp.areaId = AREA_ID;
+    }
+
+    @Test
+    public void testSet_remoteExceptionThenFail() throws Exception {
+        when(mIVehicle.set(isProperty(PROP)))
+            .thenThrow(new RemoteException("Never give up, never surrender!"))
+            .thenThrow(new RemoteException("D'OH!"));
+
+        Exception actualException = expectThrows(ServiceSpecificException.class,
+                () -> mClient.setValue(mProp));
+
+        assertThat(actualException).hasMessageThat().contains(Integer.toHexString(PROP));
+        assertThat(actualException).hasMessageThat().contains(Integer.toHexString(AREA_ID));
+    }
+
+    @Test
+    public void testSet_remoteExceptionThenOk() throws Exception {
+        when(mIVehicle.set(isProperty(PROP)))
+            .thenThrow(new RemoteException("Never give up, never surrender!"))
+            .thenReturn(StatusCode.OK);
+
+        mClient.setValue(mProp);
+    }
+
+    @Test
+    public void testSet_invalidArgument() throws Exception {
+        when(mIVehicle.set(isProperty(PROP))).thenReturn(StatusCode.INVALID_ARG);
+
+        Exception actualException = expectThrows(IllegalArgumentException.class,
+                () -> mClient.setValue(mProp));
+
+        assertThat(actualException).hasMessageThat().contains(Integer.toHexString(PROP));
+        assertThat(actualException).hasMessageThat().contains(Integer.toHexString(AREA_ID));
+    }
+
+    @Test
+    public void testSet_otherError() throws Exception {
+        when(mIVehicle.set(isProperty(PROP))).thenReturn(StatusCode.INTERNAL_ERROR);
+
+        Exception actualException = expectThrows(ServiceSpecificException.class,
+                () -> mClient.setValue(mProp));
+
+        assertThat(actualException).hasMessageThat().contains(Integer.toHexString(PROP));
+        assertThat(actualException).hasMessageThat().contains(Integer.toHexString(AREA_ID));
+    }
+
+    @Test
+    public void testSet_ok() throws Exception {
+        when(mIVehicle.set(isProperty(PROP))).thenReturn(StatusCode.OK);
+    }
+}
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 24821ba..71e51f3 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
@@ -465,7 +465,7 @@
         callback.assertCalled();
 
         // Make sure the arguments were properly converted
-        assertSwitchUserSetRequest(reqCaptor.get(), SwitchUserMessageType.ANDROID_SWITCH, mUser10);
+        assertHALSetRequest(reqCaptor.get(), SwitchUserMessageType.ANDROID_SWITCH, mUser10);
 
         // Assert response
         assertCallbackStatus(callback, HalCallback.STATUS_WRONG_HAL_RESPONSE);
@@ -488,7 +488,7 @@
         callback.assertCalled();
 
         // Make sure the arguments were properly converted
-        assertSwitchUserSetRequest(reqCaptor.get(), SwitchUserMessageType.ANDROID_SWITCH, mUser10);
+        assertHALSetRequest(reqCaptor.get(), SwitchUserMessageType.ANDROID_SWITCH, mUser10);
 
         // Assert response
         assertCallbackStatus(callback, HalCallback.STATUS_OK);
@@ -513,7 +513,7 @@
         callback.assertCalled();
 
         // Make sure the arguments were properly converted
-        assertSwitchUserSetRequest(reqCaptor.get(), SwitchUserMessageType.ANDROID_SWITCH, mUser10);
+        assertHALSetRequest(reqCaptor.get(), SwitchUserMessageType.ANDROID_SWITCH, mUser10);
 
         // Assert response
         assertCallbackStatus(callback, HalCallback.STATUS_OK);
@@ -556,7 +556,7 @@
         callback.assertCalled();
 
         // Make sure the arguments were properly converted
-        assertSwitchUserSetRequest(reqCaptor.get(), SwitchUserMessageType.ANDROID_SWITCH, mUser10);
+        assertHALSetRequest(reqCaptor.get(), SwitchUserMessageType.ANDROID_SWITCH, mUser10);
 
         // Assert response
         assertCallbackStatus(callback, HalCallback.STATUS_WRONG_HAL_RESPONSE);
@@ -576,7 +576,24 @@
                 ArgumentCaptor.forClass(VehiclePropValue.class);
         verify(mVehicleHal).set(propCaptor.capture());
         VehiclePropValue prop = propCaptor.getValue();
-        assertPostSwitchResponseSetRequest(prop, SwitchUserMessageType.ANDROID_POST_SWITCH,
+        assertHALSetRequest(prop, SwitchUserMessageType.ANDROID_POST_SWITCH,
+                mUser10);
+    }
+
+    @Test
+    public void testUserSwitchLegacy_noUsersInfo() {
+        assertThrows(NullPointerException.class,
+                () -> mUserHalService.legacyUserSwitch(mUser10, null));
+    }
+
+    @Test
+    public void testUserSwitchLegacy_HalCalledWithCorrectProp() {
+        mUserHalService.legacyUserSwitch(mUser10, mUsersInfo);
+        ArgumentCaptor<VehiclePropValue> propCaptor =
+                ArgumentCaptor.forClass(VehiclePropValue.class);
+        verify(mVehicleHal).set(propCaptor.capture());
+        VehiclePropValue prop = propCaptor.getValue();
+        assertHALSetRequest(prop, SwitchUserMessageType.LEGACY_ANDROID_SWITCH,
                 mUser10);
     }
 
@@ -779,17 +796,7 @@
         assertUsersInfo(req, mUsersInfo, 2);
     }
 
-    private void assertSwitchUserSetRequest(VehiclePropValue req, int messageType,
-            UserInfo targetUserInfo) {
-        assertThat(req.value.int32Values.get(1)).isEqualTo(messageType);
-        assertWithMessage("targetuser.id mismatch").that(req.value.int32Values.get(2))
-                .isEqualTo(targetUserInfo.userId);
-        assertWithMessage("targetuser.flags mismatch").that(req.value.int32Values.get(3))
-                .isEqualTo(targetUserInfo.flags);
-        assertUsersInfo(req, mUsersInfo, 4);
-    }
-
-    private void assertPostSwitchResponseSetRequest(VehiclePropValue req, int messageType,
+    private void assertHALSetRequest(VehiclePropValue req, int messageType,
             UserInfo targetUserInfo) {
         assertThat(req.value.int32Values.get(1)).isEqualTo(messageType);
         assertWithMessage("targetuser.id mismatch").that(req.value.int32Values.get(2))
diff --git a/tests/carservice_unit_test/src/com/android/car/pm/VendorServiceControllerTest.java b/tests/carservice_unit_test/src/com/android/car/pm/VendorServiceControllerTest.java
index ff1abb9..3be64a5 100644
--- a/tests/carservice_unit_test/src/com/android/car/pm/VendorServiceControllerTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/pm/VendorServiceControllerTest.java
@@ -174,6 +174,9 @@
 
         // Switch user to foreground
         mockGetCurrentUser(FG_USER_ID);
+        // TODO(b/155918094): Update this test,
+        UserInfo nullUser = new UserInfo(UserHandle.USER_NULL, "null user", 0);
+        when(mUserManager.getUserInfo(UserHandle.USER_NULL)).thenReturn(nullUser);
         sendUserLifecycleEvent(CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING, FG_USER_ID);
 
         // Expect only services with ASAP trigger to be started
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 23e1038..abfd48d 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
@@ -22,11 +22,13 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.notNull;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 import static org.testng.Assert.assertThrows;
 
@@ -37,6 +39,7 @@
 import android.car.ICarUserService;
 import android.car.test.mocks.AbstractExtendedMockitoTestCase;
 import android.car.user.CarUserManager;
+import android.car.user.CarUserManager.UserSwitchUiCallback;
 import android.car.user.GetUserIdentificationAssociationResponse;
 import android.car.user.UserSwitchResult;
 import android.content.pm.UserInfo;
@@ -140,6 +143,20 @@
     }
 
     @Test
+    public void testSetSwitchUserUICallback_success() throws Exception {
+        UserSwitchUiCallback callback = (u)-> { };
+
+        mMgr.setUserSwitchUiCallback(callback);
+
+        verify(mService).setUserSwitchUiCallback(any());
+    }
+
+    @Test
+    public void testSetSwitchUserUICallback_nullCallback() throws Exception {
+        assertThrows(IllegalArgumentException.class, () -> mMgr.setUserSwitchUiCallback(null));
+    }
+
+    @Test
     public void testGetUserIdentificationAssociation_nullTypes() throws Exception {
         assertThrows(IllegalArgumentException.class,
                 () -> mMgr.getUserIdentificationAssociation(null));
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 73a6296..f52e48d 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
@@ -93,6 +93,7 @@
 import com.android.car.hal.UserHalService;
 import com.android.internal.R;
 import com.android.internal.infra.AndroidFuture;
+import com.android.internal.os.IResultReceiver;
 import com.android.internal.util.Preconditions;
 
 import org.junit.Before;
@@ -139,6 +140,7 @@
     @Mock private Resources mMockedResources;
     @Mock private Drawable mMockedDrawable;
     @Mock private UserMetrics mUserMetrics;
+    @Mock IResultReceiver mSwitchUserUiReceiver;
 
     private final BlockingUserLifecycleListener mUserLifecycleListener =
             BlockingUserLifecycleListener.newDefaultListener();
@@ -219,13 +221,14 @@
     public void testOnUserLifecycleEvent_nofityListener() throws Exception {
         // Arrange
         mCarUserService.addUserLifecycleListener(mUserLifecycleListener);
+        mockExistingUsers();
 
         // Act
-        int userId = 11;
-        sendUserLifecycleEvent(userId, CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING);
+        sendUserSwitchingEvent(mAdminUser.id, mRegularUser.id);
 
         // Verify
-        verifyListenerOnEventInvoked(userId, CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING);
+        verifyListenerOnEventInvoked(mRegularUser.id,
+                CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING);
     }
 
     @Test
@@ -236,16 +239,17 @@
         doThrow(new RuntimeException("Failed onEvent invocation")).when(
                 failureListener).onEvent(any(UserLifecycleEvent.class));
         mCarUserService.addUserLifecycleListener(failureListener);
+        mockExistingUsers();
 
         // Adding the non-failure listener later.
         mCarUserService.addUserLifecycleListener(mUserLifecycleListener);
 
         // Act
-        int userId = 11;
-        sendUserLifecycleEvent(userId, CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING);
+        sendUserSwitchingEvent(mAdminUser.id, mRegularUser.id);
 
         // Verify
-        verifyListenerOnEventInvoked(userId, CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING);
+        verifyListenerOnEventInvoked(mRegularUser.id,
+                CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING);
     }
 
     private void verifyListenerOnEventInvoked(int expectedNewUserId, int expectedEventType)
@@ -270,14 +274,10 @@
      * Test that the {@link CarUserService} updates last active user on user switch.
      */
     @Test
-    public void testLastActiveUserUpdatedOnUserSwitch() {
-        int lastActiveUserId = 99;
-        UserInfo persistentUser = new UserInfo(lastActiveUserId, "persistent user",
-                NO_USER_INFO_FLAGS);
-        doReturn(persistentUser).when(mMockedUserManager).getUserInfo(lastActiveUserId);
-        sendUserSwitchingEvent(lastActiveUserId);
-
-        verify(mMockedCarUserManagerHelper).setLastActiveUser(lastActiveUserId);
+    public void testLastActiveUserUpdatedOnUserSwitch() throws Exception {
+        mockExistingUsers();
+        sendUserSwitchingEvent(mAdminUser.id, mRegularUser.id);
+        verify(mMockedCarUserManagerHelper).setLastActiveUser(mRegularUser.id);
     }
 
     /**
@@ -908,6 +908,51 @@
     }
 
     @Test
+    public void testHalUserSwitchOnAndroidSwitch_successfulNoExitingUserSwitch() {
+        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);
+    }
+
+    @Test
+    public void testHalUserSwitchOnAndroidSwitch_failureExitingUserSwitch() throws Exception {
+        mockExistingUsersAndCurrentUser(mAdminUser);
+        int requestId = 42;
+        mSwitchUserResponse.status = SwitchUserStatus.SUCCESS;
+        mSwitchUserResponse.requestId = requestId;
+        mockHalSwitch(mAdminUser.id, mGuestUser, mSwitchUserResponse);
+        mockAmSwitchUser(mGuestUser, true);
+        mCarUserService.switchUser(mGuestUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture);
+
+        sendUserSwitchingEvent(mAdminUser.id, mGuestUser.id);
+
+        verify(mUserHal, never()).legacyUserSwitch(any(), any());
+    }
+
+    @Test
+    public void testSetSwitchUserUI_receiverSetAndCalled() throws Exception {
+        mockExistingUsersAndCurrentUser(mAdminUser);
+        int requestId = 42;
+        mSwitchUserResponse.status = SwitchUserStatus.SUCCESS;
+        mSwitchUserResponse.requestId = requestId;
+        mockHalSwitch(mAdminUser.id, mGuestUser, mSwitchUserResponse);
+        mockAmSwitchUser(mGuestUser, true);
+
+        mCarUserService.setUserSwitchUiCallback(mSwitchUserUiReceiver);
+        mCarUserService.switchUser(mGuestUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture);
+
+        // update current user due to successful user switch
+        verify(mSwitchUserUiReceiver).send(mGuestUser.id, null);
+    }
+
+    @Test
     public void testGetUserInfo_nullReceiver() throws Exception {
         assertThrows(NullPointerException.class, () -> mCarUserService
                 .getInitialUserInfo(mGetUserInfoRequestType, mAsyncCallTimeoutMs, null));
@@ -1091,12 +1136,12 @@
     }
 
     @Test
-    public void testUserMetric_SendEvent() {
-        int userId = 99;
-        sendUserSwitchingEvent(userId);
+    public void testUserMetric_SendEvent() throws Exception {
+        mockExistingUsersAndCurrentUser(mAdminUser);
+        sendUserSwitchingEvent(mAdminUser.id, mRegularUser.id);
 
         verify(mUserMetrics).onEvent(CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING,
-                0, UserHandle.USER_NULL, userId);
+                0, mAdminUser.id, mRegularUser.id);
     }
 
     @Test
@@ -1419,18 +1464,19 @@
         }
     }
 
-    private void sendUserLifecycleEvent(@UserIdInt int userId,
+    private void sendUserLifecycleEvent(@UserIdInt int fromUserId, @UserIdInt int toUserId,
             @UserLifecycleEventType int eventType) {
-        mCarUserService.onUserLifecycleEvent(eventType, /* timestampMs= */ 0,
-                /* fromUserId= */ UserHandle.USER_NULL, userId);
+        mCarUserService.onUserLifecycleEvent(eventType, /* timestampMs= */ 0, fromUserId, toUserId);
     }
 
     private void sendUserUnlockedEvent(@UserIdInt int userId) {
-        sendUserLifecycleEvent(userId, CarUserManager.USER_LIFECYCLE_EVENT_TYPE_UNLOCKED);
+        sendUserLifecycleEvent(/* fromUser */ 0, userId,
+                CarUserManager.USER_LIFECYCLE_EVENT_TYPE_UNLOCKED);
     }
 
-    private void sendUserSwitchingEvent(@UserIdInt int userId) {
-        sendUserLifecycleEvent(userId, CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING);
+    private void sendUserSwitchingEvent(@UserIdInt int fromUserId, @UserIdInt int toUserId) {
+        sendUserLifecycleEvent(fromUserId, toUserId,
+                CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING);
     }
 
     @NonNull
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
new file mode 100644
index 0000000..026754c
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/user/ExperimentalCarUserManagerUnitTest.java
@@ -0,0 +1,197 @@
+/*
+ * 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 static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+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.ExperimentalCarUserManager;
+import android.content.pm.UserInfo;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+import java.util.Arrays;
+import java.util.List;
+
+public final class ExperimentalCarUserManagerUnitTest extends AbstractExtendedMockitoTestCase {
+
+    @Mock
+    private Car mCar;
+    @Mock
+    private UserManager mUserManager;
+    @Mock
+    private ICarUserService mService;
+
+    private ExperimentalCarUserManager mManager;
+
+    @Before public void setFixtures() {
+        mManager =
+                ExperimentalCarUserManager.from(new CarUserManager(mCar, mService, mUserManager));
+    }
+
+    @Test
+    public void testCreateDriver_Success_Admin() throws Exception {
+        expectCreateDriverSucceed(10);
+        int userId = mManager.createDriver("test driver", true);
+        assertThat(userId).isEqualTo(10);
+    }
+
+    @Test
+    public void testCreateDriver_Success_NonAdmin() throws Exception {
+        expectCreateDriverSucceed(10);
+        int userId = mManager.createDriver("test driver", false);
+        assertThat(userId).isEqualTo(10);
+    }
+
+    @Test
+    public void testCreateDriver_Error() throws Exception {
+        expectCreateDriverFail();
+        int userId = mManager.createDriver("test driver", false);
+        assertThat(userId).isEqualTo(UserHandle.USER_NULL);
+    }
+
+    @Test
+    public void testCreatePassenger_Success() throws Exception {
+        expectCreatePassengerSucceed();
+        int userId = mManager.createPassenger("test passenger", 10);
+        assertThat(userId).isNotEqualTo(UserHandle.USER_NULL);
+    }
+
+    @Test
+    public void testCreatePassenger_Error() throws Exception {
+        expectCreatePassengerFail();
+        int userId = mManager.createPassenger("test passenger", 20);
+        assertThat(userId).isEqualTo(UserHandle.USER_NULL);
+    }
+
+    @Test
+    public void testSwitchDriver_Success() throws Exception {
+        expectSwitchDriverSucceed();
+        boolean success = mManager.switchDriver(10);
+        assertThat(success).isTrue();
+    }
+
+    @Test
+    public void testSwitchDriver_Error() throws Exception {
+        expectSwitchDriverFail();
+        boolean success = mManager.switchDriver(20);
+        assertThat(success).isFalse();
+    }
+
+    @Test
+    public void testGetAllDrivers() throws Exception {
+        List<UserInfo> userInfos = UserTestingHelper.newUsers(10, 20, 30);
+        when(mService.getAllDrivers()).thenReturn(userInfos);
+        List<Integer> drivers = mManager.getAllDrivers();
+        assertThat(drivers).containsExactly(10, 20, 30);
+    }
+
+    @Test
+    public void testGetAllPassengers() throws Exception {
+        List<UserInfo> userInfos = UserTestingHelper.newUsers(100, 101, 102);
+        when(mService.getPassengers(10)).thenReturn(userInfos);
+        when(mService.getPassengers(20)).thenReturn(Arrays.asList());
+
+        List<Integer> passengers = mManager.getPassengers(10);
+        assertThat(passengers).containsExactly(100, 101, 102);
+
+        passengers = mManager.getPassengers(20);
+        assertThat(passengers).isEmpty();
+    }
+
+    @Test
+    public void testStartPassenger_Success() throws Exception {
+        expectStartPassengerSucceed();
+        boolean success = mManager.startPassenger(100, /* zoneId = */ 1);
+        assertThat(success).isTrue();
+    }
+
+    @Test
+    public void testStartPassenger_Error() throws Exception {
+        expectStartPassengerFail();
+        boolean success = mManager.startPassenger(200, /* zoneId = */ 1);
+        assertThat(success).isFalse();
+    }
+
+    @Test
+    public void testStopPassenger_Success() throws Exception {
+        expectStopPassengerSucceed();
+        boolean success = mManager.stopPassenger(100);
+        assertThat(success).isTrue();
+    }
+
+    @Test
+    public void testStopPassenger_Error() throws Exception {
+        expectStopPassengerFail();
+        boolean success = mManager.stopPassenger(200);
+        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 expectCreateDriverFail() throws Exception {
+        when(mService.createDriver(eq("test driver"), anyBoolean())).thenReturn(null);
+    }
+
+    private void expectCreatePassengerSucceed() throws Exception {
+        UserInfo userInfo = UserTestingHelper.newUser(100);
+        when(mService.createPassenger("test passenger", /* driverId = */ 10)).thenReturn(userInfo);
+    }
+
+    private void expectCreatePassengerFail() throws Exception {
+        when(mService.createPassenger("test passenger", /* driverId = */ 10)).thenReturn(null);
+    }
+
+    private void expectSwitchDriverSucceed() throws Exception {
+        when(mService.switchDriver(10)).thenReturn(true);
+    }
+
+    private void expectSwitchDriverFail() throws Exception {
+        when(mService.switchDriver(20)).thenReturn(false);
+    }
+
+    private void expectStartPassengerSucceed() throws Exception {
+        when(mService.startPassenger(100, /* zoneId = */ 1)).thenReturn(true);
+    }
+
+    private void expectStartPassengerFail() throws Exception {
+        when(mService.startPassenger(200, /* zoneId = */ 1)).thenReturn(false);
+    }
+
+    private void expectStopPassengerSucceed() throws Exception {
+        when(mService.stopPassenger(100)).thenReturn(true);
+    }
+
+    private void expectStopPassengerFail() throws Exception {
+        when(mService.stopPassenger(200)).thenReturn(false);
+    }
+}
diff --git a/user/car-user-lib/src/android/car/userlib/CommonConstants.java b/user/car-user-lib/src/android/car/userlib/CommonConstants.java
new file mode 100644
index 0000000..ef9a354
--- /dev/null
+++ b/user/car-user-lib/src/android/car/userlib/CommonConstants.java
@@ -0,0 +1,42 @@
+/*
+ * 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.userlib;
+
+/**
+ * Provides constants used by both CarService and CarServiceHelper packages.
+ */
+public final class CommonConstants {
+
+    private CommonConstants() {
+        throw new UnsupportedOperationException("contains only static constants");
+    }
+
+    /**
+     * Constants used on {@link android.os.Bundle bundles} sent by
+     * {@link com.android.car.user.CarUserService} on binder calls.
+     */
+    public static final class CarUserServiceConstants {
+
+        public static final String BUNDLE_USER_ID = "user.id";
+        public static final String BUNDLE_USER_FLAGS = "user.flags";
+        public static final String BUNDLE_USER_NAME = "user.name";
+        public static final String BUNDLE_INITIAL_INFO_ACTION = "initial_info.action";
+
+        private CarUserServiceConstants() {
+            throw new UnsupportedOperationException("contains only static constants");
+        }
+    }
+}
diff --git a/watchdog/product/carwatchdog.mk b/watchdog/product/carwatchdog.mk
index 6829177..028f67b 100644
--- a/watchdog/product/carwatchdog.mk
+++ b/watchdog/product/carwatchdog.mk
@@ -16,10 +16,10 @@
 PRODUCT_PACKAGES += carwatchdogd
 
 # SELinux public policies for car watchdog services
-BOARD_PLAT_PUBLIC_SEPOLICY_DIR += packages/services/Car/watchdog/sepolicy/public
+PRODUCT_PUBLIC_SEPOLICY_DIRS += packages/services/Car/watchdog/sepolicy/public
 
 # SELinux private policies for car watchdog services
-BOARD_PLAT_PRIVATE_SEPOLICY_DIR += packages/services/Car/watchdog/sepolicy/private
+PRODUCT_PRIVATE_SEPOLICY_DIRS += packages/services/Car/watchdog/sepolicy/private
 
 # Include carwatchdog testclient if the build is userdebug or eng
 ifneq (,$(filter userdebug eng, $(TARGET_BUILD_VARIANT)))
diff --git a/watchdog/server/Android.bp b/watchdog/server/Android.bp
index 71da2db..e50b24a 100644
--- a/watchdog/server/Android.bp
+++ b/watchdog/server/Android.bp
@@ -143,7 +143,7 @@
     init_rc: ["carwatchdogd.rc"],
     shared_libs: [
       "libwatchdog_binder_mediator",
-    	"libwatchdog_ioperfcollection",
+      "libwatchdog_ioperfcollection",
       "libwatchdog_process_service",
     ],
     vintf_fragments: ["carwatchdogd.xml"],
diff --git a/watchdog/server/carwatchdogd.rc b/watchdog/server/carwatchdogd.rc
index e75f21a..c2eb104 100644
--- a/watchdog/server/carwatchdogd.rc
+++ b/watchdog/server/carwatchdogd.rc
@@ -35,10 +35,10 @@
 
 on early-init
     # Number of top stats per category
-    setprop ro.carwatchdog.top_n_stats_per_category 5
+    setprop ro.carwatchdog.top_n_stats_per_category 10
 
     # Number of top stats per sub-category
-    setprop ro.carwatchdog.top_n_stats_per_subcategory 3
+    setprop ro.carwatchdog.top_n_stats_per_subcategory 5
 
     # Cache size for the periodically collected records
     setprop ro.carwatchdog.periodic_collection_buffer_size 180
diff --git a/watchdog/server/src/IoPerfCollection.cpp b/watchdog/server/src/IoPerfCollection.cpp
index 63d47ef..2799417 100644
--- a/watchdog/server/src/IoPerfCollection.cpp
+++ b/watchdog/server/src/IoPerfCollection.cpp
@@ -22,6 +22,7 @@
 #include <android-base/file.h>
 #include <android-base/parseint.h>
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <binder/IServiceManager.h>
 #include <cutils/android_filesystem_config.h>
 #include <inttypes.h>
@@ -30,7 +31,9 @@
 #include <pthread.h>
 #include <pwd.h>
 
+#include <algorithm>
 #include <iomanip>
+#include <iterator>
 #include <limits>
 #include <string>
 #include <thread>
@@ -50,14 +53,16 @@
 using android::base::Error;
 using android::base::ParseUint;
 using android::base::Result;
+using android::base::Split;
 using android::base::StringAppendF;
+using android::base::StringPrintf;
 using android::base::WriteStringToFd;
 using android::content::pm::IPackageManagerNative;
 
 namespace {
 
-const int32_t kDefaultTopNStatsPerCategory = 5;
-const int32_t kDefaultTopNStatsPerSubcategory = 3;
+const int32_t kDefaultTopNStatsPerCategory = 10;
+const int32_t kDefaultTopNStatsPerSubcategory = 5;
 const std::chrono::seconds kDefaultBoottimeCollectionInterval = 1s;
 const std::chrono::seconds kDefaultPeriodicCollectionInterval = 10s;
 // Number of periodic collection perf data snapshots to cache in memory.
@@ -72,6 +77,24 @@
 
 const std::string kDumpMajorDelimiter = std::string(100, '-') + "\n";
 
+constexpr const char* kHelpText =
+        "\nCustom I/O performance data collection dump options:\n"
+        "%s: Starts custom I/O performance data collection. Customize the collection behavior with "
+        "the following optional arguments:\n"
+        "\t%s <seconds>: Modifies the collection interval. Default behavior is to collect once "
+        "every %lld seconds.\n"
+        "\t%s <seconds>: Modifies the maximum collection duration. Default behavior is to collect "
+        "until %ld minutes before automatically stopping the custom collection and discarding "
+        "the collected data.\n"
+        "\t%s <package name>,<package, name>,...: Comma-separated value containing package names. "
+        "When provided, the results are filtered only to the provided package names. Default "
+        "behavior is to list the results for the top %d packages.\n"
+        "%s: Stops custom I/O performance data collection and generates a dump of "
+        "the collection report.\n\n"
+        "When no options are specified, the carwatchdog report contains the I/O performance "
+        "data collected during boot-time and over the last %ld minutes before the report "
+        "generation.";
+
 double percentage(uint64_t numer, uint64_t denom) {
     return denom == 0 ? 0.0 : (static_cast<double>(numer) / static_cast<double>(denom)) * 100.0;
 }
@@ -401,17 +424,18 @@
     }
 
     if (args[0] == String16(kStartCustomCollectionFlag)) {
-        if (args.size() > 5) {
-            return Error(INVALID_OPERATION) << "Number of arguments to start custom "
-                                            << "I/O performance data collection cannot exceed 5";
+        if (args.size() > 7) {
+            return Error(BAD_VALUE) << "Number of arguments to start custom I/O performance data "
+                                    << "collection cannot exceed 7";
         }
         std::chrono::nanoseconds interval = kCustomCollectionInterval;
         std::chrono::nanoseconds maxDuration = kCustomCollectionDuration;
+        std::unordered_set<std::string> filterPackages;
         for (size_t i = 1; i < args.size(); ++i) {
             if (args[i] == String16(kIntervalFlag)) {
                 const auto& ret = parseSecondsFlag(args, i + 1);
                 if (!ret) {
-                    return Error(FAILED_TRANSACTION)
+                    return Error(BAD_VALUE)
                             << "Failed to parse " << kIntervalFlag << ": " << ret.error();
                 }
                 interval = std::chrono::duration_cast<std::chrono::nanoseconds>(*ret);
@@ -421,21 +445,34 @@
             if (args[i] == String16(kMaxDurationFlag)) {
                 const auto& ret = parseSecondsFlag(args, i + 1);
                 if (!ret) {
-                    return Error(FAILED_TRANSACTION)
+                    return Error(BAD_VALUE)
                             << "Failed to parse " << kMaxDurationFlag << ": " << ret.error();
                 }
                 maxDuration = std::chrono::duration_cast<std::chrono::nanoseconds>(*ret);
                 ++i;
                 continue;
             }
+            if (args[i] == String16(kFilterPackagesFlag)) {
+                if (args.size() < i + 1) {
+                    return Error(BAD_VALUE)
+                            << "Must provide value for '" << kFilterPackagesFlag << "' flag";
+                }
+                std::vector<std::string> packages =
+                        Split(std::string(String8(args[i + 1]).string()), ",");
+                std::copy(packages.begin(), packages.end(),
+                          std::inserter(filterPackages, filterPackages.end()));
+                ++i;
+                continue;
+            }
             ALOGW("Unknown flag %s provided to start custom I/O performance data collection",
                   String8(args[i]).string());
-            return Error(INVALID_OPERATION) << "Unknown flag " << String8(args[i]).string()
-                                            << " provided to start custom I/O performance data "
-                                            << "collection";
+            return Error(BAD_VALUE) << "Unknown flag " << String8(args[i]).string()
+                                    << " provided to start custom I/O performance data "
+                                    << "collection";
         }
-        const auto& ret = startCustomCollection(interval, maxDuration);
+        const auto& ret = startCustomCollection(interval, maxDuration, filterPackages);
         if (!ret) {
+            WriteStringToFd(ret.error().message(), fd);
             return ret;
         }
         return {};
@@ -443,19 +480,37 @@
 
     if (args[0] == String16(kEndCustomCollectionFlag)) {
         if (args.size() != 1) {
-            ALOGW("Number of arguments to end custom I/O performance data collection cannot "
-                  "exceed 1");
+            ALOGW("Number of arguments to stop custom I/O performance data collection cannot "
+                  "exceed 1. Stopping the data collection.");
+            WriteStringToFd("Number of arguments to stop custom I/O performance data collection "
+                            "cannot exceed 1. Stopping the data collection.",
+                            fd);
         }
-        const auto& ret = endCustomCollection(fd);
-        if (!ret) {
-            return ret;
-        }
-        return {};
+        return endCustomCollection(fd);
     }
 
-    return Error(INVALID_OPERATION)
-            << "Dump arguments start neither with " << kStartCustomCollectionFlag << " nor with "
-            << kEndCustomCollectionFlag << " flags";
+    return Error(BAD_VALUE) << "I/O perf collection dump arguments start neither with "
+                            << kStartCustomCollectionFlag << " nor with "
+                            << kEndCustomCollectionFlag << " flags";
+}
+
+bool IoPerfCollection::dumpHelpText(int fd) {
+    long periodicCacheMinutes =
+            (std::chrono::duration_cast<std::chrono::seconds>(mPeriodicCollection.interval)
+                     .count() *
+             mPeriodicCollection.maxCacheSize) /
+            60;
+    return WriteStringToFd(StringPrintf(kHelpText, kStartCustomCollectionFlag, kIntervalFlag,
+                                        std::chrono::duration_cast<std::chrono::seconds>(
+                                                kCustomCollectionInterval)
+                                                .count(),
+                                        kMaxDurationFlag,
+                                        std::chrono::duration_cast<std::chrono::minutes>(
+                                                kCustomCollectionDuration)
+                                                .count(),
+                                        kFilterPackagesFlag, mTopNStatsPerCategory,
+                                        kEndCustomCollectionFlag, periodicCacheMinutes),
+                           fd);
 }
 
 Result<void> IoPerfCollection::dumpCollection(int fd) {
@@ -512,8 +567,9 @@
     return {};
 }
 
-Result<void> IoPerfCollection::startCustomCollection(std::chrono::nanoseconds interval,
-                                                     std::chrono::nanoseconds maxDuration) {
+Result<void> IoPerfCollection::startCustomCollection(
+        std::chrono::nanoseconds interval, std::chrono::nanoseconds maxDuration,
+        const std::unordered_set<std::string>& filterPackages) {
     if (interval < kMinCollectionInterval || maxDuration < kMinCollectionInterval) {
         return Error(INVALID_OPERATION)
                 << "Collection interval and maximum duration must be >= "
@@ -532,6 +588,7 @@
     mCustomCollection = {
             .interval = interval,
             .maxCacheSize = std::numeric_limits<std::size_t>::max(),
+            .filterPackages = filterPackages,
             .lastCollectionUptime = mHandlerLooper->now(),
             .records = {},
     };
@@ -664,11 +721,11 @@
     if (!ret) {
         return ret;
     }
-    ret = collectProcessIoPerfDataLocked(&record.processIoPerfData);
+    ret = collectProcessIoPerfDataLocked(*collectionInfo, &record.processIoPerfData);
     if (!ret) {
         return ret;
     }
-    ret = collectUidIoPerfDataLocked(&record.uidIoPerfData);
+    ret = collectUidIoPerfDataLocked(*collectionInfo, &record.uidIoPerfData);
     if (!ret) {
         return ret;
     }
@@ -679,7 +736,8 @@
     return {};
 }
 
-Result<void> IoPerfCollection::collectUidIoPerfDataLocked(UidIoPerfData* uidIoPerfData) {
+Result<void> IoPerfCollection::collectUidIoPerfDataLocked(const CollectionInfo& collectionInfo,
+                                                          UidIoPerfData* uidIoPerfData) {
     if (!mUidIoStats->enabled()) {
         // Don't return an error to avoid pre-mature termination. Instead, fetch data from other
         // collectors.
@@ -721,16 +779,20 @@
         for (auto it = topNReads.begin(); it != topNReads.end(); ++it) {
             const UidIoUsage* curRead = *it;
             if (curRead->ios.sumReadBytes() < curUsage.ios.sumReadBytes()) {
-                topNReads.erase(topNReads.end() - 1);
                 topNReads.emplace(it, &curUsage);
+                if (collectionInfo.filterPackages.empty()) {
+                    topNReads.pop_back();
+                }
                 break;
             }
         }
         for (auto it = topNWrites.begin(); it != topNWrites.end(); ++it) {
             const UidIoUsage* curWrite = *it;
             if (curWrite->ios.sumWriteBytes() < curUsage.ios.sumWriteBytes()) {
-                topNWrites.erase(topNWrites.end() - 1);
                 topNWrites.emplace(it, &curUsage);
+                if (collectionInfo.filterPackages.empty()) {
+                    topNWrites.pop_back();
+                }
                 break;
             }
         }
@@ -759,6 +821,11 @@
         if (mUidToPackageNameMapping.find(usage->uid) != mUidToPackageNameMapping.end()) {
             stats.packageName = mUidToPackageNameMapping[usage->uid];
         }
+        if (!collectionInfo.filterPackages.empty() &&
+            collectionInfo.filterPackages.find(stats.packageName) ==
+                    collectionInfo.filterPackages.end()) {
+            continue;
+        }
         uidIoPerfData->topNReads.emplace_back(stats);
     }
 
@@ -779,6 +846,11 @@
         if (mUidToPackageNameMapping.find(usage->uid) != mUidToPackageNameMapping.end()) {
             stats.packageName = mUidToPackageNameMapping[usage->uid];
         }
+        if (!collectionInfo.filterPackages.empty() &&
+            collectionInfo.filterPackages.find(stats.packageName) ==
+                    collectionInfo.filterPackages.end()) {
+            continue;
+        }
         uidIoPerfData->topNWrites.emplace_back(stats);
     }
     return {};
@@ -804,7 +876,7 @@
 }
 
 Result<void> IoPerfCollection::collectProcessIoPerfDataLocked(
-        ProcessIoPerfData* processIoPerfData) {
+        const CollectionInfo& collectionInfo, ProcessIoPerfData* processIoPerfData) {
     if (!mProcPidStat->enabled()) {
         // Don't return an error to avoid pre-mature termination. Instead, fetch data from other
         // collectors.
@@ -832,16 +904,20 @@
         for (auto it = topNIoBlockedUids.begin(); it != topNIoBlockedUids.end(); ++it) {
             const UidProcessStats* topStats = *it;
             if (topStats->ioBlockedTasksCnt < curStats.ioBlockedTasksCnt) {
-                topNIoBlockedUids.erase(topNIoBlockedUids.end() - 1);
                 topNIoBlockedUids.emplace(it, &curStats);
+                if (collectionInfo.filterPackages.empty()) {
+                    topNIoBlockedUids.pop_back();
+                }
                 break;
             }
         }
         for (auto it = topNMajorFaultUids.begin(); it != topNMajorFaultUids.end(); ++it) {
             const UidProcessStats* topStats = *it;
             if (topStats->majorFaults < curStats.majorFaults) {
-                topNMajorFaultUids.erase(topNMajorFaultUids.end() - 1);
                 topNMajorFaultUids.emplace(it, &curStats);
+                if (collectionInfo.filterPackages.empty()) {
+                    topNMajorFaultUids.pop_back();
+                }
                 break;
             }
         }
@@ -867,6 +943,11 @@
         if (mUidToPackageNameMapping.find(it->uid) != mUidToPackageNameMapping.end()) {
             stats.packageName = mUidToPackageNameMapping[it->uid];
         }
+        if (!collectionInfo.filterPackages.empty() &&
+            collectionInfo.filterPackages.find(stats.packageName) ==
+                    collectionInfo.filterPackages.end()) {
+            continue;
+        }
         for (const auto& pIt : it->topNIoBlockedProcesses) {
             if (pIt.count == 0) {
                 break;
@@ -891,6 +972,11 @@
         if (mUidToPackageNameMapping.find(it->uid) != mUidToPackageNameMapping.end()) {
             stats.packageName = mUidToPackageNameMapping[it->uid];
         }
+        if (!collectionInfo.filterPackages.empty() &&
+            collectionInfo.filterPackages.find(stats.packageName) ==
+                    collectionInfo.filterPackages.end()) {
+            continue;
+        }
         for (const auto& pIt : it->topNMajorFaultProcesses) {
             if (pIt.count == 0) {
                 break;
diff --git a/watchdog/server/src/IoPerfCollection.h b/watchdog/server/src/IoPerfCollection.h
index 4fef328..4cccb05 100644
--- a/watchdog/server/src/IoPerfCollection.h
+++ b/watchdog/server/src/IoPerfCollection.h
@@ -49,6 +49,7 @@
 constexpr const char* kEndCustomCollectionFlag = "--stop_io";
 constexpr const char* kIntervalFlag = "--interval";
 constexpr const char* kMaxDurationFlag = "--max_duration";
+constexpr const char* kFilterPackagesFlag = "--filter_packages";
 
 // Performance data collected from the `/proc/uid_io/stats` file.
 struct UidIoPerfData {
@@ -110,6 +111,8 @@
 struct CollectionInfo {
     std::chrono::nanoseconds interval = 0ns;  // Collection interval between subsequent collections.
     size_t maxCacheSize = 0;                  // Maximum cache size for the collection.
+    std::unordered_set<std::string> filterPackages;  // Filter the output only to the specified
+                                                     // packages.
     nsecs_t lastCollectionUptime = 0;         // Used to calculate the uptime for next collection.
     std::vector<IoPerfRecord> records;        // Cache of collected performance records.
 };
@@ -187,6 +190,9 @@
     // Returns any error observed during the dump generation.
     virtual android::base::Result<void> dump(int fd, const Vector<String16>& args);
 
+    // Dumps the help text.
+    bool dumpHelpText(int fd);
+
 private:
     // Generates a dump from the boot-time and periodic collection events.
     android::base::Result<void> dumpCollection(int fd);
@@ -200,8 +206,11 @@
     // |maxDuration| is reached, the looper receives a message to end the collection, discards the
     // collected data, and starts the periodic collection. This is needed to ensure the custom
     // collection doesn't run forever when a subsequent |endCustomCollection| call is not received.
-    android::base::Result<void> startCustomCollection(std::chrono::nanoseconds interval,
-                                                      std::chrono::nanoseconds maxDuration);
+    // When |kFilterPackagesFlag| value is provided, the results are filtered only to the specified
+    // package names.
+    android::base::Result<void> startCustomCollection(
+            std::chrono::nanoseconds interval, std::chrono::nanoseconds maxDuration,
+            const std::unordered_set<std::string>& filterPackages);
 
     // Ends the current custom collection, generates a dump, sends message to looper to start the
     // periodic collection, and returns immediately. Returns an error when there is no custom
@@ -218,7 +227,8 @@
     android::base::Result<void> collectLocked(CollectionInfo* collectionInfo);
 
     // Collects performance data from the `/proc/uid_io/stats` file.
-    android::base::Result<void> collectUidIoPerfDataLocked(UidIoPerfData* uidIoPerfData);
+    android::base::Result<void> collectUidIoPerfDataLocked(const CollectionInfo& collectionInfo,
+                                                           UidIoPerfData* uidIoPerfData);
 
     // Collects performance data from the `/proc/stats` file.
     android::base::Result<void> collectSystemIoPerfDataLocked(SystemIoPerfData* systemIoPerfData);
@@ -226,7 +236,7 @@
     // Collects performance data from the `/proc/[pid]/stat` and
     // `/proc/[pid]/task/[tid]/stat` files.
     android::base::Result<void> collectProcessIoPerfDataLocked(
-            ProcessIoPerfData* processIoPerfData);
+            const CollectionInfo& collectionInfo, ProcessIoPerfData* processIoPerfData);
 
     // Updates the |mUidToPackageNameMapping| for the given |uids|.
     android::base::Result<void> updateUidToPackageNameMapping(
@@ -296,6 +306,7 @@
     FRIEND_TEST(IoPerfCollectionTest, TestValidProcStatFile);
     FRIEND_TEST(IoPerfCollectionTest, TestValidProcPidContents);
     FRIEND_TEST(IoPerfCollectionTest, TestProcPidContentsLessThanTopNStatsLimit);
+    FRIEND_TEST(IoPerfCollectionTest, TestCustomCollectionFiltersPackageNames);
 };
 
 }  // namespace watchdog
diff --git a/watchdog/server/src/WatchdogBinderMediator.cpp b/watchdog/server/src/WatchdogBinderMediator.cpp
index da4ea29..0a482fd 100644
--- a/watchdog/server/src/WatchdogBinderMediator.cpp
+++ b/watchdog/server/src/WatchdogBinderMediator.cpp
@@ -18,6 +18,7 @@
 
 #include "WatchdogBinderMediator.h"
 
+#include <android-base/file.h>
 #include <android-base/parseint.h>
 #include <android-base/stringprintf.h>
 #include <android/automotive/watchdog/BootPhase.h>
@@ -38,10 +39,19 @@
 using android::base::ParseUint;
 using android::base::Result;
 using android::base::StringPrintf;
+using android::base::WriteStringToFd;
 using android::binder::Status;
 
 namespace {
 
+constexpr const char* kHelpFlag = "--help";
+constexpr const char* kHelpShortFlag = "-h";
+constexpr const char* kHelpText =
+        "CarWatchdog daemon dumpsys help page:\n"
+        "Format: dumpsys android.automotive.watchdog.ICarWatchdog/default [options]\n\n"
+        "%s or %s: Displays this help text.\n"
+        "When no options are specified, carwatchdog report is generated.\n";
+
 Status checkSystemPermission() {
     if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
         return Status::fromExceptionCode(Status::EX_SECURITY,
@@ -94,21 +104,48 @@
         }
         return OK;
     }
+
+    if (args[0] == String16(kHelpFlag) || args[0] == String16(kHelpShortFlag)) {
+        if (!dumpHelpText(fd, "")) {
+            ALOGW("Failed to write help text to fd");
+            return FAILED_TRANSACTION;
+        }
+        return OK;
+    }
+
     if (args[0] == String16(kStartCustomCollectionFlag) ||
         args[0] == String16(kEndCustomCollectionFlag)) {
         auto ret = mIoPerfCollection->dump(fd, args);
-        std::string mode = args[0] == String16(kStartCustomCollectionFlag) ? "start" : "end";
         if (!ret.ok()) {
-            ALOGW("Failed to %s custom I/O perf collection: %s", mode.c_str(),
-                  ret.error().message().c_str());
+            std::string mode = args[0] == String16(kStartCustomCollectionFlag) ? "start" : "end";
+            std::string errorMsg = StringPrintf("Failed to %s custom I/O perf collection: %s",
+                                                mode.c_str(), ret.error().message().c_str());
+            if (ret.error().code() == BAD_VALUE) {
+                dumpHelpText(fd, errorMsg);
+            } else {
+                ALOGW("%s", errorMsg.c_str());
+            }
             return ret.error().code();
         }
         return OK;
     }
-    ALOGW("Invalid dump arguments");
+    dumpHelpText(fd, "Invalid dump arguments");
     return INVALID_OPERATION;
 }
 
+bool WatchdogBinderMediator::dumpHelpText(int fd, std::string errorMsg) {
+    if (!errorMsg.empty()) {
+        ALOGW("Error: %s", errorMsg.c_str());
+        if (!WriteStringToFd(StringPrintf("Error: %s\n\n", errorMsg.c_str()), fd)) {
+            ALOGW("Failed to write error message to fd");
+            return false;
+        }
+    }
+
+    return WriteStringToFd(StringPrintf(kHelpText, kHelpFlag, kHelpShortFlag), fd) &&
+            mIoPerfCollection->dumpHelpText(fd);
+}
+
 Status WatchdogBinderMediator::registerMediator(const sp<ICarWatchdogClient>& mediator) {
     Status status = checkSystemPermission();
     if (!status.isOk()) {
diff --git a/watchdog/server/src/WatchdogBinderMediator.h b/watchdog/server/src/WatchdogBinderMediator.h
index a3b19a2..530659b 100644
--- a/watchdog/server/src/WatchdogBinderMediator.h
+++ b/watchdog/server/src/WatchdogBinderMediator.h
@@ -83,6 +83,8 @@
     void binderDied(const android::wp<IBinder>& who) override {
         return mWatchdogProcessService->binderDied(who);
     }
+    bool dumpHelpText(int fd, std::string errorMsg);
+
     android::sp<WatchdogProcessService> mWatchdogProcessService;
     android::sp<IoPerfCollection> mIoPerfCollection;
 
diff --git a/watchdog/server/tests/IoPerfCollectionTest.cpp b/watchdog/server/tests/IoPerfCollectionTest.cpp
index 898447b..82287bf 100644
--- a/watchdog/server/tests/IoPerfCollectionTest.cpp
+++ b/watchdog/server/tests/IoPerfCollectionTest.cpp
@@ -826,6 +826,169 @@
     collector->terminate();
 }
 
+TEST(IoPerfCollectionTest, TestCustomCollectionFiltersPackageNames) {
+    sp<UidIoStatsStub> uidIoStatsStub = new UidIoStatsStub(true);
+    sp<ProcStatStub> procStatStub = new ProcStatStub(true);
+    sp<ProcPidStatStub> procPidStatStub = new ProcPidStatStub(true);
+    sp<LooperStub> looperStub = new LooperStub();
+
+    sp<IoPerfCollection> collector = new IoPerfCollection();
+    collector->mUidIoStats = uidIoStatsStub;
+    collector->mProcStat = procStatStub;
+    collector->mProcPidStat = procPidStatStub;
+    collector->mHandlerLooper = looperStub;
+    // Filter by package name should ignore this limit.
+    collector->mTopNStatsPerCategory = 1;
+
+    auto ret = collector->start();
+    ASSERT_TRUE(ret) << ret.error().message();
+
+    // Dummy boot-time collection
+    uidIoStatsStub->push({});
+    procStatStub->push(ProcStatInfo{});
+    procPidStatStub->push({});
+    ret = looperStub->pollCache();
+    ASSERT_TRUE(ret) << ret.error().message();
+
+    // Dummy Periodic collection
+    ret = collector->onBootFinished();
+    ASSERT_TRUE(ret) << ret.error().message();
+    uidIoStatsStub->push({});
+    procStatStub->push(ProcStatInfo{});
+    procPidStatStub->push({});
+    ret = looperStub->pollCache();
+    ASSERT_TRUE(ret) << ret.error().message();
+
+    // Start custom Collection
+    Vector<String16> args;
+    args.push_back(String16(kStartCustomCollectionFlag));
+    args.push_back(String16(kIntervalFlag));
+    args.push_back(String16(std::to_string(kTestCustomInterval.count()).c_str()));
+    args.push_back(String16(kMaxDurationFlag));
+    args.push_back(String16(std::to_string(kTestCustomCollectionDuration.count()).c_str()));
+    args.push_back(String16(kFilterPackagesFlag));
+    args.push_back(String16("android.car.cts,system_server"));
+
+    ret = collector->dump(-1, args);
+    ASSERT_TRUE(ret.ok()) << ret.error().message();
+
+    // Custom collection
+    collector->mUidToPackageNameMapping[1009] = "android.car.cts";
+    collector->mUidToPackageNameMapping[2001] = "system_server";
+    collector->mUidToPackageNameMapping[3456] = "random_process";
+    uidIoStatsStub->push({
+            {1009, {.uid = 1009, .ios = {0, 14000, 0, 16000, 0, 100}}},
+            {2001, {.uid = 2001, .ios = {0, 3400, 0, 6700, 0, 200}}},
+            {3456, {.uid = 3456, .ios = {0, 4200, 0, 5600, 0, 300}}},
+    });
+    procStatStub->push(ProcStatInfo{
+            /*stats=*/{2900, 7900, 4900, 8900, /*ioWaitTime=*/5900, 6966, 7980, 0, 0, 2930},
+            /*runnableCnt=*/100,
+            /*ioBlockedCnt=*/57,
+    });
+    procPidStatStub->push({{.tgid = 100,
+                            .uid = 1009,
+                            .process = {.pid = 100,
+                                        .comm = "cts_test",
+                                        .state = "D",
+                                        .ppid = 1,
+                                        .majorFaults = 50900,
+                                        .numThreads = 2,
+                                        .startTime = 234},
+                            .threads = {{100,
+                                         {.pid = 100,
+                                          .comm = "cts_test",
+                                          .state = "D",
+                                          .ppid = 1,
+                                          .majorFaults = 50900,
+                                          .numThreads = 1,
+                                          .startTime = 234}},
+                                        {200,
+                                         {.pid = 200,
+                                          .comm = "cts_test_2",
+                                          .state = "D",
+                                          .ppid = 1,
+                                          .majorFaults = 0,
+                                          .numThreads = 1,
+                                          .startTime = 290}}}},
+                           {.tgid = 1000,
+                            .uid = 2001,
+                            .process = {.pid = 1000,
+                                        .comm = "system_server",
+                                        .state = "D",
+                                        .ppid = 1,
+                                        .majorFaults = 1234,
+                                        .numThreads = 1,
+                                        .startTime = 345},
+                            .threads = {{1000,
+                                         {.pid = 1000,
+                                          .comm = "system_server",
+                                          .state = "D",
+                                          .ppid = 1,
+                                          .majorFaults = 1234,
+                                          .numThreads = 1,
+                                          .startTime = 345}}}},
+                           {.tgid = 4000,
+                            .uid = 3456,
+                            .process = {.pid = 4000,
+                                        .comm = "random_process",
+                                        .state = "D",
+                                        .ppid = 1,
+                                        .majorFaults = 3456,
+                                        .numThreads = 1,
+                                        .startTime = 890},
+                            .threads = {{4000,
+                                         {.pid = 4000,
+                                          .comm = "random_process",
+                                          .state = "D",
+                                          .ppid = 1,
+                                          .majorFaults = 50900,
+                                          .numThreads = 1,
+                                          .startTime = 890}}}}});
+    IoPerfRecord expected = {
+            .uidIoPerfData = {.topNReads = {{.userId = 0,
+                                             .packageName = "android.car.cts",
+                                             .bytes = {0, 14000},
+                                             .fsync{0, 100}},
+                                            {.userId = 0,
+                                             .packageName = "system_server",
+                                             .bytes = {0, 3400},
+                                             .fsync{0, 200}}},
+                              .topNWrites = {{.userId = 0,
+                                              .packageName = "android.car.cts",
+                                              .bytes = {0, 16000},
+                                              .fsync{0, 100}},
+                                             {.userId = 0,
+                                              .packageName = "system_server",
+                                              .bytes = {0, 6700},
+                                              .fsync{0, 200}}},
+                              .total = {{0, 21600}, {0, 28300}, {0, 600}}},
+            .systemIoPerfData = {.cpuIoWaitTime = 5900,
+                                 .totalCpuTime = 48376,
+                                 .ioBlockedProcessesCnt = 57,
+                                 .totalProcessesCnt = 157},
+            .processIoPerfData =
+                    {.topNIoBlockedUids = {{0, "android.car.cts", 2, {{"cts_test", 2}}},
+                                           {0, "system_server", 1, {{"system_server", 1}}}},
+                     .topNIoBlockedUidsTotalTaskCnt = {2, 1},
+                     .topNMajorFaultUids = {{0, "android.car.cts", 50900, {{"cts_test", 50900}}},
+                                            {0, "system_server", 1234, {{"system_server", 1234}}}},
+                     .totalMajorFaults = 55590,
+                     .majorFaultsPercentChange = 0},
+    };
+    ret = looperStub->pollCache();
+    ASSERT_TRUE(ret) << ret.error().message();
+    ASSERT_EQ(looperStub->numSecondsElapsed(), 0) << "Custom collection didn't start immediately";
+
+    ASSERT_EQ(collector->mCurrCollectionEvent, CollectionEvent::CUSTOM);
+    ASSERT_EQ(collector->mCustomCollection.records.size(), 1);
+    ASSERT_TRUE(isEqual(collector->mCustomCollection.records[0], expected))
+            << "Custom collection record doesn't match.\nExpected:\n"
+            << toString(expected) << "\nActual:\n"
+            << toString(collector->mCustomCollection.records[0]);
+    collector->terminate();
+}
+
 TEST(IoPerfCollectionTest, TestCustomCollectionTerminatesAfterMaxDuration) {
     sp<UidIoStatsStub> uidIoStatsStub = new UidIoStatsStub(true);
     sp<ProcStatStub> procStatStub = new ProcStatStub(true);
@@ -885,9 +1048,9 @@
 
     ASSERT_EQ(collector->mCurrCollectionEvent, CollectionEvent::CUSTOM);
     ASSERT_GT(collector->mCustomCollection.records.size(), 0);
-    // Next looper message was injected during startCustomCollectionLocked to end the custom
-    // collection after |kTestCustomCollectionDuration|. Thus on processing this message
-    // the custom collection should terminate.
+    // Next looper message was injected during startCustomCollection to end the custom collection
+    // after |kTestCustomCollectionDuration|. Thus on processing this message the custom collection
+    // should terminate.
     ret = looperStub->pollCache();
     ASSERT_TRUE(ret) << ret.error().message();
     ASSERT_EQ(looperStub->numSecondsElapsed(),
@@ -955,7 +1118,7 @@
     ASSERT_TRUE(collector.mUidIoStats->enabled()) << "Temporary file is inaccessible";
 
     struct UidIoPerfData actualUidIoPerfData = {};
-    auto ret = collector.collectUidIoPerfDataLocked(&actualUidIoPerfData);
+    auto ret = collector.collectUidIoPerfDataLocked(CollectionInfo{}, &actualUidIoPerfData);
     ASSERT_RESULT_OK(ret);
     EXPECT_TRUE(isEqual(expectedUidIoPerfData, actualUidIoPerfData))
         << "First snapshot doesn't match.\nExpected:\n"
@@ -1005,7 +1168,7 @@
     });
     ASSERT_TRUE(WriteStringToFile(secondSnapshot, tf.path));
     actualUidIoPerfData = {};
-    ret = collector.collectUidIoPerfDataLocked(&actualUidIoPerfData);
+    ret = collector.collectUidIoPerfDataLocked(CollectionInfo{}, &actualUidIoPerfData);
     ASSERT_RESULT_OK(ret);
     EXPECT_TRUE(isEqual(expectedUidIoPerfData, actualUidIoPerfData))
         << "Second snapshot doesn't match.\nExpected:\n"
@@ -1050,7 +1213,7 @@
     ASSERT_TRUE(collector.mUidIoStats->enabled()) << "Temporary file is inaccessible";
 
     struct UidIoPerfData actualUidIoPerfData = {};
-    const auto& ret = collector.collectUidIoPerfDataLocked(&actualUidIoPerfData);
+    const auto& ret = collector.collectUidIoPerfDataLocked(CollectionInfo{}, &actualUidIoPerfData);
     ASSERT_RESULT_OK(ret);
     EXPECT_TRUE(isEqual(expectedUidIoPerfData, actualUidIoPerfData))
         << "Collected data doesn't match.\nExpected:\n"
@@ -1212,7 +1375,7 @@
             << "Files under the temporary proc directory are inaccessible";
 
     struct ProcessIoPerfData actualProcessIoPerfData = {};
-    ret = collector.collectProcessIoPerfDataLocked(&actualProcessIoPerfData);
+    ret = collector.collectProcessIoPerfDataLocked(CollectionInfo{}, &actualProcessIoPerfData);
     ASSERT_TRUE(ret) << "Failed to collect first snapshot: " << ret.error();
     EXPECT_TRUE(isEqual(expectedProcessIoPerfData, actualProcessIoPerfData))
             << "First snapshot doesn't match.\nExpected:\n"
@@ -1272,7 +1435,7 @@
     collector.mProcPidStat->mPath = secondSnapshot.path;
 
     actualProcessIoPerfData = {};
-    ret = collector.collectProcessIoPerfDataLocked(&actualProcessIoPerfData);
+    ret = collector.collectProcessIoPerfDataLocked(CollectionInfo{}, &actualProcessIoPerfData);
     ASSERT_TRUE(ret) << "Failed to collect second snapshot: " << ret.error();
     EXPECT_TRUE(isEqual(expectedProcessIoPerfData, actualProcessIoPerfData))
             << "Second snapshot doesn't match.\nExpected:\n"
@@ -1315,7 +1478,7 @@
     collector.mTopNStatsPerSubcategory = 3;
     collector.mProcPidStat = new ProcPidStat(prodDir.path);
     struct ProcessIoPerfData actualProcessIoPerfData = {};
-    ret = collector.collectProcessIoPerfDataLocked(&actualProcessIoPerfData);
+    ret = collector.collectProcessIoPerfDataLocked(CollectionInfo{}, &actualProcessIoPerfData);
     ASSERT_TRUE(ret) << "Failed to collect proc pid contents: " << ret.error();
     EXPECT_TRUE(isEqual(expectedProcessIoPerfData, actualProcessIoPerfData))
             << "proc pid contents don't match.\nExpected:\n"
diff --git a/watchdog/server/tests/LooperStub.cpp b/watchdog/server/tests/LooperStub.cpp
index a8fc4f9..1b7aa82 100644
--- a/watchdog/server/tests/LooperStub.cpp
+++ b/watchdog/server/tests/LooperStub.cpp
@@ -32,7 +32,7 @@
 using android::base::Result;
 
 const std::chrono::milliseconds kLooperPollTimeout = 10ms;
-const std::chrono::milliseconds kStubPollCheckTimeout = 100ms;
+const std::chrono::milliseconds kStubPollCheckTimeout = 200ms;
 
 int LooperStub::pollAll(int /*timeoutMillis*/) {
     {