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);
+ }
+}