Merge "Handle garage mode I/O overuse monitoring." into sc-dev
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/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/evs/CarEvsService.java b/service/src/com/android/car/evs/CarEvsService.java
index b9d19eb..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,8 +40,12 @@
 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;
@@ -159,6 +161,8 @@
     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());
@@ -427,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:
@@ -443,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;
         }
 
@@ -466,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;
                     }
@@ -612,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. */
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/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/CarUserManagerTest.java b/tests/android_car_api_test/src/android/car/apitest/CarUserManagerTest.java
index c673df2..62f2b0a 100644
--- a/tests/android_car_api_test/src/android/car/apitest/CarUserManagerTest.java
+++ b/tests/android_car_api_test/src/android/car/apitest/CarUserManagerTest.java
@@ -32,7 +32,6 @@
 
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
-import org.junit.Ignore;
 import org.junit.Test;
 
 public final class CarUserManagerTest extends CarMultiUserTestBase {
@@ -146,7 +145,6 @@
      * resume to same guest user.
      */
     @Test
-    @Ignore("b/190865475")
     public void testSecuredGuestUserResumeToSameUser() throws Exception {
         // Create new guest user
         UserInfo guestUser = createGuest();
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_unit_test/src/com/android/car/SetMultimapTest.java b/tests/carservice_unit_test/src/com/android/car/SetMultimapTest.java
new file mode 100644
index 0000000..fa3a09c
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/SetMultimapTest.java
@@ -0,0 +1,161 @@
+/*
+ * 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 com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
+
+import org.junit.Test;
+
+public final class SetMultimapTest {
+
+    @Test
+    public void testGet_empty_returnsEmptySet() {
+        SetMultimap<Integer, String> map = new SetMultimap<>();
+
+        assertThat(map.get(1)).isEmpty();
+    }
+
+    @Test
+    public void testGet_keyDoesNotExist_returnsEmptySet() {
+        SetMultimap<Integer, String> map = createFromMultimap(ImmutableMultimap.of(1, "value1"));
+
+        assertThat(map.get(2)).isEmpty();
+    }
+
+    @Test
+    public void testGet() {
+        SetMultimap<Integer, String> map = new SetMultimap<>();
+
+        map.put(1, "value1");
+        assertThat(map.get(1)).containsExactly("value1");
+        map.put(1, "value2");
+        assertThat(map.get(1)).containsExactly("value1", "value2");
+        map.put(1, "value3");
+        assertThat(map.get(1)).containsExactly("value1", "value2", "value3");
+    }
+
+    @Test
+    public void testPut_duplicateValue() {
+        SetMultimap<Integer, String> map = new SetMultimap<>();
+        map.put(1, "value1");
+        map.put(1, "value1");
+
+        assertThat(map.get(1)).containsExactly("value1");
+    }
+
+    @Test
+    public void testContainsEntry() {
+        SetMultimap<Integer, String> map = createFromMultimap(ImmutableMultimap.of(1, "value1"));
+
+        assertThat(map.containsEntry(1, "value1")).isTrue();
+    }
+
+    @Test
+    public void testContainsEntry_keyDoesNotExist() {
+        SetMultimap<Integer, String> map = createFromMultimap(ImmutableMultimap.of(1, "value1"));
+
+        assertThat(map.containsEntry(2, "value1")).isFalse();
+    }
+
+    @Test
+    public void testContainsEntry_valueDoesNotExist() {
+        SetMultimap<Integer, String> map = createFromMultimap(ImmutableMultimap.of(1, "value1"));
+
+        assertThat(map.containsEntry(1, "value2")).isFalse();
+    }
+
+    @Test
+    public void testRemove_success() {
+        SetMultimap<Integer, String> map = createFromMultimap(
+                ImmutableMultimap.of(1, "value1", 1, "value2", 2, "value3"));
+
+        assertThat(map.remove(1, "value1")).isTrue();
+        assertContainsExactlyEntries(map, ImmutableMultimap.of(1, "value2", 2, "value3"));
+    }
+
+    @Test
+    public void testRemove_lastEntryOfKey() {
+        SetMultimap<Integer, String> map = createFromMultimap(
+                ImmutableMultimap.of(1, "value1", 1, "value2", 2, "value3"));
+
+        assertThat(map.remove(2, "value3")).isTrue();
+        assertContainsExactlyEntries(map, ImmutableMultimap.of(1, "value1", 1, "value2"));
+    }
+
+    @Test
+    public void testRemove_keyDoesNotExist() {
+        SetMultimap<Integer, String> map = createFromMultimap(
+                ImmutableMultimap.of(1, "value1", 1, "value2", 2, "value3"));
+
+        assertThat(map.remove(3, "value3")).isFalse();
+        assertContainsExactlyEntries(
+                map, ImmutableMultimap.of(1, "value1", 1, "value2", 2, "value3"));
+    }
+
+    @Test
+    public void testRemove_entryDoesNotExist() {
+        SetMultimap<Integer, String> map = createFromMultimap(
+                ImmutableMultimap.of(1, "value1", 1, "value2", 2, "value3"));
+
+        assertThat(map.remove(1, "value3")).isFalse();
+        assertContainsExactlyEntries(
+                map, ImmutableMultimap.of(1, "value1", 1, "value2", 2, "value3"));
+    }
+
+    @Test
+    public void testClear() {
+        SetMultimap<Integer, String> map = createFromMultimap(
+                ImmutableMultimap.of(1, "value1", 1, "value2", 2, "value3"));
+
+        map.clear();
+
+        assertContainsExactlyEntries(map, ImmutableMultimap.<Integer, String>of());
+    }
+
+    @Test
+    public void testKeySet() {
+        SetMultimap<Integer, String> map = createFromMultimap(
+                ImmutableMultimap.of(1, "value1", 1, "value2", 2, "value3"));
+
+        assertThat(map.keySet()).containsExactly(1, 2);
+    }
+
+    @Test
+    public void testKeySet_empty() {
+        SetMultimap<Integer, String> map = new SetMultimap<>();
+
+        assertThat(map.keySet()).isEmpty();
+    }
+
+    private static <K, V> SetMultimap<K, V> createFromMultimap(Multimap<K, V> multimap) {
+        SetMultimap<K, V> map = new SetMultimap<>();
+        multimap.entries().forEach(entry -> map.put(entry.getKey(), entry.getValue()));
+
+        return map;
+    }
+
+    private static <K, V> void assertContainsExactlyEntries(
+            SetMultimap<K, V> actual, Multimap<K, V> expected) {
+        ImmutableMultimap.Builder<K, V> multimapBuilder = ImmutableMultimap.builder();
+        actual.keySet().forEach(key -> multimapBuilder.putAll(key, actual.get(key)));
+
+        assertThat(multimapBuilder.build()).containsExactlyEntriesIn(expected);
+    }
+}