Merge "Improved test coverage for GarageModeService" into sc-dev
diff --git a/car-internal-lib/Android.bp b/car-internal-lib/Android.bp
index 9d6aef7..2937bd1 100644
--- a/car-internal-lib/Android.bp
+++ b/car-internal-lib/Android.bp
@@ -28,5 +28,6 @@
         "src/com/android/car/internal/SystemConstants.java",
         "src/com/android/car/internal/ICarServiceHelper.aidl",
         "src/com/android/car/internal/ICarSystemServerClient.aidl",
+        "src/com/android/car/internal/testing/ExcludeFromCodeCoverageGeneratedReport.java",
     ],
 }
diff --git a/car-internal-lib/src/com/android/car/internal/SystemConstants.java b/car-internal-lib/src/com/android/car/internal/SystemConstants.java
index 0025ea7..88356f1 100644
--- a/car-internal-lib/src/com/android/car/internal/SystemConstants.java
+++ b/car-internal-lib/src/com/android/car/internal/SystemConstants.java
@@ -15,6 +15,10 @@
  */
 package com.android.car.internal;
 
+import static com.android.car.internal.testing.ExcludeFromCodeCoverageGeneratedReport.BOILERPLATE_CODE;
+
+import com.android.car.internal.testing.ExcludeFromCodeCoverageGeneratedReport;
+
 /**
  * Provides common constants for CarService, CarServiceHelperService and other packages.
  */
@@ -22,6 +26,8 @@
 
     public static final String ICAR_SYSTEM_SERVER_CLIENT = "ICarSystemServerClient";
 
+    @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE,
+            details = "private constructor")
     private SystemConstants() {
         throw new UnsupportedOperationException("contains only static constants");
     }
diff --git a/car-internal-lib/src/com/android/car/internal/testing/ExcludeFromCodeCoverageGeneratedReport.java b/car-internal-lib/src/com/android/car/internal/testing/ExcludeFromCodeCoverageGeneratedReport.java
new file mode 100644
index 0000000..b13d295
--- /dev/null
+++ b/car-internal-lib/src/com/android/car/internal/testing/ExcludeFromCodeCoverageGeneratedReport.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2021 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.internal.testing;
+
+import android.annotation.IntDef;
+
+import com.android.car.internal.testing.ExcludeFromCodeCoverageGeneratedReport.Reason;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+// NOTE: it's copied from car-lib but using a different package
+/**
+ * Annotation used to mark code to be excluded from coverage report.
+ */
+@Target({ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.METHOD})
+public @interface ExcludeFromCodeCoverageGeneratedReport {
+
+    // Reason annotation and its associated constant values
+    int DEPRECATED_CODE = 0;
+    int BOILERPLATE_CODE = 1;
+    int DUMP_INFO = 2;
+    int DEBUGGING_CODE = 3;
+
+    @IntDef(prefix = "REASON_", value = {
+            DEPRECATED_CODE,
+            BOILERPLATE_CODE,
+            DUMP_INFO,
+            DEBUGGING_CODE
+    })
+    @interface Reason { }
+
+    /**
+     * The reason explaining why the code is being excluded from the code coverage report.
+     * <p>
+     * Possible reasons to exclude code from coverage report are:
+     * <p><ul>
+     * <li>{@link ExcludeFromCodeCoverageGeneratedReport#DEPRECATED_CODE} to exclude deprecated
+     * code from coverage report
+     * <li>{@link ExcludeFromCodeCoverageGeneratedReport#BOILERPLATE_CODE} to exclude boilerplate
+     * code like {@link java.lang.Object} methods, {@link android.os.Parcel} methods, etc
+     * <li>{@link ExcludeFromCodeCoverageGeneratedReport#DUMP_INFO} to exclude dump info methods
+     * <li>{@link ExcludeFromCodeCoverageGeneratedReport#DEBUGGING_CODE} to exclude debugging
+     * purpose
+     * code
+     * </ul><p>
+     */
+    @Reason int reason();
+
+    /**
+     * Optional field used to provide extra details about the excluded code (e.g. it can be used to
+     * tag a follow up bug).
+     */
+    String details() default "";
+}
diff --git a/car-lib/Android.bp b/car-lib/Android.bp
index 9c11299..84bbc0d 100644
--- a/car-lib/Android.bp
+++ b/car-lib/Android.bp
@@ -86,7 +86,8 @@
     srcs: [
         "src/com/android/car/internal/common/**/*.java",
         "src/com/android/car/internal/common/EventLogTags.logtags",
-    ]
+        "src/com/android/car/internal/ExcludeFromCodeCoverageGeneratedReport.java",
+    ],
 }
 
 java_library {
diff --git a/car-lib/src/android/car/VehiclePropertyIds.java b/car-lib/src/android/car/VehiclePropertyIds.java
index 94162b7..2e83510 100644
--- a/car-lib/src/android/car/VehiclePropertyIds.java
+++ b/car-lib/src/android/car/VehiclePropertyIds.java
@@ -377,7 +377,12 @@
     public static final int CURRENT_GEAR = 289408001;
     /**
      * Parking brake state.
-     * Requires permission: {@link Car#PERMISSION_POWERTRAIN}.
+     *
+     * <p>PARKING_BRAKE_ON property is {@link VehicleAreaType#VEHICLE_AREA_TYPE_GLOBAL}, {@link
+     * CarPropertyConfig#VEHICLE_PROPERTY_ACCESS_READ}, {@link
+     * CarPropertyConfig#VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE}, and returns a Boolean type value.
+     *
+     * <p>Requires permission: {@link Car#PERMISSION_POWERTRAIN}.
      */
     @RequiresPermission(Car.PERMISSION_POWERTRAIN)
     public static final int PARKING_BRAKE_ON = 287310850;
@@ -394,8 +399,16 @@
     @RequiresPermission(Car.PERMISSION_ENERGY)
     public static final int FUEL_LEVEL_LOW = 287310853;
     /**
-     * Night mode
-     * Requires permission: {@link Car#PERMISSION_EXTERIOR_ENVIRONMENT}.
+     * Night mode.
+     *
+     * <p>NIGHT_MODE property is {@link VehicleAreaType#VEHICLE_AREA_TYPE_GLOBAL}, {@link
+     * CarPropertyConfig#VEHICLE_PROPERTY_ACCESS_READ}, {@link
+     * CarPropertyConfig#VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE}, and returns a Boolean type value.
+     *
+     * <p>True indicates that the night mode sensor has detected that the car cabin environment has
+     * low light.
+     *
+     * <p>Requires permission: {@link Car#PERMISSION_EXTERIOR_ENVIRONMENT}.
      */
     @RequiresPermission(Car.PERMISSION_EXTERIOR_ENVIRONMENT)
     public static final int NIGHT_MODE = 287310855;
diff --git a/car-lib/src/android/car/input/CustomInputEvent.java b/car-lib/src/android/car/input/CustomInputEvent.java
index fd5412c..c82fb25 100644
--- a/car-lib/src/android/car/input/CustomInputEvent.java
+++ b/car-lib/src/android/car/input/CustomInputEvent.java
@@ -16,11 +16,14 @@
 
 package android.car.input;
 
+import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.BOILERPLATE_CODE;
+
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
 import com.android.internal.util.DataClass;
 
 /**
@@ -177,6 +180,7 @@
 
     @Override
     @DataClass.Generated.Member
+    @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE)
     public String toString() {
         // You can override field toString logic by defining methods like:
         // String fieldNameToString() { ... }
@@ -190,6 +194,7 @@
 
     @Override
     @DataClass.Generated.Member
+    @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE)
     public boolean equals(@android.annotation.Nullable Object o) {
         // You can override field equality logic by defining either of the methods like:
         // boolean fieldNameEquals(CustomInputEvent other) { ... }
@@ -208,6 +213,7 @@
 
     @Override
     @DataClass.Generated.Member
+    @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE)
     public int hashCode() {
         // You can override field hashCode logic by defining methods like:
         // int fieldNameHashCode() { ... }
@@ -232,6 +238,7 @@
 
     @Override
     @DataClass.Generated.Member
+    @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE)
     public int describeContents() {
         return 0;
     }
@@ -311,6 +318,7 @@
                     + ".Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true,"
                     + " genAidl=true)")
     @Deprecated
+    @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE)
     private void __metadata() {
     }
 
diff --git a/car-lib/src/android/car/input/RotaryEvent.java b/car-lib/src/android/car/input/RotaryEvent.java
index 565f039..27f2eee 100644
--- a/car-lib/src/android/car/input/RotaryEvent.java
+++ b/car-lib/src/android/car/input/RotaryEvent.java
@@ -15,11 +15,14 @@
  */
 package android.car.input;
 
+import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.BOILERPLATE_CODE;
+
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
 import com.android.internal.util.DataClass;
 
 import java.util.Arrays;
@@ -80,6 +83,7 @@
     }
 
     @Override
+    @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE)
     public String toString() {
         return new StringBuilder(128)
                 .append("RotaryEvent{")
@@ -170,6 +174,7 @@
 
     @Override
     @DataClass.Generated.Member
+    @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE)
     public boolean equals(@android.annotation.Nullable Object o) {
         // You can override field equality logic by defining either of the methods like:
         // boolean fieldNameEquals(RotaryEvent other) { ... }
@@ -188,6 +193,7 @@
 
     @Override
     @DataClass.Generated.Member
+    @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE)
     public int hashCode() {
         // You can override field hashCode logic by defining methods like:
         // int fieldNameHashCode() { ... }
@@ -214,6 +220,7 @@
 
     @Override
     @DataClass.Generated.Member
+    @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE)
     public int describeContents() { return 0; }
 
     /** @hide */
@@ -259,6 +266,7 @@
             sourceFile = "packages/services/Car/car-lib/src/android/car/input/RotaryEvent.java",
             inputSignatures = "private final @android.car.input.CarInputManager.InputTypeEnum int mInputType\nprivate final  boolean mClockwise\nprivate final @android.annotation.NonNull long[] mUptimeMillisForClicks\npublic  int getNumberOfClicks()\npublic  long getUptimeMillisForClick(int)\npublic @java.lang.Override java.lang.String toString()\nclass RotaryEvent extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true)")
     @Deprecated
+    @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE)
     private void __metadata() {}
 
 
diff --git a/car-lib/src/android/car/watchdog/IoOveruseAlertThreshold.java b/car-lib/src/android/car/watchdog/IoOveruseAlertThreshold.java
index 153e811..a9886ac 100644
--- a/car-lib/src/android/car/watchdog/IoOveruseAlertThreshold.java
+++ b/car-lib/src/android/car/watchdog/IoOveruseAlertThreshold.java
@@ -16,11 +16,14 @@
 
 package android.car.watchdog;
 
+import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.BOILERPLATE_CODE;
+
 import android.annotation.NonNull;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.os.Parcelable;
 
+import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
 import com.android.internal.util.DataClass;
 
 /**
@@ -166,6 +169,7 @@
             sourceFile = "packages/services/Car/car-lib/src/android/car/watchdog/IoOveruseAlertThreshold.java",
             inputSignatures = "private @android.annotation.SuppressLint long mDurationInSeconds\nprivate  long mWrittenBytesPerSecond\nclass IoOveruseAlertThreshold extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true)")
     @Deprecated
+    @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE)
     private void __metadata() {}
 
 
diff --git a/car-lib/src/android/car/watchdog/IoOveruseConfiguration.java b/car-lib/src/android/car/watchdog/IoOveruseConfiguration.java
index 7dec00c..608c3c3 100644
--- a/car-lib/src/android/car/watchdog/IoOveruseConfiguration.java
+++ b/car-lib/src/android/car/watchdog/IoOveruseConfiguration.java
@@ -16,10 +16,13 @@
 
 package android.car.watchdog;
 
+import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.BOILERPLATE_CODE;
+
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.os.Parcelable;
 
+import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
 import com.android.internal.util.DataClass;
 
 import java.util.List;
@@ -414,6 +417,7 @@
             sourceFile = "packages/services/Car/car-lib/src/android/car/watchdog/IoOveruseConfiguration.java",
             inputSignatures = "private @android.annotation.NonNull android.car.watchdog.PerStateBytes mComponentLevelThresholds\nprivate @android.annotation.NonNull java.util.Map<java.lang.String,android.car.watchdog.PerStateBytes> mPackageSpecificThresholds\nprivate @android.annotation.NonNull java.util.Map<java.lang.String,android.car.watchdog.PerStateBytes> mAppCategorySpecificThresholds\nprivate @android.annotation.NonNull java.util.List<android.car.watchdog.IoOveruseAlertThreshold> mSystemWideThresholds\nclass IoOveruseConfiguration extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genBuilder=true, genHiddenConstDefs=true)")
     @Deprecated
+    @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE)
     private void __metadata() {}
 
 
diff --git a/car-lib/src/android/car/watchdog/IoOveruseStats.java b/car-lib/src/android/car/watchdog/IoOveruseStats.java
index acb30db..4b5c480 100644
--- a/car-lib/src/android/car/watchdog/IoOveruseStats.java
+++ b/car-lib/src/android/car/watchdog/IoOveruseStats.java
@@ -16,9 +16,12 @@
 
 package android.car.watchdog;
 
+import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.BOILERPLATE_CODE;
+
 import android.annotation.NonNull;
 import android.os.Parcelable;
 
+import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
 import com.android.internal.util.DataClass;
 
 /**
@@ -414,6 +417,7 @@
             sourceFile = "packages/services/Car/car-lib/src/android/car/watchdog/IoOveruseStats.java",
             inputSignatures = "private  long mStartTime\nprivate  long mDurationInSeconds\nprivate  long mTotalOveruses\nprivate  long mTotalTimesKilled\nprivate  long mTotalBytesWritten\nprivate  boolean mKillableOnOveruse\nprivate @android.annotation.NonNull android.car.watchdog.PerStateBytes mRemainingWriteBytes\nclass IoOveruseStats extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenBuilder=true)")
     @Deprecated
+    @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE)
     private void __metadata() {}
 
 
diff --git a/car-lib/src/android/car/watchdog/PackageKillableState.java b/car-lib/src/android/car/watchdog/PackageKillableState.java
index 53eff70..cd9cfda 100644
--- a/car-lib/src/android/car/watchdog/PackageKillableState.java
+++ b/car-lib/src/android/car/watchdog/PackageKillableState.java
@@ -16,11 +16,14 @@
 
 package android.car.watchdog;
 
+import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.BOILERPLATE_CODE;
+
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.annotation.UserIdInt;
 import android.os.Parcelable;
 
+import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
 import com.android.internal.util.DataClass;
 
 /**
@@ -243,6 +246,7 @@
             sourceFile = "packages/services/Car/car-lib/src/android/car/watchdog/PackageKillableState.java",
             inputSignatures = "public static final  int KILLABLE_STATE_YES\npublic static final  int KILLABLE_STATE_NO\npublic static final  int KILLABLE_STATE_NEVER\nprivate @android.annotation.NonNull java.lang.String mPackageName\nprivate @android.annotation.UserIdInt int mUserId\nprivate @android.car.watchdog.PackageKillableState.KillableState int mKillableState\nclass PackageKillableState extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)")
     @Deprecated
+    @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE)
     private void __metadata() {}
 
 
diff --git a/car-lib/src/android/car/watchdog/PerStateBytes.java b/car-lib/src/android/car/watchdog/PerStateBytes.java
index 421f607..9343f93 100644
--- a/car-lib/src/android/car/watchdog/PerStateBytes.java
+++ b/car-lib/src/android/car/watchdog/PerStateBytes.java
@@ -16,8 +16,11 @@
 
 package android.car.watchdog;
 
+import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.BOILERPLATE_CODE;
+
 import android.os.Parcelable;
 
+import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
 import com.android.internal.util.DataClass;
 
 /**
@@ -167,6 +170,7 @@
             sourceFile = "packages/services/Car/car-lib/src/android/car/watchdog/PerStateBytes.java",
             inputSignatures = "private  long mForegroundModeBytes\nprivate  long mBackgroundModeBytes\nprivate  long mGarageModeBytes\nclass PerStateBytes extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true)")
     @Deprecated
+    @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE)
     private void __metadata() {}
 
 
diff --git a/car-lib/src/android/car/watchdog/ResourceOveruseConfiguration.java b/car-lib/src/android/car/watchdog/ResourceOveruseConfiguration.java
index b179590..03a972a 100644
--- a/car-lib/src/android/car/watchdog/ResourceOveruseConfiguration.java
+++ b/car-lib/src/android/car/watchdog/ResourceOveruseConfiguration.java
@@ -16,6 +16,8 @@
 
 package android.car.watchdog;
 
+import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.BOILERPLATE_CODE;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -23,6 +25,7 @@
 import android.annotation.SystemApi;
 import android.os.Parcelable;
 
+import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
 import com.android.internal.util.DataClass;
 
 import java.lang.annotation.Retention;
@@ -525,6 +528,7 @@
             sourceFile = "packages/services/Car/car-lib/src/android/car/watchdog/ResourceOveruseConfiguration.java",
             inputSignatures = "public static final  int COMPONENT_TYPE_SYSTEM\npublic static final  int COMPONENT_TYPE_VENDOR\npublic static final  int COMPONENT_TYPE_THIRD_PARTY\npublic static final  java.lang.String APPLICATION_CATEGORY_TYPE_MAPS\npublic static final  java.lang.String APPLICATION_CATEGORY_TYPE_MEDIA\nprivate @android.car.watchdog.ResourceOveruseConfiguration.ComponentType int mComponentType\nprivate @android.annotation.NonNull java.util.List<java.lang.String> mSafeToKillPackages\nprivate @android.annotation.NonNull java.util.List<java.lang.String> mVendorPackagePrefixes\nprivate @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.String> mPackagesToAppCategoryTypes\nprivate @android.annotation.Nullable android.car.watchdog.IoOveruseConfiguration mIoOveruseConfiguration\nclass ResourceOveruseConfiguration extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genBuilder=true, genHiddenConstDefs=true)")
     @Deprecated
+    @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE)
     private void __metadata() {}
 
 
diff --git a/car-lib/src/android/car/watchdog/ResourceOveruseStats.java b/car-lib/src/android/car/watchdog/ResourceOveruseStats.java
index c57acfd..992cff2 100644
--- a/car-lib/src/android/car/watchdog/ResourceOveruseStats.java
+++ b/car-lib/src/android/car/watchdog/ResourceOveruseStats.java
@@ -16,11 +16,14 @@
 
 package android.car.watchdog;
 
+import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.BOILERPLATE_CODE;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Parcelable;
 import android.os.UserHandle;
 
+import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
 import com.android.internal.util.DataClass;
 
 /**
@@ -256,6 +259,7 @@
             sourceFile = "packages/services/Car/car-lib/src/android/car/watchdog/ResourceOveruseStats.java",
             inputSignatures = "private @android.annotation.NonNull java.lang.String mPackageName\nprivate @android.annotation.NonNull android.os.UserHandle mUserHandle\nprivate @android.annotation.Nullable android.car.watchdog.IoOveruseStats mIoOveruseStats\nclass ResourceOveruseStats extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenBuilder=true)")
     @Deprecated
+    @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE)
     private void __metadata() {}
 
 
diff --git a/car-lib/src/com/android/car/internal/common/CommonConstants.java b/car-lib/src/com/android/car/internal/common/CommonConstants.java
index 82b158a..d4d728e 100644
--- a/car-lib/src/com/android/car/internal/common/CommonConstants.java
+++ b/car-lib/src/com/android/car/internal/common/CommonConstants.java
@@ -16,8 +16,12 @@
 
 package com.android.car.internal.common;
 
+import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.BOILERPLATE_CODE;
+
 import android.annotation.IntDef;
 
+import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -28,6 +32,7 @@
  */
 public final class CommonConstants {
 
+    @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE)
     private CommonConstants() {
         throw new UnsupportedOperationException("contains only static constants");
     }
diff --git a/car-lib/src/com/android/car/internal/common/UserHelperLite.java b/car-lib/src/com/android/car/internal/common/UserHelperLite.java
index 4b43e06..c4b8516 100644
--- a/car-lib/src/com/android/car/internal/common/UserHelperLite.java
+++ b/car-lib/src/com/android/car/internal/common/UserHelperLite.java
@@ -16,11 +16,15 @@
 
 package com.android.car.internal.common;
 
+import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.BOILERPLATE_CODE;
+
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.os.UserHandle;
 import android.os.UserManager;
 
+import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
+
 /**
  * Provides user information related helper methods.
  *
@@ -28,6 +32,8 @@
  */
 public final class UserHelperLite {
 
+    @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE,
+            details = "private constructor")
     private UserHelperLite() {
         throw new UnsupportedOperationException("contains only static methods");
     }
diff --git a/car-test-lib/src/android/car/test/mocks/CarArgumentMatchers.java b/car-test-lib/src/android/car/test/mocks/CarArgumentMatchers.java
index 1d1c399..f3f0475 100644
--- a/car-test-lib/src/android/car/test/mocks/CarArgumentMatchers.java
+++ b/car-test-lib/src/android/car/test/mocks/CarArgumentMatchers.java
@@ -19,6 +19,7 @@
 
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.content.Intent;
 import android.content.pm.UserInfo;
 import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
 import android.os.UserHandle;
@@ -27,6 +28,7 @@
 import org.mockito.ArgumentMatcher;
 
 import java.util.Arrays;
+import java.util.Objects;
 
 /**
  * Provides custom mockito matcher for Android / Car objects.
@@ -51,6 +53,13 @@
     }
 
     /**
+     * Matches an {@link Intent} for the given action and package name.
+     */
+    public static Intent intentFor(String action, String packageName) {
+        return argThat(new IntentMatcher(action, packageName));
+    }
+
+    /**
      * Matches if a {@link VehiclePropValue} has the given {@code prop}.
      */
     public static VehiclePropValue isProperty(int prop) {
@@ -165,6 +174,30 @@
         }
     }
 
+    private static final class IntentMatcher implements ArgumentMatcher<Intent> {
+
+        private final String mAction;
+        private final String mPackageName;
+
+        private IntentMatcher(String action, String packageName) {
+            mAction = Objects.requireNonNull(action, "action cannot be null");
+            mPackageName = Objects.requireNonNull(packageName, "packageName cannot be null");
+        }
+
+        @Override
+        public boolean matches(Intent argument) {
+            Log.v(TAG, "IntentMatcher: argument=" + argument + ", this=" + this);
+
+            return argument != null && mAction.equals(argument.getAction())
+                    && mPackageName.equals(argument.getPackage());
+        }
+
+        @Override
+        public String toString() {
+            return "intentFor(action=" + mAction + ", pkg=" + mPackageName + ')';
+        }
+    }
+
     private CarArgumentMatchers() {
         throw new UnsupportedOperationException("contains only static methods");
     }
diff --git a/car_product/build/preinstalled-packages-product-car-base.xml b/car_product/build/preinstalled-packages-product-car-base.xml
index 6745012..8b75bcb 100644
--- a/car_product/build/preinstalled-packages-product-car-base.xml
+++ b/car_product/build/preinstalled-packages-product-car-base.xml
@@ -307,18 +307,11 @@
     <install-in-user-type package="com.android.certinstaller">
         <install-in user-type="FULL" />
     </install-in-user-type>
-    <!-- TODO(b/189246976) STOPSHIP Remove for SYSTEM user since this package
-    is needed for SYSTEM user only if the device supports feature_device_admin
-    and it's used by some device owner APIs-->
     <install-in-user-type package="com.android.pacprocessor">
         <install-in user-type="FULL" />
-        <install-in user-type="SYSTEM" />
     </install-in-user-type>
-    <!-- TODO(b/189246976) STOPSHIP Remove for SYSTEM user same as previous
-    package -->
     <install-in-user-type package="com.android.proxyhandler">
         <install-in user-type="FULL" />
-        <install-in user-type="SYSTEM" />
     </install-in-user-type>
     <install-in-user-type package="com.android.vpndialogs">
         <install-in user-type="FULL" />
diff --git a/car_product/overlay/frameworks/base/core/res/res/values/config.xml b/car_product/overlay/frameworks/base/core/res/res/values/config.xml
index f675d4e..d7c566c 100644
--- a/car_product/overlay/frameworks/base/core/res/res/values/config.xml
+++ b/car_product/overlay/frameworks/base/core/res/res/values/config.xml
@@ -22,6 +22,8 @@
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Enable multi-user. -->
     <bool name="config_enableMultiUserUI">true</bool>
+    <!--  Maximum number of supported users -->
+    <integer name="config_multiuserMaximumUsers">4</integer>
     <!-- Maximum number of users we allow to be running at a time.
          For automotive, background user will be immediately stopped upon user switching but
          up to this many users can be running in garage mode.
diff --git a/cpp/evs/sampleDriver/bufferCopy.cpp b/cpp/evs/sampleDriver/bufferCopy.cpp
index d9578e6..098af61 100644
--- a/cpp/evs/sampleDriver/bufferCopy.cpp
+++ b/cpp/evs/sampleDriver/bufferCopy.cpp
@@ -123,14 +123,27 @@
 void fillRGBAFromYUYV(const BufferDesc& tgtBuff, uint8_t* tgt, void* imgData, unsigned imgStride) {
     const AHardwareBuffer_Desc* pDesc =
         reinterpret_cast<const AHardwareBuffer_Desc*>(&tgtBuff.buffer.description);
+    // Converts YUY2ToARGB (little endian).  Please note that libyuv uses the
+    // little endian while we're using the big endian in RGB format names.
+    const auto dstStrideInBytes = pDesc->stride * 4;  // 4-byte per pixel
     auto result = libyuv::YUY2ToARGB((const uint8_t*)imgData,
                                      imgStride,             // input stride in bytes
                                      tgt,
-                                     (pDesc->stride << 2),  // output stride in bytes
+                                     dstStrideInBytes,      // output stride in bytes
                                      pDesc->width,
                                      pDesc->height);
     if (result) {
-        LOG(ERROR) << "Failed to convert YUYV to ARGB.";
+        LOG(ERROR) << "Failed to convert YUYV to BGRA.";
+        return;
+    }
+
+    // Swaps R and B pixels to convert BGRA to RGBA in place.
+    // TODO(b/190783702): Consider allocating an extra space to store ARGB data
+    //                    temporarily if below operation is too slow.
+    result = libyuv::ABGRToARGB(tgt, dstStrideInBytes, tgt, dstStrideInBytes,
+                                pDesc->width, pDesc->height);
+    if (result) {
+        LOG(ERROR) << "Failed to convert BGRA to RGBA.";
     }
 }
 
diff --git a/cpp/powerpolicy/server/carpowerpolicyd.xml b/cpp/powerpolicy/server/carpowerpolicyd.xml
index e8df893..0d2462f 100644
--- a/cpp/powerpolicy/server/carpowerpolicyd.xml
+++ b/cpp/powerpolicy/server/carpowerpolicyd.xml
@@ -16,6 +16,7 @@
 <manifest version="1.0" type="framework">
     <hal format="aidl">
         <name>android.frameworks.automotive.powerpolicy</name>
+        <version>1</version>
         <interface>
             <name>ICarPowerPolicyServer</name>
             <instance>default</instance>
diff --git a/cpp/watchdog/server/carwatchdogd.xml b/cpp/watchdog/server/carwatchdogd.xml
index c0c8933..c7adc27 100644
--- a/cpp/watchdog/server/carwatchdogd.xml
+++ b/cpp/watchdog/server/carwatchdogd.xml
@@ -16,6 +16,7 @@
 <manifest version="1.0" type="framework">
     <hal format="aidl">
         <name>android.automotive.watchdog</name>
+        <version>3</version>
         <interface>
             <name>ICarWatchdog</name>
             <instance>default</instance>
diff --git a/packages/CarDeveloperOptions/tests/unit/src/com/android/car/developeroptions/CarDeveloperOptionsIntentTest.java b/packages/CarDeveloperOptions/tests/unit/src/com/android/car/developeroptions/CarDeveloperOptionsIntentTest.java
index 2520d60..066af56 100644
--- a/packages/CarDeveloperOptions/tests/unit/src/com/android/car/developeroptions/CarDeveloperOptionsIntentTest.java
+++ b/packages/CarDeveloperOptions/tests/unit/src/com/android/car/developeroptions/CarDeveloperOptionsIntentTest.java
@@ -16,7 +16,7 @@
 
 package com.android.car.developeroptions;
 
-import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import android.content.Context;
 import android.content.Intent;
@@ -30,6 +30,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
@@ -51,30 +52,50 @@
     public void testAvailableIntentActivities_onlyAllowlisted() {
         List<ResolveInfo> results = mPm.queryIntentActivities(createSettingsPackageIntent(),
                 PackageManager.MATCH_ALL);
+        List<String> nonMatchingItems = new ArrayList<>();
         for (ResolveInfo resolved : results) {
-            assertThat(ACTIVITY_ALLOWLIST).contains(resolved.getComponentInfo().name);
+            if (!ACTIVITY_ALLOWLIST.contains(resolved.getComponentInfo().name)) {
+                nonMatchingItems.add(resolved.getComponentInfo().name);
+            }
         }
+        assertWithMessage("Unexpected activities found: " + nonMatchingItems.toString())
+                .that(nonMatchingItems.size()).isEqualTo(0);
     }
 
     @Test
     public void testAvailableIntentServices_returnsZero() {
         List<ResolveInfo> results = mPm.queryIntentServices(createSettingsPackageIntent(),
                 PackageManager.MATCH_ALL);
-        assertThat(results.size()).isEqualTo(0);
+        List<String> nonMatchingItems = new ArrayList<>();
+        for (ResolveInfo resolved : results) {
+            nonMatchingItems.add(resolved.getComponentInfo().name);
+        }
+        assertWithMessage("Unexpected services found: " + nonMatchingItems.toString())
+                .that(nonMatchingItems.size()).isEqualTo(0);
     }
 
     @Test
     public void testAvailableBroadcastReceivers_returnsZero() {
         List<ResolveInfo> results = mPm.queryBroadcastReceivers(createSettingsPackageIntent(),
                 PackageManager.MATCH_ALL);
-        assertThat(results.size()).isEqualTo(0);
+        List<String> nonMatchingItems = new ArrayList<>();
+        for (ResolveInfo resolved : results) {
+            nonMatchingItems.add(resolved.getComponentInfo().name);
+        }
+        assertWithMessage("Unexpected broadcast receivers found: " + nonMatchingItems.toString())
+                .that(nonMatchingItems.size()).isEqualTo(0);
     }
 
     @Test
     public void testAvailableContentProviders_returnsZero() {
         List<ResolveInfo> results = mPm.queryIntentContentProviders(createSettingsPackageIntent(),
                 PackageManager.MATCH_ALL);
-        assertThat(results.size()).isEqualTo(0);
+        List<String> nonMatchingItems = new ArrayList<>();
+        for (ResolveInfo resolved : results) {
+            nonMatchingItems.add(resolved.getComponentInfo().name);
+        }
+        assertWithMessage("Unexpected content providers found: " + nonMatchingItems.toString())
+                .that(nonMatchingItems.size()).isEqualTo(0);
     }
 
     private Intent createSettingsPackageIntent() {
diff --git a/service/Android.bp b/service/Android.bp
index 1bc18eb..7d24ac6 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -106,6 +106,10 @@
         "src/com/android/car/Utils.java",
     ],
 
+    static_libs: [
+        "com.android.car.internal.common",
+    ],
+
     product_variables: {
             pdk: {
                 enabled: false,
diff --git a/service/AndroidManifest.xml b/service/AndroidManifest.xml
index 8f90480..d3724dd 100644
--- a/service/AndroidManifest.xml
+++ b/service/AndroidManifest.xml
@@ -859,10 +859,10 @@
          android:description="@string/car_permission_desc_set_car_vendor_category_10"/>
 
     <!-- Allows an application to receive Car input events.
-         <p>Protection level: signature
+         <p>Protection level: signature|privileged
     -->
     <permission android:name="android.car.permission.CAR_MONITOR_INPUT"
-                android:protectionLevel="signature"
+                android:protectionLevel="signature|privileged"
                 android:label="@string/car_permission_label_monitor_input"
                 android:description="@string/car_permission_desc_monitor_input"/>
 
diff --git a/service/jni/evs/EvsServiceContext.cpp b/service/jni/evs/EvsServiceContext.cpp
index f338eef..b0cc129 100644
--- a/service/jni/evs/EvsServiceContext.cpp
+++ b/service/jni/evs/EvsServiceContext.cpp
@@ -119,8 +119,13 @@
     }
 
     if (isCameraOpened()) {
-        LOG(DEBUG) << "Camera " << id << " is has opened already.";
-        return true;
+        if (!strcmp(id, mCameraIdInUse)) {
+            LOG(DEBUG) << "Camera " << id << " is has opened already.";
+            return true;
+        } else {
+            // Close a current camera device.
+            mService->closeCamera(mCamera);
+        }
     }
 
     sp<IEvsCamera> camera = IEvsCamera::castFrom(mService->openCamera(id));
@@ -140,6 +145,7 @@
         std::lock_guard<std::mutex> lock(mLock);
         mCamera = camera;
         mStreamHandler = streamHandler;
+        mCameraIdInUse = id;
     }
 
     return true;
diff --git a/service/jni/evs/EvsServiceContext.h b/service/jni/evs/EvsServiceContext.h
index d96eadb..84c0012 100644
--- a/service/jni/evs/EvsServiceContext.h
+++ b/service/jni/evs/EvsServiceContext.h
@@ -169,6 +169,9 @@
     // Bookkeeps descriptors of received frame buffers.
     std::map<int, hardware::automotive::evs::V1_1::BufferDesc> mBufferRecords GUARDED_BY(mLock);
 
+    // A name of the camera device currently in use.
+    const char* mCameraIdInUse;
+
     // Service name for EVS enumerator
     static const char* kServiceName;
 
diff --git a/service/res/values-de/strings.xml b/service/res/values-de/strings.xml
index 490e7c8..156265d 100644
--- a/service/res/values-de/strings.xml
+++ b/service/res/values-de/strings.xml
@@ -16,7 +16,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="car_permission_label" msgid="2215078736675564541">"Fahrzeuginformationen"</string>
+    <string name="car_permission_label" msgid="2215078736675564541">"Fahrzeug­informationen"</string>
     <string name="car_permission_desc" msgid="3584369074931334964">"Zugriff auf Informationen deines Autos"</string>
     <string name="car_permission_label_camera" msgid="3725702064841827180">"auf die Autokamera zuzugreifen"</string>
     <string name="car_permission_desc_camera" msgid="917024932164501426">"Auf Autokamera(s) zugreifen."</string>
diff --git a/service/res/values-es/strings.xml b/service/res/values-es/strings.xml
index 003d498..12ee40f 100644
--- a/service/res/values-es/strings.xml
+++ b/service/res/values-es/strings.xml
@@ -16,7 +16,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="car_permission_label" msgid="2215078736675564541">"Información sobre el coche"</string>
+    <string name="car_permission_label" msgid="2215078736675564541">"información sobre el coche"</string>
     <string name="car_permission_desc" msgid="3584369074931334964">"acceder a los datos de tu coche"</string>
     <string name="car_permission_label_camera" msgid="3725702064841827180">"acceder a la cámara del coche"</string>
     <string name="car_permission_desc_camera" msgid="917024932164501426">"Acceder a las cámaras del coche."</string>
diff --git a/service/res/values-it/strings.xml b/service/res/values-it/strings.xml
index 05b2c23..566a98a 100644
--- a/service/res/values-it/strings.xml
+++ b/service/res/values-it/strings.xml
@@ -17,7 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_permission_label" msgid="2215078736675564541">"Informazioni sull\'auto"</string>
-    <string name="car_permission_desc" msgid="3584369074931334964">"Accedi alle informazioni della tua auto"</string>
+    <string name="car_permission_desc" msgid="3584369074931334964">"accedere alle informazioni della tua auto"</string>
     <string name="car_permission_label_camera" msgid="3725702064841827180">"Accesso alla videocamera dell\'automobile"</string>
     <string name="car_permission_desc_camera" msgid="917024932164501426">"Consente di accedere alle videocamere dell\'automobile."</string>
     <string name="car_permission_label_energy" msgid="7409144323527821558">"Accesso alle informazioni sulla carica dell\'automobile"</string>
diff --git a/service/res/values-ky/strings.xml b/service/res/values-ky/strings.xml
index ede73eb..04afe22 100644
--- a/service/res/values-ky/strings.xml
+++ b/service/res/values-ky/strings.xml
@@ -100,7 +100,7 @@
     <string name="car_permission_label_request_evs_activity" msgid="3906551972883482883">"EVS\'ти алдын ала көрүү аракетин сурануу"</string>
     <string name="car_permission_desc_request_evs_activity" msgid="4582768053649138488">"Тутумдан EVS\'ти алдын ала көрүү аракетин иштетүүнү сурануу"</string>
     <string name="car_permission_label_control_evs_activity" msgid="2030069860204405679">"EVS\'ти алдын ала көрүү аракетин көзөмөлдөө"</string>
-    <string name="car_permission_desc_control_evs_activity" msgid="691646545916976346">"Тутумдун EVS\'ти алдын ала көрүү аракетин көзөмөлдөө"</string>
+    <string name="car_permission_desc_control_evs_activity" msgid="691646545916976346">"Системанын EVS\'ти алдын ала көрүү аракетин көзөмөлдөө"</string>
     <string name="car_permission_label_use_evs_camera" msgid="3607720208623955067">"EVS камерасын иштетүү"</string>
     <string name="car_permission_desc_use_evs_camera" msgid="1625845902221003985">"EVS камерасынын агымдарына жазылуу"</string>
     <string name="car_permission_label_monitor_evs_status" msgid="2091521314159379622">"EVS кызматынын статусун көзөмөлдөө"</string>
@@ -174,7 +174,7 @@
     <string name="importance_default" msgid="8587741629268312938">"Демейки маанилүүлүк"</string>
     <string name="importance_high" msgid="3141530792377745041">"Өтө маанилүү"</string>
     <string name="factory_reset_notification_title" msgid="2530056626309489398">"Баштапкы абалга кайтаруу талап кылынат"</string>
-    <string name="factory_reset_notification_text" msgid="6517642677900094724">"Инфозоок тутумундагы бардык маалымат өчүрүлөт. Баштапкы абалга келтирилгенден кийин жаңы профилди жөндөп алсаңыз болот."</string>
+    <string name="factory_reset_notification_text" msgid="6517642677900094724">"Инфозоок тутумундагы бардык нерселер өчүрүлөт. Баштапкы абалга келтирилгенден кийин жаңы профилди жөндөп алсаңыз болот."</string>
     <string name="factory_reset_notification_button" msgid="5450535366202106371">"Дагы"</string>
     <string name="factory_reset_parked_title" msgid="258340498079453871">"Инфозоок тутумун баштапкы абалга келтирүү"</string>
     <string name="factory_reset_parked_text" msgid="910347526834275166">"Тутум баштапкы абалга кайтаруу жана бардык маалыматты өчүрүү сурамын алды. Аны азыр аткарсаңыз болот же унаа кийинки жолу от алганда тутум башкапкы абалга кайтарылат. Андан кийин жаңы профилди жөндөп алсаңыз болот."</string>
diff --git a/service/src/com/android/car/CanBusErrorNotifier.java b/service/src/com/android/car/CanBusErrorNotifier.java
deleted file mode 100644
index e4770af..0000000
--- a/service/src/com/android/car/CanBusErrorNotifier.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.car;
-
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.content.Context;
-import android.os.Build;
-import android.util.Log;
-import android.util.Slog;
-
-import com.android.internal.annotations.GuardedBy;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Class used to notify user about CAN bus failure.
- */
-final class CanBusErrorNotifier {
-    private static final String TAG = CarLog.tagFor(CanBusErrorNotifier.class);
-    private static final int NOTIFICATION_ID = 1;
-    private static final boolean IS_RELEASE_BUILD = "user".equals(Build.TYPE);
-
-    private final Context mContext;
-    private final NotificationManager mNotificationManager;
-
-    // Contains a set of objects that reported failure. The notification will be hidden only when
-    // this set is empty (all reported objects are in love and peace with the vehicle).
-    @GuardedBy("this")
-    private final Set<Object> mReportedObjects = new HashSet<>();
-
-    CanBusErrorNotifier(Context context) {
-        mNotificationManager = (NotificationManager) context.getSystemService(
-                Context.NOTIFICATION_SERVICE);
-        mContext = context;
-    }
-
-    public void removeFailureReport(Object sender) {
-        setCanBusFailure(false, sender);
-    }
-
-    public void reportFailure(Object sender) {
-        setCanBusFailure(true, sender);
-    }
-
-    private void setCanBusFailure(boolean failed, Object sender) {
-        boolean shouldShowNotification;
-        synchronized (this) {
-            boolean changed = failed
-                    ? mReportedObjects.add(sender) : mReportedObjects.remove(sender);
-
-            if (!changed) {
-                return;
-            }
-
-            shouldShowNotification = !mReportedObjects.isEmpty();
-        }
-
-        if (Log.isLoggable(TAG, Log.INFO)) {
-            Slog.i(TAG, "Changing CAN bus failure state to " + shouldShowNotification);
-        }
-
-        if (shouldShowNotification) {
-            showNotification();
-        } else {
-            hideNotification();
-        }
-    }
-
-    private void showNotification() {
-        if (IS_RELEASE_BUILD) {
-            // TODO: for user, we should show message to take car to the dealer. bug:32096297
-            return;
-        }
-        Notification notification =
-                new Notification.Builder(mContext, NotificationChannel.DEFAULT_CHANNEL_ID)
-                        .setContentTitle(mContext.getString(R.string.car_can_bus_failure))
-                        .setContentText(mContext.getString(R.string.car_can_bus_failure_desc))
-                        .setSmallIcon(R.drawable.car_ic_error)
-                        .setOngoing(true)
-                        .build();
-        mNotificationManager.notify(TAG, NOTIFICATION_ID, notification);
-    }
-
-    private void hideNotification() {
-        if (IS_RELEASE_BUILD) {
-            // TODO: for user, we should show message to take car to the dealer. bug:32096297
-            return;
-        }
-        mNotificationManager.cancel(TAG, NOTIFICATION_ID);
-    }
-}
diff --git a/service/src/com/android/car/CarBugreportManagerService.java b/service/src/com/android/car/CarBugreportManagerService.java
index 2be403d..67eec21 100644
--- a/service/src/com/android/car/CarBugreportManagerService.java
+++ b/service/src/com/android/car/CarBugreportManagerService.java
@@ -42,6 +42,7 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.BufferedReader;
 import java.io.DataInputStream;
@@ -86,6 +87,7 @@
     private static final int SOCKET_CONNECTION_RETRY_DELAY_IN_MS = 5000;
 
     private final Context mContext;
+    private final boolean mIsUserBuild;
     private final Object mLock = new Object();
 
     private final HandlerThread mHandlerThread = CarServiceUtils.getHandlerThread(
@@ -100,7 +102,14 @@
      * @param context the context
      */
     public CarBugreportManagerService(Context context) {
+        // Per https://source.android.com/setup/develop/new-device, user builds are debuggable=0
+        this(context, !Build.IS_DEBUGGABLE);
+    }
+
+    @VisibleForTesting
+    CarBugreportManagerService(Context context, boolean isUserBuild) {
         mContext = context;
+        mIsUserBuild = isUserBuild;
     }
 
     @Override
@@ -186,8 +195,7 @@
 
     /** Checks only on user builds. */
     private void ensureTheCallerIsDesignatedBugReportApp() {
-        if (Build.IS_DEBUGGABLE) {
-            // Per https://source.android.com/setup/develop/new-device, user builds are debuggable=0
+        if (!mIsUserBuild) {
             return;
         }
         String defaultAppPkgName = mContext.getString(R.string.config_car_bugreport_application);
diff --git a/service/src/com/android/car/CarLog.java b/service/src/com/android/car/CarLog.java
index 23644a3..d629b50 100644
--- a/service/src/com/android/car/CarLog.java
+++ b/service/src/com/android/car/CarLog.java
@@ -16,6 +16,10 @@
 
 package com.android.car;
 
+import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.BOILERPLATE_CODE;
+
+import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
+
 /**
  * Helper class for class tags for CarService.
  */
@@ -49,4 +53,10 @@
         if (tag.matches(MATCHER)) return tag;
         return PREFIX + tag;
     }
+
+    @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE,
+            details = "private constructor")
+    private CarLog() {
+        throw new UnsupportedOperationException("contains only static methods");
+    }
 }
diff --git a/service/src/com/android/car/CarService.java b/service/src/com/android/car/CarService.java
index 6a90f9c..7aa2cca 100644
--- a/service/src/com/android/car/CarService.java
+++ b/service/src/com/android/car/CarService.java
@@ -54,28 +54,11 @@
 
     private static final boolean IS_USER_BUILD = "user".equals(Build.TYPE);
 
-    private CanBusErrorNotifier mCanBusErrorNotifier;
     private ICarImpl mICarImpl;
     private IVehicle mVehicle;
 
     private String mVehicleInterfaceName;
 
-    // If 10 crashes of Vehicle HAL occurred within 10 minutes then thrown an exception in
-    // Car Service.
-    private final CrashTracker mVhalCrashTracker = new CrashTracker(
-            10,  // Max crash count.
-            10 * 60 * 1000,  // 10 minutes - sliding time window.
-            () -> {
-                if (IS_USER_BUILD) {
-                    Slog.e(CarLog.TAG_SERVICE, "Vehicle HAL keeps crashing, notifying user...");
-                    mCanBusErrorNotifier.reportFailure(CarService.this);
-                } else {
-                    throw new RuntimeException(
-                            "Vehicle HAL crashed too many times in a given time frame");
-                }
-            }
-    );
-
     private final VehicleDeathRecipient mVehicleDeathRecipient = new VehicleDeathRecipient();
 
     @Override
@@ -84,8 +67,6 @@
                 Trace.TRACE_TAG_SYSTEM_SERVER, CAR_SERVICE_INIT_TIMING_MIN_DURATION_MS);
         initTiming.traceBegin("CarService.onCreate");
 
-        mCanBusErrorNotifier = new CanBusErrorNotifier(this /* context */);
-
         initTiming.traceBegin("getVehicle");
         mVehicle = getVehicle();
         initTiming.traceEnd();
@@ -107,7 +88,6 @@
         mICarImpl = new ICarImpl(this,
                 mVehicle,
                 SystemInterface.Builder.defaultSystemInterface(this).build(),
-                mCanBusErrorNotifier,
                 mVehicleInterfaceName);
         mICarImpl.init();
 
@@ -129,7 +109,6 @@
         EventLog.writeEvent(EventLogTags.CAR_SERVICE_CREATE, mVehicle == null ? 0 : 1);
         Slog.i(CarLog.TAG_SERVICE, "Service onDestroy");
         mICarImpl.release();
-        mCanBusErrorNotifier.removeFailureReport(this);
 
         if (mVehicle != null) {
             try {
@@ -176,10 +155,6 @@
             vehicle = getVehicle();
         }
 
-        if (vehicle != null) {
-            mCanBusErrorNotifier.removeFailureReport(this);
-        }
-
         return vehicle;
     }
 
@@ -202,34 +177,8 @@
         @Override
         public void serviceDied(long cookie) {
             EventLog.writeEvent(EventLogTags.CAR_SERVICE_VHAL_DIED, cookie);
-            if (RESTART_CAR_SERVICE_WHEN_VHAL_CRASH) {
-                Slog.wtf(CarLog.TAG_SERVICE, "***Vehicle HAL died. Car service will restart***");
-                Process.killProcess(Process.myPid());
-                return;
-            }
-
-            Slog.wtf(CarLog.TAG_SERVICE, "***Vehicle HAL died.***");
-
-            try {
-                mVehicle.unlinkToDeath(this);
-            } catch (RemoteException e) {
-                Slog.e(CarLog.TAG_SERVICE, "Failed to unlinkToDeath", e); // Log and continue.
-            }
-            mVehicle = null;
-
-            mVhalCrashTracker.crashDetected();
-
-            Slog.i(CarLog.TAG_SERVICE,
-                    "Trying to reconnect to Vehicle HAL: " + mVehicleInterfaceName);
-            mVehicle = getVehicleWithTimeout(WAIT_FOR_VEHICLE_HAL_TIMEOUT_MS);
-            if (mVehicle == null) {
-                throw new IllegalStateException("Failed to reconnect to Vehicle HAL");
-            }
-
-            linkToDeath(mVehicle, this);
-
-            Slog.i(CarLog.TAG_SERVICE, "Notifying car service Vehicle HAL reconnected...");
-            mICarImpl.vehicleHalReconnected(mVehicle);
+            Slog.wtf(CarLog.TAG_SERVICE, "***Vehicle HAL died. Car service will restart***");
+            Process.killProcess(Process.myPid());
         }
     }
 
diff --git a/service/src/com/android/car/CarShellCommand.java b/service/src/com/android/car/CarShellCommand.java
index dcc9371..1df0b63 100644
--- a/service/src/com/android/car/CarShellCommand.java
+++ b/service/src/com/android/car/CarShellCommand.java
@@ -90,6 +90,7 @@
 
 import com.android.car.am.FixedActivityService;
 import com.android.car.audio.CarAudioService;
+import com.android.car.evs.CarEvsService;
 import com.android.car.garagemode.GarageModeService;
 import com.android.car.hal.InputHalService;
 import com.android.car.hal.UserHalService;
@@ -179,6 +180,9 @@
     private static final String DRIVING_STATE_PARK = "park";
     private static final String DRIVING_STATE_REVERSE = "reverse";
 
+    private static final String COMMAND_SET_REARVIEW_CAMERA_ID = "set-rearview-camera-id";
+    private static final String COMMAND_GET_REARVIEW_CAMERA_ID = "get-rearview-camera-id";
+
     private static final String[] CREATE_OR_MANAGE_USERS_PERMISSIONS = new String[] {
             android.Manifest.permission.CREATE_USERS,
             android.Manifest.permission.MANAGE_USERS
@@ -336,6 +340,7 @@
     private final GarageModeService mGarageModeService;
     private final CarUserService mCarUserService;
     private final CarOccupantZoneService mCarOccupantZoneService;
+    private final CarEvsService mCarEvsService;
     private long mKeyDownTime;
 
     CarShellCommand(Context context,
@@ -351,7 +356,8 @@
             SystemInterface systemInterface,
             GarageModeService garageModeService,
             CarUserService carUserService,
-            CarOccupantZoneService carOccupantZoneService) {
+            CarOccupantZoneService carOccupantZoneService,
+            CarEvsService carEvsService) {
         mContext = context;
         mHal = hal;
         mCarAudioService = carAudioService;
@@ -366,6 +372,7 @@
         mGarageModeService = garageModeService;
         mCarUserService = carUserService;
         mCarOccupantZoneService = carOccupantZoneService;
+        mCarEvsService = carEvsService;
     }
 
     @Override
@@ -566,6 +573,15 @@
         pw.printf("\t%s [%s] [%s]\n", COMMAND_POWER_OFF, POWER_OFF_SKIP_GARAGEMODE,
                 POWER_OFF_SHUTDOWN);
         pw.println("\t  Powers off the car.");
+
+        pw.printf("\t%s <CAMERA_ID>\n", COMMAND_SET_REARVIEW_CAMERA_ID);
+        pw.println("\t  Configures a target camera device CarEvsService to use.");
+        pw.println("\t  If CAMEAR_ID is \"default\", this command will configure CarEvsService ");
+        pw.println("\t  to use its default camera device.");
+
+        pw.printf("\t%s\n", COMMAND_GET_REARVIEW_CAMERA_ID);
+        pw.println("\t  Gets the name of the camera device CarEvsService is using for " +
+                "the rearview.");
     }
 
     private static int showInvalidArguments(IndentingPrintWriter pw) {
@@ -882,6 +898,12 @@
             case COMMAND_POWER_OFF:
                 powerOff(args, writer);
                 break;
+            case COMMAND_SET_REARVIEW_CAMERA_ID:
+                setRearviewCameraId(args, writer);
+                break;
+            case COMMAND_GET_REARVIEW_CAMERA_ID:
+                getRearviewCameraId(writer);
+                break;
 
             default:
                 writer.println("Unknown command: \"" + cmd + "\"");
@@ -2029,6 +2051,25 @@
 
     }
 
+    // Set a target camera device for the rearview
+    private void setRearviewCameraId(String[] args, IndentingPrintWriter writer) {
+        if (args.length != 2) {
+            showInvalidArguments(writer);
+            return;
+        }
+
+        if (!mCarEvsService.setRearviewCameraIdFromCommand(args[1])) {
+            writer.println("Failed to set CarEvsService rearview camera device id.");
+        } else {
+            writer.printf("CarEvsService is set to use %s.\n", args[1]);
+        }
+    }
+
+    private void getRearviewCameraId(IndentingPrintWriter writer) {
+        writer.printf("CarEvsService is using %s for the rearview.\n",
+                mCarEvsService.getRearviewCameraIdFromCommand());
+    }
+
     // Check if the given property is global
     private static boolean isPropertyAreaTypeGlobal(@Nullable String property) {
         if (property == null) {
diff --git a/service/src/com/android/car/ICarImpl.java b/service/src/com/android/car/ICarImpl.java
index 7c6433a..68af541 100644
--- a/service/src/com/android/car/ICarImpl.java
+++ b/service/src/com/android/car/ICarImpl.java
@@ -18,6 +18,8 @@
 
 import static com.android.car.CarService.CAR_SERVICE_INIT_TIMING_MIN_DURATION_MS;
 import static com.android.car.CarService.CAR_SERVICE_INIT_TIMING_TAG;
+import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DEPRECATED_CODE;
+import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
 import static com.android.car.internal.SystemConstants.ICAR_SYSTEM_SERVER_CLIENT;
 
 import android.annotation.MainThread;
@@ -60,6 +62,7 @@
 import com.android.car.evs.CarEvsService;
 import com.android.car.garagemode.GarageModeService;
 import com.android.car.hal.VehicleHal;
+import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
 import com.android.car.internal.ICarServiceHelper;
 import com.android.car.internal.ICarSystemServerClient;
 import com.android.car.internal.common.EventLogTags;
@@ -156,15 +159,15 @@
     private final ICarSystemServerClientImpl mICarSystemServerClientImpl;
 
     public ICarImpl(Context serviceContext, IVehicle vehicle, SystemInterface systemInterface,
-            CanBusErrorNotifier errorNotifier, String vehicleInterfaceName) {
-        this(serviceContext, vehicle, systemInterface, errorNotifier, vehicleInterfaceName,
+            String vehicleInterfaceName) {
+        this(serviceContext, vehicle, systemInterface, vehicleInterfaceName,
                 /* carUserService= */ null, /* carWatchdogService= */ null,
                 /* powerPolicyDaemon= */ null);
     }
 
     @VisibleForTesting
     ICarImpl(Context serviceContext, IVehicle vehicle, SystemInterface systemInterface,
-            CanBusErrorNotifier errorNotifier, String vehicleInterfaceName,
+            String vehicleInterfaceName,
             @Nullable CarUserService carUserService,
             @Nullable CarWatchdogService carWatchdogService,
             @Nullable ICarPowerPolicySystemNotification powerPolicyDaemon) {
@@ -642,23 +645,11 @@
     }
 
     @Override
+    @ExcludeFromCodeCoverageGeneratedReport(reason = DEPRECATED_CODE)
     public int getCarConnectionType() {
         return Car.CONNECTION_TYPE_EMBEDDED;
     }
 
-    public CarServiceBase getCarInternalService(String serviceName) {
-        switch (serviceName) {
-            case INTERNAL_INPUT_SERVICE:
-                return mCarInputService;
-            case INTERNAL_SYSTEM_ACTIVITY_MONITORING_SERVICE:
-                return mSystemActivityMonitoringService;
-            default:
-                Slog.w(CarLog.TAG_SERVICE, "getCarInternalService for unknown service:"
-                        + serviceName);
-                return null;
-        }
-    }
-
     public static void assertVehicleHalMockPermission(Context context) {
         assertPermission(context, Car.PERMISSION_MOCK_VEHICLE_HAL);
     }
@@ -739,6 +730,7 @@
     }
 
     @Override
+    @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
     protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
                 != PackageManager.PERMISSION_GRANTED) {
@@ -753,6 +745,7 @@
         }
     }
 
+    @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
     private void dumpIndenting(FileDescriptor fd, IndentingPrintWriter writer, String[] args) {
         if (args == null || args.length == 0 || (args.length > 0 && "-a".equals(args[0]))) {
             writer.println("*Dump car service*");
@@ -797,6 +790,7 @@
         }
     }
 
+    @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
     private void dumpAllHals(IndentingPrintWriter writer) {
         writer.println("*Dump Vehicle HAL*");
         writer.println("Vehicle HAL Interface: " + mVehicleInterfaceName);
@@ -809,6 +803,7 @@
         }
     }
 
+    @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
     private void showDumpHelp(IndentingPrintWriter writer) {
         writer.println("Car service dump usage:");
         writer.println("[NO ARG]");
@@ -849,15 +844,17 @@
         return new CarShellCommand(mContext, mHal, mCarAudioService, mCarPackageManagerService,
                 mCarProjectionService, mCarPowerManagementService, mFixedActivityService,
                 mFeatureController, mCarInputService, mCarNightService, mSystemInterface,
-                mGarageModeService, mCarUserService, mCarOccupantZoneService);
+                mGarageModeService, mCarUserService, mCarOccupantZoneService, mCarEvsService);
     }
 
+    @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
     private void dumpListOfServices(IndentingPrintWriter writer) {
         for (CarServiceBase service : mAllServices) {
             writer.println(service.getClass().getName());
         }
     }
 
+    @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
     private void dumpAllServices(IndentingPrintWriter writer) {
         writer.println("*Dump all services*");
         for (CarServiceBase service : mAllServices) {
@@ -868,6 +865,7 @@
         }
     }
 
+    @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
     private void dumpIndividualServices(IndentingPrintWriter writer, String... serviceNames) {
         for (String serviceName : serviceNames) {
             writer.printf("** Dumping %s\n\n", serviceName);
@@ -888,6 +886,7 @@
                 .findFirst().orElse(null);
     }
 
+    @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
     private void dumpService(CarServiceBase service, IndentingPrintWriter writer) {
         try {
             service.dump(writer);
diff --git a/service/src/com/android/car/PerUserCarService.java b/service/src/com/android/car/PerUserCarService.java
index ee4dafe..6a9291a 100644
--- a/service/src/com/android/car/PerUserCarService.java
+++ b/service/src/com/android/car/PerUserCarService.java
@@ -76,7 +76,7 @@
 
         if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)) {
             mPerUserCarDevicePolicyService = PerUserCarDevicePolicyService.getInstance(context);
-            mPerUserCarDevicePolicyService.registerBroadcastReceiver();
+            mPerUserCarDevicePolicyService.onCreate();
         } else if (DBG) {
             Slogf.d(TAG, "Not setting PerUserCarDevicePolicyService because device doesn't have %s",
                     PackageManager.FEATURE_DEVICE_ADMIN);
diff --git a/service/src/com/android/car/SparseArrayStream.java b/service/src/com/android/car/SparseArrayStream.java
index cbe2ca3..574f107 100644
--- a/service/src/com/android/car/SparseArrayStream.java
+++ b/service/src/com/android/car/SparseArrayStream.java
@@ -15,9 +15,13 @@
  */
 package com.android.car;
 
+import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.BOILERPLATE_CODE;
+
 import android.util.Pair;
 import android.util.SparseArray;
 
+import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
+
 import java.util.stream.IntStream;
 import java.util.stream.Stream;
 
@@ -37,4 +41,10 @@
         return IntStream.range(0, array.size()).mapToObj(
             i -> new Pair<>(array.keyAt(i), array.valueAt(i)));
     }
+
+    @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE,
+            details = "private constructor")
+    private SparseArrayStream() {
+        throw new UnsupportedOperationException("contains only static methods");
+    }
 }
diff --git a/service/src/com/android/car/Utils.java b/service/src/com/android/car/Utils.java
index 41ab67b..c6f5ef9 100644
--- a/service/src/com/android/car/Utils.java
+++ b/service/src/com/android/car/Utils.java
@@ -15,6 +15,8 @@
  */
 package com.android.car;
 
+import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.BOILERPLATE_CODE;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
@@ -23,6 +25,8 @@
 import android.bluetooth.BluetoothProfile;
 import android.util.SparseArray;
 
+import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
+
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.nio.ByteBuffer;
@@ -140,7 +144,7 @@
      * A specific service in CarService can choose to use a circular buffer of N records to keep
      * track of the last N transitions.
      */
-    public static class TransitionLog {
+    public static final class TransitionLog {
         private String mServiceName; // name of the service or tag
         private Object mFromState; // old state
         private Object mToState; // new state
@@ -166,8 +170,9 @@
 
         @Override
         public String toString() {
-            return timeToLog(mTimestampMs) + " " + mServiceName + ": " + (mExtra != null ? mExtra
-                    : "") + " changed from " + mFromState + " to " + mToState;
+            return timeToLog(mTimestampMs) + " " + mServiceName + ": "
+                    + (mExtra != null ? mExtra + " " : "")
+                    + "changed from " + mFromState + " to " + mToState;
         }
     }
 
@@ -278,4 +283,9 @@
         return outputStream.toByteArray();
     }
 
+    @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE,
+            details = "private constructor")
+    private Utils() {
+        throw new UnsupportedOperationException("contains only static methods");
+    }
 }
diff --git a/service/src/com/android/car/admin/NewUserDisclaimerActivity.java b/service/src/com/android/car/admin/NewUserDisclaimerActivity.java
index 4101c34..8a186a4 100644
--- a/service/src/com/android/car/admin/NewUserDisclaimerActivity.java
+++ b/service/src/com/android/car/admin/NewUserDisclaimerActivity.java
@@ -30,6 +30,7 @@
 import com.android.car.CarLog;
 import com.android.car.R;
 import com.android.car.admin.ui.ManagedDeviceTextView;
+import com.android.internal.annotations.VisibleForTesting;
 
 // TODO(b/171603586): STOPSHIP move UI related activities to CarSettings
 /**
@@ -65,6 +66,11 @@
         // and/or integrate it with UserNoticeService
     }
 
+    @VisibleForTesting
+    Button getAcceptButton() {
+        return mAcceptButton;
+    }
+
     private void accept() {
         if (DEBUG) Slog.d(TAG, "user accepted");
 
@@ -72,13 +78,8 @@
         finish();
     }
 
-    private static Intent newIntent(Context context) {
-        return new Intent(context, NewUserDisclaimerActivity.class);
-    }
-
     static void showNotification(Context context) {
-        PendingIntent pendingIntent = PendingIntent.getActivity(context, NOTIFICATION_ID,
-                newIntent(context), PendingIntent.FLAG_IMMUTABLE, null);
+        PendingIntent pendingIntent = getPendingIntent(context, /* extraFlags= */ 0);
 
         Notification notification = NotificationHelper
                 .newNotificationBuilder(context, NotificationManager.IMPORTANCE_DEFAULT)
@@ -104,7 +105,13 @@
                     + context.getUserId());
         }
         context.getSystemService(NotificationManager.class).cancel(NOTIFICATION_ID);
-        PendingIntent.getActivity(context, NOTIFICATION_ID, newIntent(context),
-                PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT).cancel();
+        getPendingIntent(context, PendingIntent.FLAG_UPDATE_CURRENT).cancel();
+    }
+
+    @VisibleForTesting
+    static PendingIntent getPendingIntent(Context context, int extraFlags) {
+        return PendingIntent.getActivity(context, NOTIFICATION_ID,
+                new Intent(context, NewUserDisclaimerActivity.class),
+                PendingIntent.FLAG_IMMUTABLE | extraFlags);
     }
 }
diff --git a/service/src/com/android/car/admin/NotificationHelper.java b/service/src/com/android/car/admin/NotificationHelper.java
index 44bba11..217bb46 100644
--- a/service/src/com/android/car/admin/NotificationHelper.java
+++ b/service/src/com/android/car/admin/NotificationHelper.java
@@ -16,6 +16,8 @@
 
 package com.android.car.admin;
 
+import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.BOILERPLATE_CODE;
+
 import android.annotation.NonNull;
 import android.app.Notification;
 import android.app.NotificationChannel;
@@ -24,18 +26,26 @@
 import android.os.Bundle;
 
 import com.android.car.R;
+import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
+import com.android.internal.annotations.VisibleForTesting;
 
-// TODO(b/171603586): STOPSHIP move this class to CarSettings
+import java.util.Objects;
+
+// TODO: move this class to CarSettings or at least to some common package (not admin)
+
 /**
  * Helper for notification-related tasks
  */
 public final class NotificationHelper {
 
     static final int FACTORY_RESET_NOTIFICATION_ID = 42;
-    public static final int NEW_USER_DISCLAIMER_NOTIFICATION_ID = 108;
 
-    static final String IMPORTANCE_DEFAULT_ID = "importance_default";
-    static final String IMPORTANCE_HIGH_ID = "importance_high";
+    static final int NEW_USER_DISCLAIMER_NOTIFICATION_ID = 108;
+
+    @VisibleForTesting
+    static final String CHANNEL_ID_DEFAULT = "channel_id_default";
+    @VisibleForTesting
+    static final String CHANNEL_ID_HIGH = "channel_id_high";
 
     /**
      * Creates a notification (and its notification channel) for the given importance type, setting
@@ -48,14 +58,16 @@
     @NonNull
     public static Notification.Builder newNotificationBuilder(Context context,
             @NotificationManager.Importance int importance) {
-        String importanceId, importanceName;
+        Objects.requireNonNull(context, "context cannot be null");
+
+        String channelId, importanceName;
         switch (importance) {
             case NotificationManager.IMPORTANCE_DEFAULT:
-                importanceId = IMPORTANCE_DEFAULT_ID;
+                channelId = CHANNEL_ID_DEFAULT;
                 importanceName = context.getString(R.string.importance_default);
                 break;
             case NotificationManager.IMPORTANCE_HIGH:
-                importanceId = IMPORTANCE_HIGH_ID;
+                channelId = CHANNEL_ID_HIGH;
                 importanceName = context.getString(R.string.importance_high);
                 break;
             default:
@@ -63,15 +75,17 @@
         }
         NotificationManager notificationMgr = context.getSystemService(NotificationManager.class);
         notificationMgr.createNotificationChannel(
-                new NotificationChannel(importanceId, importanceName, importance));
+                new NotificationChannel(channelId, importanceName, importance));
 
         Bundle extras = new Bundle();
         extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
                 context.getString(com.android.internal.R.string.android_system_label));
 
-        return new Notification.Builder(context, importanceId).addExtras(extras);
+        return new Notification.Builder(context, channelId).addExtras(extras);
     }
 
+    @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE,
+            details = "private constructor")
     private NotificationHelper() {
         throw new UnsupportedOperationException("Contains only static methods");
     }
diff --git a/service/src/com/android/car/admin/PerUserCarDevicePolicyService.java b/service/src/com/android/car/admin/PerUserCarDevicePolicyService.java
index 4373a74..50ff4f7 100644
--- a/service/src/com/android/car/admin/PerUserCarDevicePolicyService.java
+++ b/service/src/com/android/car/admin/PerUserCarDevicePolicyService.java
@@ -16,6 +16,7 @@
 package com.android.car.admin;
 
 import static com.android.car.admin.CarDevicePolicyService.DEBUG;
+import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
 
 import android.annotation.IntDef;
 import android.app.admin.DevicePolicyManager;
@@ -28,12 +29,14 @@
 import android.util.Slog;
 
 import com.android.car.CarLog;
+import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
 
-// TODO(b/175057848) add unit tests
 /**
  * User-specific {@code CarDevicePolicyManagerService}.
  */
@@ -92,6 +95,8 @@
      * Gests the singleton instance, creating it if necessary.
      */
     public static PerUserCarDevicePolicyService getInstance(Context context) {
+        Objects.requireNonNull(context, "context cannot be null");
+
         synchronized (SLOCK) {
             if (sInstance == null) {
                 sInstance = new PerUserCarDevicePolicyService(context.getApplicationContext());
@@ -102,14 +107,15 @@
         }
     }
 
-    private PerUserCarDevicePolicyService(Context context) {
+    @VisibleForTesting
+    PerUserCarDevicePolicyService(Context context) {
         mContext = context;
     }
 
     /**
-     * Register a broadcast receiver to receive the proper events.
+     * Callback for when the service is created.
      */
-    public void registerBroadcastReceiver() {
+    public void onCreate() {
         if (DEBUG) Slog.d(TAG, "registering BroadcastReceiver");
 
         mContext.registerReceiver(mBroadcastReceiver, new IntentFilter(
@@ -130,6 +136,7 @@
     /**
      * Dump its contents.
      */
+    @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
     public void dump(IndentingPrintWriter pw) {
         synchronized (SLOCK) {
             pw.printf("mNewUserDisclaimerStatus: %s\n",
@@ -166,7 +173,16 @@
         }
     }
 
-    private String newUserDisclaimerStatusToString(@NewUserDisclaimerStatus int status) {
+    @VisibleForTesting
+    @NewUserDisclaimerStatus
+    int getNewUserDisclaimerStatus() {
+        synchronized (SLOCK) {
+            return mNewUserDisclaimerStatus;
+        }
+    }
+
+    @VisibleForTesting
+    static String newUserDisclaimerStatusToString(@NewUserDisclaimerStatus int status) {
         return DebugUtils.constantToString(PerUserCarDevicePolicyService.class,
                 PREFIX_NEW_USER_DISCLAIMER_STATUS, status);
     }
diff --git a/service/src/com/android/car/cluster/ClusterHomeService.java b/service/src/com/android/car/cluster/ClusterHomeService.java
index 0baa793..b032d93 100644
--- a/service/src/com/android/car/cluster/ClusterHomeService.java
+++ b/service/src/com/android/car/cluster/ClusterHomeService.java
@@ -21,6 +21,7 @@
 import static com.android.car.hal.ClusterHalService.DISPLAY_OFF;
 import static com.android.car.hal.ClusterHalService.DISPLAY_ON;
 import static com.android.car.hal.ClusterHalService.DONT_CARE;
+import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
 
 import android.app.ActivityOptions;
 import android.car.Car;
@@ -54,6 +55,7 @@
 import com.android.car.R;
 import com.android.car.am.FixedActivityService;
 import com.android.car.hal.ClusterHalService;
+import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
 import com.android.server.utils.Slogf;
 
 /**
@@ -173,6 +175,7 @@
     }
 
     @Override
+    @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
     public void dump(IndentingPrintWriter writer) {
         // TODO: record the latest states from both sides
     }
diff --git a/service/src/com/android/car/cluster/ClusterNavigationService.java b/service/src/com/android/car/cluster/ClusterNavigationService.java
index 28e5a65..de6aa89 100644
--- a/service/src/com/android/car/cluster/ClusterNavigationService.java
+++ b/service/src/com/android/car/cluster/ClusterNavigationService.java
@@ -15,9 +15,10 @@
  */
 package com.android.car.cluster;
 
+import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
+
 import android.car.Car;
 import android.car.CarAppFocusManager;
-import android.car.cluster.renderer.IInstrumentCluster;
 import android.car.cluster.renderer.IInstrumentClusterNavigation;
 import android.car.navigation.CarNavigationInstrumentCluster;
 import android.content.Context;
@@ -33,6 +34,7 @@
 import com.android.car.CarLog;
 import com.android.car.CarServiceBase;
 import com.android.car.ICarImpl;
+import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
 import com.android.internal.annotations.GuardedBy;
 
 import java.util.Objects;
@@ -117,6 +119,7 @@
     }
 
     @Override
+    @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
     public void dump(IndentingPrintWriter writer) {
         writer.println("**" + getClass().getSimpleName() + "**");
         synchronized (mLock) {
@@ -155,8 +158,6 @@
         if (appType != CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION) {
             return;
         }
-
-        IInstrumentCluster service;
         ContextOwner requester = new ContextOwner(uid, pid);
         ContextOwner newOwner = acquire ? requester : NO_OWNER;
         ClusterNavigationServiceCallback callback;
diff --git a/service/src/com/android/car/cluster/InstrumentClusterService.java b/service/src/com/android/car/cluster/InstrumentClusterService.java
index a36c898..4737b21 100644
--- a/service/src/com/android/car/cluster/InstrumentClusterService.java
+++ b/service/src/com/android/car/cluster/InstrumentClusterService.java
@@ -18,6 +18,8 @@
 import static android.car.cluster.renderer.InstrumentClusterRenderingService.EXTRA_BUNDLE_KEY_FOR_INSTRUMENT_CLUSTER_HELPER;
 import static android.car.settings.CarSettings.Global.DISABLE_INSTRUMENTATION_SERVICE;
 
+import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
+
 import android.annotation.SystemApi;
 import android.app.ActivityOptions;
 import android.car.cluster.IInstrumentClusterManagerCallback;
@@ -52,6 +54,7 @@
 import com.android.car.R;
 import com.android.car.am.FixedActivityService;
 import com.android.car.cluster.ClusterNavigationService.ContextOwner;
+import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
 import com.android.car.user.CarUserService;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -296,6 +299,7 @@
     }
 
     @Override
+    @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
     public void dump(IndentingPrintWriter writer) {
         writer.println("**" + getClass().getSimpleName() + "**");
         synchronized (mLock) {
diff --git a/service/src/com/android/car/evs/CarEvsService.java b/service/src/com/android/car/evs/CarEvsService.java
index 764fb08..b9d19eb 100644
--- a/service/src/com/android/car/evs/CarEvsService.java
+++ b/service/src/com/android/car/evs/CarEvsService.java
@@ -50,6 +50,7 @@
 import android.hardware.automotive.vehicle.V2_0.VehicleGear;
 import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -151,6 +152,8 @@
         }
     }
 
+    private static final String COMMAND_TO_USE_DEFAULT_CAMERA = "default";
+
     private final Context mContext;
     private final EvsHalService mEvsHalService;
     private final CarPropertyService mPropertyService;
@@ -523,6 +526,12 @@
     // EVS_SERVICE_REQUEST.
     private boolean mUseGearSelection = true;
 
+    // When this is set, CarEvsService will attempt to open a camera device the user sets.
+    private boolean mUseCameraIdOverride = false;
+
+    // This is a device name to be used when mUseCameraIdOverride is true.
+    private String mCameraIdOverride;
+
     private void setSessionToken(IBinder token) {
         synchronized (mLock) {
             mSessionToken = token;
@@ -960,6 +969,54 @@
         }
     }
 
+    /**
+     * Sets a camera device for the rearview.
+     *
+     * <p>Requires {@link android.car.Car.PERMISSION_USE_CAR_EVS_CAMERA} permissions to access.
+     *
+     * @param id A string identifier of a target camera device.
+     * @return This method return a false if this runs in a release build; otherwise, this returns
+     *         true.
+     */
+    public boolean setRearviewCameraIdFromCommand(@NonNull String id) {
+        ICarImpl.assertPermission(mContext, Car.PERMISSION_USE_CAR_EVS_CAMERA);
+        Objects.requireNonNull(id);
+
+        if (!Build.IS_DEBUGGABLE) {
+            // This method is not allowed in the release build.
+            return false;
+        }
+
+        if (id.equalsIgnoreCase(COMMAND_TO_USE_DEFAULT_CAMERA)) {
+            mUseCameraIdOverride = false;
+            Slog.i(TAG_EVS, "CarEvsService is set to use the default device for the rearview.");
+        } else {
+            mCameraIdOverride = id;
+            mUseCameraIdOverride = true;
+            Slog.i(TAG_EVS, "CarEvsService is set to use " + id + " for the rearview.");
+        }
+
+        return true;
+    }
+
+    /**
+     * Gets an identifier of a current camera device for the rearview.
+     *
+     * <p>Requires {@link android.car.Car.PERMISSION_MONITOR_CAR_EVS_STATUS} permissions to
+     * access.
+     *
+     * @return A string identifier of current rearview camera device.
+     */
+    @NonNull
+    public String getRearviewCameraIdFromCommand() {
+        ICarImpl.assertPermission(mContext, Car.PERMISSION_MONITOR_CAR_EVS_STATUS);
+        if (mUseCameraIdOverride) {
+            return mCameraIdOverride;
+        } else {
+            return mContext.getString(R.string.config_evsRearviewCameraId);
+        }
+    }
+
     /** Handles client disconnections; may request to stop a video stream. */
     private void handleClientDisconnected(ICarEvsStreamCallback callback) {
         // If the last stream client is disconnected before it stops a video stream, request to stop
@@ -995,8 +1052,14 @@
             return false;
         }
 
-        if (!nativeOpenCamera(mNativeEvsServiceObj,
-                mContext.getString(R.string.config_evsRearviewCameraId))) {
+        String cameraId;
+        if (mUseCameraIdOverride) {
+            cameraId = mCameraIdOverride;
+        } else {
+            cameraId = mContext.getString(R.string.config_evsRearviewCameraId);
+        }
+
+        if (!nativeOpenCamera(mNativeEvsServiceObj, cameraId)) {
             Slog.e(TAG_EVS, "Failed to open a target camera device");
             return false;
         }
diff --git a/service/src/com/android/car/user/CarUserNoticeService.java b/service/src/com/android/car/user/CarUserNoticeService.java
index a96478b..8148867 100644
--- a/service/src/com/android/car/user/CarUserNoticeService.java
+++ b/service/src/com/android/car/user/CarUserNoticeService.java
@@ -18,6 +18,8 @@
 
 import static android.car.hardware.power.CarPowerManager.CarPowerStateListener;
 
+import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
+
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
@@ -53,6 +55,7 @@
 import com.android.car.CarLog;
 import com.android.car.CarServiceBase;
 import com.android.car.R;
+import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -425,6 +428,7 @@
     }
 
     @Override
+    @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
     public void dump(IndentingPrintWriter writer) {
         synchronized (mLock) {
             if (mServiceIntent == null) {
diff --git a/service/src/com/android/car/user/CarUserService.java b/service/src/com/android/car/user/CarUserService.java
index de5739a..e61419b 100644
--- a/service/src/com/android/car/user/CarUserService.java
+++ b/service/src/com/android/car/user/CarUserService.java
@@ -22,6 +22,7 @@
 
 import static com.android.car.PermissionHelper.checkHasAtLeastOnePermissionGranted;
 import static com.android.car.PermissionHelper.checkHasDumpPermissionGranted;
+import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -98,6 +99,7 @@
 import com.android.car.CarUxRestrictionsManagerService;
 import com.android.car.R;
 import com.android.car.hal.UserHalService;
+import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
 import com.android.car.internal.ICarServiceHelper;
 import com.android.car.internal.common.CommonConstants.UserLifecycleEventType;
 import com.android.car.internal.common.EventLogTags;
@@ -351,6 +353,7 @@
     }
 
     @Override
+    @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
     public void dump(@NonNull IndentingPrintWriter writer) {
         checkHasDumpPermissionGranted("dump()");
 
diff --git a/service/src/com/android/car/user/InitialUserSetter.java b/service/src/com/android/car/user/InitialUserSetter.java
index 1d35f93..29406db 100644
--- a/service/src/com/android/car/user/InitialUserSetter.java
+++ b/service/src/com/android/car/user/InitialUserSetter.java
@@ -17,6 +17,8 @@
 
 import static android.car.userlib.UserHalHelper.userFlagsToString;
 
+import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -39,6 +41,7 @@
 import android.util.TimingsTraceLog;
 
 import com.android.car.CarLog;
+import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
 import com.android.car.internal.common.UserHelperLite;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
@@ -663,6 +666,7 @@
     /**
      * Dumps it state.
      */
+    @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
     public void dump(@NonNull PrintWriter writer) {
         writer.println("InitialUserSetter");
         String indent = "  ";
diff --git a/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml b/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
index 098c7b2..a16af8a 100644
--- a/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
+++ b/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
@@ -83,6 +83,8 @@
     <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"/>
+    <!-- Use for sensor access in Sensors fragment. -->
+    <uses-permission android:name="android.permission.HIGH_SAMPLING_RATE_SENSORS"/>
     <uses-permission android:name="android.permission.INJECT_EVENTS"/>
     <!-- use for CarServiceUnitTest and CarServiceTest -->
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/>
diff --git a/tests/android_car_api_test/src/android/car/apitest/CarMultiUserTestBase.java b/tests/android_car_api_test/src/android/car/apitest/CarMultiUserTestBase.java
index eb331f5..0823c5f 100644
--- a/tests/android_car_api_test/src/android/car/apitest/CarMultiUserTestBase.java
+++ b/tests/android_car_api_test/src/android/car/apitest/CarMultiUserTestBase.java
@@ -189,12 +189,22 @@
 
     @NonNull
     protected UserInfo createUser() throws Exception {
-        return createUser("NonGuest", /* isGuest= */ false);
+        return createUser("NonGuest");
+    }
+
+    @NonNull
+    protected UserInfo createUser(String name) throws Exception {
+        return createUser(name, /* isGuest= */ false);
     }
 
     @NonNull
     protected UserInfo createGuest() throws Exception {
-        return createUser("Guest", /* isGuest= */ true);
+        return createGuest("Guest");
+    }
+
+    @NonNull
+    protected UserInfo createGuest(String name) throws Exception {
+        return createUser(name, /* isGuest= */ true);
     }
 
     @NonNull
@@ -323,6 +333,18 @@
         Log.v(TAG, "Set: " + SystemProperties.get(property));
     }
 
+
+    protected void assertUserInfo(UserInfo actualUser, UserInfo expectedUser) {
+        assertWithMessage("Wrong id for user %s", actualUser.toFullString())
+                .that(actualUser.id).isEqualTo(expectedUser.id);
+        assertWithMessage("Wrong name for user %s", actualUser.toFullString())
+                .that(actualUser.name).isEqualTo(expectedUser.name);
+        assertWithMessage("Wrong type for user %s", actualUser.toFullString())
+                .that(actualUser.userType).isEqualTo(expectedUser.userType);
+        assertWithMessage("Wrong flags for user %s", actualUser.toFullString())
+                .that(actualUser.flags).isEqualTo(expectedUser.flags);
+    }
+
     private static boolean isUserCreatedByTheseTests(UserInfo user) {
         return user.name != null && user.name.startsWith(NEW_USER_NAME_PREFIX);
     }
diff --git a/tests/android_car_api_test/src/android/car/apitest/CarUserManagerTest.java b/tests/android_car_api_test/src/android/car/apitest/CarUserManagerTest.java
index 03cea19..c673df2 100644
--- a/tests/android_car_api_test/src/android/car/apitest/CarUserManagerTest.java
+++ b/tests/android_car_api_test/src/android/car/apitest/CarUserManagerTest.java
@@ -65,6 +65,34 @@
         }
     }
 
+    @Test
+    public void testCreateUser() throws Exception {
+        UserInfo newUser = createUser("DaNewUserInTheBlock");
+        assertWithMessage("(%s).isGuest()", newUser.toFullString()).that(newUser.isGuest())
+                .isFalse();
+
+        assertWithMessage("user(%s).name", newUser.toFullString()).that(newUser.name)
+                .contains("DaNewUserInTheBlock");
+
+        // Make sure the user exists
+        UserInfo loadedUser = getUser(newUser.id);
+        assertUserInfo(newUser, loadedUser);
+    }
+
+    @Test
+    public void testCreateGuest() throws Exception {
+        UserInfo newGuest = createGuest("DaNewGuestInTheBlock");
+        assertWithMessage("(%s).isGuest()", newGuest.toFullString()).that(newGuest.isGuest())
+                .isTrue();
+
+        assertWithMessage("guest(%s).name ", newGuest.toFullString()).that(newGuest.name)
+                .contains("DaNewGuestInTheBlock");
+
+        // Make sure the guest exists
+        UserInfo loadedGuest = getUser(newGuest.id);
+        assertUserInfo(newGuest, loadedGuest);
+    }
+
     /**
      * Tests resume behavior when current user is ephemeral guest, a new guest user should be
      * created and switched to.
diff --git a/tests/carservice_test/src/com/android/car/CarEvsManagerTest.java b/tests/carservice_test/src/com/android/car/CarEvsManagerTest.java
index 8aeea95..cb15747 100644
--- a/tests/carservice_test/src/com/android/car/CarEvsManagerTest.java
+++ b/tests/carservice_test/src/com/android/car/CarEvsManagerTest.java
@@ -96,7 +96,9 @@
 
     @After
     public void tearDown() throws Exception {
-        mEvsManager.stopVideoStream();
+        if (mEvsManager != null) {
+            mEvsManager.stopVideoStream();
+        }
     }
 
     @Test
diff --git a/tests/carservice_test/src/com/android/car/ICarImplTest.java b/tests/carservice_test/src/com/android/car/ICarImplTest.java
index 574b8ed..c0ba62a 100644
--- a/tests/carservice_test/src/com/android/car/ICarImplTest.java
+++ b/tests/carservice_test/src/com/android/car/ICarImplTest.java
@@ -165,7 +165,7 @@
         doThrow(new NullPointerException()).when(mContext).getDataDir();
 
         ICarImpl carImpl = new ICarImpl(mContext, mMockVehicle, mFakeSystemInterface,
-                /* errorNotifier= */ null, "MockedCar", /* carUserService= */ null,
+                "MockedCar", /* carUserService= */ null,
                 mCarWatchdogService, new MockedCarTestBase.FakeCarPowerPolicyDaemon());
         carImpl.init();
         Car mCar = new Car(mContext, carImpl, /* handler= */ null);
diff --git a/tests/carservice_test/src/com/android/car/MockedCarTestBase.java b/tests/carservice_test/src/com/android/car/MockedCarTestBase.java
index 9794b19..4119b8c 100644
--- a/tests/carservice_test/src/com/android/car/MockedCarTestBase.java
+++ b/tests/carservice_test/src/com/android/car/MockedCarTestBase.java
@@ -232,7 +232,7 @@
         // This should be done here as feature property is accessed inside the constructor.
         initMockedHal();
         mCarImpl = new ICarImpl(mMockedCarTestContext, mMockedVehicleHal, mFakeSystemInterface,
-                /* errorNotifier= */ null , "MockedCar", mCarUserService, mCarWatchdogService,
+                "MockedCar", mCarUserService, mCarWatchdogService,
                 mPowerPolicyDaemon);
 
         spyOnBeforeCarImplInit();
diff --git a/tests/carservice_test/src/com/android/car/garagemode/GarageModeTest.java b/tests/carservice_test/src/com/android/car/garagemode/GarageModeTest.java
index 499a95f..6133baf 100644
--- a/tests/carservice_test/src/com/android/car/garagemode/GarageModeTest.java
+++ b/tests/carservice_test/src/com/android/car/garagemode/GarageModeTest.java
@@ -98,10 +98,12 @@
         ArrayList<Integer> userToStartInBackground = new ArrayList<>(Arrays.asList(101, 102, 103));
         when(mCarUserService.startAllBackgroundUsersInGarageMode())
                 .thenReturn(userToStartInBackground);
-        mGarageMode.enterGarageMode(/* future= */ null);
+
         CountDownLatch latch = new CountDownLatch(3); // 3 for three users
         mockCarUserServiceStopUserCall(getEventListener(), latch);
 
+        mGarageMode.enterGarageMode(/* future= */ null);
+
         mGarageMode.cancel();
 
         waitForHandlerThreadToFinish(latch);
diff --git a/tests/carservice_unit_test/src/android/car/util/concurrent/AndroidAsyncFutureTest.java b/tests/carservice_unit_test/src/android/car/util/concurrent/AndroidAsyncFutureTest.java
new file mode 100644
index 0000000..85b8d5e
--- /dev/null
+++ b/tests/carservice_unit_test/src/android/car/util/concurrent/AndroidAsyncFutureTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2021 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.util.concurrent;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import com.android.internal.infra.AndroidFuture;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import java.util.function.BiConsumer;
+
+public final class AndroidAsyncFutureTest {
+
+    @Mock
+    private AndroidFuture<Integer> mFuture;
+
+    private MockitoSession mMockSession;
+
+    private AndroidAsyncFuture<Integer> mAndroidAsyncFuture;
+
+    @Before
+    public void setupMocks() {
+        mMockSession = mockitoSession()
+                .initMocks(this)
+                .strictness(Strictness.LENIENT)
+                .startMocking();
+        mAndroidAsyncFuture = new AndroidAsyncFuture<Integer>(mFuture);
+    }
+
+    @After
+    public void tearDown() {
+        mMockSession.finishMocking();
+    }
+
+    @Test
+    public void testGet() throws Exception {
+        Integer input = 0;
+        when(mFuture.get()).thenReturn(input);
+
+        Integer result = mAndroidAsyncFuture.get();
+
+        assertThat(result).isEqualTo(input);
+        verify(mFuture).get();
+    }
+
+    @Test
+    public void testGetWithTimeout() throws Exception {
+        long timeout = 100;
+        TimeUnit timeUnit = TimeUnit.SECONDS;
+        Integer input = 0;
+        when(mFuture.get(timeout, timeUnit)).thenReturn(input);
+
+        Integer result = mAndroidAsyncFuture.get(timeout, timeUnit);
+
+        assertThat(result).isEqualTo(input);
+        verify(mFuture).get(timeout, timeUnit);
+    }
+
+    @Test
+    public void testWhenCompleteAsync() throws Exception {
+        Executor executor = (a) -> {};
+        BiConsumer<Integer, Throwable> biConsumer = (l, m) -> {};
+
+        AsyncFuture asyncFuture = mAndroidAsyncFuture.whenCompleteAsync(biConsumer, executor);
+
+        assertThat(asyncFuture).isEqualTo(mAndroidAsyncFuture);
+        verify(mFuture).whenCompleteAsync(biConsumer, executor);
+    }
+}
diff --git a/tests/carservice_unit_test/src/com/android/car/CarBugreportManagerServiceTest.java b/tests/carservice_unit_test/src/com/android/car/CarBugreportManagerServiceTest.java
new file mode 100644
index 0000000..7662bf6
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/CarBugreportManagerServiceTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.expectThrows;
+
+import android.car.ICarBugreportCallback;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.ParcelFileDescriptor;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.mockito.junit.MockitoRule;
+
+/**
+ * Unit tests for {@link CarBugreportManagerService}.
+ *
+ * <p>Run {@code atest CarServiceUnitTest:CarBugreportManagerServiceTest}.
+ */
+@SmallTest
+@RunWith(MockitoJUnitRunner.class)
+public class CarBugreportManagerServiceTest {
+    private static final boolean DUMPSTATE_DRY_RUN = true;
+
+    @Rule public MockitoRule rule = MockitoJUnit.rule();
+
+    private CarBugreportManagerService mService;
+
+    @Mock private Context mMockContext;
+    @Mock private Resources mMockResources;
+    @Mock private PackageManager mMockPackageManager;
+    @Mock private ICarBugreportCallback mMockCallback;
+    @Mock private ParcelFileDescriptor mMockOutput;
+    @Mock private ParcelFileDescriptor mMockExtraOutput;
+
+    @Before
+    public void setUp() {
+        when(mMockContext.getResources()).thenReturn(mMockResources);
+        when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+    }
+
+    @After
+    public void tearDown() {
+        if (mService != null) {
+            mService.release();
+        }
+    }
+
+    @Test
+    public void test_requestBugreport_failsIfNotDesignatedAppOnUserBuild() {
+        mService = new CarBugreportManagerService(mMockContext, /* isUserBuild= */ true);
+        mService.init();
+        when(mMockPackageManager.checkSignatures(anyInt(), anyInt()))
+                .thenReturn(PackageManager.SIGNATURE_MATCH);
+        when(mMockPackageManager.getNameForUid(anyInt())).thenReturn("current_app_name");
+        when(mMockResources.getString(
+                R.string.config_car_bugreport_application)).thenReturn("random_app_name");
+
+        SecurityException expected =
+                expectThrows(SecurityException.class,
+                        () -> mService.requestBugreport(mMockOutput, mMockExtraOutput,
+                                mMockCallback, DUMPSTATE_DRY_RUN));
+
+        assertThat(expected).hasMessageThat().contains(
+                "Caller current_app_name is not a designated bugreport app");
+    }
+
+    @Test
+    public void test_requestBugreport_failsIfNotSignedWithPlatformKeys() {
+        mService = new CarBugreportManagerService(mMockContext);
+        mService.init();
+        when(mMockPackageManager.checkSignatures(anyInt(), anyInt()))
+                .thenReturn(PackageManager.SIGNATURE_NO_MATCH);
+        when(mMockPackageManager.getNameForUid(anyInt())).thenReturn("current_app_name");
+
+        SecurityException expected =
+                expectThrows(SecurityException.class,
+                        () -> mService.requestBugreport(mMockOutput, mMockExtraOutput,
+                                mMockCallback, DUMPSTATE_DRY_RUN));
+
+        assertThat(expected).hasMessageThat().contains(
+                "Caller current_app_name does not have the right signature");
+    }
+}
diff --git a/tests/carservice_unit_test/src/com/android/car/CarInputServiceTest.java b/tests/carservice_unit_test/src/com/android/car/CarInputServiceTest.java
index 21da1d6..2e90523 100644
--- a/tests/carservice_unit_test/src/com/android/car/CarInputServiceTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/CarInputServiceTest.java
@@ -17,6 +17,7 @@
 package com.android.car;
 
 import static android.car.CarOccupantZoneManager.DisplayTypeEnum;
+import static android.car.input.CustomInputEvent.INPUT_CODE_F1;
 
 import static com.android.compatibility.common.util.SystemUtil.eventually;
 
@@ -36,6 +37,7 @@
 import static org.mockito.Mockito.ignoreStubs;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
@@ -44,6 +46,10 @@
 import android.bluetooth.BluetoothProfile;
 import android.car.CarOccupantZoneManager;
 import android.car.CarProjectionManager;
+import android.car.input.CarInputManager;
+import android.car.input.CustomInputEvent;
+import android.car.input.ICarInputCallback;
+import android.car.input.RotaryEvent;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -57,7 +63,6 @@
 import android.os.UserHandle;
 import android.service.voice.VoiceInteractionSession;
 import android.telecom.TelecomManager;
-import android.view.Display;
 import android.view.KeyEvent;
 
 import androidx.test.core.app.ApplicationProvider;
@@ -68,6 +73,7 @@
 
 import com.google.common.collect.Range;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -120,6 +126,108 @@
         when(mShouldCallButtonEndOngoingCallSupplier.getAsBoolean()).thenReturn(false);
     }
 
+    @After
+    public void tearDown() {
+        if (mCarInputService != null) {
+            mCarInputService.release();
+        }
+    }
+
+    @Test
+    public void testOnRotaryEvent_injectingRotaryNavigationEvent() {
+        RotaryEvent event = new RotaryEvent(
+                /* inputType= */ CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION,
+                /* clockwise= */ true,
+                /* uptimeMillisForClicks= */ new long[]{1, 1});
+        when(mCaptureController.onRotaryEvent(
+                same(CarOccupantZoneManager.DISPLAY_TYPE_MAIN), same(event))).thenReturn(true);
+        when(mCaptureController.onKeyEvent(anyInt(), any(KeyEvent.class))).thenReturn(true);
+
+        mCarInputService.onRotaryEvent(event, CarOccupantZoneManager.DISPLAY_TYPE_MAIN);
+
+        // Since mCaptureController processed RotaryEvent, then no KeyEvent was generated or
+        // processed
+        verify(mCarOccupantZoneService, never()).getDisplayIdForDriver(anyInt());
+        verify(mCaptureController, never()).onKeyEvent(anyInt(), any(KeyEvent.class));
+        verify(mDefaultMainListener, never()).onKeyEvent(any(KeyEvent.class));
+    }
+
+    @Test
+    public void testOnRotaryEvent_injectingRotaryVolumeEvent() {
+        RotaryEvent event = new RotaryEvent(
+                /* inputType= */ CarInputManager.INPUT_TYPE_ROTARY_VOLUME,
+                /* clockwise= */ true,
+                /* uptimeMillisForClicks= */ new long[]{1, 1});
+        when(mCaptureController.onRotaryEvent(
+                same(CarOccupantZoneManager.DISPLAY_TYPE_MAIN), same(event))).thenReturn(false);
+        when(mCaptureController.onKeyEvent(anyInt(), any(KeyEvent.class))).thenReturn(true);
+
+        mCarInputService.onRotaryEvent(event, CarOccupantZoneManager.DISPLAY_TYPE_MAIN);
+
+        // Since mCaptureController processed RotaryEvent, then KeyEvent was generated or
+        // processed
+        int numberOfGeneratedKeyEvents = 4;
+        verify(mCarOccupantZoneService, times(numberOfGeneratedKeyEvents)).getDisplayIdForDriver(
+                eq(CarOccupantZoneManager.DISPLAY_TYPE_MAIN));
+        verify(mCaptureController, times(numberOfGeneratedKeyEvents)).onKeyEvent(anyInt(),
+                any(KeyEvent.class));
+        verify(mDefaultMainListener, never()).onKeyEvent(any(KeyEvent.class));
+    }
+
+    @Test
+    public void testOnRotaryEvent_injectingRotaryNavigation_notConsumedByCaptureController() {
+        RotaryEvent event = new RotaryEvent(
+                /* inputType= */ CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION,
+                /* clockwise= */ true,
+                /* uptimeMillisForClicks= */ new long[]{1, 1});
+        when(mCaptureController.onRotaryEvent(
+                same(CarOccupantZoneManager.DISPLAY_TYPE_MAIN), same(event))).thenReturn(false);
+
+        mCarInputService.onRotaryEvent(event, CarOccupantZoneManager.DISPLAY_TYPE_MAIN);
+
+        // Since mCaptureController processed RotaryEvent, then KeyEvent was generated or
+        // processed
+        int numberOfGeneratedKeyEvents = 4;
+        verify(mCarOccupantZoneService, times(numberOfGeneratedKeyEvents)).getDisplayIdForDriver(
+                eq(CarOccupantZoneManager.DISPLAY_TYPE_MAIN));
+        verify(mCaptureController, times(numberOfGeneratedKeyEvents)).onKeyEvent(anyInt(),
+                any(KeyEvent.class));
+        verify(mDefaultMainListener, times(numberOfGeneratedKeyEvents)).onKeyEvent(
+                any(KeyEvent.class));
+    }
+
+    @Test
+    public void testRequestInputEventCapture_delegatesToCaptureController() {
+        ICarInputCallback callback = mock(ICarInputCallback.class);
+        int[] inputTypes = new int[]{CarInputManager.INPUT_TYPE_CUSTOM_INPUT_EVENT};
+        int requestFlags = CarInputManager.CAPTURE_REQ_FLAGS_ALLOW_DELAYED_GRANT;
+        mCarInputService.requestInputEventCapture(callback,
+                CarOccupantZoneManager.DISPLAY_TYPE_MAIN, inputTypes, requestFlags);
+
+        verify(mCaptureController).requestInputEventCapture(same(callback),
+                eq(CarOccupantZoneManager.DISPLAY_TYPE_MAIN), same(inputTypes), eq(requestFlags));
+    }
+
+    @Test
+    public void testOnCustomInputEvent_delegatesToCaptureController() {
+        CustomInputEvent event = new CustomInputEvent(INPUT_CODE_F1,
+                CarOccupantZoneManager.DISPLAY_TYPE_MAIN, /* repeatCounter= */ 1);
+
+        mCarInputService.onCustomInputEvent(event);
+
+        verify(mCaptureController).onCustomInputEvent(same(event));
+    }
+
+    @Test
+    public void testReleaseInputEventCapture_delegatesToCaptureController() {
+        ICarInputCallback callback = mock(ICarInputCallback.class);
+        mCarInputService.releaseInputEventCapture(callback,
+                CarOccupantZoneManager.DISPLAY_TYPE_MAIN);
+
+        verify(mCaptureController).releaseInputEventCapture(same(callback),
+                eq(CarOccupantZoneManager.DISPLAY_TYPE_MAIN));
+    }
+
     @Test
     public void ensureBluetoothAdapterWasInitialized() {
         eventually(() -> verify(mBluetoothAdapter).getProfileProxy(same(mContext),
@@ -570,7 +678,6 @@
         KeyEvent event = new KeyEvent(/* downTime= */ currentTime,
                 /* eventTime= */ currentTime, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER,
                 /* repeat= */ 0);
-
         event.setDisplayId(android.view.Display.INVALID_DISPLAY);
 
         injectKeyEventAndVerify(event, CarOccupantZoneManager.DISPLAY_TYPE_MAIN);
@@ -585,7 +692,7 @@
         KeyEvent event = new KeyEvent(/* downTime= */ currentTime,
                 /* eventTime= */ currentTime, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER,
                 /* repeat= */ 0);
-        event.setDisplayId(android.view.Display.DEFAULT_DISPLAY);
+        event.setDisplayId(android.view.Display.INVALID_DISPLAY);
 
         injectKeyEventAndVerify(event, CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER);
 
@@ -594,20 +701,17 @@
     }
 
     private void injectKeyEventAndVerify(KeyEvent event, @DisplayTypeEnum int displayType) {
-        // Arrange
         doReturn(PackageManager.PERMISSION_GRANTED).when(mContext).checkCallingOrSelfPermission(
                 android.Manifest.permission.INJECT_EVENTS);
-
         int someDisplayId = Integer.MAX_VALUE;
         when(mCarOccupantZoneService.getDisplayIdForDriver(anyInt())).thenReturn(someDisplayId);
-
         assertThat(event.getDisplayId()).isNotEqualTo(someDisplayId);
 
-        // Act
         mCarInputService.injectKeyEvent(event, displayType);
 
-        // Assert display id was updated as expected
-        assertThat(event.getDisplayId()).isEqualTo(someDisplayId);
+        verify(mCarOccupantZoneService).getDisplayIdForDriver(displayType);
+        assertWithMessage("Event's display id not updated as expected").that(
+                event.getDisplayId()).isEqualTo(someDisplayId);
     }
 
     @Test
diff --git a/tests/carservice_unit_test/src/com/android/car/PermissionHelperTest.java b/tests/carservice_unit_test/src/com/android/car/PermissionHelperTest.java
new file mode 100644
index 0000000..d8ff974
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/PermissionHelperTest.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car;
+
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.testng.Assert.expectThrows;
+
+import android.app.ActivityManager;
+import android.car.test.mocks.AbstractExtendedMockitoTestCase;
+import android.os.Binder;
+import android.util.Log;
+
+import org.junit.Test;
+
+public final class PermissionHelperTest extends AbstractExtendedMockitoTestCase {
+
+    private static final String TAG = PermissionHelperTest.class.getSimpleName();
+    private static final String MESSAGE = "D'OH!";
+
+    private static final int UID = Binder.getCallingUid();
+
+    private static final String PERMISSION1 = "LicenseToKill";
+    private static final String PERMISSION2 = "LicenseToLove";
+
+    @Override
+    protected void onSessionBuilder(CustomMockitoSessionBuilder session) {
+        session.spyStatic(ActivityManager.class);
+    }
+
+    @Test
+    public void testHasAtLeastOnePermission_none() {
+        mockPermission(PERMISSION1, PERMISSION_DENIED);
+        mockPermission(PERMISSION2, PERMISSION_DENIED);
+
+        assertWithMessage("has at least %s", PERMISSION2).that(
+                PermissionHelper.hasAtLeastOnePermissionGranted(UID, PERMISSION1, PERMISSION2))
+                .isFalse();
+    }
+
+    @Test
+    public void testHasAtLeastOnePermission_one() {
+        mockPermission(PERMISSION1, PERMISSION_DENIED);
+        mockPermission(PERMISSION2, PERMISSION_GRANTED);
+
+        assertWithMessage("has at least %s", PERMISSION2).that(
+                PermissionHelper.hasAtLeastOnePermissionGranted(UID, PERMISSION1, PERMISSION2))
+                .isTrue();
+    }
+
+    @Test
+    public void testHasAtLeastOnePermission_both() {
+        mockPermission(PERMISSION1, PERMISSION_GRANTED);
+        mockPermission(PERMISSION2, PERMISSION_GRANTED);
+
+        assertWithMessage("has at least %s", PERMISSION2).that(
+                PermissionHelper.hasAtLeastOnePermissionGranted(UID, PERMISSION1, PERMISSION2))
+                .isTrue();
+    }
+
+    @Test
+    public void testCheckHasAtLeastOnePermission_none() {
+        mockPermission(PERMISSION1, PERMISSION_DENIED);
+        mockPermission(PERMISSION2, PERMISSION_DENIED);
+
+        SecurityException exception = expectThrows(SecurityException.class, () -> PermissionHelper
+                .checkHasAtLeastOnePermissionGranted(MESSAGE, PERMISSION1, PERMISSION2));
+
+        assertExceptionMessageContains(exception, MESSAGE);
+    }
+
+    @Test
+    public void testCheckHasAtLeastOnePermissionGranted_one() {
+        mockPermission(PERMISSION1, PERMISSION_DENIED);
+        mockPermission(PERMISSION2, PERMISSION_GRANTED);
+
+        PermissionHelper.checkHasAtLeastOnePermissionGranted(MESSAGE, PERMISSION1, PERMISSION2);
+    }
+
+    @Test
+    public void testCheckHasAtLeastOnePermissionGranted_both() {
+        mockPermission(PERMISSION1, PERMISSION_DENIED);
+        mockPermission(PERMISSION2, PERMISSION_GRANTED);
+
+        PermissionHelper.checkHasAtLeastOnePermissionGranted(MESSAGE, PERMISSION1, PERMISSION2);
+    }
+
+    @Test
+    public void testCheckHasDumpPermissionGranted_notGranted() {
+        mockPermission(android.Manifest.permission.DUMP, PERMISSION_DENIED);
+
+        SecurityException exception = expectThrows(SecurityException.class,
+                () -> PermissionHelper.checkHasDumpPermissionGranted(MESSAGE));
+
+        assertExceptionMessageContains(exception, MESSAGE);
+        assertExceptionMessageContains(exception, android.Manifest.permission.DUMP);
+    }
+
+    @Test
+    public void testCheckHasDumpPermissionGranted_granted() {
+        mockPermission(android.Manifest.permission.DUMP, PERMISSION_GRANTED);
+
+        PermissionHelper.checkHasDumpPermissionGranted(MESSAGE);
+    }
+
+    private void assertExceptionMessageContains(Exception exception, String subString) {
+        assertWithMessage("exception (%s) message", exception).that(exception.getMessage())
+                .contains(subString);
+    }
+
+    private void mockPermission(String permission, int value) {
+        Log.d(TAG, "mockHasPermissions(): uid=" + UID + ", permission=" + permission
+                + ", granted=" + (value == PERMISSION_GRANTED));
+
+        doReturn(value).when(() -> ActivityManager.checkComponentPermission(eq(permission),
+                eq(UID), anyInt(), anyBoolean()));
+    }
+}
diff --git a/tests/carservice_unit_test/src/com/android/car/SparseArrayStreamTest.java b/tests/carservice_unit_test/src/com/android/car/SparseArrayStreamTest.java
new file mode 100644
index 0000000..3016027
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/SparseArrayStreamTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static java.util.stream.Collectors.toList;
+
+import android.util.Pair;
+import android.util.SparseArray;
+
+import com.google.common.collect.ImmutableList;
+
+import org.junit.Test;
+
+import java.util.List;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+public final class SparseArrayStreamTest {
+
+    @Test
+    public void testKeyStream() {
+        SparseArray<String> array = createSparseStringArray();
+        ImmutableList<Integer> expected = ImmutableList.of(5, 20, 1000);
+
+        IntStream actual = SparseArrayStream.keyStream(array);
+
+        assertContainsExactlyInOrder(actual.boxed(), expected);
+    }
+
+    @Test
+    public void testKeyStream_empty() {
+        assertContainsExactlyInOrder(
+                SparseArrayStream.keyStream(new SparseArray<String>()).boxed(), ImmutableList.of());
+    }
+
+    @Test
+    public void testValueStream() {
+        SparseArray<String> array = createSparseStringArray();
+        ImmutableList<String> expected = ImmutableList.of("five", "twenty", "thousand");
+
+        Stream<String> actual = SparseArrayStream.valueStream(array);
+
+        assertContainsExactlyInOrder(actual, expected);
+    }
+
+    @Test
+    public void testValueStream_empty() {
+        assertContainsExactlyInOrder(
+                SparseArrayStream.valueStream(new SparseArray<String>()), ImmutableList.of());
+    }
+
+    @Test
+    public void testPairStream() {
+        SparseArray<String> array = createSparseStringArray();
+        ImmutableList<Pair<Integer, String>> expected = ImmutableList.of(
+                new Pair<>(5, "five"), new Pair<>(20, "twenty"), new Pair<>(1000, "thousand"));
+
+        Stream<Pair<Integer, String>> actual = SparseArrayStream.pairStream(array);
+
+        assertContainsExactlyInOrder(actual, expected);
+    }
+
+    @Test
+    public void testPairStream_empty() {
+        assertContainsExactlyInOrder(
+                SparseArrayStream.pairStream(new SparseArray<String>()), ImmutableList.of());
+    }
+
+    private static SparseArray<String> createSparseStringArray() {
+        SparseArray<String> array = new SparseArray<>();
+        array.put(5, "five");
+        array.put(20, "twenty");
+        array.put(1000, "thousand");
+
+        return array;
+    }
+
+    private static <T> void assertContainsExactlyInOrder(Stream<T> actual, List<T> expected) {
+        // TODO: Use Truth8 StreamSubject when it becomes available.
+        assertThat(actual.collect(toList())).containsExactlyElementsIn(expected).inOrder();
+    }
+}
diff --git a/tests/carservice_unit_test/src/com/android/car/UtilsGetAdapterStateNameTest.java b/tests/carservice_unit_test/src/com/android/car/UtilsGetAdapterStateNameTest.java
new file mode 100644
index 0000000..c536e9a
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/UtilsGetAdapterStateNameTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.bluetooth.BluetoothAdapter;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+@RunWith(Parameterized.class)
+public final class UtilsGetAdapterStateNameTest {
+
+    private final int mState;
+    private final String mName;
+
+    public UtilsGetAdapterStateNameTest(int state, String name) {
+        mState = state;
+        mName = name;
+    }
+
+    @Test
+    public void testGetAdapterStateName() {
+        String result = Utils.getAdapterStateName(mState);
+
+        assertThat(result).contains(String.valueOf(mState));
+        assertThat(result).ignoringCase().contains(mName);
+    }
+
+    @Parameterized.Parameters
+    public static Collection provideParams() {
+        return Arrays.asList(
+            new Object[][] {
+                {BluetoothAdapter.STATE_ON, "on"},
+                {BluetoothAdapter.STATE_OFF, "off"},
+                {BluetoothAdapter.STATE_TURNING_ON, "turning on"},
+                {BluetoothAdapter.STATE_TURNING_OFF, "turning off"},
+                {9, "unknown"},
+                {14, "unknown"}
+            });
+    }
+}
diff --git a/tests/carservice_unit_test/src/com/android/car/UtilsGetBondStateNameTest.java b/tests/carservice_unit_test/src/com/android/car/UtilsGetBondStateNameTest.java
new file mode 100644
index 0000000..9a89e59
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/UtilsGetBondStateNameTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.bluetooth.BluetoothDevice;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+@RunWith(Parameterized.class)
+public final class UtilsGetBondStateNameTest {
+
+    private final int mState;
+    private final String mName;
+
+    public UtilsGetBondStateNameTest(int state, String name) {
+        mState = state;
+        mName = name;
+    }
+
+    @Test
+    public void testGetBondStateName() {
+        String result = Utils.getBondStateName(mState);
+
+        assertThat(result).contains(String.valueOf(mState));
+        assertThat(result).ignoringCase().contains(mName);
+    }
+
+    @Parameterized.Parameters
+    public static Collection provideParams() {
+        return Arrays.asList(
+            new Object[][] {
+                {BluetoothDevice.BOND_BONDED, "bonded"},
+                {BluetoothDevice.BOND_BONDING, "bonding"},
+                {BluetoothDevice.BOND_NONE, "unbonded"},
+                {9, "unknown"},
+                {13, "unknown"}
+            });
+    }
+}
diff --git a/tests/carservice_unit_test/src/com/android/car/UtilsGetConnectionStateNameTest.java b/tests/carservice_unit_test/src/com/android/car/UtilsGetConnectionStateNameTest.java
new file mode 100644
index 0000000..74bfffb
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/UtilsGetConnectionStateNameTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.bluetooth.BluetoothAdapter;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+@RunWith(Parameterized.class)
+public final class UtilsGetConnectionStateNameTest {
+
+    private final int mState;
+    private final String mName;
+
+    public UtilsGetConnectionStateNameTest(int state, String name) {
+        mState = state;
+        mName = name;
+    }
+
+    @Test
+    public void testGetConnectionStateName() {
+        String result = Utils.getConnectionStateName(mState);
+
+        assertThat(result).contains(String.valueOf(mState));
+        assertThat(result).ignoringCase().contains(mName);
+    }
+
+    @Parameterized.Parameters
+    public static Collection provideParams() {
+        return Arrays.asList(
+            new Object[][] {
+                {BluetoothAdapter.STATE_CONNECTED, "connected"},
+                {BluetoothAdapter.STATE_DISCONNECTED, "disconnected"},
+                {BluetoothAdapter.STATE_CONNECTING, "connecting"},
+                {BluetoothAdapter.STATE_DISCONNECTING, "disconnecting"},
+                {-1, "unknown"},
+                {4, "unknown"}
+            });
+    }
+}
diff --git a/tests/carservice_unit_test/src/com/android/car/UtilsGetProfileNameTest.java b/tests/carservice_unit_test/src/com/android/car/UtilsGetProfileNameTest.java
new file mode 100644
index 0000000..54220b3
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/UtilsGetProfileNameTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.bluetooth.BluetoothProfile;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+@RunWith(Parameterized.class)
+public final class UtilsGetProfileNameTest {
+
+    private final int mProfile;
+    private final String mName;
+
+    public UtilsGetProfileNameTest(int profile, String name) {
+        mProfile = profile;
+        mName = name;
+    }
+
+    @Test
+    public void testGetProfileName() {
+        String result = Utils.getProfileName(mProfile);
+
+        assertThat(result).contains(String.valueOf(mProfile));
+        assertThat(result).ignoringCase().contains(mName);
+    }
+
+    @Parameterized.Parameters
+    public static Collection provideParams() {
+        return Arrays.asList(
+            new Object[][] {
+                {BluetoothProfile.HEADSET, "HFP Server"},
+                {BluetoothProfile.A2DP, "A2DP Source"},
+                {BluetoothProfile.HEALTH, "HDP"},
+                {BluetoothProfile.HID_HOST, "HID Host"},
+                {BluetoothProfile.PAN, "PAN"},
+                {BluetoothProfile.PBAP, "PBAP Server"},
+                {BluetoothProfile.GATT, "GATT Client"},
+                {BluetoothProfile.GATT_SERVER, "GATT Server"},
+                {BluetoothProfile.MAP, "MAP Server"},
+                {BluetoothProfile.SAP, "SAP"},
+                {BluetoothProfile.A2DP_SINK, "A2DP Sink"},
+                {BluetoothProfile.AVRCP_CONTROLLER, "AVRCP Controller"},
+                {BluetoothProfile.AVRCP, "AVRCP Target"},
+                {BluetoothProfile.HEADSET_CLIENT, "HFP Client"},
+                {BluetoothProfile.PBAP_CLIENT, "PBAP Client"},
+                {BluetoothProfile.MAP_CLIENT, "MAP Client"},
+                {BluetoothProfile.HID_DEVICE, "HID Device"},
+                {BluetoothProfile.OPP, "OPP"},
+                {BluetoothProfile.HEARING_AID, "Hearing Aid"},
+                {0, "unknown"},
+                {22, "unknown"}
+            });
+    }
+}
diff --git a/tests/carservice_unit_test/src/com/android/car/UtilsGetProfilePriorityNameTest.java b/tests/carservice_unit_test/src/com/android/car/UtilsGetProfilePriorityNameTest.java
new file mode 100644
index 0000000..4ed8175
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/UtilsGetProfilePriorityNameTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.bluetooth.BluetoothProfile;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+@RunWith(Parameterized.class)
+public final class UtilsGetProfilePriorityNameTest {
+
+    private final int mPriority;
+    private final String mName;
+
+    public UtilsGetProfilePriorityNameTest(int priority, String name) {
+        mPriority = priority;
+        mName = name;
+    }
+
+    @Test
+    public void testGetProfilePriorityName() {
+        String result = Utils.getProfilePriorityName(mPriority);
+
+        assertThat(result).contains(String.valueOf(mPriority));
+        assertThat(result).ignoringCase().contains(mName);
+    }
+
+    @Parameterized.Parameters
+    public static Collection provideParams() {
+        return Arrays.asList(
+            new Object[][] {
+                // Any value >= PRIORITY_AUTO_CONNECT maps to "PRIORITY_AUTO_CONNECT".
+                {1001, "PRIORITY_AUTO_CONNECT"},
+                {BluetoothProfile.PRIORITY_AUTO_CONNECT, "PRIORITY_AUTO_CONNECT"},
+                // PRIORITY_AUTO_CONNECT > value >= PRIORITY_ON maps to "PRIORITY_ON".
+                {999, "PRIORITY_ON"},
+                {BluetoothProfile.PRIORITY_ON, "PRIORITY_ON"},
+                // PRIORITY_ON > value >= PRIORITY_OFF mpas to "PRIORITY_OFF".
+                {99, "PRIORITY_OFF"},
+                {BluetoothProfile.PRIORITY_OFF, "PRIORITY_OFF"},
+                // value < PRIORITY_OFF maps to "PRIORITY_UNDEFINED".
+                {BluetoothProfile.PRIORITY_UNDEFINED, "PRIORITY_UNDEFINED"},
+                {-2, "PRIORITY_UNDEFINED"}
+            });
+    }
+}
diff --git a/tests/carservice_unit_test/src/com/android/car/UtilsTest.java b/tests/carservice_unit_test/src/com/android/car/UtilsTest.java
new file mode 100644
index 0000000..16285b4
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/UtilsTest.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothDevice;
+import android.text.TextUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.UUID;
+
+@RunWith(MockitoJUnitRunner.class)
+public final class UtilsTest {
+
+    @Mock
+    private BluetoothDevice mMockBluetoothDevice;
+
+    @Test
+    public void testGetDeviceDebugInfo() {
+        when(mMockBluetoothDevice.getName()).thenReturn("deviceName");
+        when(mMockBluetoothDevice.getAddress()).thenReturn("deviceAddress");
+
+        assertThat(Utils.getDeviceDebugInfo(mMockBluetoothDevice))
+            .isEqualTo("(name = deviceName, addr = deviceAddress)");
+    }
+
+    @Test
+    public void testGetDeviceDebugInfo_nullDevice() {
+        assertThat(Utils.getDeviceDebugInfo(null)).isEqualTo("(null)");
+    }
+
+    @Test
+    public void testTransitionLogToString() {
+        Utils.TransitionLog transitionLog =
+                new Utils.TransitionLog("serviceName", "state1", "state2", 1623777864000L);
+        String result = transitionLog.toString();
+
+        assertThat(result).startsWith("06-15 17:24:24");
+        assertThat(result).contains("serviceName:");
+        assertThat(result).contains("from state1 to state2");
+    }
+
+    @Test
+    public void testTransitionLogToString_withExtra() {
+        Utils.TransitionLog transitionLog =
+                new Utils.TransitionLog("serviceName", "state1", "state2", 1623777864000L, "extra");
+        String result = transitionLog.toString();
+
+        assertThat(result).startsWith("06-15 17:24:24");
+        assertThat(result).contains("serviceName:");
+        assertThat(result).contains("extra");
+        assertThat(result).contains("from state1 to state2");
+    }
+
+    @Test
+    public void testLongToBytes() {
+        long longValue = 1234567890L;
+        byte[] expected = new byte[] {0, 0, 0, 0, 73, -106, 2, -46};
+
+        assertThat(Utils.longToBytes(longValue)).isEqualTo(expected);
+    }
+
+    @Test
+    public void testBytesToLong() {
+        byte[] bytes = new byte[] {0, 0, 0, 0, 73, -106, 2, -46};
+        long expected = 1234567890L;
+
+        assertThat(Utils.bytesToLong(bytes)).isEqualTo(expected);
+    }
+
+    @Test
+    public void testByteArrayToHexString() {
+        assertThat(Utils.byteArrayToHexString(new byte[] {0, 1, 2, -3})).isEqualTo("000102fd");
+    }
+
+    @Test
+    public void testUuidToBytes() {
+        UUID uuid = new UUID(123456789L, 987654321L);
+        byte[] expected = new byte[] {0, 0, 0, 0, 7, 91, -51, 21, 0, 0, 0, 0, 58, -34, 104, -79};
+
+        assertThat(Utils.uuidToBytes(uuid)).isEqualTo(expected);
+    }
+
+    @Test
+    public void testBytesToUUID() {
+        byte[] bytes = new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, -9, -8, -7, -6, -5, -4, -3};
+        UUID expected = new UUID(72623859790382856L, 718316418130246909L);
+
+        assertThat(Utils.bytesToUUID(bytes).getLeastSignificantBits())
+                .isEqualTo(718316418130246909L);
+        assertThat(Utils.bytesToUUID(bytes).getMostSignificantBits()).isEqualTo(72623859790382856L);
+        assertThat(Utils.bytesToUUID(bytes)).isEqualTo(expected);
+    }
+
+    @Test
+    public void testBytesToUUID_invalidLength() {
+        byte[] bytes = new byte[] {0};
+
+        assertThat(Utils.bytesToUUID(bytes)).isNull();
+    }
+
+    @Test
+    public void testGenerateRandomNumberString() {
+        String result = Utils.generateRandomNumberString(25);
+
+        assertThat(result).hasLength(25);
+        assertThat(TextUtils.isDigitsOnly(result)).isTrue();
+    }
+
+    @Test
+    public void testConcatByteArrays() {
+        byte[] bytes1 = new byte[] {1, 2, 3};
+        byte[] bytes2 = new byte[] {4, 5, 6};
+        Byte[] expected = new Byte[] {1, 2, 3, 4, 5, 6};
+
+        assertThat(Utils.concatByteArrays(bytes1, bytes2)).asList()
+                .containsExactlyElementsIn(expected).inOrder();
+    }
+}
diff --git a/tests/carservice_unit_test/src/com/android/car/admin/NewUserDisclaimerActivityTest.java b/tests/carservice_unit_test/src/com/android/car/admin/NewUserDisclaimerActivityTest.java
new file mode 100644
index 0000000..b1e8b72
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/admin/NewUserDisclaimerActivityTest.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2021 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.admin;
+
+import static android.app.Notification.EXTRA_TEXT;
+import static android.app.Notification.EXTRA_TITLE;
+import static android.app.Notification.FLAG_ONGOING_EVENT;
+
+import static com.android.car.admin.NotificationHelper.CHANNEL_ID_DEFAULT;
+import static com.android.car.admin.NotificationHelper.NEW_USER_DISCLAIMER_NOTIFICATION_ID;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.UiAutomation;
+import android.car.test.mocks.AbstractExtendedMockitoTestCase;
+import android.car.test.mocks.JavaMockitoHelper;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+import android.widget.Button;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+
+import com.android.car.R;
+import com.android.car.admin.ui.ManagedDeviceTextView;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+import java.util.concurrent.CountDownLatch;
+
+@RunWith(AndroidJUnit4.class)
+public final class NewUserDisclaimerActivityTest extends AbstractExtendedMockitoTestCase {
+
+    private static final String TAG = NewUserDisclaimerActivityTest.class.getSimpleName();
+
+    private static final long TIMEOUT_MS = 1_000;
+
+    private final Context mRealContext = InstrumentationRegistry.getInstrumentation()
+            .getContext();
+
+    private final UiAutomation mUiAutomation =
+            InstrumentationRegistry.getInstrumentation().getUiAutomation();
+
+    // NOTE: Cannot launch activity automatically as we need to mock
+    // PerUserCarDevicePolicyService.getInstance() first
+    @Rule
+    public ActivityTestRule<NewUserDisclaimerActivity> mActivityRule = new ActivityTestRule(
+            NewUserDisclaimerActivity.class,  /* initialTouchMode= */ false,
+            /* launchActivity= */ false);
+
+    private NewUserDisclaimerActivity mActivity;
+
+    private Context mSpiedContext;
+
+    @Mock
+    private PerUserCarDevicePolicyService mService;
+
+    @Mock
+    private NotificationManager mNotificationManager;
+
+    @Override
+    protected void onSessionBuilder(CustomMockitoSessionBuilder session) {
+        session.spyStatic(PerUserCarDevicePolicyService.class);
+    }
+
+    @Before
+    public void setFixtures() {
+        Log.v(TAG, "setFixtures(): mocking PerUserCarDevicePolicyService.getInstance()");
+        doReturn(mService).when(() -> PerUserCarDevicePolicyService.getInstance(any()));
+        mSpiedContext = spy(mRealContext);
+
+        when(mSpiedContext.getSystemService(NotificationManager.class))
+                .thenReturn(mNotificationManager);
+
+        Log.v(TAG, "setFixtures(): launching activitiy");
+        mActivity = mActivityRule.launchActivity(/* intent= */ null);
+
+        // It's called onResume()
+        verify(mService).setShown();
+    }
+
+    @Test
+    public void testAccept() throws Exception {
+        CountDownLatch latch = new CountDownLatch(1);
+        mActivity.runOnUiThread(() -> {
+            mActivity.onCreate(/* savedInstanceState= */ null);
+            Button button = mActivity.getAcceptButton();
+            Log.d(TAG, "Clicking accept button: " + button);
+            button.performClick();
+            latch.countDown();
+        });
+        JavaMockitoHelper.await(latch, TIMEOUT_MS);
+
+        verify(mService).setAcknowledged();
+        assertWithMessage("activity is finishing").that(mActivity.isFinishing()).isTrue();
+    }
+
+    @Test
+    public void testShowNotification() {
+        NewUserDisclaimerActivity.showNotification(mSpiedContext);
+
+        ArgumentCaptor<Notification> captor = ArgumentCaptor.forClass(Notification.class);
+        verify(mNotificationManager).notify(eq(NEW_USER_DISCLAIMER_NOTIFICATION_ID),
+                captor.capture());
+
+        Notification notification = captor.getValue();
+        assertWithMessage("notification").that(notification).isNotNull();
+        assertNotificationContents(notification);
+    }
+
+    @Test
+    public void testCancelNotification() throws Exception {
+        PendingIntent pendingIntent = NewUserDisclaimerActivity.getPendingIntent(mSpiedContext,
+                /* extraFlags = */ 0);
+        CountDownLatch cancelLatch = new CountDownLatch(1);
+        pendingIntent.registerCancelListener(pi -> cancelLatch.countDown());
+
+        NewUserDisclaimerActivity.cancelNotification(mSpiedContext);
+
+        verify(mNotificationManager).cancel(NEW_USER_DISCLAIMER_NOTIFICATION_ID);
+
+        // Assert pending intent was canceled (latch is counted down by the CancelListener)
+        JavaMockitoHelper.await(cancelLatch, TIMEOUT_MS);
+    }
+
+    private void assertNotificationContents(Notification notification) {
+        assertWithMessage("notification icon").that(notification.getSmallIcon()).isNotNull();
+        assertWithMessage("notification channel").that(notification.getChannelId())
+                .isEqualTo(CHANNEL_ID_DEFAULT);
+        assertWithMessage("notification flags has FLAG_ONGOING_EVENT")
+                .that(notification.flags & FLAG_ONGOING_EVENT).isEqualTo(FLAG_ONGOING_EVENT);
+
+        assertWithMessage("notification content pending intent")
+                .that(notification.contentIntent)
+                .isNotNull();
+        assertWithMessage("notification content pending intent is immutable")
+                .that(notification.contentIntent.isImmutable()).isTrue();
+        // Need android.permission.GET_INTENT_SENDER_INTENT to get the Intent
+        Intent intent;
+        mUiAutomation.adoptShellPermissionIdentity();
+        try {
+            intent = notification.contentIntent.getIntent();
+        } finally {
+            mUiAutomation.dropShellPermissionIdentity();
+        }
+        assertWithMessage("content intent").that(intent).isNotNull();
+        assertWithMessage("content intent component").that(intent.getComponent())
+                .isEqualTo(mActivity.getComponentName());
+
+        assertWithMessage("notification extras").that(notification.extras).isNotNull();
+        assertWithMessage("value of extra %s", EXTRA_TITLE)
+                .that(notification.extras.getString(EXTRA_TITLE))
+                .isEqualTo(mRealContext.getString(R.string.new_user_managed_notification_title));
+        assertWithMessage("value of extra %s", EXTRA_TEXT)
+                .that(notification.extras.getString(EXTRA_TEXT))
+                .isEqualTo(ManagedDeviceTextView.getManagedDeviceText(mRealContext));
+    }
+}
diff --git a/tests/carservice_unit_test/src/com/android/car/admin/NotificationHelperInvalidImportanceTest.java b/tests/carservice_unit_test/src/com/android/car/admin/NotificationHelperInvalidImportanceTest.java
new file mode 100644
index 0000000..de4af6d
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/admin/NotificationHelperInvalidImportanceTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2021 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.admin;
+
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_MAX;
+import static android.app.NotificationManager.IMPORTANCE_MIN;
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+
+import static com.android.car.admin.NotificationHelper.newNotificationBuilder;
+
+import static org.testng.Assert.assertThrows;
+
+import android.content.Context;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+@RunWith(Parameterized.class)
+public final class NotificationHelperInvalidImportanceTest {
+
+    @Rule
+    public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+    @Mock
+    private Context mContext;
+
+    private final int mImportance;
+
+    public NotificationHelperInvalidImportanceTest(int importance) {
+        mImportance = importance;
+    }
+
+    @Test
+    public void testNewNotificationBuilder() {
+        assertThrows(IllegalArgumentException.class,
+                () -> newNotificationBuilder(mContext, mImportance));
+    }
+
+    @Parameterized.Parameters
+    public static Collection<Integer> provideParams() {
+        return Arrays.asList(
+                IMPORTANCE_LOW,
+                IMPORTANCE_MAX,
+                IMPORTANCE_MIN,
+                IMPORTANCE_NONE,
+                IMPORTANCE_UNSPECIFIED,
+                Integer.MIN_VALUE,
+                Integer.MAX_VALUE);
+    }
+}
diff --git a/tests/carservice_unit_test/src/com/android/car/admin/NotificationHelperTest.java b/tests/carservice_unit_test/src/com/android/car/admin/NotificationHelperTest.java
new file mode 100644
index 0000000..8610265
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/admin/NotificationHelperTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021 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.admin;
+
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+
+import static com.android.car.admin.NotificationHelper.newNotificationBuilder;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.testng.Assert.expectThrows;
+
+import android.content.Context;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public final class NotificationHelperTest {
+
+    @Mock
+    private Context mContext;
+
+    @Test
+    public void testNewNotificationBuilder_nullContext() {
+        NullPointerException exception = expectThrows(NullPointerException.class,
+                () -> newNotificationBuilder(/* context= */ null, IMPORTANCE_HIGH));
+
+        assertWithMessage("exception message").that(exception.getMessage()).contains("context");
+    }
+}
diff --git a/tests/carservice_unit_test/src/com/android/car/admin/NotificationHelperValidImportanceTest.java b/tests/carservice_unit_test/src/com/android/car/admin/NotificationHelperValidImportanceTest.java
new file mode 100644
index 0000000..7997954
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/admin/NotificationHelperValidImportanceTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2021 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.admin;
+
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+
+import static com.android.car.admin.NotificationHelper.CHANNEL_ID_DEFAULT;
+import static com.android.car.admin.NotificationHelper.CHANNEL_ID_HIGH;
+import static com.android.car.admin.NotificationHelper.newNotificationBuilder;
+import static com.android.internal.R.string.android_system_label;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Bundle;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.car.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.Arrays;
+import java.util.List;
+
+
+@RunWith(Parameterized.class)
+public final class NotificationHelperValidImportanceTest {
+
+    private static final String SYSTEM_LABEL = "System, I am your Label!";
+    private static final String IMPORTANCE_NAME = "The name is Bond, Importance Bond!";
+
+    @Rule
+    public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+    private final Context mRealContext = InstrumentationRegistry.getInstrumentation().getContext();
+
+    private Context mSpiedContext;
+
+    @Mock
+    private NotificationManager mNotificationMgr;
+
+    private final int mImportance;
+    private final String mChannelId;
+    private final int mResId;
+
+    public NotificationHelperValidImportanceTest(int importance, String channelId, int resId) {
+        mImportance = importance;
+        mChannelId = channelId;
+        mResId = resId;
+    }
+
+    @Before
+    public void setContext() {
+        mSpiedContext = spy(mRealContext);
+        when(mSpiedContext.getSystemService(NotificationManager.class))
+                .thenReturn(mNotificationMgr);
+        when(mSpiedContext.getString(android_system_label)).thenReturn(SYSTEM_LABEL);
+        when(mSpiedContext.getString(mResId)).thenReturn(IMPORTANCE_NAME);
+    }
+
+    @Test
+    public void testNewNotificationBuilder() {
+        Notification.Builder builder = newNotificationBuilder(mSpiedContext, mImportance);
+        assertWithMessage("builder").that(builder).isNotNull();
+
+        // Check custom label
+        Bundle extras = builder.getExtras();
+        assertWithMessage("extras").that(extras).isNotNull();
+        String label = extras.getString(Notification.EXTRA_SUBSTITUTE_APP_NAME);
+        assertWithMessage("label (extra %s)", Notification.EXTRA_SUBSTITUTE_APP_NAME).that(label)
+                .isEqualTo(SYSTEM_LABEL);
+
+        // Check notification arguments
+        ArgumentCaptor<NotificationChannel> captor = ArgumentCaptor
+                .forClass(NotificationChannel.class);
+        verify(mNotificationMgr).createNotificationChannel(captor.capture());
+        NotificationChannel channel = captor.getValue();
+        assertWithMessage("channel id").that(channel.getId()).isEqualTo(mChannelId);
+        assertWithMessage("importance").that(channel.getImportance()).isEqualTo(mImportance);
+        assertWithMessage("name").that(channel.getName()).isEqualTo(IMPORTANCE_NAME);
+    }
+
+    @Parameterized.Parameters
+    public static List<Object[]> provideParams() {
+        return Arrays.asList(new Object[][] {
+            { IMPORTANCE_DEFAULT, CHANNEL_ID_DEFAULT, R.string.importance_default },
+            { IMPORTANCE_HIGH, CHANNEL_ID_HIGH, R.string.importance_high }
+        });
+    }
+}
diff --git a/tests/carservice_unit_test/src/com/android/car/admin/PerUserCarDevicePolicyServiceTest.java b/tests/carservice_unit_test/src/com/android/car/admin/PerUserCarDevicePolicyServiceTest.java
new file mode 100644
index 0000000..796afd5
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/admin/PerUserCarDevicePolicyServiceTest.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2021 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.admin;
+
+import static android.app.admin.DevicePolicyManager.ACTION_SHOW_NEW_USER_DISCLAIMER;
+
+import static com.android.car.admin.PerUserCarDevicePolicyService.NEW_USER_DISCLAIMER_STATUS_ACKED;
+import static com.android.car.admin.PerUserCarDevicePolicyService.NEW_USER_DISCLAIMER_STATUS_NEVER_RECEIVED;
+import static com.android.car.admin.PerUserCarDevicePolicyService.NEW_USER_DISCLAIMER_STATUS_NOTIFICATION_SENT;
+import static com.android.car.admin.PerUserCarDevicePolicyService.NEW_USER_DISCLAIMER_STATUS_RECEIVED;
+import static com.android.car.admin.PerUserCarDevicePolicyService.NEW_USER_DISCLAIMER_STATUS_SHOWN;
+import static com.android.car.admin.PerUserCarDevicePolicyService.newUserDisclaimerStatusToString;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.expectThrows;
+
+import android.app.admin.DevicePolicyManager;
+import android.car.test.mocks.AbstractExtendedMockitoTestCase;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import com.android.car.admin.PerUserCarDevicePolicyService.NewUserDisclaimerStatus;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+public final class PerUserCarDevicePolicyServiceTest extends AbstractExtendedMockitoTestCase {
+
+    @Mock
+    private Context mContext;
+
+    private PerUserCarDevicePolicyService mInstance;
+
+    @Mock
+    private DevicePolicyManager mDpm;
+
+    @Override
+    protected void onSessionBuilder(CustomMockitoSessionBuilder session) {
+        session.spyStatic(NewUserDisclaimerActivity.class);
+    }
+
+    @Before
+    public void setFixtures() {
+        when(mContext.getApplicationContext()).thenReturn(mContext);
+        when(mContext.getSystemService(DevicePolicyManager.class)).thenReturn(mDpm);
+
+        mInstance = new PerUserCarDevicePolicyService(mContext);
+        assertStatusString(NEW_USER_DISCLAIMER_STATUS_NEVER_RECEIVED);
+    }
+
+    @Test
+    public void testGetInstance() {
+        PerUserCarDevicePolicyService instance1 = PerUserCarDevicePolicyService
+                .getInstance(mContext);
+        assertWithMessage("getInstance()#1").that(instance1).isNotNull();
+        assertWithMessage("getInstance()#1").that(instance1).isNotSameInstanceAs(mInstance);
+
+        PerUserCarDevicePolicyService instance2 = PerUserCarDevicePolicyService
+                .getInstance(mContext);
+        assertWithMessage("getInstance()#2").that(instance2).isNotNull();
+        assertWithMessage("getInstance()#2").that(instance2).isNotSameInstanceAs(mInstance);
+
+        assertWithMessage("getInstance()#2").that(instance2).isSameInstanceAs(instance1);
+        assertWithMessage("getInstance()#1").that(instance1).isSameInstanceAs(instance2);
+    }
+
+    @Test
+    public void testGetInstance_nullContext() {
+        NullPointerException exception = expectThrows(NullPointerException.class,
+                () -> PerUserCarDevicePolicyService.getInstance(null));
+        assertWithMessage("exception message").that(exception.getMessage()).contains("context");
+    }
+
+    @Test
+    public void testCreateAndDestroy() {
+        BroadcastReceiver receiver = callOnCreate();
+
+        callOnDestroy(receiver);
+    }
+
+    @Test
+    public void testShowWhenIntentReceived() {
+        doAnswer((inv) -> {
+            assertStatusString(NEW_USER_DISCLAIMER_STATUS_RECEIVED);
+            return null;
+        }).when(() -> NewUserDisclaimerActivity.showNotification(any()));
+        BroadcastReceiver receiver  = callOnCreate();
+
+        sendShowNewUserDisclaimerBroadcast(receiver);
+
+        assertStatusString(NEW_USER_DISCLAIMER_STATUS_NOTIFICATION_SENT);
+        verify(() -> NewUserDisclaimerActivity.showNotification(mContext));
+    }
+
+    @Test
+    public void testSetShown() {
+        mInstance.setShown();
+
+        assertStatusString(NEW_USER_DISCLAIMER_STATUS_SHOWN);
+    }
+
+    @Test
+    public void testSetAcknowledged() {
+        doNothing().when(() -> NewUserDisclaimerActivity.cancelNotification(any()));
+
+        mInstance.setAcknowledged();
+
+        assertStatusString(NEW_USER_DISCLAIMER_STATUS_ACKED);
+        verify(() -> NewUserDisclaimerActivity.cancelNotification(mContext));
+
+        verify(mDpm).resetNewUserDisclaimer();
+    }
+
+    private BroadcastReceiver callOnCreate() {
+        ArgumentCaptor<BroadcastReceiver> captor = ArgumentCaptor.forClass(BroadcastReceiver.class);
+
+        mInstance.onCreate();
+
+        verify(mContext).registerReceiver(captor.capture(), any());
+        BroadcastReceiver receiver = captor.getValue();
+        assertWithMessage("BroadcastReceiver captured on onCreate()").that(receiver).isNotNull();
+
+        return receiver;
+    }
+
+    private void callOnDestroy(BroadcastReceiver receiver) {
+        mInstance.onDestroy();
+
+        verify(mContext).unregisterReceiver(receiver);
+    }
+
+    private void sendShowNewUserDisclaimerBroadcast(BroadcastReceiver receiver) {
+        receiver.onReceive(mContext, new Intent(ACTION_SHOW_NEW_USER_DISCLAIMER));
+    }
+
+    private void assertStatusString(@NewUserDisclaimerStatus int expectedStatus) {
+        int actualStatus = mInstance.getNewUserDisclaimerStatus();
+        assertWithMessage("newUserDisclaimerStatus (%s=%s, %s=%s)",
+                expectedStatus, newUserDisclaimerStatusToString(expectedStatus),
+                actualStatus, newUserDisclaimerStatusToString(actualStatus))
+                        .that(actualStatus).isEqualTo(expectedStatus);
+    }
+}
diff --git a/tests/carservice_unit_test/src/com/android/car/hal/PropertyHalServiceIdsTest.java b/tests/carservice_unit_test/src/com/android/car/hal/PropertyHalServiceIdsTest.java
index e128b19..814ccba 100644
--- a/tests/carservice_unit_test/src/com/android/car/hal/PropertyHalServiceIdsTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/hal/PropertyHalServiceIdsTest.java
@@ -16,6 +16,9 @@
 
 package com.android.car.hal;
 
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
 import android.car.Car;
 import android.car.VehicleHvacFanDirection;
 import android.car.VehiclePropertyIds;
@@ -29,10 +32,9 @@
 
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.car.internal.PropertyPermissionMapping;
 import com.android.car.vehiclehal.VehiclePropValueBuilder;
 
-import com.google.common.truth.Truth;
-
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
@@ -50,7 +52,7 @@
     public MockitoRule mMockitoRule = MockitoJUnit.rule();
 
     private PropertyHalServiceIds mPropertyHalServiceIds;
-
+    private PropertyPermissionMapping mPermissionMapping;
     private static final String TAG = PropertyHalServiceIdsTest.class.getSimpleName();
     private static final int VENDOR_PROPERTY_1 = 0x21e01111;
     private static final int VENDOR_PROPERTY_2 = 0x21e01112;
@@ -92,6 +94,7 @@
     @Before
     public void setUp() {
         mPropertyHalServiceIds = new PropertyHalServiceIds();
+        mPermissionMapping = new PropertyPermissionMapping();
         // set up read permission and write permission to VENDOR_PROPERTY_1
         CONFIG_ARRAY.add(VENDOR_PROPERTY_1);
         CONFIG_ARRAY.add(VehicleVendorPermission.PERMISSION_DEFAULT);
@@ -117,14 +120,18 @@
      */
     @Test
     public void checkPermissionForSystemProperty() {
-        Assert.assertEquals(Car.PERMISSION_CAR_ENGINE_DETAILED,
-                mPropertyHalServiceIds.getReadPermission(VehiclePropertyIds.ENGINE_OIL_LEVEL));
-        Assert.assertNull(
-                mPropertyHalServiceIds.getWritePermission(VehiclePropertyIds.ENGINE_OIL_LEVEL));
-        Assert.assertEquals(Car.PERMISSION_CONTROL_CAR_CLIMATE,
-                mPropertyHalServiceIds.getReadPermission(VehiclePropertyIds.HVAC_FAN_SPEED));
-        Assert.assertEquals(Car.PERMISSION_CONTROL_CAR_CLIMATE,
-                mPropertyHalServiceIds.getWritePermission(VehiclePropertyIds.HVAC_FAN_SPEED));
+        assertThat(mPropertyHalServiceIds.getReadPermission(VehiclePropertyIds.ENGINE_OIL_LEVEL))
+                .isEqualTo(Car.PERMISSION_CAR_ENGINE_DETAILED);
+        assertThat(mPropertyHalServiceIds.getWritePermission(VehiclePropertyIds.ENGINE_OIL_LEVEL))
+                .isNull();
+        assertThat(mPropertyHalServiceIds.getReadPermission(VehiclePropertyIds.HVAC_FAN_SPEED))
+                .isEqualTo(Car.PERMISSION_CONTROL_CAR_CLIMATE);
+        assertThat(mPropertyHalServiceIds.getWritePermission(VehiclePropertyIds.HVAC_FAN_SPEED))
+                .isEqualTo(Car.PERMISSION_CONTROL_CAR_CLIMATE);
+        assertThat(mPermissionMapping.getReadPermission(VehiclePropertyIds.HVAC_FAN_SPEED))
+                .isEqualTo(Car.PERMISSION_CONTROL_CAR_CLIMATE);
+        assertThat(mPermissionMapping.getWritePermission(VehiclePropertyIds.HVAC_FAN_SPEED))
+                .isEqualTo(Car.PERMISSION_CONTROL_CAR_CLIMATE);
     }
     /**
      * Test {@link PropertyHalServiceIds#customizeVendorPermission(List)}
@@ -133,28 +140,31 @@
     public void checkPermissionForVendorProperty() {
         // test insert a valid config
         mPropertyHalServiceIds.customizeVendorPermission(CONFIG_ARRAY);
+        assertThat(mPropertyHalServiceIds.getReadPermission(VENDOR_PROPERTY_1))
+                .isEqualTo(Car.PERMISSION_VENDOR_EXTENSION);
+        assertThat(mPropertyHalServiceIds.getWritePermission(VENDOR_PROPERTY_1)).isNull();
 
-        Assert.assertEquals(Car.PERMISSION_VENDOR_EXTENSION,
-                mPropertyHalServiceIds.getReadPermission(VENDOR_PROPERTY_1));
-        Assert.assertNull(mPropertyHalServiceIds.getWritePermission(VENDOR_PROPERTY_1));
+        assertThat(mPropertyHalServiceIds.getReadPermission(VENDOR_PROPERTY_2))
+                .isEqualTo(android.car.hardware.property
+                        .VehicleVendorPermission.PERMISSION_GET_CAR_VENDOR_CATEGORY_ENGINE);
+        assertThat(mPropertyHalServiceIds.getWritePermission(VENDOR_PROPERTY_2))
+                .isEqualTo(android.car.hardware.property
+                        .VehicleVendorPermission.PERMISSION_SET_CAR_VENDOR_CATEGORY_ENGINE);
 
-        Assert.assertEquals(android.car.hardware.property
-                        .VehicleVendorPermission.PERMISSION_GET_CAR_VENDOR_CATEGORY_ENGINE,
-                mPropertyHalServiceIds.getReadPermission(VENDOR_PROPERTY_2));
-        Assert.assertEquals(android.car.hardware.property
-                        .VehicleVendorPermission.PERMISSION_SET_CAR_VENDOR_CATEGORY_ENGINE,
-                mPropertyHalServiceIds.getWritePermission(VENDOR_PROPERTY_2));
+        assertThat(mPropertyHalServiceIds.getReadPermission(VENDOR_PROPERTY_3))
+                .isEqualTo(android.car.hardware.property
+                        .VehicleVendorPermission.PERMISSION_GET_CAR_VENDOR_CATEGORY_INFO);
+        assertThat(mPropertyHalServiceIds.getWritePermission(VENDOR_PROPERTY_3))
+                .isEqualTo(Car.PERMISSION_VENDOR_EXTENSION);
 
-        Assert.assertEquals(android.car.hardware.property
-                        .VehicleVendorPermission.PERMISSION_GET_CAR_VENDOR_CATEGORY_INFO,
-                mPropertyHalServiceIds.getReadPermission(VENDOR_PROPERTY_3));
-        Assert.assertEquals(Car.PERMISSION_VENDOR_EXTENSION,
-                mPropertyHalServiceIds.getWritePermission(VENDOR_PROPERTY_3));
-
-        Assert.assertEquals(Car.PERMISSION_VENDOR_EXTENSION,
-                mPropertyHalServiceIds.getReadPermission(VENDOR_PROPERTY_4));
-        Assert.assertEquals(Car.PERMISSION_VENDOR_EXTENSION,
-                mPropertyHalServiceIds.getWritePermission(VENDOR_PROPERTY_4));
+        assertThat(mPropertyHalServiceIds.getReadPermission(VENDOR_PROPERTY_4))
+                .isEqualTo(Car.PERMISSION_VENDOR_EXTENSION);
+        assertThat(mPropertyHalServiceIds.getWritePermission(VENDOR_PROPERTY_4))
+                .isEqualTo(Car.PERMISSION_VENDOR_EXTENSION);
+        assertThat(mPermissionMapping.getReadPermission(VENDOR_PROPERTY_4))
+                .isEqualTo(Car.PERMISSION_VENDOR_EXTENSION);
+        assertThat(mPermissionMapping.getWritePermission(VENDOR_PROPERTY_4))
+                .isEqualTo(Car.PERMISSION_VENDOR_EXTENSION);
 
         // test insert invalid config
         try {
@@ -171,10 +181,12 @@
     @Test
     public void checkVendorPropertyId() {
         for (int vendorProp : VENDOR_PROPERTY_IDS) {
-            Assert.assertTrue(mPropertyHalServiceIds.isSupportedProperty(vendorProp));
+            assertWithMessage("Property does not exist.").that(
+                    mPropertyHalServiceIds.isSupportedProperty(vendorProp)).isTrue();
         }
         for (int systemProp : SYSTEM_PROPERTY_IDS) {
-            Assert.assertTrue(mPropertyHalServiceIds.isSupportedProperty(systemProp));
+            assertWithMessage("Property does not exist.").that(
+                    mPropertyHalServiceIds.isSupportedProperty(systemProp)).isTrue();
         }
     }
 
@@ -183,14 +195,11 @@
      */
     @Test
     public void testPayload() {
-        Truth.assertThat(mPropertyHalServiceIds.checkPayload(GEAR_WITH_VALID_VALUE)).isTrue();
-        Truth.assertThat(mPropertyHalServiceIds.checkPayload(GEAR_WITH_EXTRA_VALUE)).isFalse();
-        Truth.assertThat(mPropertyHalServiceIds.checkPayload(GEAR_WITH_INVALID_VALUE)).isFalse();
-        Truth.assertThat(mPropertyHalServiceIds.checkPayload(GEAR_WITH_INVALID_TYPE_VALUE))
-                .isFalse();
-
-        Truth.assertThat(mPropertyHalServiceIds.checkPayload(HVAC_FAN_DIRECTIONS_VALID)).isTrue();
-        Truth.assertThat(mPropertyHalServiceIds.checkPayload(HVAC_FAN_DIRECTIONS_INVALID))
-                .isFalse();
+        assertThat(mPropertyHalServiceIds.checkPayload(GEAR_WITH_VALID_VALUE)).isTrue();
+        assertThat(mPropertyHalServiceIds.checkPayload(GEAR_WITH_EXTRA_VALUE)).isFalse();
+        assertThat(mPropertyHalServiceIds.checkPayload(GEAR_WITH_INVALID_VALUE)).isFalse();
+        assertThat(mPropertyHalServiceIds.checkPayload(GEAR_WITH_INVALID_TYPE_VALUE)).isFalse();
+        assertThat(mPropertyHalServiceIds.checkPayload(HVAC_FAN_DIRECTIONS_VALID)).isTrue();
+        assertThat(mPropertyHalServiceIds.checkPayload(HVAC_FAN_DIRECTIONS_INVALID)).isFalse();
     }
 }
diff --git a/tests/carservice_unit_test/src/com/android/car/watchdog/CarWatchdogManagerUnitTest.java b/tests/carservice_unit_test/src/com/android/car/watchdog/CarWatchdogManagerUnitTest.java
index a8b1362..1828c01 100644
--- a/tests/carservice_unit_test/src/com/android/car/watchdog/CarWatchdogManagerUnitTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/watchdog/CarWatchdogManagerUnitTest.java
@@ -40,6 +40,7 @@
 import android.car.watchdog.ICarWatchdogService;
 import android.car.watchdog.ICarWatchdogServiceCallback;
 import android.car.watchdog.IResourceOveruseListener;
+import android.car.watchdog.IoOveruseAlertThreshold;
 import android.car.watchdog.IoOveruseConfiguration;
 import android.car.watchdog.IoOveruseStats;
 import android.car.watchdog.PackageKillableState;
@@ -68,6 +69,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.concurrent.Executor;
 
@@ -476,6 +478,80 @@
                 .isEqualTo(null);
     }
 
+    @Test
+    public void testIoOveruseAlertThresholdToStringAndDescribeContent() {
+        IoOveruseAlertThreshold ioOveruseAlertThreshold = new IoOveruseAlertThreshold(1, 2);
+        int content = ioOveruseAlertThreshold.describeContents();
+        String string = ioOveruseAlertThreshold.toString();
+
+        assertThat(content).isEqualTo(0);
+        assertThat(string).isEqualTo(
+                "IoOveruseAlertThreshold { durationInSeconds = 1, writtenBytesPerSecond = 2 }");
+    }
+
+    @Test
+    public void testIoOveruseConfigurationToStringAndDescribeContent() {
+        PerStateBytes expectedPerStateBytes = new PerStateBytes(6666666, 7777777, 8888888);
+        IoOveruseConfiguration ioOveruseConfiguration = new IoOveruseConfiguration.Builder(
+                expectedPerStateBytes,
+                new HashMap<>(), new HashMap<>(), new ArrayList<>()).build();
+        int content = ioOveruseConfiguration.describeContents();
+        String string = ioOveruseConfiguration.toString();
+
+        assertThat(content).isEqualTo(0);
+        assertThat(string)
+                .isEqualTo("IoOveruseConfiguration { componentLevelThresholds = "
+                        + expectedPerStateBytes
+                        + ", packageSpecificThresholds = {}, appCategorySpecificThresholds = {}, "
+                        + "systemWideThresholds = [] }");
+    }
+
+    @Test
+    public void testIoOveruseStatsDescribeContent() {
+        IoOveruseStats ioOveruseStats =
+                new IoOveruseStats.Builder(0, 10).setStartTime(1).setDurationInSeconds(5).build();
+        int content = ioOveruseStats.describeContents();
+
+        assertThat(content).isEqualTo(0);
+    }
+
+    @Test
+    public void testPackageKillableStateToStringAndDescribeContent() {
+        PackageKillableState packageKillableState = new PackageKillableState("packageName", 10, 1);
+        int content = packageKillableState.describeContents();
+        String string = packageKillableState.toString();
+
+        assertThat(content).isEqualTo(0);
+        assertThat(string).isEqualTo(
+                "PackageKillableState { packageName = packageName, userId = 10, "
+                        + "killableState = "
+                        + PackageKillableState.killableStateToString(1)
+                        + " }");
+    }
+
+    @Test
+    public void testResourceOveruseConfigurationToStringAndDescribeContent() {
+        ResourceOveruseConfiguration resourceOveruseConfiguration =
+                new ResourceOveruseConfiguration.Builder(
+                    ResourceOveruseConfiguration.COMPONENT_TYPE_SYSTEM, new ArrayList<>(),
+                    new ArrayList<>(), new HashMap<>())
+                .addPackagesToAppCategoryTypes("key", "value")
+                .addSafeToKillPackages("safeToKillApp")
+                .addVendorPackagePrefixes("vendorPackagePrefix")
+                .build();
+        int content = resourceOveruseConfiguration.describeContents();
+        String string = resourceOveruseConfiguration.toString();
+
+        assertThat(content).isEqualTo(0);
+        assertThat(string)
+                .isEqualTo("ResourceOveruseConfiguration { componentType = "
+                        + ResourceOveruseConfiguration.componentTypeToString(
+                                ResourceOveruseConfiguration.COMPONENT_TYPE_SYSTEM)
+                        + ", safeToKillPackages = [safeToKillApp], vendorPackagePrefixes = "
+                        + "[vendorPackagePrefix], packagesToAppCategoryTypes = {key=value}, "
+                        + "ioOveruseConfiguration = null }");
+    }
+
     private ICarWatchdogServiceCallback registerClient(
             CarWatchdogManager.CarWatchdogClientCallback client) throws Exception {
         mCarWatchdogManager.registerClient(mExecutor, client, TIMEOUT_CRITICAL);