Watchdog monitors flash memory usage by tracking the total amount of disk I/O writes made by all apps and services using the per-UID disk I/O stats exposed by the Kernel at the location `/proc/uid_io/stats`. When an app or service exceeds the disk I/O overuse threshold, Watchdog takes actions on the app or service. The disk I/O overuse thresholds and the action to take on overuse is predefined in the disk I/O overuse configuration.
Overuse thresholds
- The disk I/O overuse thresholds are enforced on a daily basis, that is, all writes made by an app/service are aggregated since the beginning of the current UTC calendar day and checked against the thresholds defined in the overuse configurations.
- When a vehicle is started multiple times on a given day, the Watchdog module stores the disk I/O usage stats on flash memory and aggregates them since the beginning of the current UTC calendar day.
Overuse actions
When an app repeatedly exceeds the defined disk I/O overuse thresholds, Watchdog takes actions defined in the overuse configuration.
- All vendor apps and services are considered critical for the overall system stability, so they are not terminated on disk I/O overuse. However, the overuse configuration can define a list of safe-to-terminate vendor apps and services.
- All third-party apps are safe-to-terminate.
When an app or service is safe-to-terminate, Watchdog disables the app or service with the app
component state
PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
.
Overuse configuration
The overuse configuration contains the disk I/O overuse thresholds and actions. Default overuse configurations are defined in the system and vendor images, and shipped with the build. Vendors can optionally include the vendor configuration in the vendor image. When the vendor configuration is not provided, the system configuration is used for the vendor apps and services as well.
Watchdog exposes system APIs through CarWatchdogManager
, which lets
vendors apps or services update the vendor configuration
anytime.
Overuse configuration definition
Overuse configuration is split by the component type, for example, system, vendor, and third party. OEMs must update only the vendor component configuration.
Vendor configuration
Vendor configuration defines the disk I/O overuse thresholds and actions for all vendor apps and services, and all maps and media apps. The configuration contains the below configuration fields.
- Vendor package prefixes. All packages installed in the vendor partition are considered as vendor packages. In addition to these packages, vendors can classify preinstalled packages as vendor packages by adding the package prefixes to the vendor package prefixes config. This config doesn't accept regular expressions.
- Safe-to-terminate packages. Vendors can specify which vendor packages are safe to be terminated by adding the complete package names to the safe-to-terminate packages config.
- Application category mappings. Vendors can map any package (including third-party packages) to one of the two supported app categories - Map and Media apps. This mapping is done to provide maps and media apps higher disk I/O overuse thresholds because these apps tend to download and write more data to disk than other app types.
- Component level thresholds. Defines generic thresholds for all vendor packages (that is, packages not covered by Package specific thresholds or Application category specific thresholds get these thresholds). Vendors must define non-zero component-level thresholds when defining disk I/O overuse configuration.
- Package specific thresholds. Vendors can define special thresholds for specific vendor packages. The mappings should contain the complete package names. The thresholds defined in this config takes precedence over thresholds defined in other configs for a given package.
- Application category specific thresholds. Vendors can specify special thresholds for specific app categories. The app categories must be one of the supported categories - Maps and Media apps. The thresholds defined in this config are mapped to specific packages using Application category mappings.
- System-wide thresholds. Vendors must not specify this config.
Vendor package prefixes, Safe-to-terminate packages, Component level thresholds, and Package specific thresholds configs are updatable only by the vendor configuration for vendor apps and services. Application category specific thresholds config can be updated only by the vendor configuration for all maps and media apps.
The overuse thresholds contain the amount of bytes allowed to be written during:
- An app or service foreground mode versus background mode
- System garage mode
This classification allows user facing foreground apps and services to write more data than background apps and services. In Garage mode, apps and services tend to download updates, so each needs a higher threshold than apps and services running in other modes.
System and third-party configurations
OEMs should not update the system and third-party configurations.
- System configuration defines I/O overuse thresholds and actions for
system apps and services.
- This configuration can also update the Application category mappings. Thus, this config field is shared between system and vendor configurations.
- Third-party configuration defines thresholds for all third-party
apps. All apps that are not preinstalled in the system are
third-party apps.
- All third-party apps receive the same thresholds (for example, no third-party app receives special thresholds) except maps and media apps, whose thresholds are defined by the vendor configuration.
- The below disk I/O overuse thresholds are the default thresholds for the
third-party apps. These thresholds are shipped with the system image.
- 3 GiB write in the app foreground mode.
- 2 GiB write in the app background mode.
- 4 GiB write in the system garage mode.
- These are base thresholds. These thresholds are updated as more is learned about disk I/O usage.
Overuse configuration XML format
Default vendor configuration can be placed (this is optional) at the location
/vendor/etc/automotive/watchdog/resource_overuse_configuration.xml
in the build image. When this configuration isn't specified, the system-defined
configuration is applied for vendor apps and services as well.
The XML file should contain only one tag for each config field. I/O overuse configuration must be defined in the XML file. All threshold values should be specified in the MiB unit.
A sample XML configuration is provided below:
<resourceOveruseConfiguration version="1.0"> <componentType> VENDOR </componentType> <!-- List of safe to kill vendor packages. --> <safeToKillPackages> <package> com.vendor.package.A </package> <package> com.vendor.package.B </package> </safeToKillPackages> <!-- List of vendor package prefixes. --> <vendorPackagePrefixes> <packagePrefix> com.vendor.package </packagePrefix> </vendorPackagePrefixes> <!-- List of unique package names to app category mappings. --> <packagesToAppCategoryTypes> <packageAppCategory type="MEDIA"> com.vendor.package.A </packageAppCategory> <packageAppCategory type="MAPS"> com.google.package.B </packageAppCategory> <packageAppCategory type="MEDIA"> com.third.party.package.C </packageAppCategory> </packagesToAppCategoryTypes> <ioOveruseConfiguration> <!-- Thresholds in MiB for all vendor packages that don't have package specific thresholds. --> <componentLevelThresholds> <state id="foreground_mode"> 1024 </state> <state id="background_mode"> 512 </state> <state id="garage_mode"> 3072 </state> </componentLevelThresholds> <packageSpecificThresholds> <!-- IDs must be unique --> <perStateThreshold id="com.vendor.package.C"> <state id="foreground_mode"> 400 </state> <state id="background_mode"> 100 </state> <state id="garage_mode"> 200 </state> </perStateThreshold> <perStateThreshold id="com.vendor.package.D"> <state id="foreground_mode"> 1024 </state> <state id="background_mode"> 500 </state> <state id="garage_mode"> 2048 </state> </perStateThreshold> </packageSpecificThresholds> <!-- Application category specific thresholds. --> <appCategorySpecificThresholds> <!-- One entry per supported application category --> <perStateThreshold id="MEDIA"> <state id="foreground_mode"> 600 </state> <state id="background_mode"> 700 </state> <state id="garage_mode"> 1024 </state> </perStateThreshold> <perStateThreshold id="MAPS"> <state id="foreground_mode"> 800 </state> <state id="background_mode"> 900 </state> <state id="garage_mode"> 2048 </state> </perStateThreshold> </appCategorySpecificThresholds> </ioOveruseConfiguration> </resourceOveruseConfiguration>
Update overuse configuration through CarWatchdogManager system APIs
The above XML configuration can be provided only in the build image. If an OEM chooses to update the on-device configuration after a build is released, they can use the following APIs to make changes to the on-device configuration.
- Grant the permission
Car.PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG
to the caller. - Must use the existing configurations to update and set the
new configurations. Use the API
CarWatchdogManager.getResourceOveruseConfigurations
to get the existing configurations. If existing configurations are not used, all configurations (including system and third-party configurations) are overwritten, which is not recommended. - Update the existing configurations with the delta changes and set the new configurations. Do not update the system and third-party component configurations.
- Use the API
CarWatchdogManager.setResourceOveruseConfigurations
to set the new configurations. - To get and set the disk I/O overuse configurations use the flag
CarWatchdogManager.FLAG_RESOURCE_OVERUSE_IO
.
Here's a sample implementation that updates the resource overuse configurations:
void updateResourceOveruseConfigurations() { CarWatchdogManager manager = (CarWatchdogManager) car.getCarManager(Car.CAR_WATCHDOG_SERVICE); List<ResourceOveruseConfiguration> resourceOveruseConfigurations = manager.getResourceOveruseConfigurations( CarWatchdogManager.FLAG_RESOURCE_OVERUSE_IO); List<ResourceOveruseConfiguration> newResourceOveruseConfigurations = new List<>(); ResourceOveruseConfiguration vendorConfiguration; for(ResourceOveruseConfiguration config : resourceOveruseConfigurations) { // Do not update the configurations of the system and third-party component types. if (config.getComponentType() != ResourceOveruseConfiguration.COMPONENT_TYPE_VENDOR) { newResourceOveruseConfigurations.add(config); continue; } vendorConfiguration = config; } if (vendorConfiguration == null) { ResourceOveruseConfiguration.Builder vendorConfigBuilder = new ResourceOveruseConfiguration.Builder(); initializeConfig(vendorConfigBuilder); newResourceOveruseConfigurations.add(vendorConfigBuilder.build()); } else { ResourceOveruseConfiguration newVendorConfig = updateConfig(vendorConfiguration); newResourceOveruseConfigurations.add(newVendorConfig); } int result = manager.setResourceOveruseConfigurations( newResourceOveruseConfigurations, if (result != CarWatchdogManager.RETURN_CODE_SUCCESS) { // Failed to set the resource overuse configurations. } } /** Sets the delta between the old configuration and the new configuration. */ ResourceOveruseConfiguration updateConfig( ResourceOveruseConfiguration oldConfiguration) { // Replace com.vendor.package.A with com.vendor.package.B in the safe-to-kill list. List<String> safeToKillPackages = oldConfiguration.getSafeToKillPackages(); safeToKillPackages.remove("com.vendor.package.A"); safeToKillPackages.add("com.vendor.package.B"); ResourceOveruseConfiguration.Builder configBuilder = new ResourceOveruseConfiguration.Builder( oldConfiguration.getComponentType(), safeToKillPackages, oldConfiguration.getVendorPackagePrefixes(), oldConfiguration.getPackagesToAppCategoryTypes()); configBuilder.addVendorPackagePrefixes("com.vendor."); configBuilder.addPackagesToAppCategoryTypes("com.vendor.package.B", ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS); IoOveruseConfiguration oldIoConfiguration = oldConfiguration.getIoOveruseConfiguration(); IoOveruseConfiguration.Builder ioConfigBuilder = new IoOveruseConfiguration.Builder( oldIoConfiguration.getComponentLevelThresholds(), oldIoConfiguration.getPackageSpecificThresholds(), oldIoConfiguration.getAppCategorySpecificThresholds(), oldIoConfiguration.getSystemWideThresholds()); // Define the amount of bytes based on the flash memory specification, expected lifetime, // and estimated average amount of bytes written by a package during different modes. ioConfigBuilder.addPackageSpecificThresholds("com.vendor.package.B", new PerStateBytes(/* foregroundModeBytes= */ 2 * 1024 * 1024 * 1024, /* backgroundModeBytes= */ 500 * 1024 * 1024, /* garageModeBytes= */ 3 * 1024 * 1024 * 1024)); return configBuilder.setIoOveruseConfiguration(ioConfigBuilder.build()).build(); }
Apps monitoring their resource overuse
Vendor and third-party apps can listen for app specific resource
overuse notifications from Watchdog or poll CarWatchdogManager
for the app
specific resource overuse statistics for up to the past 30 days.
Listen for resource overuse notifications
Apps can implement a resource overuse listener and register the
listener with CarWatchdogManager
to receive app specific notifications when they
exceed 80% or 100% of their disk I/O overuse thresholds. Apps can use
these notifications to:
- Log the disk I/O overuse statistics for offline analysis. App developers can use this logging to debug the disk I/O overuse issue.
- Reduce the disk I/O writes until the overuse counters reset.
Java client
- Implement listener by inheriting
CarWatchdogManager.ResourceOveruseListener
:class ResourceOveruseListenerImpl implements CarWatchdogManager.ResourceOveruseListener { @Override public void onOveruse( @NonNull ResourceOveruseStats resourceOveruseStats) { // 1. Log/Upload resource overuse metrics. // 2. Reduce writes until the counters reset. IoOveruseStats ioOveruseStats = resourceOveruseStats.getIoOveruseStats(); // Stats period - [ioOveruseStats.getStartTime(), ioOveruseStats.getStartTime() // + ioOveruseStats.getDurationInSeconds()] // Total I/O overuses - ioOveruseStats.getTotalOveruses() // Total bytes written - ioOveruseStats.getTotalBytesWritten() // Remaining write bytes for the current UTC calendar day - // ioOveruseStats.getRemainingWriteBytes() } } }
- Register the listener instance by calling
CarWatchdogManager.addResourceOveruseListener
private void addResourceOveruseListener() { CarWatchdogManager manager = (CarWatchdogManager) car.getCarManager(Car.CAR_WATCHDOG_SERVICE); // Choose a proper executor to handle resource overuse notifications. Executor executor = mContext.getMainExecutor(); manager.addResourceOveruseListener( executor, CarWatchdogManager.FLAG_RESOURCE_OVERUSE_IO, mListenerImpl); }
- Unregister the listener instance when the app has finished listening to:
private void removeResourceOveruseListener() { CarWatchdogManager manager = (CarWatchdogManager) car.getCarManager(Car.CAR_WATCHDOG_SERVICE); mCarWatchdogManager.removeResourceOveruseListener( mListenerImpl); }
Native client
- Include
carwatchdog_aidl_interface-ndk_platform
in theshared_libs
dependency of the build rule.Android.bp
cc_binary { name: "sample_native_client", srcs: [ "src/*.cpp" ], shared_libs: [ "carwatchdog_aidl_interface-ndk_platform", "libbinder_ndk", ], vendor: true, }
- Add SELinux policy to allow the vendor service domain to use binder
(
binder_user
macro) and add the vendor service domain to thecarwatchdog
client domain(carwatchdog_client_domain macro)
. See the code below forsample_client.te
andfile_contexts
.sample_client.te
type sample_client, domain; type sample_client_exec, exec_type, file_type, vendor_file_type; carwatchdog_client_domain(sample_client) init_daemon_domain(sample_client) binder_use(sample_client)
file_contexts
/vendor/bin/sample_native_client u:object_r:sample_client_exec:s0
- Implement the resource overuse listener by inheriting
BnResourceOveruseListener
. OverrideBnResourceOveruseListener::onOveruse
to handle resource overuse notifications.ResourceOveruseListenerImpl.h
class ResourceOveruseListenerImpl : public BnResourceOveruseListener { public: ndk::ScopedAStatus onOveruse( ResourceOveruseStats resourceOveruseStats) override; private: void initialize(); void terminate(); std::shared_ptr<ICarWatchdog> mWatchdogServer; std::shared_ptr<IResourceOveruseListener> mListener; }
ResourceOveruseListenerImpl.cpp
ndk::ScopedAStatus ResourceOveruseListenerImpl::onOveruse( ResourceOveruseStats resourceOveruseStats) { // 1. Log/Upload resource overuse metrics. // 2. Reduce writes until the counters reset. if (stats.getTag() != ResourceOveruseStats::ioOveruseStats) { // Received resourceOveruseStats doesn't contain I/O overuse stats. } const IoOveruseStats& ioOveruseStats = stats.get(); // Stats period - [ioOveruseStats.startTime, // ioOveruseStats.startTime + ioOveruseStats.durationInSeconds] // Total I/O overuses - ioOveruseStats.totalOveruses // Total bytes written - ioOveruseStats.writtenBytes // Remaining write bytes for the current UTC calendar day - // ioOveruseStats.remainingWriteBytes return ndk::ScopedAStatus::ok(); }
- Start a binder thread pool and register the resource overuse listener
with the watchdog server. Watchdog server is registered under the service name
android.automotive.watchdog.ICarWatchdog/default
.main.cpp
int main(int argc, char** argv) { ABinderProcess_setThreadPoolMaxThreadCount(1); ABinderProcess_startThreadPool(); std::shared_ptr<ResourceOveruseListenerImpl> listener = ndk::SharedRefBase::make<ResourceOveruseListenerImpl>(); // The listener is added in initialize(). listener->initialize(); ... Run service ... // The listener is removed in terminate(). listener->terminate(); }
ResourceOveruseListenerImpl.cpp
void ResourceOveruseListener::initialize() { ndk::SpAIBinder binder(AServiceManager_getService( "android.automotive.watchdog.ICarWatchdog/default")); std::shared_ptr<ICarWatchdog> server = ICarWatchdog::fromBinder(binder); mWatchdogServer = server; std::shared_ptr<IResourceOveruseListener> listener = IResourceOveruseListener::fromBinder(this->asBinder()); mWatchdogServer->addResourceOveruseListener( std::vector<int>{ResourceType.IO}, listener); mListener = listener; } void ResourceOveruseListener::terminate() { mWatchdogServer->removeResourceOveruseListener(mListener); }
Poll resource overuse statistics
Apps can poll CarWatchdogManager for the app-specific I/O overuse statistics ATS for the most recent 30 days.
Java client
Use CarWatchdogManager.getResourceOveruseStats
to get the
resource overuse stats. Pass the CarWatchdogManager.FLAG_RESOURCE_OVERUSE_IO
flag to get the disk I/O overuse stats.
private void getResourceOveruseStats() { CarWatchdogManager manager = (CarWatchdogManager) car.getCarManager(Car.CAR_WATCHDOG_SERVICE); // Returns resource overuse stats with I/O overuse stats for the past // 7 days. Stats are available for up to the past 30 days. ResourceOveruseStats resourceOveruseStats = mCarWatchdogManager.getResourceOveruseStats( CarWatchdogManager.FLAG_RESOURCE_OVERUSE_IO, CarWatchdogManager.STATS_PERIOD_PAST_7_DAYS); IoOveruseStats ioOveruseStats = resourceOveruseStats.getIoOveruseStats(); // Stats period - [ioOveruseStats.getStartTime(), ioOveruseStats.getStartTime() // + ioOveruseStats.getDurationInSeconds()] // Total I/O overuses - ioOveruseStats.getTotalOveruses() // Total bytes written - ioOveruseStats.getTotalBytesWritten() // Remaining write bytes for the UTC calendar day - // ioOveruseStats.getRemainingWriteBytes() }
Native client
Use CarWatchdogServer.getResourceOveruseStats
to get the
resource overuse stats. Pass the ResourceType.IO
enum to fetch disk I/O overuse
stats.
void getResourceOveruseStats() { ndk::SpAIBinder binder(AServiceManager_getService( "android.automotive.watchdog.ICarWatchdog/default")); std::shared_ptr<ICarWatchdog> server = ICarWatchdog::fromBinder(binder); // Returns the stats only for the current UTC calendar day. const std::vector<ResourceOveruseStats> resourceOveruseStats; ndk::ScopedAStatus status = server.getResourceOveruseStats( std::vector<int>{ResourceType.IO}, &resourceOveruseStats); if (!status.isOk()) { // Failed to get the resource overuse stats. return; } for (const auto& stats : resourceOveruseStats) { if (stats.getTag() != ResourceOveruseStats::ioOveruseStats) { continue; } const IoOveruseStats& ioOveruseStats = stats.get(); // Stats period - [ioOveruseStats.startTime, // ioOveruseStats.startTime + ioOveruseStats.durationInSeconds] // Total I/O overuses - ioOveruseStats.totalOveruses // Total bytes written - ioOveruseStats.writtenBytes // Remaining write bytes for the current UTC calendar day - // ioOveruseStats.remainingWriteBytes } }
Resource overuse user experience
The following sections describe the user experience when resource overuse occurs.
Prioritize app performance setting
The app Settings page contains settings forPrioritize app performance
(see the image below), which allows users to prioritize an app's performance over the system and
long-term hardware performance. This setting is available only for apps that are safe to be
terminated on resource overuse. Otherwise, this setting is disabled. When this setting is
toggled off (the default setting) for an app, the app can be terminated on resource overuse.
Otherwise, the app is not terminated on resource overuse.
When the user toggles on this setting, the following confirmation dialog describes the implications of toggling on the setting:
After 90 days, this setting is automatically reset to the default. The day limit can be
modified with an RRO overlay app using watchdogUserPackageSettingsResetDays
,
up to a maximum of 180 days. To learn more, see
Change the value of an app's resources at runtime. The
following example overlay tag can be included in AndroidManifest.xml
:
<overlay android:priority="<insert-value>" android:targetPackage="com.android.car.updatable" android:targetName="CarServiceCustomization" android:resourcesMap="@xml/overlays" />
In res/values/config.xml
:
<resources> <integer name="watchdogUserPackageSettingsResetDays">value</integer> </resources>
In res/xml/overlays.xml
:
<overlay> <item target="integer/watchdogUserPackageSettingsResetDays" value="@integer/watchdogUserPackageSettingsResetDays" /> </overlay>
Performance-impacting apps setting
The Settings app contains a Performance-impacting apps section (see Figure 1). When tapped, a list of apps that have been restricted due to flash memory overuse and that negatively impacts system performance are displayed. This follows CDD 3.5.1 requirement [C-1-1].
Figure 1. Performance-impacting apps.
Apps terminated due to resource overuse are listed here (see Figure 2). The listed apps can be prioritized. To learn more, see Prioritize app performance setting.
Figure 2. List of apps terminated due to resource overuse.
User notification
When an app or service repeatedly overuses disk I/O (for example, writes data to disk beyond the defined thresholds) within a certain period and is safe to be terminated on resource overuse, the user is notified after the vehicle enters the allow-driver-distraction state.
The first user notification (during a drive) is posted as a heads-up notification and the other notifications are posted on the notification center.
For example, when an app repeatedly overuses disk I/O, the user receives the following notification:
- When the user clicks on the Prioritize app button, the app's settings page is launched, where the user can toggle on or off the Prioritize app performance setting.
- When the user clicks on the Disable app button, the app is disabled until the user launches the app or enables it on the app's settings page.
- For uninstallable apps, the Disable app button is replaced with the Uninstall app button. When the user clicks on the Uninstall app button, the app's Settings page is launched, from which the user can uninstall the app.
Recommendation for launcher implementation
When apps are disabled due to resource overuse, the apps disappear from the
default launcher app because CarService updates the apps' enabled state as
PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
.
OEMs must update the builtin launcher implementation to display these apps as unusual,
so users can use them if needed. See the following recommendations based on the build release.
Android SC V2 release
- The launcher implementation should use the
MATCH_DISABLED_UNTIL_USED_COMPONENTS
flag when retrieving the list of packages to show on the launcher. - When the user clicks an apps that is in the
PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
state, the launcher app must enable the app by setting the enabled state as: