Merge "Add unit tests for com.android.car.SetMultimap." into sc-dev
diff --git a/car-lib/src/android/car/hardware/power/CarPowerPolicy.java b/car-lib/src/android/car/hardware/power/CarPowerPolicy.java
index 99fb245..bbc37eb 100644
--- a/car-lib/src/android/car/hardware/power/CarPowerPolicy.java
+++ b/car-lib/src/android/car/hardware/power/CarPowerPolicy.java
@@ -16,9 +16,12 @@
 
 package android.car.hardware.power;
 
+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;
 
 /**
@@ -186,6 +189,7 @@
             sourceFile = "packages/services/Car/car-lib/src/android/car/hardware/power/CarPowerPolicy.java",
             inputSignatures = "private final @android.annotation.NonNull java.lang.String mPolicyId\nprivate final @android.annotation.NonNull int[] mEnabledComponents\nprivate final @android.annotation.NonNull int[] mDisabledComponents\nclass CarPowerPolicy extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true)")
     @Deprecated
+    @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE)
     private void __metadata() {}
 
 
diff --git a/car-lib/src/android/car/hardware/power/CarPowerPolicyFilter.java b/car-lib/src/android/car/hardware/power/CarPowerPolicyFilter.java
index 29b8ad6..2fce4b1 100644
--- a/car-lib/src/android/car/hardware/power/CarPowerPolicyFilter.java
+++ b/car-lib/src/android/car/hardware/power/CarPowerPolicyFilter.java
@@ -16,9 +16,12 @@
 
 package android.car.hardware.power;
 
+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;
 
 /**
@@ -165,6 +168,7 @@
             sourceFile = "packages/services/Car/car-lib/src/android/car/hardware/power/CarPowerPolicyFilter.java",
             inputSignatures = "private @android.annotation.NonNull int[] mComponents\nclass CarPowerPolicyFilter extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true)")
     @Deprecated
+    @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE)
     private void __metadata() {}
 
 
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/telemetry/CarTelemetryManager.java b/car-lib/src/android/car/telemetry/CarTelemetryManager.java
index 169a05c..3d3cf50 100644
--- a/car-lib/src/android/car/telemetry/CarTelemetryManager.java
+++ b/car-lib/src/android/car/telemetry/CarTelemetryManager.java
@@ -254,7 +254,7 @@
      * The {@link ManifestKey} is used to uniquely identify a manifest. If a manifest of the same
      * name already exists in {@link com.android.car.telemetry.CarTelemetryService}, then the
      * version will be compared. If the version is strictly higher, the existing manifest will be
-     * replaced by the new one.
+     * replaced by the new one. All cache and intermediate results will be cleared if replaced.
      * TODO(b/185420981): Update javadoc after CarTelemetryService has concrete implementation.
      *
      * @param key      the unique key to identify the manifest.
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-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/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/sepolicy/private/carpowerpolicy.te b/cpp/powerpolicy/sepolicy/private/carpowerpolicy.te
index a7184ab..4b980f6 100644
--- a/cpp/powerpolicy/sepolicy/private/carpowerpolicy.te
+++ b/cpp/powerpolicy/sepolicy/private/carpowerpolicy.te
@@ -19,3 +19,7 @@
 
 # Allow reading and writing /sys/power/
 allow carpowerpolicyd sysfs_power:file rw_file_perms;
+
+# Allow updating properties to control boot animation
+set_prop(carpowerpolicyd, debug_prop)
+set_prop(carpowerpolicyd, bootanim_system_prop)
diff --git a/cpp/powerpolicy/server/src/CarPowerPolicyServer.cpp b/cpp/powerpolicy/server/src/CarPowerPolicyServer.cpp
index 4c9b260..10005e9 100644
--- a/cpp/powerpolicy/server/src/CarPowerPolicyServer.cpp
+++ b/cpp/powerpolicy/server/src/CarPowerPolicyServer.cpp
@@ -567,7 +567,6 @@
     if (!ret.ok()) {
         ALOGW("Failed to apply power policy: %s", ret.error().message().c_str());
     }
-    mSilentModeHandler.updateKernelSilentMode(isSilent);
 }
 
 bool CarPowerPolicyServer::isRegisteredLocked(const sp<ICarPowerPolicyChangeCallback>& callback) {
diff --git a/cpp/powerpolicy/server/src/SilentModeHandler.cpp b/cpp/powerpolicy/server/src/SilentModeHandler.cpp
index 45a7976..c57b6fa 100644
--- a/cpp/powerpolicy/server/src/SilentModeHandler.cpp
+++ b/cpp/powerpolicy/server/src/SilentModeHandler.cpp
@@ -39,6 +39,7 @@
 using ::android::base::GetProperty;
 using ::android::base::ReadFileToString;
 using ::android::base::Result;
+using ::android::base::SetProperty;
 using ::android::base::StringPrintf;
 using ::android::base::Trim;
 using ::android::base::unique_fd;
@@ -46,9 +47,13 @@
 
 namespace {
 
-constexpr const char* kPropertySystemBootReason = "sys.boot.reason";
-constexpr const char* kSilentModeHwStateFilename = "/sys/power/pm_silentmode_hw_state";
-constexpr const char* kKernelSilentModeFilename = "/sys/power/pm_silentmode_kernel";
+constexpr const char kPropertySystemBootReason[] = "sys.boot.reason";
+constexpr const char kSilentModeHwStateFilename[] = "/sys/power/pm_silentmode_hw_state";
+constexpr const char kKernelSilentModeFilename[] = "/sys/power/pm_silentmode_kernel";
+// To prevent boot animation from being started.
+constexpr const char kPropertyNoBootAnimation[] = "debug.sf.nobootanimation";
+// To stop boot animation while it is being played.
+constexpr const char kPropertyBootAnimationExit[] = "service.bootanim.exit";
 constexpr int kEventBufferSize = 512;
 
 bool fileExists(const char* filename) {
@@ -76,9 +81,7 @@
         mSilentModeByHwState = false;
     }
     if (mForcedMode) {
-        if (auto ret = updateKernelSilentModeInternal(mSilentModeByHwState); !ret.ok()) {
-            ALOGW("Failed to update kernel silent mode: %s", ret.error().message().c_str());
-        }
+        handleSilentModeChange(mSilentModeByHwState);
         mSilentModeChangeHandler->notifySilentModeChange(mSilentModeByHwState);
         ALOGI("Now in forced mode: monitoring %s is disabled", kSilentModeHwStateFilename);
     } else {
@@ -95,27 +98,6 @@
     return mSilentModeByHwState;
 }
 
-Result<void> SilentModeHandler::updateKernelSilentMode(bool silent) {
-    if (mForcedMode) {
-        return Error() << "Cannot update " << mKernelSilentModeFilename << " in forced mode";
-    }
-    return updateKernelSilentModeInternal(silent);
-}
-
-Result<void> SilentModeHandler::updateKernelSilentModeInternal(bool silent) {
-    int fd = open(mKernelSilentModeFilename.c_str(), O_WRONLY | O_NONBLOCK);
-    if (fd < 0) {
-        return Error() << "Failed to open " << mKernelSilentModeFilename;
-    }
-    Result<void> status = {};
-    if (const auto& value = silent ? kValueSilentMode : kValueNonSilentMode;
-        !WriteStringToFd(value, fd)) {
-        status = Error() << "Failed to write " << value << " to fd " << fd;
-    }
-    close(fd);
-    return status;
-}
-
 void SilentModeHandler::stopMonitoringSilentModeHwState(bool shouldWaitThread) {
     if (mIsMonitoring) {
         mIsMonitoring = false;
@@ -226,10 +208,49 @@
     if (newSilentMode != oldSilentMode) {
         ALOGI("%s is set to %s", mSilentModeHwStateFilename.c_str(),
               newSilentMode ? "silent" : "non-silent");
+        handleSilentModeChange(newSilentMode);
         mSilentModeChangeHandler->notifySilentModeChange(newSilentMode);
     }
 }
 
+void SilentModeHandler::handleSilentModeChange(bool silent) {
+    if (auto ret = updateKernelSilentMode(silent); !ret.ok()) {
+        ALOGW("Failed to update kernel silent mode: %s", ret.error().message().c_str());
+    }
+    if (auto ret = enableBootAnimation(!silent); !ret.ok()) {
+        ALOGW("Failed to %s boot animation: %s", mSilentModeByHwState ? "disabling" : "enabling",
+              ret.error().message().c_str());
+    }
+}
+
+Result<void> SilentModeHandler::enableBootAnimation(bool enabled) {
+    const std::string value = enabled ? "0" : "1";
+    if (!SetProperty(kPropertyNoBootAnimation, value)) {
+        return Error() << "Failed to set " << kPropertyNoBootAnimation << " property to " << value;
+    }
+    if (!enabled) {
+        if (!SetProperty(kPropertyBootAnimationExit, value)) {
+            return Error() << "Failed to set " << kPropertyBootAnimationExit << " property to "
+                           << value;
+        }
+    }
+    return {};
+}
+
+Result<void> SilentModeHandler::updateKernelSilentMode(bool silent) {
+    int fd = open(mKernelSilentModeFilename.c_str(), O_WRONLY | O_NONBLOCK);
+    if (fd < 0) {
+        return Error() << "Failed to open " << mKernelSilentModeFilename;
+    }
+    Result<void> status = {};
+    if (const auto& value = silent ? kValueSilentMode : kValueNonSilentMode;
+        !WriteStringToFd(value, fd)) {
+        status = Error() << "Failed to write " << value << " to fd " << fd;
+    }
+    close(fd);
+    return status;
+}
+
 }  // namespace powerpolicy
 }  // namespace automotive
 }  // namespace frameworks
diff --git a/cpp/powerpolicy/server/src/SilentModeHandler.h b/cpp/powerpolicy/server/src/SilentModeHandler.h
index 44a2835..9fae109 100644
--- a/cpp/powerpolicy/server/src/SilentModeHandler.h
+++ b/cpp/powerpolicy/server/src/SilentModeHandler.h
@@ -60,17 +60,17 @@
     void release();
     // Returns the current Silent Mode.
     bool isSilentMode();
-    // Write the value corresponding to the given silent state to /sys/power/pm_silentmode_kernel.
-    android::base::Result<void> updateKernelSilentMode(bool silent);
     // Stops monitoring the change on /sys/power/pm_silentmode_hw_state.
     void stopMonitoringSilentModeHwState(bool shouldWaitThread);
     // Dumps the internal state.
     android::base::Result<void> dump(int fd, const Vector<String16>& args);
 
 private:
-    android::base::Result<void> updateKernelSilentModeInternal(bool silent);
+    android::base::Result<void> updateKernelSilentMode(bool silent);
     void startMonitoringSilentModeHwState();
     void handleSilentModeHwStateChange();
+    void handleSilentModeChange(bool silent);
+    android::base::Result<void> enableBootAnimation(bool enabled);
 
     android::Mutex mMutex;
     bool mSilentModeByHwState GUARDED_BY(mMutex);
diff --git a/cpp/powerpolicy/server/tests/SilentModeHandlerTest.cpp b/cpp/powerpolicy/server/tests/SilentModeHandlerTest.cpp
index a090b3a..1eaec81 100644
--- a/cpp/powerpolicy/server/tests/SilentModeHandlerTest.cpp
+++ b/cpp/powerpolicy/server/tests/SilentModeHandlerTest.cpp
@@ -93,6 +93,8 @@
         return Trim(value);
     }
 
+    void updateKernelSilentMode(bool isSilent) { mHandler->updateKernelSilentMode(isSilent); }
+
 private:
     SilentModeHandler* mHandler;
     TemporaryFile mFileSilentModeHwState;
@@ -176,12 +178,12 @@
     handlerPeer.injectBootReason(kBootReasonNormal);
     handlerPeer.init();
 
-    handler.updateKernelSilentMode(true);
+    handlerPeer.updateKernelSilentMode(true);
 
     ASSERT_EQ(handlerPeer.readKernelSilentMode(), kValueSilentMode)
             << "Kernel silent mode file should have 1";
 
-    handler.updateKernelSilentMode(false);
+    handlerPeer.updateKernelSilentMode(false);
 
     ASSERT_EQ(handlerPeer.readKernelSilentMode(), kValueNonSilentMode)
             << "Kernel silent mode file should have 0";
diff --git a/cpp/watchdog/server/src/OveruseConfigurationXmlHelper.cpp b/cpp/watchdog/server/src/OveruseConfigurationXmlHelper.cpp
index 308bb8e..a3bf4a9 100644
--- a/cpp/watchdog/server/src/OveruseConfigurationXmlHelper.cpp
+++ b/cpp/watchdog/server/src/OveruseConfigurationXmlHelper.cpp
@@ -59,6 +59,7 @@
 using ::android::base::Trim;
 using ::android::binder::Status;
 using ::tinyxml2::XML_SUCCESS;
+using ::tinyxml2::XMLDeclaration;
 using ::tinyxml2::XMLDocument;
 using ::tinyxml2::XMLElement;
 
@@ -95,6 +96,8 @@
 
 constexpr const char kAttrId[] = "id";
 constexpr const char kAttrType[] = "type";
+constexpr const char kAttrVersion[] = "version";
+constexpr const char kVersionNumber[] = "1.0";
 
 Result<const XMLElement*> readExactlyOneElement(const char* tag, const XMLElement* rootElement) {
     const XMLElement* element = rootElement->FirstChildElement(tag);
@@ -432,6 +435,249 @@
     return configuration;
 }
 
+Result<void> writeComponentType(ComponentType componentType, XMLElement* rootElement) {
+    XMLElement* childElement = rootElement->InsertNewChildElement(kTagComponentType);
+    if (!childElement) {
+        return Error() << "Failed to insert new child element with tag '" << kTagComponentType
+                       << "'";
+    }
+    childElement->SetText(toString(componentType).c_str());
+    return {};
+}
+
+Result<void> writeSafeToKillPackages(const std::vector<std::string>& safeToKillPackages,
+                                     XMLElement* rootElement) {
+    if (safeToKillPackages.empty()) {
+        return {};
+    }
+    XMLElement* outerElement = rootElement->InsertNewChildElement(kTagSafeToKillPackages);
+    if (!outerElement) {
+        return Error() << "Failed to insert new child element with tag '" << kTagSafeToKillPackages
+                       << "'";
+    }
+    for (const auto& package : safeToKillPackages) {
+        XMLElement* innerElement = outerElement->InsertNewChildElement(kTagPackage);
+        if (!innerElement) {
+            return Error() << "Failed to insert new child element with tag '" << kTagPackage << "'";
+        }
+        innerElement->SetText(package.c_str());
+    }
+    return {};
+}
+
+Result<void> writeVendorPackagePrefixes(const std::vector<std::string>& vendorPackagePrefixes,
+                                        XMLElement* rootElement) {
+    if (vendorPackagePrefixes.empty()) {
+        return {};
+    }
+    XMLElement* outerElement = rootElement->InsertNewChildElement(kTagVendorPackagePrefixes);
+    if (!outerElement) {
+        return Error() << "Failed to insert new child element with tag '"
+                       << kTagVendorPackagePrefixes << "'";
+    }
+    for (const auto& packagePrefix : vendorPackagePrefixes) {
+        XMLElement* innerElement = outerElement->InsertNewChildElement(kTagPackagePrefix);
+        if (!innerElement) {
+            return Error() << "Failed to insert new child element with tag '" << kTagPackagePrefix
+                           << "'";
+        }
+        innerElement->SetText(packagePrefix.c_str());
+    }
+    return {};
+}
+
+Result<void> writePackageToAppCategoryTypes(const std::vector<PackageMetadata>& packageMetadata,
+                                            XMLElement* rootElement) {
+    if (packageMetadata.empty()) {
+        return {};
+    }
+    XMLElement* outerElement = rootElement->InsertNewChildElement(kTagPackageToAppCategoryTypes);
+    if (!outerElement) {
+        return Error() << "Failed to insert new child element with tag '"
+                       << kTagPackageToAppCategoryTypes << "'";
+    }
+    for (const auto& meta : packageMetadata) {
+        XMLElement* innerElement = outerElement->InsertNewChildElement(kTagPackageAppCategory);
+        if (!innerElement) {
+            return Error() << "Failed to insert new child element with tag '"
+                           << kTagPackageAppCategory << "'";
+        }
+        innerElement->SetAttribute(kAttrType, toString(meta.appCategoryType).c_str());
+        innerElement->SetText(meta.packageName.c_str());
+    }
+    return {};
+}
+
+Result<void> writePerStateBytes(const PerStateBytes& perStateBytes, XMLElement* rootElement) {
+    const auto writeStateElement = [&](const char* state, int64_t value) -> Result<void> {
+        XMLElement* childElement = rootElement->InsertNewChildElement(kTagState);
+        if (!childElement) {
+            return Error() << "Failed to insert new child element with tag '" << kTagState << "'";
+        }
+        childElement->SetAttribute(kAttrId, state);
+        childElement->SetText(value);
+        return {};
+    };
+    if (const auto result = writeStateElement(kStateIdForegroundMode,
+                                              perStateBytes.foregroundBytes / kOneMegaByte);
+        !result.ok()) {
+        return Error() << "Failed to write bytes for state '" << kStateIdForegroundMode
+                       << "': " << result.error();
+    }
+    if (const auto result = writeStateElement(kStateIdBackgroundMode,
+                                              perStateBytes.backgroundBytes / kOneMegaByte);
+        !result.ok()) {
+        return Error() << "Failed to write bytes for state '" << kStateIdBackgroundMode
+                       << "': " << result.error();
+    }
+    if (const auto result =
+                writeStateElement(kStateIdGarageMode, perStateBytes.garageModeBytes / kOneMegaByte);
+        !result.ok()) {
+        return Error() << "Failed to write bytes for state '" << kStateIdGarageMode
+                       << "': " << result.error();
+    }
+    return {};
+}
+
+Result<void> writeComponentLevelThresholds(const PerStateIoOveruseThreshold& thresholds,
+                                           XMLElement* rootElement) {
+    XMLElement* childElement = rootElement->InsertNewChildElement(kTagComponentLevelThresholds);
+    if (!childElement) {
+        return Error() << "Failed to insert new child element with tag '"
+                       << kTagComponentLevelThresholds << "'";
+    }
+    if (const auto result = writePerStateBytes(thresholds.perStateWriteBytes, childElement);
+        !result.ok()) {
+        return Error() << "Failed to write per-state bytes: " << result.error();
+    }
+    return {};
+}
+
+Result<void> writePerStateThresholds(const PerStateIoOveruseThreshold& thresholds,
+                                     XMLElement* rootElement) {
+    XMLElement* childElement = rootElement->InsertNewChildElement(kTagPerStateThreshold);
+    if (!childElement) {
+        return Error() << "Failed to insert new child element with tag '" << kTagPerStateThreshold
+                       << "'";
+    }
+    childElement->SetAttribute(kAttrId, thresholds.name.c_str());
+    if (const auto result = writePerStateBytes(thresholds.perStateWriteBytes, childElement);
+        !result.ok()) {
+        return Error() << "Failed to write per-state bytes: " << result.error();
+    }
+    return {};
+}
+
+Result<void> writePackageSpecificThresholds(
+        const std::vector<PerStateIoOveruseThreshold>& thresholds, XMLElement* rootElement) {
+    XMLElement* childElement = rootElement->InsertNewChildElement(kTagPackageSpecificThresholds);
+    if (!childElement) {
+        return Error() << "Failed to insert new child element with tag '"
+                       << kTagPackageSpecificThresholds << "'";
+    }
+    for (const auto threshold : thresholds) {
+        if (const auto result = writePerStateThresholds(threshold, childElement); !result.ok()) {
+            return Error() << "Failed to write per-state thresholds for '" << threshold.name
+                           << "': " << result.error();
+        }
+    }
+    return {};
+}
+
+Result<void> writeAppCategorySpecificThresholds(
+        const std::vector<PerStateIoOveruseThreshold>& thresholds, XMLElement* rootElement) {
+    XMLElement* childElement =
+            rootElement->InsertNewChildElement(kTagAppCategorySpecificThresholds);
+    if (!childElement) {
+        return Error() << "Failed to insert new child element with tag '"
+                       << kTagAppCategorySpecificThresholds << "'";
+    }
+    for (const auto threshold : thresholds) {
+        if (const auto result = writePerStateThresholds(threshold, childElement); !result.ok()) {
+            return Error() << "Failed to write per-state thresholds for '" << threshold.name
+                           << "': " << result.error();
+        }
+    }
+    return {};
+}
+
+Result<void> writeAlertThresholds(const IoOveruseAlertThreshold& alertThresholds,
+                                  XMLElement* rootElement) {
+    XMLElement* outerElement = rootElement->InsertNewChildElement(kTagAlertThreshold);
+    if (!outerElement) {
+        return Error() << "Failed to insert new child element with tag '" << kTagAlertThreshold
+                       << "'";
+    }
+    const auto writeParamElement = [&](const char* param, int64_t value) -> Result<void> {
+        XMLElement* innerElement = outerElement->InsertNewChildElement(kTagParam);
+        if (!innerElement) {
+            return Error() << "Failed to insert new child element with tag '" << kTagParam << "'";
+        }
+        innerElement->SetAttribute(kAttrId, param);
+        innerElement->SetText(value);
+        return {};
+    };
+    if (const auto result =
+                writeParamElement(kParamIdDurationSeconds, alertThresholds.durationInSeconds);
+        !result.ok()) {
+        return Error() << "Failed to write duration for param '" << kParamIdDurationSeconds
+                       << "': " << result.error();
+    }
+    if (const auto result = writeParamElement(kParamIdWrittenBytesPerSecond,
+                                              alertThresholds.writtenBytesPerSecond);
+        !result.ok()) {
+        return Error() << "Failed to write bps for param '" << kParamIdWrittenBytesPerSecond
+                       << "': " << result.error();
+    }
+    return {};
+}
+
+Result<void> writeSystemWideThresholds(const std::vector<IoOveruseAlertThreshold>& thresholds,
+                                       XMLElement* rootElement) {
+    XMLElement* childElement = rootElement->InsertNewChildElement(kTagSystemWideThresholds);
+    if (!childElement) {
+        return Error() << "Failed to insert new child element with tag '"
+                       << kTagSystemWideThresholds << "'";
+    }
+    for (const auto threshold : thresholds) {
+        if (const auto result = writeAlertThresholds(threshold, childElement); !result.ok()) {
+            return Error() << "Failed to write I/O overuse alert thresholds:" << result.error();
+        }
+    }
+    return {};
+}
+
+Result<void> writeIoOveruseConfiguration(const IoOveruseConfiguration& configuration,
+                                         XMLElement* rootElement) {
+    XMLElement* childElement = rootElement->InsertNewChildElement(kTagIoOveruseConfiguration);
+    if (!childElement) {
+        return Error() << "Failed to insert new child element with tag '"
+                       << kTagIoOveruseConfiguration << "'";
+    }
+    if (const auto result =
+                writeComponentLevelThresholds(configuration.componentLevelThresholds, childElement);
+        !result.ok()) {
+        return Error() << "Failed to write component-wide thresholds: " << result.error();
+    }
+    if (const auto result = writePackageSpecificThresholds(configuration.packageSpecificThresholds,
+                                                           childElement);
+        !result.ok()) {
+        return Error() << "Failed to write package specific thresholds: " << result.error();
+    }
+    if (const auto result =
+                writeAppCategorySpecificThresholds(configuration.categorySpecificThresholds,
+                                                   childElement);
+        !result.ok()) {
+        return Error() << "Failed to write app category specific thresholds: " << result.error();
+    }
+    if (const auto result =
+                writeSystemWideThresholds(configuration.systemWideThresholds, childElement);
+        !result.ok()) {
+        return Error() << "Failed to write system-wide thresholds: " << result.error();
+    }
+    return {};
+}
+
 }  // namespace
 
 Result<ResourceOveruseConfiguration> OveruseConfigurationXmlHelper::parseXmlFile(
@@ -478,9 +724,54 @@
 }
 
 Result<void> OveruseConfigurationXmlHelper::writeXmlFile(
-        [[maybe_unused]] const ResourceOveruseConfiguration& configuration,
-        [[maybe_unused]] const char* filePath) {
-    // TODO(b/185287136): Write the configuration to file.
+        const ResourceOveruseConfiguration& configuration, const char* filePath) {
+    XMLDocument xmlDoc;
+    if (XMLDeclaration* declaration = xmlDoc.NewDeclaration(); declaration) {
+        xmlDoc.InsertEndChild(declaration);
+    } else {
+        return Error() << "Failed to create new xml declaration";
+    }
+    XMLElement* rootElement = xmlDoc.NewElement(kTagResourceOveruseConfiguration);
+    if (!rootElement) {
+        return Error() << "Failed to create new xml element for tag '"
+                       << kTagResourceOveruseConfiguration << "'";
+    }
+    rootElement->SetAttribute(kAttrVersion, kVersionNumber);
+    xmlDoc.InsertEndChild(rootElement);
+    if (const auto result = writeComponentType(configuration.componentType, rootElement);
+        !result.ok()) {
+        return Error() << "Failed to write component type: " << result.error();
+    }
+    if (const auto result = writeSafeToKillPackages(configuration.safeToKillPackages, rootElement);
+        !result.ok()) {
+        return Error() << "Failed to write safe-to-kill packages: " << result.error();
+    }
+    if (const auto result =
+                writeVendorPackagePrefixes(configuration.vendorPackagePrefixes, rootElement);
+        !result.ok()) {
+        return Error() << "Failed to write vendor package prefixes: " << result.error();
+    }
+    if (const auto result =
+                writePackageToAppCategoryTypes(configuration.packageMetadata, rootElement);
+        !result.ok()) {
+        return Error() << "Failed to write package to app category types: " << result.error();
+    }
+    if (configuration.resourceSpecificConfigurations.size() != 1 ||
+        configuration.resourceSpecificConfigurations[0].getTag() !=
+                ResourceSpecificConfiguration::ioOveruseConfiguration) {
+        return Error() << "Must provide exactly one I/O overuse configuration";
+    }
+    IoOveruseConfiguration ioOveruseConfig =
+            configuration.resourceSpecificConfigurations[0]
+                    .get<ResourceSpecificConfiguration::ioOveruseConfiguration>();
+    if (const auto result = writeIoOveruseConfiguration(ioOveruseConfig, rootElement);
+        !result.ok()) {
+        return Error() << "Failed to write I/O overuse configuration: " << result.error();
+    }
+    if (const auto xmlError = xmlDoc.SaveFile(filePath); xmlError != XML_SUCCESS) {
+        return Error() << "Failed to write XML configuration to file '" << filePath
+                       << "': " << XMLDocument::ErrorIDToName(xmlError);
+    }
     return {};
 }
 
diff --git a/cpp/watchdog/server/tests/OveruseConfigurationXmlHelperTest.cpp b/cpp/watchdog/server/tests/OveruseConfigurationXmlHelperTest.cpp
index 88006d7..9e45fd0 100644
--- a/cpp/watchdog/server/tests/OveruseConfigurationXmlHelperTest.cpp
+++ b/cpp/watchdog/server/tests/OveruseConfigurationXmlHelperTest.cpp
@@ -165,6 +165,138 @@
     }
 }
 
+TEST(OveruseConfigurationXmlHelperTest, TestWriteXmlFileWithSystemConfiguration) {
+    auto ioConfig = constructIoOveruseConfig(
+            /*componentLevel=*/toPerStateIoOveruseThreshold(ComponentType::SYSTEM,
+                                                            300 * kOneMegaByte, 150 * kOneMegaByte,
+                                                            500 * kOneMegaByte),
+            /*packageSpecific=*/
+            {toPerStateIoOveruseThreshold("system.package.C", 400 * kOneMegaByte,
+                                          100 * kOneMegaByte, 200 * kOneMegaByte),
+             toPerStateIoOveruseThreshold("system.package.D", 1024 * kOneMegaByte,
+                                          500 * kOneMegaByte, 2048 * kOneMegaByte)},
+            /*categorySpecific=*/{},
+            /*systemWide=*/{toIoOveruseAlertThreshold(10, 200), toIoOveruseAlertThreshold(5, 50)});
+    ResourceOveruseConfiguration expected =
+            constructResourceOveruseConfig(ComponentType::SYSTEM,
+                                           /*safeToKill=*/{"system.package.A", "system.package.B"},
+                                           /*vendorPrefixes=*/{},
+                                           /*packageMetadata=*/
+                                           {toPackageMetadata("system.package.A",
+                                                              ApplicationCategoryType::MEDIA),
+                                            toPackageMetadata("system.package.B",
+                                                              ApplicationCategoryType::MAPS)},
+                                           ioConfig);
+    TemporaryFile temporaryFile;
+    ASSERT_NE(temporaryFile.fd, -1);
+
+    ASSERT_RESULT_OK(OveruseConfigurationXmlHelper::writeXmlFile(expected, temporaryFile.path));
+
+    ALOGW("Wrote to file: %s", temporaryFile.path);
+
+    auto actual = OveruseConfigurationXmlHelper::parseXmlFile(temporaryFile.path);
+
+    ASSERT_RESULT_OK(actual);
+
+    EXPECT_THAT(*actual, ResourceOveruseConfigurationMatcher(expected))
+            << "Expected: " << expected.toString() << "\nActual: " << actual->toString();
+
+    temporaryFile.release();
+}
+
+TEST(OveruseConfigurationXmlHelperTest, TestWriteXmlFileWithVendorConfiguration) {
+    auto ioConfig = constructIoOveruseConfig(
+            /*componentLevel=*/toPerStateIoOveruseThreshold(ComponentType::VENDOR,
+                                                            1024 * kOneMegaByte, 512 * kOneMegaByte,
+                                                            3072 * kOneMegaByte),
+            /*packageSpecific=*/
+            {toPerStateIoOveruseThreshold("com.vendor.package.C", 400 * kOneMegaByte,
+                                          100 * kOneMegaByte, 200 * kOneMegaByte),
+             toPerStateIoOveruseThreshold("com.vendor.package.D", 1024 * kOneMegaByte,
+                                          500 * kOneMegaByte, 2048 * kOneMegaByte)},
+            /*categorySpecific=*/
+            {toPerStateIoOveruseThreshold("MAPS", 800 * kOneMegaByte, 900 * kOneMegaByte,
+                                          2048 * kOneMegaByte),
+             toPerStateIoOveruseThreshold("MEDIA", 600 * kOneMegaByte, 700 * kOneMegaByte,
+                                          1024 * kOneMegaByte)},
+            /*systemWide=*/{});
+    ResourceOveruseConfiguration expected =
+            constructResourceOveruseConfig(ComponentType::VENDOR,
+                                           /*safeToKill=*/
+                                           {"com.vendor.package.A", "com.vendor.package.B"},
+                                           /*vendorPrefixes=*/{"com.vendor.package"},
+                                           /*packageMetadata=*/
+                                           {toPackageMetadata("com.vendor.package.A",
+                                                              ApplicationCategoryType::MEDIA),
+                                            toPackageMetadata("com.vendor.package.B",
+                                                              ApplicationCategoryType::MAPS),
+                                            toPackageMetadata("com.third.party.package.C",
+                                                              ApplicationCategoryType::MEDIA),
+                                            toPackageMetadata("system.package.D",
+                                                              ApplicationCategoryType::MAPS)},
+                                           ioConfig);
+    TemporaryFile temporaryFile;
+    ASSERT_NE(temporaryFile.fd, -1);
+
+    ASSERT_RESULT_OK(OveruseConfigurationXmlHelper::writeXmlFile(expected, temporaryFile.path));
+
+    ALOGW("Wrote to file: %s", temporaryFile.path);
+
+    auto actual = OveruseConfigurationXmlHelper::parseXmlFile(temporaryFile.path);
+
+    ASSERT_RESULT_OK(actual);
+
+    EXPECT_THAT(*actual, ResourceOveruseConfigurationMatcher(expected))
+            << "Expected: " << expected.toString() << "\nActual: " << actual->toString();
+
+    temporaryFile.release();
+}
+
+TEST(OveruseConfigurationXmlHelperTest, TestWriteXmlFileWithThirdPartyConfiguration) {
+    auto ioConfig = constructIoOveruseConfig(
+            /*componentLevel=*/toPerStateIoOveruseThreshold(ComponentType::THIRD_PARTY,
+                                                            300 * kOneMegaByte, 150 * kOneMegaByte,
+                                                            500 * kOneMegaByte),
+            /*packageSpecific=*/{},
+            /*categorySpecific=*/{},
+            /*systemWide=*/{});
+    ResourceOveruseConfiguration expected =
+            constructResourceOveruseConfig(ComponentType::THIRD_PARTY,
+                                           /*safeToKill=*/{},
+                                           /*vendorPrefixes=*/{},
+                                           /*packageMetadata=*/{}, ioConfig);
+    TemporaryFile temporaryFile;
+    ASSERT_NE(temporaryFile.fd, -1);
+
+    ASSERT_RESULT_OK(OveruseConfigurationXmlHelper::writeXmlFile(expected, temporaryFile.path));
+
+    ALOGW("Wrote to file: %s", temporaryFile.path);
+
+    auto actual = OveruseConfigurationXmlHelper::parseXmlFile(temporaryFile.path);
+
+    ASSERT_RESULT_OK(actual);
+
+    EXPECT_THAT(*actual, ResourceOveruseConfigurationMatcher(expected))
+            << "Expected: " << expected.toString() << "\nActual: " << actual->toString();
+
+    temporaryFile.release();
+}
+
+TEST(OveruseConfigurationXmlHelperTest, TestFailsWriteXmlFileWithInvalidConfig) {
+    ResourceOveruseConfiguration resourceOveruseConfig;
+    resourceOveruseConfig.componentType = ComponentType::THIRD_PARTY;
+
+    TemporaryFile temporaryFile;
+    ASSERT_NE(temporaryFile.fd, -1);
+
+    ASSERT_FALSE(
+            OveruseConfigurationXmlHelper::writeXmlFile(resourceOveruseConfig, temporaryFile.path)
+                    .ok())
+            << "Should fail to write invalid config";
+
+    temporaryFile.release();
+}
+
 }  // namespace watchdog
 }  // namespace automotive
 }  // namespace android
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/config.xml b/service/res/values/config.xml
index e5d37d3..f0c611c 100644
--- a/service/res/values/config.xml
+++ b/service/res/values/config.xml
@@ -404,7 +404,11 @@
     <integer name="config_maxSuspendWaitDuration">180000</integer>
 
     <!-- A name of a camera device that provides the rearview through EVS service -->
-    <string name="config_evsRearviewCameraId">/dev/video10</string>
+    <string name="config_evsRearviewCameraId" translatable="false">/dev/video10</string>
+
+    <!-- The camera Activity name for EVS, if defined, the Activity will be launched by
+         CarEvsService. -->
+    <string name="config_evsCameraActivity" translatable="false"></string>
 
     <!-- A configuration flag to adjust Wifi for suspend. -->
     <bool name="config_wifiAdjustmentForSuspend">false</bool>
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 78ff99d..68af541 100644
--- a/service/src/com/android/car/ICarImpl.java
+++ b/service/src/com/android/car/ICarImpl.java
@@ -844,7 +844,7 @@
         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)
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/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..b4235c9 100644
--- a/service/src/com/android/car/evs/CarEvsService.java
+++ b/service/src/com/android/car/evs/CarEvsService.java
@@ -23,6 +23,7 @@
 import static android.car.evs.CarEvsManager.SERVICE_STATE_INACTIVE;
 import static android.car.evs.CarEvsManager.SERVICE_STATE_REQUESTED;
 import static android.car.evs.CarEvsManager.SERVICE_STATE_UNAVAILABLE;
+import static android.car.evs.CarEvsManager.STREAM_EVENT_STREAM_STOPPED;
 
 import static com.android.car.CarLog.TAG_EVS;
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
@@ -30,9 +31,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.car.Car;
-import android.car.hardware.CarPropertyValue;
-import android.car.hardware.property.CarPropertyEvent;
-import android.car.hardware.property.ICarPropertyEventListener;
 import android.car.evs.CarEvsBufferDescriptor;
 import android.car.evs.CarEvsManager;
 import android.car.evs.CarEvsManager.CarEvsError;
@@ -42,14 +40,19 @@
 import android.car.evs.CarEvsStatus;
 import android.car.evs.ICarEvsStatusListener;
 import android.car.evs.ICarEvsStreamCallback;
+import android.car.hardware.CarPropertyValue;
+import android.car.hardware.property.CarPropertyEvent;
+import android.car.hardware.property.ICarPropertyEventListener;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.hardware.HardwareBuffer;
 import android.hardware.automotive.vehicle.V2_0.VehicleArea;
 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,11 +154,15 @@
         }
     }
 
+    private static final String COMMAND_TO_USE_DEFAULT_CAMERA = "default";
+
     private final Context mContext;
     private final EvsHalService mEvsHalService;
     private final CarPropertyService mPropertyService;
     private final Object mLock = new Object();
 
+    private final ComponentName mEvsCameraActivity;
+
     // This handler is to monitor the client sends a video stream request within a given time
     // after a state transition to the REQUESTED state.
     private final Handler mHandler = new Handler(Looper.getMainLooper());
@@ -424,8 +431,10 @@
                         // was transited to the ACTIVE state by a request that has the same priority
                         // with current request.
                         return ERROR_NONE;
+                    } else {
+                        // Stop stream on all lower priority clients.
+                        processStreamEvent(STREAM_EVENT_STREAM_STOPPED);
                     }
-
                     break;
 
                 default:
@@ -440,6 +449,20 @@
             mState = SERVICE_STATE_REQUESTED;
             mServiceType = service;
             mLastRequestPriority = priority;
+
+            if (mEvsCameraActivity != null) {
+                Intent evsIntent = new Intent(Intent.ACTION_MAIN)
+                        .setComponent(mEvsCameraActivity)
+                        .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+                        .addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT)
+                        .addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
+                        .addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+                if (priority == REQUEST_PRIORITY_HIGH) {
+                    mSessionToken = new Binder();
+                    evsIntent.putExtra(CarEvsManager.EXTRA_SESSION_TOKEN, mSessionToken);
+                }
+                mContext.startActivity(evsIntent);
+            }
             return ERROR_NONE;
         }
 
@@ -463,7 +486,7 @@
 
                 case SERVICE_STATE_REQUESTED:
                     // CarEvsService is reserved for higher priority clients.
-                    if (!isSessionToken(token)) {
+                    if (priority == REQUEST_PRIORITY_HIGH && !isSessionToken(token)) {
                         // Declines a request with an expired token.
                         return ERROR_BUSY;
                     }
@@ -523,6 +546,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;
@@ -603,6 +632,14 @@
         mContext = context;
         mPropertyService = propertyService;
         mEvsHalService = halService;
+
+        String activityName = mContext.getResources().getString(R.string.config_evsCameraActivity);
+        if (!activityName.isEmpty()) {
+            mEvsCameraActivity = ComponentName.unflattenFromString(activityName);
+        } else {
+            mEvsCameraActivity = null;
+        }
+        if (DBG) Slog.d(TAG_EVS, "evsCameraActivity=" + mEvsCameraActivity);
     }
 
     /** Implements EvsHalService.EvsHalEventListener to monitor VHAL properties. */
@@ -960,6 +997,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 +1080,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/power/CarPowerManagementService.java b/service/src/com/android/car/power/CarPowerManagementService.java
index b0c440b..b9d029b 100644
--- a/service/src/com/android/car/power/CarPowerManagementService.java
+++ b/service/src/com/android/car/power/CarPowerManagementService.java
@@ -1139,7 +1139,6 @@
 
     void notifySilentModeChange(boolean silent) {
         Slogf.i(TAG, "Silent mode is set to %b", silent);
-        mSilentModeHandler.updateKernelSilentMode(silent);
         if (silent) {
             applyPreemptivePowerPolicy(PolicyReader.POWER_POLICY_ID_NO_USER_INTERACTION);
         } else {
diff --git a/service/src/com/android/car/power/SilentModeHandler.java b/service/src/com/android/car/power/SilentModeHandler.java
index 11d6c8c..2631c01 100644
--- a/service/src/com/android/car/power/SilentModeHandler.java
+++ b/service/src/com/android/car/power/SilentModeHandler.java
@@ -246,6 +246,7 @@
                     newSilentMode = mSilentModeByHwState;
                 }
                 if (newSilentMode != oldSilentMode) {
+                    updateKernelSilentMode(newSilentMode);
                     mService.notifySilentModeChange(newSilentMode);
                 }
             }
diff --git a/service/src/com/android/car/telemetry/Channel.java b/service/src/com/android/car/telemetry/Channel.java
deleted file mode 100644
index 100e227..0000000
--- a/service/src/com/android/car/telemetry/Channel.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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.telemetry;
-
-import android.os.Bundle;
-
-/**
- * Buffers data passing from publishers to subscribers. There is one channel per script receptor.
- *
- * TODO(b/187525360, b/187743369): make it a class or create an impl class for this.
- */
-public interface Channel {
-    /**
-     * Buffers the given data.
-     *
-     * TODO(b/189241508): Use ScriptExecutor supported data structure
-     */
-    void push(Bundle message);
-
-    /** Returns the publisher configuration for this channel. */
-    TelemetryProto.Publisher getPublisherConfig();
-
-    // TODO(b/187525360, b/187743369): Add other methods to check/get data from the channel.
-}
diff --git a/service/src/com/android/car/telemetry/databroker/DataBroker.java b/service/src/com/android/car/telemetry/databroker/DataBroker.java
index 4f96966..01535de 100644
--- a/service/src/com/android/car/telemetry/databroker/DataBroker.java
+++ b/service/src/com/android/car/telemetry/databroker/DataBroker.java
@@ -28,7 +28,7 @@
     /**
      * ScriptResultListener is the listener for script results.
      */
-    public interface ScriptResultListener {
+    interface ScriptResultListener {
         /**
          * Notifies listener of script result.
          *
@@ -50,13 +50,22 @@
 
     /**
      * Adds an active {@link com.android.car.telemetry.TelemetryProto.MetricsConfig} that is pending
-     * execution.
+     * execution. When updating the MetricsConfig to a newer version, the caller must call
+     * {@link #removeMetricsConfiguration(TelemetryProto.MetricsConfig)} first to clear the old
+     * MetricsConfig.
+     * TODO(b/191378559): Define behavior when metricsConfig contains invalid config
+     *
+     * @param metricsConfig to be added and queued for execution.
+     * @return true for success, false for failure.
      */
-    void addMetricsConfiguration(TelemetryProto.MetricsConfig metricsConfig);
+    boolean addMetricsConfiguration(TelemetryProto.MetricsConfig metricsConfig);
 
     /**
      * Removes a {@link com.android.car.telemetry.TelemetryProto.MetricsConfig} and all its
      * relevant subscriptions.
+     *
+     * @param metricsConfig to be removed from DataBroker.
+     * @return true for success, false for failure.
      */
-    void removeMetricsConfiguration(TelemetryProto.MetricsConfig metricsConfig);
+    boolean removeMetricsConfiguration(TelemetryProto.MetricsConfig metricsConfig);
 }
diff --git a/service/src/com/android/car/telemetry/databroker/DataBrokerImpl.java b/service/src/com/android/car/telemetry/databroker/DataBrokerImpl.java
index ff12dc2..e76aa10 100644
--- a/service/src/com/android/car/telemetry/databroker/DataBrokerImpl.java
+++ b/service/src/com/android/car/telemetry/databroker/DataBrokerImpl.java
@@ -16,9 +16,17 @@
 
 package com.android.car.telemetry.databroker;
 
-import com.android.car.telemetry.TelemetryProto;
+import android.util.ArrayMap;
 
+import com.android.car.telemetry.TelemetryProto;
+import com.android.car.telemetry.TelemetryProto.MetricsConfig;
+import com.android.car.telemetry.publisher.LogcatPublisher;
+import com.android.car.telemetry.publisher.Publisher;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Implementation of the data path component of CarTelemetryService. Forwards the published data
@@ -28,6 +36,16 @@
 
     private final ScriptResultListener mScriptResultListener;
 
+    // Publisher is created per data source type. Publishers are kept alive once created. This map
+    // is used to check if a publisher already exists for a given type to prevent duplicate
+    // instantiation.
+    private final Map<TelemetryProto.Publisher.PublisherCase, Publisher> mPublisherMap =
+            new ArrayMap<>();
+
+    // Maps MetricsConfig's name to its subscriptions. This map is useful when removing a
+    // MetricsConfig.
+    private final Map<String, List<DataSubscriber>> mSubscriptionMap = new ArrayMap<>();
+
     public DataBrokerImpl(ScriptResultListener scriptResultListener) {
         mScriptResultListener = scriptResultListener;
     }
@@ -38,12 +56,70 @@
     }
 
     @Override
-    public void addMetricsConfiguration(TelemetryProto.MetricsConfig metricsConfig) {
-        // TODO(b/187743369): implement
+    public boolean addMetricsConfiguration(MetricsConfig metricsConfig) {
+        // if metricsConfig already exists, it should not be added again
+        if (mSubscriptionMap.containsKey(metricsConfig.getName())) {
+            return false;
+        }
+        // Create the subscribers for this metrics configuration
+        List<DataSubscriber> dataSubscribers = new ArrayList<>();
+        for (TelemetryProto.Subscriber subscriber : metricsConfig.getSubscribersList()) {
+            // protobuf publisher to a concrete Publisher
+            Publisher publisher = getOrCreatePublisherFromType(
+                    subscriber.getPublisher().getPublisherCase());
+
+            // create DataSubscriber from TelemetryProto.Subscriber
+            DataSubscriber dataSubscriber = new DataSubscriber(metricsConfig, subscriber);
+            dataSubscribers.add(dataSubscriber);
+
+            publisher.addSubscriber(dataSubscriber); // add subscriber to receive data
+        }
+        mSubscriptionMap.put(metricsConfig.getName(), dataSubscribers);
+        return true;
     }
 
     @Override
-    public void removeMetricsConfiguration(TelemetryProto.MetricsConfig metricsConfig) {
-        // TODO(b/187743369): implement
+    public boolean removeMetricsConfiguration(MetricsConfig metricsConfig) {
+        if (!mSubscriptionMap.containsKey(metricsConfig.getName())) {
+            return false;
+        }
+        // get the subscriptions associated with this MetricsConfig, remove it from the map
+        List<DataSubscriber> dataSubscribers = mSubscriptionMap.remove(metricsConfig.getName());
+        // for each subscriber, remove it from publishers
+        for (DataSubscriber subscriber : dataSubscribers) {
+            Publisher publisher = getOrCreatePublisherFromType(
+                    subscriber.getPublisherParam().getPublisherCase());
+            publisher.removeSubscriber(subscriber);
+            // TODO(b/187743369): remove related tasks from the queue
+        }
+        return true;
+    }
+
+    /**
+     * Gets and returns a {@link com.android.car.telemetry.publisher.Publisher} if it exists in
+     * the map, or creates one from the {@link com.android.car.telemetry.TelemetryProto.Publisher}'s
+     * type.
+     */
+    private Publisher getOrCreatePublisherFromType(
+            TelemetryProto.Publisher.PublisherCase type) {
+        Publisher publisher = mPublisherMap.get(type);
+        // check if publisher exists for this source
+        if (publisher != null) {
+            return publisher;
+        }
+        // TODO(b/187743369): use switch statement to create the correct publisher
+        publisher = new LogcatPublisher();
+        mPublisherMap.put(type, publisher);
+        return publisher;
+    }
+
+    @VisibleForTesting
+    Map<TelemetryProto.Publisher.PublisherCase, Publisher> getPublisherMap() {
+        return mPublisherMap;
+    }
+
+    @VisibleForTesting
+    Map<String, List<DataSubscriber>> getSubscriptionMap() {
+        return mSubscriptionMap;
     }
 }
diff --git a/service/src/com/android/car/telemetry/databroker/DataSubscriber.java b/service/src/com/android/car/telemetry/databroker/DataSubscriber.java
new file mode 100644
index 0000000..a32cc66
--- /dev/null
+++ b/service/src/com/android/car/telemetry/databroker/DataSubscriber.java
@@ -0,0 +1,39 @@
+/*
+ * 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.telemetry.databroker;
+
+import com.android.car.telemetry.TelemetryProto;
+
+/**
+ * Subscriber class that receive published data and schedules tasks for execution on the data.
+ */
+public class DataSubscriber {
+    private final TelemetryProto.Subscriber mSubscriber;
+
+    public DataSubscriber(TelemetryProto.MetricsConfig metricsConfig,
+            TelemetryProto.Subscriber subscriber) {
+        mSubscriber = subscriber;
+    }
+
+    /**
+     * Returns the publisher param {@link com.android.car.telemetry.TelemetryProto.Publisher} that
+     * contains the data source and the config.
+     */
+    public TelemetryProto.Publisher getPublisherParam() {
+        return mSubscriber.getPublisher();
+    }
+}
diff --git a/service/src/com/android/car/telemetry/publisher/LogcatPublisher.java b/service/src/com/android/car/telemetry/publisher/LogcatPublisher.java
index a8795c2..7bebe07 100644
--- a/service/src/com/android/car/telemetry/publisher/LogcatPublisher.java
+++ b/service/src/com/android/car/telemetry/publisher/LogcatPublisher.java
@@ -16,7 +16,7 @@
 
 package com.android.car.telemetry.publisher;
 
-import com.android.car.telemetry.Channel;
+import com.android.car.telemetry.databroker.DataSubscriber;
 
 /**
  * Publisher for Android logs (adb logcat).
@@ -24,13 +24,13 @@
  * TODO(b/187525360): Move the logic from LogcatReader here.
  */
 public class LogcatPublisher implements Publisher {
-    /** See {@link Publisher#addChannel()} for details. */
-    public void addChannel(Channel channel) {
+    @Override
+    public void addSubscriber(DataSubscriber dataSubscriber) {
         // TODO(b/187525360): implement
     }
 
-    /** See {@link Publisher#removeChannel()} for details. */
-    public void removeChannel(Channel channel) {
+    @Override
+    public void removeSubscriber(DataSubscriber dataSubscriber) {
         // TODO(b/187525360): implement
     }
 }
diff --git a/service/src/com/android/car/telemetry/publisher/Publisher.java b/service/src/com/android/car/telemetry/publisher/Publisher.java
index 5962711..d4d670e 100644
--- a/service/src/com/android/car/telemetry/publisher/Publisher.java
+++ b/service/src/com/android/car/telemetry/publisher/Publisher.java
@@ -16,30 +16,30 @@
 
 package com.android.car.telemetry.publisher;
 
-import com.android.car.telemetry.Channel;
+import com.android.car.telemetry.databroker.DataSubscriber;
 
 /**
- * Interface for publishers. It's implemented per data source, data is sent to {@link Channel}.
- * Publisher stops itself when there are no channels.
+ * Interface for publishers. It's implemented per data source, data is sent to
+ * {@link DataSubscriber}. Publisher stops itself when there are no subscribers.
  *
  * <p>Note that it doesn't map 1-1 to {@link TelemetryProto.Publisher} configuration. Single
  * publisher instance can send data as several {@link TelemetryProto.Publisher} to subscribers.
  */
 public interface Publisher {
+    /** Adds a subscriber that listens for data produced by this publisher. */
     /**
-     * Adds a channel to the publisher. Publisher will immediately start pushing
-     * data to the channel.
-     *
-     * @param channel a channel to publish data.
-     *
-     * @throws IllegalArgumentException if the channel was added before.
+     * Adds a subscriber that listens for data produced by this publisher.
+     * @param dataSubscriber a subscriber to receive data
+     * @throws IllegalArgumentException if the subscriber was added before.
      */
-    void addChannel(Channel channel);
+    void addSubscriber(DataSubscriber dataSubscriber);
 
+    /**  */
     /**
-     * Removes the channel from the publisher.
+     * Removes a subscriber from the publisher.
      *
-     * @throws IllegalArgumentException if the channel was not found.
+     * @param dataSubscriber to be removed
+     * @throws IllegalArgumentException if the subscriber was not found.
      */
-    void removeChannel(Channel channel);
+    void removeSubscriber(DataSubscriber dataSubscriber);
 }
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/CarCtsFakeLauncher/src/com/android/car/fakelauncher/LauncherActivity.java b/tests/CarCtsFakeLauncher/src/com/android/car/fakelauncher/LauncherActivity.java
index 42d802a..0e73284 100644
--- a/tests/CarCtsFakeLauncher/src/com/android/car/fakelauncher/LauncherActivity.java
+++ b/tests/CarCtsFakeLauncher/src/com/android/car/fakelauncher/LauncherActivity.java
@@ -28,7 +28,7 @@
 /**
  * A placeholder launcher for CTS.
  *
- * Current CarLauncher invokes Maps Activity in its ActivityView and it creates unexpected events
+ * Current CarLauncher invokes Maps Activity in its TaskView and it creates unexpected events
  * for CTS and they make CTS fail.
  */
 public class LauncherActivity extends Activity {
diff --git a/tests/CarEvsCameraPreviewApp/AndroidManifest.xml b/tests/CarEvsCameraPreviewApp/AndroidManifest.xml
index bf54d88..6e4c254 100644
--- a/tests/CarEvsCameraPreviewApp/AndroidManifest.xml
+++ b/tests/CarEvsCameraPreviewApp/AndroidManifest.xml
@@ -23,6 +23,8 @@
     <uses-permission android:name="android.car.permission.USE_CAR_EVS_CAMERA" />
     <uses-permission android:name="android.car.permission.MONITOR_CAR_EVS_STATUS" />
 
+    <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW" />
+
     <application android:label="@string/app_name"
             android:icon="@drawable/rearview"
             android:hardwareAccelerated="true"
@@ -31,11 +33,10 @@
         <activity android:name=".CarEvsCameraPreviewActivity"
                 android:exported="true"
                 android:label="@string/app_name"
-                android:launchMode="singleTask"
                 android:resizeableActivity="false"
                 android:screenOrientation="landscape"
                 android:showForAllUsers="true"
-                android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
+                android:theme="@style/Theme.Transparent"
                 android:turnScreenOn="true">
         </activity>
 
diff --git a/tests/CarEvsCameraPreviewApp/res/layout/evs_preview_activity.xml b/tests/CarEvsCameraPreviewApp/res/layout/evs_preview_activity.xml
index 5745d8d..bfd138b 100644
--- a/tests/CarEvsCameraPreviewApp/res/layout/evs_preview_activity.xml
+++ b/tests/CarEvsCameraPreviewApp/res/layout/evs_preview_activity.xml
@@ -10,21 +10,18 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:id="@+id/evs_preview_container"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:background="@android:color/transparent"
+              android:orientation="vertical">
+    <Button
+        android:id="@+id/close_button"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:orientation="horizontal">
-      <TextureView
-        android:id="@+id/texture"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_alignParentTop="true" />
-      <SeekBar
-        android:id="@+id/saturationAdjustBar"
-        android:layout_width="600dp"
         android:layout_height="wrap_content"
-        android:layout_gravity="bottom|center_horizontal"
-        android:layout_marginBottom="100dp"
-        android:max="100"
-        android:progress="50"/>
-</FrameLayout>
+        android:text="@string/close_button_text"
+        android:textColor="@color/button_text"
+        android:background="@color/button_background"
+        android:textSize="@dimen/close_button_text_size"/>
+</LinearLayout>
diff --git a/tests/CarEvsCameraPreviewApp/res/values/colors.xml b/tests/CarEvsCameraPreviewApp/res/values/colors.xml
new file mode 100644
index 0000000..36632cb
--- /dev/null
+++ b/tests/CarEvsCameraPreviewApp/res/values/colors.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <color name="button_text">@*android:color/car_body1_light</color>
+    <color name="button_background">@*android:color/car_card_dark</color>
+</resources>
diff --git a/tests/CarEvsCameraPreviewApp/res/values/dimens.xml b/tests/CarEvsCameraPreviewApp/res/values/dimens.xml
new file mode 100644
index 0000000..35bc2c4
--- /dev/null
+++ b/tests/CarEvsCameraPreviewApp/res/values/dimens.xml
@@ -0,0 +1,22 @@
+<!--
+  ~ 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.
+  -->
+<resources>
+    <!-- dimensions for evs camera preview in the system window -->
+    <dimen name="camera_preview_width">800dp</dimen>
+    <dimen name="camera_preview_height">600dp</dimen>
+
+    <dimen name="close_button_text_size">30sp</dimen>
+</resources>
\ No newline at end of file
diff --git a/tests/CarEvsCameraPreviewApp/res/values/strings.xml b/tests/CarEvsCameraPreviewApp/res/values/strings.xml
index c386e1d..4653f01 100644
--- a/tests/CarEvsCameraPreviewApp/res/values/strings.xml
+++ b/tests/CarEvsCameraPreviewApp/res/values/strings.xml
@@ -17,4 +17,5 @@
 
 <resources>
     <string name="app_name">EvsCameraPreview</string>
+    <string name="close_button_text">Close</string>
 </resources>
diff --git a/tests/CarEvsCameraPreviewApp/res/values/themes.xml b/tests/CarEvsCameraPreviewApp/res/values/themes.xml
new file mode 100644
index 0000000..14b98bb
--- /dev/null
+++ b/tests/CarEvsCameraPreviewApp/res/values/themes.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources>
+    <style name="Theme.Transparent" parent="android:Theme">
+        <item name="android:windowIsTranslucent">true</item>
+        <item name="android:windowBackground">@android:color/transparent</item>
+        <item name="android:windowContentOverlay">@null</item>
+        <item name="android:windowNoTitle">true</item>
+        <item name="android:backgroundDimEnabled">false</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/tests/CarEvsCameraPreviewApp/src/com/google/android/car/evs/CarEvsCameraPreviewActivity.java b/tests/CarEvsCameraPreviewApp/src/com/google/android/car/evs/CarEvsCameraPreviewActivity.java
index e34e4e8..bf6e950 100644
--- a/tests/CarEvsCameraPreviewApp/src/com/google/android/car/evs/CarEvsCameraPreviewActivity.java
+++ b/tests/CarEvsCameraPreviewApp/src/com/google/android/car/evs/CarEvsCameraPreviewActivity.java
@@ -25,12 +25,18 @@
 import android.car.evs.CarEvsBufferDescriptor;
 import android.car.evs.CarEvsManager;
 import android.content.Intent;
+import android.graphics.PixelFormat;
 import android.hardware.display.DisplayManager;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.util.Log;
 import android.view.Display;
+import android.view.Gravity;
+import android.view.LayoutInflater;
 import android.view.View;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.LinearLayout;
 
 import java.util.ArrayList;
 import java.util.concurrent.ExecutorService;
@@ -48,7 +54,8 @@
     private final ExecutorService mCallbackExecutor = Executors.newFixedThreadPool(1);
 
     /** GL backed surface view to render the camera preview */
-    private CarEvsCameraGLSurfaceView mView;
+    private CarEvsCameraGLSurfaceView mEvsView;
+    private LinearLayout mPreviewContainer;
 
     /** Display manager to monitor the display's state */
     private DisplayManager mDisplayManager;
@@ -59,13 +66,12 @@
     /** Tells whether or not a video stream is running */
     private boolean mStreamRunning = false;
 
-    /** True if we need to start a video stream */
-    private boolean mActivityResumed = false;
-
     private Car mCar;
     private CarEvsManager mEvsManager;
 
-    private IBinder mSessiontoken;
+    private IBinder mSessionToken;
+
+    private boolean mUseSystemWindow;
 
     /** Callback to listen to EVS stream */
     private final CarEvsManager.CarEvsStreamCallback mStreamHandler =
@@ -75,6 +81,9 @@
         public void onStreamEvent(int event) {
             // This reference implementation only monitors a stream event without any action.
             Log.i(TAG, "Received: " + event);
+            if (event == CarEvsManager.STREAM_EVENT_STREAM_STOPPED) {
+                finish();
+            }
         }
 
         @Override
@@ -141,6 +150,8 @@
         Log.d(TAG, "onCreate");
         super.onCreate(savedInstanceState);
 
+        parseExtra(getIntent());
+
         setShowWhenLocked(true);
         mDisplayManager = getSystemService(DisplayManager.class);
         mDisplayManager.registerDisplayListener(mDisplayListener, null);
@@ -152,83 +163,97 @@
         Car.createCar(getApplicationContext(), /* handler = */ null,
                 Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER, mCarServiceLifecycleListener);
 
-        mView = new CarEvsCameraGLSurfaceView(getApplication(), this);
-        setContentView(mView);
+        mEvsView = new CarEvsCameraGLSurfaceView(getApplication(), this);
+        mPreviewContainer = (LinearLayout) LayoutInflater.from(this).inflate(
+                R.layout.evs_preview_activity, /* root= */ null);
+        LinearLayout.LayoutParams viewParam = new LinearLayout.LayoutParams(
+                LinearLayout.LayoutParams.MATCH_PARENT,
+                LinearLayout.LayoutParams.MATCH_PARENT,
+                1.0f
+        );
+        mEvsView.setLayoutParams(viewParam);
+        mPreviewContainer.addView(mEvsView, 0);
+        Button closeButton = mPreviewContainer.findViewById(R.id.close_button);
+        closeButton.setOnClickListener((v) -> finish());
 
-        setSessionToken(getIntent());
+        int width = WindowManager.LayoutParams.MATCH_PARENT;
+        int height = WindowManager.LayoutParams.MATCH_PARENT;
+        int x = 0;
+        int y = 0;
+        if (mUseSystemWindow) {
+            width = getResources().getDimensionPixelOffset(R.dimen.camera_preview_width);
+            height = getResources().getDimensionPixelOffset(R.dimen.camera_preview_height);
+            x = (getResources().getDisplayMetrics().widthPixels - width) / 2;
+            y = (getResources().getDisplayMetrics().heightPixels - height) / 2;
+        }
+        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
+                width, height, x, y,
+                2020 /* WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY */,
+                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
+                PixelFormat.TRANSLUCENT);
+        params.gravity = Gravity.LEFT | Gravity.TOP;
+        if (mUseSystemWindow) {
+            WindowManager wm = getSystemService(WindowManager.class);
+            wm.addView(mPreviewContainer, params);
+        } else {
+            setContentView(mPreviewContainer, params);
+        }
     }
 
     @Override
     protected void onNewIntent(Intent intent) {
         super.onNewIntent(intent);
-        setSessionToken(intent);
+        parseExtra(intent);
     }
 
-    private void setSessionToken(Intent intent) {
+    private void parseExtra(Intent intent) {
         Bundle extras = intent.getExtras();
         if (extras == null) {
-            mSessiontoken = null;
+            mSessionToken = null;
             return;
         }
-        mSessiontoken = extras.getBinder(CarEvsManager.EXTRA_SESSION_TOKEN);
+        mSessionToken = extras.getBinder(CarEvsManager.EXTRA_SESSION_TOKEN);
+        mUseSystemWindow = mSessionToken != null;
     }
 
     @Override
     protected void onStart() {
         Log.d(TAG, "onStart");
         super.onStart();
+        handleVideoStreamLocked();
     }
 
-    @Override
-    protected void onResume() {
-        Log.d(TAG, "onResume");
-        super.onResume();
-
-        synchronized (mLock) {
-            mActivityResumed = true;
-            handleVideoStreamLocked();
-        }
-    }
-
-    @Override
-    protected void onPause() {
-        Log.d(TAG, "onPause");
-        super.onPause();
-
-        synchronized (mLock) {
-            mActivityResumed = false;
-            handleVideoStreamLocked();
-        }
-
-        synchronized (mBufferQueue) {
-            mBufferQueue.clear();
-        }
-    }
 
     @Override
     protected void onStop() {
         Log.d(TAG, "onStop");
         super.onStop();
-
-        // Request to stop current service and unregister a status listener
-        synchronized (mLock) {
-            if (mEvsManager != null) {
-                mEvsManager.stopActivity();
-                mEvsManager.clearStatusListener();
-            }
-        }
     }
 
     @Override
     protected void onDestroy() {
         super.onDestroy();
         Log.d(TAG, "onDestroy");
+        // Request to stop current service and unregister a status listener
+        synchronized (mBufferQueue) {
+            mBufferQueue.clear();
+        }
         synchronized (mLock) {
+            if (mEvsManager != null) {
+                mEvsManager.stopVideoStream();
+                mEvsManager.stopActivity();
+                mEvsManager.clearStatusListener();
+            }
             if (mCar != null) {
                 mCar.disconnect();
             }
         }
         mDisplayManager.unregisterDisplayListener(mDisplayListener);
+        if (mUseSystemWindow) {
+            WindowManager wm = getSystemService(WindowManager.class);
+            wm.removeView(mPreviewContainer);
+        }
     }
 
     private void handleVideoStreamLocked() {
@@ -237,13 +262,13 @@
             return;
         }
 
-        if (mActivityResumed && mDisplayState == Display.STATE_ON) {
+        if (mDisplayState == Display.STATE_ON) {
             // We show a camera preview only when the activity has been resumed and the display is
             // on.
             if (!mStreamRunning) {
                 Log.d(TAG, "Request to start a video stream");
                 mEvsManager.startVideoStream(CarEvsManager.SERVICE_TYPE_REARVIEW,
-                        mSessiontoken, mCallbackExecutor, mStreamHandler);
+                        mSessionToken, mCallbackExecutor, mStreamHandler);
                 mStreamRunning = true;
             }
 
diff --git a/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml b/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
index a16af8a..af573f7 100644
--- a/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
+++ b/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
@@ -194,8 +194,6 @@
             </intent-filter>
         </activity>
 
-        <activity android:name=".activityview.ActivityViewTestFragment"/>
-
         <service android:name=".vendorservice.LogLifecycleService"
              android:exported="false"
              android:directBootAware="true">
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/activity_view_test_fragment.xml b/tests/EmbeddedKitchenSinkApp/res/layout/activity_view_test_fragment.xml
deleted file mode 100644
index 568bad5..0000000
--- a/tests/EmbeddedKitchenSinkApp/res/layout/activity_view_test_fragment.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-              android:orientation="vertical"
-              android:layout_width="match_parent"
-              android:layout_height="match_parent">
-
-    <LinearLayout android:layout_width="wrap_content"
-                  android:layout_height="wrap_content"
-                  android:orientation="horizontal">
-        <Button android:id="@+id/av_launch_activity"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/av_start_activity"/>
-        <Button android:id="@+id/av_resize"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/av_resize"/>
-    </LinearLayout>
-    <RelativeLayout android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:padding="20dp">
-        <android.car.app.CarActivityView android:id="@+id/av_activityview"
-                      android:layout_width="match_parent"
-                      android:layout_height="match_parent"/>
-    </RelativeLayout>
-
-</LinearLayout>
\ No newline at end of file
diff --git a/tests/NetworkPreferenceApp/res/values/configs.xml b/tests/NetworkPreferenceApp/res/values/configs.xml
index 21e55de..c2f30d5 100644
--- a/tests/NetworkPreferenceApp/res/values/configs.xml
+++ b/tests/NetworkPreferenceApp/res/values/configs.xml
@@ -40,10 +40,12 @@
     -->
     <string-array name="config_network_preference_oem_paid_apps" translatable="false">
         <!-- <item>full.package.name</item> -->
+        <item>com.android.car.settings</item>
         <item>com.android.vending</item>
         <item>com.google.android.apps.automotive.inputmethod</item>
         <item>com.google.android.apps.maps</item>
         <item>com.google.android.car.setupwizard</item>
+        <item>com.google.android.carassistant</item>
         <item>com.google.android.configupdater</item>
         <item>com.google.android.ext.services</item>
         <item>com.google.android.ext.shared</item>
diff --git a/tests/PowerTestService/src/main.cpp b/tests/PowerTestService/src/main.cpp
index 9a87408..5da764b 100644
--- a/tests/PowerTestService/src/main.cpp
+++ b/tests/PowerTestService/src/main.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "PowerTestService: "
+#define LOG_TAG "PowerTestService"
 
 #include <signal.h>
 #include <utils/Log.h>
diff --git a/tests/android_car_api_test/src/android/car/apitest/CarApiTestBase.java b/tests/android_car_api_test/src/android/car/apitest/CarApiTestBase.java
index 4685b62..7d34052 100644
--- a/tests/android_car_api_test/src/android/car/apitest/CarApiTestBase.java
+++ b/tests/android_car_api_test/src/android/car/apitest/CarApiTestBase.java
@@ -55,7 +55,7 @@
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
 
-abstract class CarApiTestBase {
+public abstract class CarApiTestBase {
 
     private static final String TAG = CarApiTestBase.class.getSimpleName();
 
diff --git a/tests/android_car_api_test/src/android/car/apitest/CarSettingsTest.java b/tests/android_car_api_test/src/android/car/apitest/CarSettingsTest.java
new file mode 100644
index 0000000..a3070f0
--- /dev/null
+++ b/tests/android_car_api_test/src/android/car/apitest/CarSettingsTest.java
@@ -0,0 +1,101 @@
+/*
+ * 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.apitest;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.annotation.UserIdInt;
+import android.car.settings.CarSettings;
+import android.content.ContentResolver;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+@SmallTest
+public final class CarSettingsTest extends CarApiTestBase {
+
+    private static final String TAG = CarSettingsTest.class.getSimpleName();
+    private static final String SCOPE_GLOBAL = "Global";
+    private static final String SCOPE_SECURE = "Secure";
+
+    private final HashMap<String, String> mSettings = new HashMap<>();
+    private final @UserIdInt int mUserId = UserHandle.USER_CURRENT;
+
+    private final ContentResolver mContentResolver;
+
+    public CarSettingsTest() throws Exception {
+        mContentResolver = getContext().getContentResolver();
+    }
+
+    @Test
+    public void testCarSettingsNames() throws Exception {
+        loadSettingNames();
+
+        boolean isAllSettingsReadable = checkAllSettingsReadable();
+
+        assertWithMessage("car settings readable").that(isAllSettingsReadable).isTrue();
+    }
+
+    private void loadSettingNames() {
+        mSettings.put(CarSettings.Global.DEFAULT_USER_RESTRICTIONS_SET, SCOPE_GLOBAL);
+        mSettings.put(CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE, SCOPE_GLOBAL);
+        mSettings.put(CarSettings.Global.DISABLE_INSTRUMENTATION_SERVICE, SCOPE_GLOBAL);
+        mSettings.put(CarSettings.Global.ENABLE_USER_SWITCH_DEVELOPER_MESSAGE, SCOPE_GLOBAL);
+        mSettings.put(CarSettings.Global.LAST_ACTIVE_USER_ID, SCOPE_GLOBAL);
+        mSettings.put(CarSettings.Global.LAST_ACTIVE_PERSISTENT_USER_ID, SCOPE_GLOBAL);
+        mSettings.put(CarSettings.Secure.KEY_AUDIO_FOCUS_NAVIGATION_REJECTED_DURING_CALL,
+                SCOPE_SECURE);
+        mSettings.put(CarSettings.Secure.KEY_AUDIO_PERSIST_VOLUME_GROUP_MUTE_STATES, SCOPE_SECURE);
+        mSettings.put(CarSettings.Secure.KEY_ENABLE_INITIAL_NOTICE_SCREEN_TO_USER, SCOPE_SECURE);
+        mSettings.put(CarSettings.Secure.KEY_SETUP_WIZARD_IN_PROGRESS, SCOPE_SECURE);
+
+        mSettings.put(CarSettings.Secure.KEY_BLUETOOTH_A2DP_SINK_DEVICES, SCOPE_SECURE);
+        mSettings.put(CarSettings.Secure.KEY_BLUETOOTH_PAN_DEVICES, SCOPE_SECURE);
+        mSettings.put(CarSettings.Secure.KEY_BLUETOOTH_HFP_CLIENT_DEVICES, SCOPE_SECURE);
+        mSettings.put(CarSettings.Secure.KEY_BLUETOOTH_MAP_CLIENT_DEVICES, SCOPE_SECURE);
+        mSettings.put(CarSettings.Secure.KEY_BLUETOOTH_PBAP_CLIENT_DEVICES, SCOPE_SECURE);
+        mSettings.put(CarSettings.Secure.KEY_BLUETOOTH_PROFILES_INHIBITED, SCOPE_SECURE);
+    }
+
+    private boolean checkAllSettingsReadable() throws Exception {
+        Iterator<Map.Entry<String, String>> settingEntrys = mSettings.entrySet().iterator();
+        while (settingEntrys.hasNext()) {
+            Map.Entry<String, String> entry = settingEntrys.next();
+            String name = entry.getKey();
+            String scope = entry.getValue();
+            switch (scope) {
+                case SCOPE_GLOBAL:
+                    Settings.Global.getString(mContentResolver, name);
+                    break;
+                case SCOPE_SECURE:
+                    Settings.Secure.getStringForUser(mContentResolver, name, mUserId);
+                    break;
+                default:
+                    Log.e(TAG, "unsupported scope: " + scope);
+                    return false;
+            }
+        }
+        return true;
+    }
+}
diff --git a/tests/android_car_api_test/src/android/car/apitest/media/CarAudioManagerTest.java b/tests/android_car_api_test/src/android/car/apitest/media/CarAudioManagerTest.java
new file mode 100644
index 0000000..9fb2781
--- /dev/null
+++ b/tests/android_car_api_test/src/android/car/apitest/media/CarAudioManagerTest.java
@@ -0,0 +1,147 @@
+/*
+ * 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.apitest.media;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assume.assumeTrue;
+
+import android.car.Car;
+import android.car.apitest.CarApiTestBase;
+import android.car.media.CarAudioManager;
+import android.media.AudioAttributes;
+import android.media.AudioDeviceInfo;
+import android.os.Process;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class CarAudioManagerTest extends CarApiTestBase {
+
+    private CarAudioManager mCarAudioManager;
+
+    @Before
+    public void setUp() throws Exception {
+        mCarAudioManager = (CarAudioManager) getCar().getCarManager(Car.AUDIO_SERVICE);
+        assertThat(mCarAudioManager).isNotNull();
+    }
+
+    @Test
+    public void test_getAudioZoneIds() throws Exception {
+        assumeDynamicRoutingIsEnabled();
+
+        List<Integer> zoneIds = mCarAudioManager.getAudioZoneIds();
+        assertThat(zoneIds).isNotEmpty();
+        assertThat(zoneIds).contains(CarAudioManager.PRIMARY_AUDIO_ZONE);
+    }
+
+    @Test
+    public void test_isAudioFeatureEnabled() throws Exception {
+        // nothing to assert. Just call the API.
+        mCarAudioManager.isAudioFeatureEnabled(CarAudioManager.AUDIO_FEATURE_DYNAMIC_ROUTING);
+        mCarAudioManager.isAudioFeatureEnabled(CarAudioManager.AUDIO_FEATURE_VOLUME_GROUP_MUTING);
+    }
+
+    @Test
+    public void test_getVolumeGroupCount() throws Exception {
+        int primaryZoneCount = mCarAudioManager.getVolumeGroupCount();
+        assertThat(
+                mCarAudioManager.getVolumeGroupCount(CarAudioManager.PRIMARY_AUDIO_ZONE)).isEqualTo(
+                primaryZoneCount);
+    }
+
+    @Test
+    public void test_getGroupVolume() throws Exception {
+        int groudId = 0;
+        int volume = mCarAudioManager.getGroupVolume(groudId);
+        assertThat(mCarAudioManager.getGroupVolume(CarAudioManager.PRIMARY_AUDIO_ZONE,
+                groudId)).isEqualTo(volume);
+        int maxVolume = mCarAudioManager.getGroupMaxVolume(groudId);
+        assertThat(mCarAudioManager.getGroupMaxVolume(CarAudioManager.PRIMARY_AUDIO_ZONE,
+                groudId)).isEqualTo(maxVolume);
+        int minVolume = mCarAudioManager.getGroupMinVolume(groudId);
+        assertThat(mCarAudioManager.getGroupMinVolume(CarAudioManager.PRIMARY_AUDIO_ZONE,
+                groudId)).isEqualTo(minVolume);
+        assertThat(volume).isAtLeast(minVolume);
+        assertThat(volume).isAtMost(maxVolume);
+    }
+
+    @Test
+    public void test_setGroupVolume() throws Exception {
+        int groudId = 0;
+        int volume = mCarAudioManager.getGroupVolume(groudId);
+        mCarAudioManager.setGroupVolume(groudId, volume, 0);
+        mCarAudioManager.setGroupVolume(CarAudioManager.PRIMARY_AUDIO_ZONE, groudId, volume, 0);
+        assertThat(mCarAudioManager.getGroupVolume(groudId)).isEqualTo(volume);
+    }
+
+    @Test
+    public void test_getInputDevicesForZoneId() throws Exception {
+        assumeDynamicRoutingIsEnabled();
+
+        List<AudioDeviceInfo> info = mCarAudioManager.getInputDevicesForZoneId(
+                CarAudioManager.PRIMARY_AUDIO_ZONE);
+        assertThat(info).isNotEmpty();
+    }
+
+    @Test
+    public void test_getOutputDeviceForUsage() throws Exception {
+        assumeDynamicRoutingIsEnabled();
+
+        AudioDeviceInfo device = mCarAudioManager.getOutputDeviceForUsage(
+                CarAudioManager.PRIMARY_AUDIO_ZONE, AudioAttributes.USAGE_MEDIA);
+        assertThat(device).isNotNull();
+    }
+
+    @Test
+    public void test_getVolumeGroupIdForUsage() throws Exception {
+        int groupId = mCarAudioManager.getVolumeGroupIdForUsage(AudioAttributes.USAGE_MEDIA);
+        assertThat(mCarAudioManager.getVolumeGroupIdForUsage(CarAudioManager.PRIMARY_AUDIO_ZONE,
+                AudioAttributes.USAGE_MEDIA)).isEqualTo(groupId);
+        int primaryZoneCount = mCarAudioManager.getVolumeGroupCount();
+        assertThat(groupId).isLessThan(primaryZoneCount);
+    }
+
+    @Test
+    public void test_getZoneIdForUid() throws Exception {
+        assumeDynamicRoutingIsEnabled();
+
+        assertThat(mCarAudioManager.getZoneIdForUid(Process.myUid())).isEqualTo(
+                CarAudioManager.PRIMARY_AUDIO_ZONE);
+    }
+
+    @Test
+    public void test_isPlaybackOnVolumeGroupActive() throws Exception {
+        assumeDynamicRoutingIsEnabled();
+
+        // TODO(b/191660867): Better to change this to play something and asert true.
+        assertThat(
+                mCarAudioManager.isPlaybackOnVolumeGroupActive(CarAudioManager.PRIMARY_AUDIO_ZONE,
+                        0)).isFalse();
+    }
+
+    private void assumeDynamicRoutingIsEnabled() {
+        assumeTrue(mCarAudioManager.isAudioFeatureEnabled(
+                CarAudioManager.AUDIO_FEATURE_DYNAMIC_ROUTING));
+    }
+}
diff --git a/tests/carservice_test/AndroidManifest.xml b/tests/carservice_test/AndroidManifest.xml
index 7133831..7bf749e 100644
--- a/tests/carservice_test/AndroidManifest.xml
+++ b/tests/carservice_test/AndroidManifest.xml
@@ -51,7 +51,6 @@
             </intent-filter>
         </service>
 
-        <activity android:name="com.android.car.CarUxRestrictionsManagerServiceTest$ActivityViewTestActivity"/>
         <activity android:name="com.android.car.pm.ActivityBlockingActivityTest$NonDoNoHistoryActivity"
              android:noHistory="true"/>
         <activity android:name="com.android.car.pm.ActivityBlockingActivityTest$NonDoActivity"/>
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/garagemode/GarageModeServiceTest.java b/tests/carservice_test/src/com/android/car/garagemode/GarageModeServiceTest.java
index c1b45d3..cd9adfb 100644
--- a/tests/carservice_test/src/com/android/car/garagemode/GarageModeServiceTest.java
+++ b/tests/carservice_test/src/com/android/car/garagemode/GarageModeServiceTest.java
@@ -60,6 +60,15 @@
     }
 
     @Test
+    public void testInitAndRelease() {
+        mService.init();
+        mService.release();
+
+        verify(mMockController).init();
+        verify(mMockController).release();
+    }
+
+    @Test
     public void testDump_shouldSucceed() {
         when(mMockController.isGarageModeActive()).thenReturn(true);
 
@@ -68,4 +77,33 @@
         List<String> strings = mCaptorString.getAllValues();
         assertThat(strings.get(0)).isEqualTo("GarageModeInProgress true");
     }
+
+    @Test
+    public void testIsGarageModeActive_true() {
+        when(mMockController.isGarageModeActive()).thenReturn(true);
+
+        assertThat(mService.isGarageModeActive()).isTrue();
+    }
+
+    @Test
+    public void testIsGarageModeActive_false() {
+        when(mMockController.isGarageModeActive()).thenReturn(false);
+
+        assertThat(mService.isGarageModeActive()).isFalse();
+    }
+
+    @Test
+    public void testForceStartGarageMode() {
+        mService.forceStartGarageMode();
+
+        verify(mMockController).initiateGarageMode(null);
+    }
+
+    @Test
+    public void testStopAndResetGarageMode() {
+        mService.stopAndResetGarageMode();
+
+        verify(mMockController).resetGarageMode();
+    }
+
 }
diff --git a/tests/carservice_test/src/com/android/car/input/CarInputManagerTest.java b/tests/carservice_test/src/com/android/car/input/CarInputManagerTest.java
index a9e678f..5cb55c3 100644
--- a/tests/carservice_test/src/com/android/car/input/CarInputManagerTest.java
+++ b/tests/carservice_test/src/com/android/car/input/CarInputManagerTest.java
@@ -34,6 +34,7 @@
 import android.car.input.RotaryEvent;
 import android.hardware.automotive.vehicle.V2_0.VehicleDisplay;
 import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
+import android.os.SystemClock;
 import android.util.Log;
 import android.util.Pair;
 import android.view.KeyEvent;
@@ -50,6 +51,7 @@
 import org.junit.runner.RunWith;
 
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.concurrent.Executor;
@@ -264,6 +266,50 @@
     }
 
     @Test
+    public void testInjectKeyEvent_mainDisplay() throws Exception {
+        int r = mCarInputManager.requestInputEventCapture(
+                CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
+                new int[]{CarInputManager.INPUT_TYPE_ALL_INPUTS},
+                CarInputManager.CAPTURE_REQ_FLAGS_TAKE_ALL_EVENTS_FOR_DISPLAY, mCallback0);
+        assertThat(r).isEqualTo(CarInputManager.INPUT_CAPTURE_RESPONSE_SUCCEEDED);
+
+        KeyEvent keyEvent = newKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER);
+
+        mCarInputManager.injectKeyEvent(keyEvent, CarOccupantZoneManager.DISPLAY_TYPE_MAIN);
+
+        mCallback0.waitForKeyEvent();
+        assertThat(mCallback0.getkeyEvents()).containsExactly(
+                new Pair<>(CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
+                        Collections.singletonList(keyEvent)));
+    }
+
+    @Test
+    public void testInjectKeyEvent_instrumentClusterDisplay() throws Exception {
+        int r = mCarInputManager.requestInputEventCapture(
+                CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER,
+                new int[]{CarInputManager.INPUT_TYPE_ALL_INPUTS},
+                CarInputManager.CAPTURE_REQ_FLAGS_TAKE_ALL_EVENTS_FOR_DISPLAY, mCallback0);
+        assertThat(r).isEqualTo(CarInputManager.INPUT_CAPTURE_RESPONSE_SUCCEEDED);
+
+        KeyEvent keyEvent = newKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER);
+
+        mCarInputManager.injectKeyEvent(keyEvent,
+                CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER);
+
+        mCallback0.waitForKeyEvent();
+        assertThat(mCallback0.getkeyEvents()).containsExactly(
+                new Pair<>(CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER,
+                        Collections.singletonList(keyEvent)));
+    }
+
+    private static KeyEvent newKeyEvent(int action, int code) {
+        long currentTime = SystemClock.uptimeMillis();
+        return new KeyEvent(/* downTime= */ currentTime,
+                /* eventTime= */ currentTime, action, code,
+                /* repeat= */ 0);
+    }
+
+    @Test
     public void testFailWithFullCaptureHigherPriority() {
         CarInputManager carInputManager0 = createAnotherCarInputManager();
         int r = carInputManager0.requestInputEventCapture(
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/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/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/cluster/ClusterHomeServiceUnitTest.java b/tests/carservice_unit_test/src/com/android/car/cluster/ClusterHomeServiceUnitTest.java
new file mode 100644
index 0000000..4afe81e
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/cluster/ClusterHomeServiceUnitTest.java
@@ -0,0 +1,306 @@
+/*
+ * 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.cluster;
+
+import static android.car.CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER;
+import static android.car.cluster.ClusterHomeManager.UI_TYPE_CLUSTER_HOME;
+import static android.car.cluster.ClusterHomeManager.UI_TYPE_CLUSTER_NONE;
+import static android.car.navigation.CarNavigationInstrumentCluster.CLUSTER_TYPE_IMAGE_CODES_ONLY;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityOptions;
+import android.car.cluster.ClusterHomeManager;
+import android.car.cluster.ClusterState;
+import android.car.cluster.IClusterHomeCallback;
+import android.car.cluster.navigation.NavigationState.NavigationStateProto;
+import android.car.navigation.CarNavigationInstrumentCluster;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Insets;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.view.Display;
+
+import com.android.car.CarOccupantZoneService;
+import com.android.car.am.FixedActivityService;
+import com.android.car.cluster.ClusterNavigationService.ContextOwner;
+import com.android.car.hal.ClusterHalService;
+
+import com.google.protobuf.InvalidProtocolBufferException;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ClusterHomeServiceUnitTest {
+    private static final int CLUSTER_DISPLAY_ID = 99;
+    private static final int CLUSTER_WIDTH = 1024;
+    private static final int CLUSTER_HEIGHT = 600;
+    private static final int UI_TYPE_CLUSTER_MAPS = UI_TYPE_CLUSTER_HOME + 1;
+    private static final String NAV_STATE_PROTO_BUNDLE_KEY = "navstate2";
+    private static final int USER_ID = 111;
+
+    private ClusterHomeService mClusterHomeService;
+
+    @Mock
+    private Context mContext;
+    @Mock
+    private ClusterHalService mClusterHalService;
+    @Mock
+    private ClusterNavigationService mNavigationService;
+    @Mock
+    private CarOccupantZoneService mOccupantZoneService;
+    @Mock
+    private FixedActivityService mFixedActivityService;
+    @Mock
+    private DisplayManager mDisplayManager;
+    @Mock
+    private Display mClusterDisplay;
+
+    private ClusterState mClusterState;
+    private int mClusterStateChanges;
+    private byte[] mNavigationState;
+
+    private IClusterHomeCallback mClusterHomeCallback;
+    private class IClusterHomeCallbackImpl extends IClusterHomeCallback.Stub {
+        @Override
+        public void onClusterStateChanged(ClusterState state, int changes) {
+            mClusterState = state;
+            mClusterStateChanges = changes;
+        }
+
+        @Override
+        public void onNavigationStateChanged(byte[] navigationState) {
+            mNavigationState = navigationState;
+        }
+    }
+
+    private ComponentName mClusterHomeActivity = new ComponentName("clusterhome.pkg", "activity");
+
+    @Before
+    public void setUp() {
+        when(mContext.getString(com.android.car.R.string.config_clusterHomeActivity))
+                .thenReturn(mClusterHomeActivity.flattenToString());
+        when(mContext.getSystemService(DisplayManager.class)).thenReturn(mDisplayManager);
+
+        when(mOccupantZoneService.getDisplayIdForDriver(DISPLAY_TYPE_INSTRUMENT_CLUSTER))
+                .thenReturn(CLUSTER_DISPLAY_ID);
+        when(mClusterHalService.isCoreSupported()).thenReturn(true);
+        when(mClusterHalService.isNavigationStateSupported()).thenReturn(true);
+        when(mDisplayManager.getDisplay(CLUSTER_DISPLAY_ID)).thenReturn(mClusterDisplay);
+        doAnswer(invocation -> {
+            Point size = (Point) invocation.getArgument(0);
+            size.set(CLUSTER_WIDTH, CLUSTER_HEIGHT);
+            return null;
+        }).when(mClusterDisplay).getRealSize(any(Point.class));
+
+        mClusterHomeService = new ClusterHomeService(mContext, mClusterHalService,
+                mNavigationService, mOccupantZoneService, mFixedActivityService);
+        mClusterHomeService.init();
+    }
+
+    public void registerClusterHomeCallback() {
+        mClusterHomeCallback = new IClusterHomeCallbackImpl();
+        mClusterHomeService.registerCallback(mClusterHomeCallback);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (mClusterHomeCallback != null) {
+            mClusterHomeService.unregisterCallback(mClusterHomeCallback);
+        }
+        mClusterHomeService.release();
+    }
+
+    @Test
+    public void initStartsClusterHomeActivity() {
+        // ClusterHomeService.init() was called in setUp().
+
+        ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+        ArgumentCaptor<ActivityOptions> activityOptionsCaptor = ArgumentCaptor.forClass(
+                ActivityOptions.class);
+        verify(mFixedActivityService).startFixedActivityModeForDisplayAndUser(
+                intentCaptor.capture(), activityOptionsCaptor.capture(),
+                eq(CLUSTER_DISPLAY_ID), eq(UserHandle.USER_SYSTEM));
+        assertThat(intentCaptor.getValue().getComponent()).isEqualTo(mClusterHomeActivity);
+        assertThat(activityOptionsCaptor.getValue().getLaunchDisplayId())
+                .isEqualTo(CLUSTER_DISPLAY_ID);
+    }
+
+    @Test
+    public void getClusterStateReturnsClusterState() {
+        ClusterState clusterState = mClusterHomeService.getClusterState();
+
+        assertThat(clusterState.on).isFalse();
+        assertThat(clusterState.bounds).isEqualTo(new Rect(0, 0, CLUSTER_WIDTH, CLUSTER_HEIGHT));
+        assertThat(clusterState.insets).isEqualTo(Insets.NONE);
+        assertThat(clusterState.uiType).isEqualTo(UI_TYPE_CLUSTER_HOME);
+        assertThat(clusterState.displayId).isEqualTo(CLUSTER_DISPLAY_ID);
+    }
+
+    @Test
+    public void onSwitchUiSendsDisplayState() {
+        registerClusterHomeCallback();
+
+        mClusterHomeService.onSwitchUi(UI_TYPE_CLUSTER_MAPS);
+
+        // mClusterState and mClusterStateChanges are updated through mClusterHomeCallback.
+        assertThat(mClusterStateChanges).isEqualTo(ClusterHomeManager.CONFIG_UI_TYPE);
+        assertThat(mClusterState.uiType).isEqualTo(UI_TYPE_CLUSTER_MAPS);
+        assertThat(mClusterHomeService.getClusterState().uiType).isEqualTo(UI_TYPE_CLUSTER_MAPS);
+    }
+
+    @Test
+    public void displayOnSendsDisplayState() {
+        registerClusterHomeCallback();
+
+        mClusterHomeService.onDisplayState(ClusterHalService.DISPLAY_ON,
+                /* bounds= */ null, /* insets= */ null);
+
+        // mClusterState and mClusterStateChanges are updated through mClusterHomeCallback.
+        assertThat(mClusterStateChanges).isEqualTo(ClusterHomeManager.CONFIG_DISPLAY_ON_OFF);
+        assertThat(mClusterState.on).isTrue();
+        assertThat(mClusterHomeService.getClusterState().on).isTrue();
+    }
+
+    @Test
+    public void displayBoundsSendsDisplayState() {
+        registerClusterHomeCallback();
+
+        Rect newBounds = new Rect(10, 10, CLUSTER_WIDTH - 10, CLUSTER_HEIGHT - 10);
+        mClusterHomeService.onDisplayState(ClusterHalService.DONT_CARE,
+                newBounds, /* insets= */ null);
+
+        // mClusterState and mClusterStateChanges are updated through mClusterHomeCallback.
+        assertThat(mClusterStateChanges).isEqualTo(ClusterHomeManager.CONFIG_DISPLAY_BOUNDS);
+        assertThat(mClusterState.bounds).isEqualTo(newBounds);
+        assertThat(mClusterHomeService.getClusterState().bounds).isEqualTo(newBounds);
+    }
+
+    @Test
+    public void displayInsetsSendsDisplayState() {
+        registerClusterHomeCallback();
+
+        Insets newInsets = Insets.of(10, 10, 10, 10);
+        mClusterHomeService.onDisplayState(ClusterHalService.DONT_CARE, /* bounds= */ null,
+                newInsets);
+
+        // mClusterState and mClusterStateChanges are updated through mClusterHomeCallback.
+        assertThat(mClusterStateChanges).isEqualTo(ClusterHomeManager.CONFIG_DISPLAY_INSETS);
+        assertThat(mClusterState.insets).isEqualTo(newInsets);
+        assertThat(mClusterHomeService.getClusterState().insets).isEqualTo(newInsets);
+    }
+
+    @Test
+    public void onNavigationStateChangedSendsNavigationState() {
+        registerClusterHomeCallback();
+
+        Bundle bundle = new Bundle();
+        byte[] newNavState = new byte[] {(byte) 1, (byte) 2, (byte) 3};
+        bundle.putByteArray(NAV_STATE_PROTO_BUNDLE_KEY, newNavState);
+        mClusterHomeService.onNavigationStateChanged(bundle);
+
+        // mNavigationState is updated through mClusterHomeCallback.
+        assertThat(mNavigationState).isEqualTo(newNavState);
+
+        ArgumentCaptor<byte[]> navStateCaptor = ArgumentCaptor.forClass(byte[].class);
+        verify(mClusterHalService).sendNavigationState(navStateCaptor.capture());
+        assertThat(navStateCaptor.getValue()).isEqualTo(newNavState);
+    }
+
+    @Test
+    public void getInstrumentClusterInfoReturnsClusterInfo() {
+        CarNavigationInstrumentCluster clusterInfo = mClusterHomeService.getInstrumentClusterInfo();
+
+        assertThat(clusterInfo.getType()).isEqualTo(CLUSTER_TYPE_IMAGE_CODES_ONLY);
+    }
+
+    @Test
+    public void notifyNavContextOwnerChangedSendsNavigationState() throws
+            InvalidProtocolBufferException {
+        ContextOwner owner = new ContextOwner(/* uid= */ 123, /* pid= */ 456);
+        mClusterHomeService.notifyNavContextOwnerChanged(owner);
+
+        ArgumentCaptor<byte[]> navStateCaptor = ArgumentCaptor.forClass(byte[].class);
+        verify(mClusterHalService).sendNavigationState(navStateCaptor.capture());
+        NavigationStateProto navState = NavigationStateProto.parseFrom(navStateCaptor.getValue());
+        assertThat(navState.getServiceStatus())
+                .isEqualTo(NavigationStateProto.ServiceStatus.NORMAL);
+    }
+
+    @Test
+    public void reportStateInvokesHal() {
+        byte[] uiAvailability = new byte[] {(byte) 1, (byte) 1, (byte) 0, (byte) 1};
+        mClusterHomeService.reportState(UI_TYPE_CLUSTER_MAPS, UI_TYPE_CLUSTER_NONE, uiAvailability);
+
+        ArgumentCaptor<byte[]> uiAvailabilityCaptor = ArgumentCaptor.forClass(byte[].class);
+        verify(mClusterHalService).reportState(/* onOff= */ anyInt(), /* bounds= */ any(Rect.class),
+                any(Insets.class), eq(UI_TYPE_CLUSTER_MAPS), eq(UI_TYPE_CLUSTER_NONE),
+                uiAvailabilityCaptor.capture());
+        assertThat(uiAvailabilityCaptor.getValue()).isEqualTo(uiAvailability);
+    }
+
+    @Test
+    public void requestDisplayInvokesHal() {
+        mClusterHomeService.requestDisplay(UI_TYPE_CLUSTER_MAPS);
+
+        verify(mClusterHalService).requestDisplay(eq(UI_TYPE_CLUSTER_MAPS));
+    }
+
+    @Test
+    public void startFixedActivityModeAsUserInvokesFixedActivityService() {
+        Intent intent = Intent.makeMainActivity(
+                ComponentName.createRelative("test.pkg", "testClusterActivity"));
+        ActivityOptions activityOptions = ActivityOptions.makeBasic();
+        mClusterHomeService.startFixedActivityModeAsUser(
+                intent, activityOptions.toBundle(), USER_ID);
+
+        ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+        ArgumentCaptor<ActivityOptions> activityOptionsCaptor = ArgumentCaptor.forClass(
+                ActivityOptions.class);
+        verify(mFixedActivityService).startFixedActivityModeForDisplayAndUser(
+                intentCaptor.capture(), activityOptionsCaptor.capture(),
+                eq(CLUSTER_DISPLAY_ID), eq(USER_ID));
+        assertThat(intentCaptor.getValue()).isEqualTo(intent);
+        assertThat(activityOptionsCaptor.getValue().getLaunchDisplayId()).isEqualTo(
+                CLUSTER_DISPLAY_ID);
+    }
+
+    @Test
+    public void stopFixedActivityModeInvokesFixedActivityService() {
+        mClusterHomeService.stopFixedActivityMode();
+
+        verify(mFixedActivityService).stopFixedActivityMode(eq(CLUSTER_DISPLAY_ID));
+    }
+}
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/power/CarPowerManagementServiceUnitTest.java b/tests/carservice_unit_test/src/com/android/car/power/CarPowerManagementServiceUnitTest.java
index 6384d69..1ad6b7e 100644
--- a/tests/carservice_unit_test/src/com/android/car/power/CarPowerManagementServiceUnitTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/power/CarPowerManagementServiceUnitTest.java
@@ -560,6 +560,29 @@
         assertThat(listenerAudio.getCurrentPowerPolicy()).isNull();
     }
 
+    /**
+     * This test case increases the code coverage to cover methods
+     * {@code describeContents()} and {@code newArray()}. They are public APIs
+     * can not be marked out as BOILERPLATE_CODE.
+     */
+    @Test
+    public void testParcelableCreation() throws Exception {
+        grantPowerPolicyPermission();
+
+        CarPowerPolicy policy = mService.getCurrentPowerPolicy();
+        assertThat(policy.describeContents()).isEqualTo(0);
+
+        CarPowerPolicy[] policies = CarPowerPolicy.CREATOR.newArray(1);
+        assertThat(policies.length).isEqualTo(1);
+
+        CarPowerPolicyFilter filterAudio = new CarPowerPolicyFilter.Builder()
+                .setComponents(PowerComponent.AUDIO).build();
+        assertThat(filterAudio.describeContents()).isEqualTo(0);
+
+        CarPowerPolicyFilter[] filters = CarPowerPolicyFilter.CREATOR.newArray(1);
+        assertThat(filters.length).isEqualTo(1);
+    }
+
     private void suspendAndResume() throws Exception {
         Log.d(TAG, "suspend()");
         mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.SHUTDOWN_PREPARE,
diff --git a/tests/carservice_unit_test/src/com/android/car/telemetry/databroker/DataBrokerUnitTest.java b/tests/carservice_unit_test/src/com/android/car/telemetry/databroker/DataBrokerUnitTest.java
new file mode 100644
index 0000000..0e9afa2
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/telemetry/databroker/DataBrokerUnitTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.telemetry.databroker;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Bundle;
+
+import com.android.car.telemetry.TelemetryProto;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class DataBrokerUnitTest {
+    private final DataBrokerImpl mDataBroker = new DataBrokerImpl(new ScriptResultListenerImpl());
+    private static final TelemetryProto.VehiclePropertyPublisher
+            VEHICLE_PROPERTY_PUBLISHER_CONFIGURATION =
+            TelemetryProto.VehiclePropertyPublisher.newBuilder().setReadRate(
+                    1).setVehiclePropertyId(1000).build();
+    private static final TelemetryProto.Publisher PUBLISHER_CONFIGURATION =
+            TelemetryProto.Publisher.newBuilder().setVehicleProperty(
+                    VEHICLE_PROPERTY_PUBLISHER_CONFIGURATION).build();
+    private static final TelemetryProto.Subscriber SUBSCRIBER_FOO =
+            TelemetryProto.Subscriber.newBuilder().setHandler("function_name_foo").setPublisher(
+                    PUBLISHER_CONFIGURATION).build();
+    private static final TelemetryProto.MetricsConfig METRICS_CONFIG_FOO =
+            TelemetryProto.MetricsConfig.newBuilder().setName("Foo").setVersion(
+                    1).addSubscribers(SUBSCRIBER_FOO).build();
+    private static final TelemetryProto.Subscriber SUBSCRIBER_BAR =
+            TelemetryProto.Subscriber.newBuilder().setHandler("function_name_bar").setPublisher(
+                    PUBLISHER_CONFIGURATION).build();
+    private static final TelemetryProto.MetricsConfig METRICS_CONFIG_BAR =
+            TelemetryProto.MetricsConfig.newBuilder().setName("Bar").setVersion(
+                    1).addSubscribers(SUBSCRIBER_BAR).build();
+
+    @Test
+    public void testAddMetricsConfiguration_newMetricsConfig() {
+        mDataBroker.addMetricsConfiguration(METRICS_CONFIG_FOO);
+
+        assertThat(mDataBroker.getPublisherMap().containsKey(
+                TelemetryProto.Publisher.PublisherCase.VEHICLE_PROPERTY)).isTrue();
+        assertThat(mDataBroker.getSubscriptionMap()).containsKey(METRICS_CONFIG_FOO.getName());
+        // there should be one data subscriber in the subscription list of METRICS_CONFIG_FOO
+        assertThat(mDataBroker.getSubscriptionMap().get(METRICS_CONFIG_FOO.getName())).hasSize(1);
+    }
+
+    @Test
+    public void testAddMetricsConfiguration_multipleMetricsConfigsSamePublisher() {
+        mDataBroker.addMetricsConfiguration(METRICS_CONFIG_FOO);
+        mDataBroker.addMetricsConfiguration(METRICS_CONFIG_BAR);
+
+        assertThat(mDataBroker.getPublisherMap()).hasSize(1);
+        assertThat(mDataBroker.getSubscriptionMap()).containsKey(METRICS_CONFIG_FOO.getName());
+        assertThat(mDataBroker.getSubscriptionMap()).containsKey(METRICS_CONFIG_BAR.getName());
+    }
+
+    @Test
+    public void testAddMetricsConfiguration_addSameMetricsConfigs() {
+        mDataBroker.addMetricsConfiguration(METRICS_CONFIG_FOO);
+
+        boolean status = mDataBroker.addMetricsConfiguration(METRICS_CONFIG_FOO);
+
+        assertThat(status).isFalse();
+    }
+
+    @Test
+    public void testRemoveMetricsConfiguration_publisherShouldExist() {
+        mDataBroker.addMetricsConfiguration(METRICS_CONFIG_FOO);
+
+        mDataBroker.removeMetricsConfiguration(METRICS_CONFIG_FOO);
+
+        assertThat(mDataBroker.getPublisherMap()).containsKey(
+                TelemetryProto.Publisher.PublisherCase.VEHICLE_PROPERTY);
+        assertThat(mDataBroker.getSubscriptionMap()).doesNotContainKey(
+                METRICS_CONFIG_FOO.getName());
+    }
+
+    @Test
+    public void testRemoveMetricsConfiguration_removeNonexistentMetricsConfig() {
+        boolean status = mDataBroker.removeMetricsConfiguration(METRICS_CONFIG_FOO);
+
+        assertThat(status).isFalse();
+    }
+
+    private static class ScriptResultListenerImpl implements DataBroker.ScriptResultListener {
+        @Override
+        public void onScriptResult(Bundle scriptResult) {
+            // nothing to do
+        }
+    }
+}
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);