Merge "Removing remaining references of ActivityView in packages/services/Car" 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/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/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/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/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/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/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/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
+        }
+    }
+}