Loading services/core/java/com/android/server/pm/BackgroundDexOptService.java +38 −101 Original line number Diff line number Diff line Loading @@ -34,8 +34,6 @@ import android.os.ServiceManager; import android.os.SystemProperties; import android.os.UserHandle; import android.os.storage.StorageManager; import android.provider.DeviceConfig; import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; import android.util.StatsLog; Loading Loading @@ -86,12 +84,6 @@ public class BackgroundDexOptService extends JobService { // Used for calculating space threshold for downgrading unused apps. private static final int LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE = 2; private static final int DEFAULT_INACTIVE_APP_THRESHOLD_DAYS = 10; private static final String DOWNGRADE_UNUSED_APPS_ENABLED = "downgrade_unused_apps_enabled"; private static final String INACTIVE_APP_THRESHOLD_DAYS = "inactive_app_threshold_days"; private static final String LOW_STORAGE_MULTIPLIER_FOR_DOWNGRADE = "low_storage_threshold_multiplier_for_downgrade"; /** * Set of failed packages remembered across job runs. Loading @@ -111,6 +103,8 @@ public class BackgroundDexOptService extends JobService { private final AtomicBoolean mExitPostBootUpdate = new AtomicBoolean(false); private final File mDataDir = Environment.getDataDirectory(); private static final long mDowngradeUnusedAppsThresholdInMillis = getDowngradeUnusedAppsThresholdInMillis(); public static void schedule(Context context) { if (isBackgroundDexoptDisabled()) { Loading Loading @@ -352,14 +346,14 @@ public class BackgroundDexOptService extends JobService { // Only downgrade apps when space is low on device. // Threshold is selected above the lowStorageThreshold so that we can pro-actively clean // up disk before user hits the actual lowStorageThreshold. final long lowStorageThresholdForDowngrade = getLowThresholdMultiplierForDowngrade() final long lowStorageThresholdForDowngrade = LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE * lowStorageThreshold; boolean shouldDowngrade = shouldDowngrade(lowStorageThresholdForDowngrade); Log.d(TAG, "Should Downgrade " + shouldDowngrade); if (shouldDowngrade) { Set<String> unusedPackages = pm.getUnusedPackages(getDowngradeUnusedAppsThresholdInMillis()); Log.d(TAG, "Unused Packages " + String.join(",", unusedPackages)); pm.getUnusedPackages(mDowngradeUnusedAppsThresholdInMillis); Log.d(TAG, "Unsused Packages " + String.join(",", unusedPackages)); if (!unusedPackages.isEmpty()) { for (String pkg : unusedPackages) { Loading @@ -368,9 +362,12 @@ public class BackgroundDexOptService extends JobService { // Should be aborted by the scheduler. return abortCode; } if (downgradePackage(pm, pkg)) { if (downgradePackage(pm, pkg, /*isForPrimaryDex*/ true)) { updatedPackages.add(pkg); } if (supportSecondaryDex) { downgradePackage(pm, pkg, /*isForPrimaryDex*/ false); } } pkgs = new ArraySet<>(pkgs); Loading Loading @@ -418,45 +415,39 @@ public class BackgroundDexOptService extends JobService { * Try to downgrade the package to a smaller compilation filter. * eg. if the package is in speed-profile the package will be downgraded to verify. * @param pm PackageManagerService * @param pkg The package to be downgraded * @return true if the package was downgraded * @param pkg The package to be downgraded. * @param isForPrimaryDex. Apps can have several dex file, primary and secondary. * @return true if the package was downgraded. */ private boolean downgradePackage(PackageManagerService pm, String pkg) { private boolean downgradePackage(PackageManagerService pm, String pkg, boolean isForPrimaryDex) { Log.d(TAG, "Downgrading " + pkg); boolean downgradedPrimary = false; boolean dex_opt_performed = false; int reason = PackageManagerService.REASON_INACTIVE_PACKAGE_DOWNGRADE; int dexoptFlags = DexoptOptions.DEXOPT_BOOT_COMPLETE | DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB | DexoptOptions.DEXOPT_DOWNGRADE; long package_size_before = getPackageSize(pm, pkg); // An aggressive downgrade deletes the oat files. boolean aggressive = false; if (isForPrimaryDex) { // This applies for system apps or if packages location is not a directory, i.e. // monolithic install. if (!pm.canHaveOatDir(pkg)) { // For apps that don't have the oat directory, instead of downgrading, // remove their compiler artifacts from dalvik cache. pm.deleteOatArtifactsOfPackage(pkg); aggressive = true; downgradedPrimary = true; } else { downgradedPrimary = performDexOptPrimary(pm, pkg, reason, dexoptFlags); if (supportSecondaryDex()) { performDexOptSecondary(pm, pkg, reason, dexoptFlags); dex_opt_performed = performDexOptPrimary(pm, pkg, reason, dexoptFlags); } } else { dex_opt_performed = performDexOptSecondary(pm, pkg, reason, dexoptFlags); } // This metric aims to log the storage savings when downgrading. // The way disk size is measured using getPackageSize only looks at the primary apks. // Any logs that are due to secondary dex files will show 0% size reduction and pollute // the metrics. if (downgradedPrimary) { if (dex_opt_performed) { StatsLog.write(StatsLog.APP_DOWNGRADED, pkg, package_size_before, getPackageSize(pm, pkg), aggressive); getPackageSize(pm, pkg), /*aggressive=*/ false); } return downgradedPrimary; return dex_opt_performed; } private boolean supportSecondaryDex() { Loading @@ -480,7 +471,7 @@ public class BackgroundDexOptService extends JobService { * concurrent jobs because PackageDexOptimizer.performDexOpt is synchronized. * @param pm An instance of PackageManagerService * @param pkg The package to be downgraded. * @param isForPrimaryDex Apps can have several dex file, primary and secondary. * @param isForPrimaryDex. Apps can have several dex file, primary and secondary. * @return true if the package was downgraded. */ private boolean optimizePackage(PackageManagerService pm, String pkg, Loading Loading @@ -597,6 +588,12 @@ public class BackgroundDexOptService extends JobService { // the checks above. This check is not "live" - the value is determined by a background // restart with a period of ~1 minute. PackageManagerService pm = (PackageManagerService)ServiceManager.getService("package"); if (pm.isStorageLow()) { if (DEBUG_DEXOPT) { Log.i(TAG, "Low storage, skipping this run"); } return false; } final ArraySet<String> pkgs = pm.getOptimizablePackages(); if (pkgs.isEmpty()) { Loading Loading @@ -646,77 +643,17 @@ public class BackgroundDexOptService extends JobService { } private static long getDowngradeUnusedAppsThresholdInMillis() { long defaultValue = Long.MAX_VALUE; if (isDowngradeFeatureEnabled()) { return getInactiveAppsThresholdMillis(); } final String sysPropKey = "pm.dexopt.downgrade_after_inactive_days"; String sysPropValue = SystemProperties.get(sysPropKey); if (sysPropValue == null || sysPropValue.isEmpty()) { Log.w(TAG, "SysProp " + sysPropKey + " not set"); return defaultValue; return Long.MAX_VALUE; } try { return TimeUnit.DAYS.toMillis(Long.parseLong(sysPropValue)); } catch (NumberFormatException e) { Log.w(TAG, "Couldn't parse long for pm.dexopt.downgrade_after_inactive_days: " + sysPropValue + ". Returning default value instead."); return defaultValue; } } private static boolean isBackgroundDexoptDisabled() { return SystemProperties.getBoolean("pm.dexopt.disable_bg_dexopt" /* key */, false /* default */); } private static boolean isDowngradeFeatureEnabled() { // DeviceConfig enables the control of on device features via remotely configurable flags, // compared to SystemProperties which is only a way of sharing info system-widely, but are // not configurable on the server-side. String downgradeUnusedAppsEnabledFlag = DeviceConfig.getProperty( DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE, DOWNGRADE_UNUSED_APPS_ENABLED); return !TextUtils.isEmpty(downgradeUnusedAppsEnabledFlag) && Boolean.parseBoolean(downgradeUnusedAppsEnabledFlag); } private static long getInactiveAppsThresholdMillis() { long defaultValue = TimeUnit.DAYS.toMillis(DEFAULT_INACTIVE_APP_THRESHOLD_DAYS); String inactiveAppThresholdDaysFlag = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE, INACTIVE_APP_THRESHOLD_DAYS); if (!TextUtils.isEmpty(inactiveAppThresholdDaysFlag)) { try { return TimeUnit.DAYS.toMillis(Long.parseLong(inactiveAppThresholdDaysFlag)); } catch (NumberFormatException e) { Log.w(TAG, "Couldn't parse long for " + INACTIVE_APP_THRESHOLD_DAYS + " flag: " + inactiveAppThresholdDaysFlag + ". Returning default value instead."); return defaultValue; } } return defaultValue; } private static int getLowThresholdMultiplierForDowngrade() { int defaultValue = LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE; if (isDowngradeFeatureEnabled()) { String lowStorageThresholdMultiplierFlag = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE, LOW_STORAGE_MULTIPLIER_FOR_DOWNGRADE); if (!TextUtils.isEmpty(lowStorageThresholdMultiplierFlag)) { try { return Integer.parseInt(lowStorageThresholdMultiplierFlag); } catch (NumberFormatException e) { Log.w(TAG, "Couldn't parse long for " + LOW_STORAGE_MULTIPLIER_FOR_DOWNGRADE + " flag: " + lowStorageThresholdMultiplierFlag + ". Returning default value instead."); return defaultValue; } } } return defaultValue; } } tests/BackgroundDexOptServiceIntegrationTests/AndroidManifest.xml +0 −2 Original line number Diff line number Diff line Loading @@ -28,8 +28,6 @@ <uses-permission android:name="android.permission.SET_TIME" /> <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG" /> <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" /> <application> <uses-library android:name="android.test.runner" /> Loading tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java +6 −171 Original line number Diff line number Diff line Loading @@ -22,7 +22,6 @@ import android.os.Environment; import android.os.ParcelFileDescriptor; import android.os.SystemProperties; import android.os.storage.StorageManager; import android.provider.DeviceConfig; import android.util.Log; import androidx.test.InstrumentationRegistry; Loading @@ -31,9 +30,7 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; Loading @@ -55,13 +52,6 @@ import java.util.concurrent.TimeUnit; * 3. Under low storage conditions and package is recently used, check * that dexopt upgrades test app to $(getprop pm.dexopt.bg-dexopt). * * When downgrade feature is on (downgrade_unused_apps_enabled flag is set to true): * 4 On low storage, check that the inactive packages are downgraded. * 5. On low storage, check that used packages are upgraded. * 6. On storage completely full, dexopt fails. * 7. Not on low storage, unused packages are upgraded. * 8. Low storage, unused app is downgraded. When app is used again, app is upgraded. * * Each test case runs "cmd package bg-dexopt-job com.android.frameworks.bgdexopttest". * * The setup for these tests make sure this package has been configured to have been recently used Loading @@ -69,10 +59,6 @@ import java.util.concurrent.TimeUnit; * recently used, it sets the time forward more than * `getprop pm.dexopt.downgrade_after_inactive_days` days. * * For some of the tests, the DeviceConfig flags inactive_app_threshold_days and * downgrade_unused_apps_enabled are set. These turn on/off the downgrade unused apps feature for * all devices and set the time threshold for unused apps. * * For tests that require low storage, the phone is filled up. * * Run with "atest BackgroundDexOptServiceIntegrationTests". Loading @@ -94,14 +80,10 @@ public final class BackgroundDexOptServiceIntegrationTests { "pm.dexopt.downgrade_after_inactive_days", 0); // Needs to be between 1.0 and 2.0. private static final double LOW_STORAGE_MULTIPLIER = 1.5; private static final int DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS = 15; // The file used to fill up storage. private File mBigFile; @Rule public ExpectedException mExpectedException = ExpectedException.none(); // Remember start time. @BeforeClass public static void setUpAll() { Loading Loading @@ -214,27 +196,11 @@ public final class BackgroundDexOptServiceIntegrationTests { logSpaceRemaining(); } private void fillUpStorageCompletely() throws IOException { fillUpStorage((getStorageLowBytes())); } // Fill up storage so that device is in low storage condition. private void fillUpToLowStorage() throws IOException { fillUpStorage((long) (getStorageLowBytes() * LOW_STORAGE_MULTIPLIER)); } private void setInactivePackageThreshold(int threshold) { DeviceConfig.setProperty( DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE, "inactive_app_threshold_days", Integer.toString(threshold), false); } private void enableDowngradeFeature(boolean enabled) { DeviceConfig.setProperty( DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE, "downgrade_unused_apps_enabled", Boolean.toString(enabled), false); } // TODO(aeubanks): figure out how to get scheduled bg-dexopt to run private static void runBackgroundDexOpt() throws IOException { String result = runShellCommand("cmd package bg-dexopt-job " + PACKAGE_NAME); Loading Loading @@ -278,7 +244,7 @@ public final class BackgroundDexOptServiceIntegrationTests { // Test that background dexopt under normal conditions succeeds. @Test public void testBackgroundDexOpt_normalConditions_dexOptSucceeds() throws IOException { public void testBackgroundDexOpt() throws IOException { // Set filter to quicken. compilePackageWithFilter(PACKAGE_NAME, "verify"); Assert.assertEquals("verify", getCompilerFilter(PACKAGE_NAME)); Loading @@ -291,16 +257,17 @@ public final class BackgroundDexOptServiceIntegrationTests { // Test that background dexopt under low storage conditions upgrades used packages. @Test public void testBackgroundDexOpt_lowStorage_usedPkgsUpgraded() throws IOException { public void testBackgroundDexOptDowngradeSkipRecentlyUsedPackage() throws IOException { // Should be less than DOWNGRADE_AFTER_DAYS. long deltaDays = DOWNGRADE_AFTER_DAYS - 1; try { enableDowngradeFeature(false); // Set time to future. setTimeFutureDays(deltaDays); // Set filter to quicken. compilePackageWithFilter(PACKAGE_NAME, "quicken"); Assert.assertEquals("quicken", getCompilerFilter(PACKAGE_NAME)); // Fill up storage to trigger low storage threshold. fillUpToLowStorage(); Loading @@ -315,161 +282,29 @@ public final class BackgroundDexOptServiceIntegrationTests { } // Test that background dexopt under low storage conditions downgrades unused packages. // This happens if the system property pm.dexopt.downgrade_after_inactive_days is set // (e.g. on Android Go devices). @Test public void testBackgroundDexOpt_lowStorage_unusedPkgsDowngraded() throws IOException { public void testBackgroundDexOptDowngradeSuccessful() throws IOException { // Should be more than DOWNGRADE_AFTER_DAYS. long deltaDays = DOWNGRADE_AFTER_DAYS + 1; try { enableDowngradeFeature(false); // Set time to future. setTimeFutureDays(deltaDays); // Set filter to quicken. compilePackageWithFilter(PACKAGE_NAME, "quicken"); Assert.assertEquals("quicken", getCompilerFilter(PACKAGE_NAME)); // Fill up storage to trigger low storage threshold. fillUpToLowStorage(); runBackgroundDexOpt(); // Verify that downgrade is successful. Assert.assertEquals(DOWNGRADE_COMPILER_FILTER, getCompilerFilter(PACKAGE_NAME)); } finally { // Reset time. setTimeFutureDays(-deltaDays); } } // Test that the background dexopt downgrades inactive packages when the downgrade feature is // enabled. @Test public void testBackgroundDexOpt_downgradeFeatureEnabled_lowStorage_inactivePkgsDowngraded() throws IOException { // Should be more than DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS. long deltaDays = DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS + 1; try { enableDowngradeFeature(true); setInactivePackageThreshold(DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS); // Set time to future. setTimeFutureDays(deltaDays); // Set filter to quicken. compilePackageWithFilter(PACKAGE_NAME, "quicken"); Assert.assertEquals("quicken", getCompilerFilter(PACKAGE_NAME)); // Fill up storage to trigger low storage threshold. fillUpToLowStorage(); runBackgroundDexOpt(); // Verify that downgrade is successful. Assert.assertEquals(DOWNGRADE_COMPILER_FILTER, getCompilerFilter(PACKAGE_NAME)); } finally { // Reset time. setTimeFutureDays(-deltaDays); } } // Test that the background dexopt upgrades used packages when the downgrade feature is enabled. // This test doesn't fill the device storage completely, but to a multiplier of the low storage // threshold and this is why apps can still be optimized. @Test public void testBackgroundDexOpt_downgradeFeatureEnabled_lowStorage_usedPkgsUpgraded() throws IOException { enableDowngradeFeature(true); // Set filter to quicken. compilePackageWithFilter(PACKAGE_NAME, "quicken"); Assert.assertEquals("quicken", getCompilerFilter(PACKAGE_NAME)); // Fill up storage to trigger low storage threshold. fillUpToLowStorage(); runBackgroundDexOpt(); /// Verify that bg-dexopt is successful in upgrading the used packages. Assert.assertEquals(BG_DEXOPT_COMPILER_FILTER, getCompilerFilter(PACKAGE_NAME)); } // Test that the background dexopt fails and doesn't change the compilation filter of used // packages when the downgrade feature is enabled and the storage is filled up completely. // The bg-dexopt shouldn't optimise nor downgrade these packages. @Test public void testBackgroundDexOpt_downgradeFeatureEnabled_fillUpStorageCompletely_dexOptFails() throws IOException { enableDowngradeFeature(true); String previousCompilerFilter = getCompilerFilter(PACKAGE_NAME); // Fill up storage completely, without using a multiplier for the low storage threshold. fillUpStorageCompletely(); // When the bg dexopt runs with the storage filled up completely, it will fail. mExpectedException.expect(IllegalStateException.class); runBackgroundDexOpt(); /// Verify that bg-dexopt doesn't change the compilation filter of used apps. Assert.assertEquals(previousCompilerFilter, getCompilerFilter(PACKAGE_NAME)); } // Test that the background dexopt upgrades the unused packages when the downgrade feature is // on if the device is not low on storage. @Test public void testBackgroundDexOpt_downgradeFeatureEnabled_notLowStorage_unusedPkgsUpgraded() throws IOException { // Should be more than DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS. long deltaDays = DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS + 1; try { enableDowngradeFeature(true); setInactivePackageThreshold(DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS); // Set time to future. setTimeFutureDays(deltaDays); // Set filter to quicken. compilePackageWithFilter(PACKAGE_NAME, "quicken"); Assert.assertEquals("quicken", getCompilerFilter(PACKAGE_NAME)); runBackgroundDexOpt(); // Verify that bg-dexopt is successful in upgrading the unused packages when the device // is not low on storage. Assert.assertEquals(BG_DEXOPT_COMPILER_FILTER, getCompilerFilter(PACKAGE_NAME)); } finally { // Reset time. setTimeFutureDays(-deltaDays); } } // Test that when an unused package (which was downgraded) is used again, it's re-optimized when // bg-dexopt runs again. @Test public void testBackgroundDexOpt_downgradeFeatureEnabled_downgradedPkgsUpgradedAfterUse() throws IOException { // Should be more than DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS. long deltaDays = DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS + 1; try { enableDowngradeFeature(true); setInactivePackageThreshold(DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS); // Set time to future. setTimeFutureDays(deltaDays); // Fill up storage to trigger low storage threshold. fillUpToLowStorage(); // Set filter to quicken. compilePackageWithFilter(PACKAGE_NAME, "quicken"); Assert.assertEquals("quicken", getCompilerFilter(PACKAGE_NAME)); runBackgroundDexOpt(); // Verify that downgrade is successful. Assert.assertEquals(DOWNGRADE_COMPILER_FILTER, getCompilerFilter(PACKAGE_NAME)); // Reset time. setTimeFutureDays(-deltaDays); deltaDays = 0; runBackgroundDexOpt(); // Verify that bg-dexopt is successful in upgrading the unused packages that were used // again. Assert.assertEquals(BG_DEXOPT_COMPILER_FILTER, getCompilerFilter(PACKAGE_NAME)); } finally { // Reset time. setTimeFutureDays(-deltaDays); } } } Loading
services/core/java/com/android/server/pm/BackgroundDexOptService.java +38 −101 Original line number Diff line number Diff line Loading @@ -34,8 +34,6 @@ import android.os.ServiceManager; import android.os.SystemProperties; import android.os.UserHandle; import android.os.storage.StorageManager; import android.provider.DeviceConfig; import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; import android.util.StatsLog; Loading Loading @@ -86,12 +84,6 @@ public class BackgroundDexOptService extends JobService { // Used for calculating space threshold for downgrading unused apps. private static final int LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE = 2; private static final int DEFAULT_INACTIVE_APP_THRESHOLD_DAYS = 10; private static final String DOWNGRADE_UNUSED_APPS_ENABLED = "downgrade_unused_apps_enabled"; private static final String INACTIVE_APP_THRESHOLD_DAYS = "inactive_app_threshold_days"; private static final String LOW_STORAGE_MULTIPLIER_FOR_DOWNGRADE = "low_storage_threshold_multiplier_for_downgrade"; /** * Set of failed packages remembered across job runs. Loading @@ -111,6 +103,8 @@ public class BackgroundDexOptService extends JobService { private final AtomicBoolean mExitPostBootUpdate = new AtomicBoolean(false); private final File mDataDir = Environment.getDataDirectory(); private static final long mDowngradeUnusedAppsThresholdInMillis = getDowngradeUnusedAppsThresholdInMillis(); public static void schedule(Context context) { if (isBackgroundDexoptDisabled()) { Loading Loading @@ -352,14 +346,14 @@ public class BackgroundDexOptService extends JobService { // Only downgrade apps when space is low on device. // Threshold is selected above the lowStorageThreshold so that we can pro-actively clean // up disk before user hits the actual lowStorageThreshold. final long lowStorageThresholdForDowngrade = getLowThresholdMultiplierForDowngrade() final long lowStorageThresholdForDowngrade = LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE * lowStorageThreshold; boolean shouldDowngrade = shouldDowngrade(lowStorageThresholdForDowngrade); Log.d(TAG, "Should Downgrade " + shouldDowngrade); if (shouldDowngrade) { Set<String> unusedPackages = pm.getUnusedPackages(getDowngradeUnusedAppsThresholdInMillis()); Log.d(TAG, "Unused Packages " + String.join(",", unusedPackages)); pm.getUnusedPackages(mDowngradeUnusedAppsThresholdInMillis); Log.d(TAG, "Unsused Packages " + String.join(",", unusedPackages)); if (!unusedPackages.isEmpty()) { for (String pkg : unusedPackages) { Loading @@ -368,9 +362,12 @@ public class BackgroundDexOptService extends JobService { // Should be aborted by the scheduler. return abortCode; } if (downgradePackage(pm, pkg)) { if (downgradePackage(pm, pkg, /*isForPrimaryDex*/ true)) { updatedPackages.add(pkg); } if (supportSecondaryDex) { downgradePackage(pm, pkg, /*isForPrimaryDex*/ false); } } pkgs = new ArraySet<>(pkgs); Loading Loading @@ -418,45 +415,39 @@ public class BackgroundDexOptService extends JobService { * Try to downgrade the package to a smaller compilation filter. * eg. if the package is in speed-profile the package will be downgraded to verify. * @param pm PackageManagerService * @param pkg The package to be downgraded * @return true if the package was downgraded * @param pkg The package to be downgraded. * @param isForPrimaryDex. Apps can have several dex file, primary and secondary. * @return true if the package was downgraded. */ private boolean downgradePackage(PackageManagerService pm, String pkg) { private boolean downgradePackage(PackageManagerService pm, String pkg, boolean isForPrimaryDex) { Log.d(TAG, "Downgrading " + pkg); boolean downgradedPrimary = false; boolean dex_opt_performed = false; int reason = PackageManagerService.REASON_INACTIVE_PACKAGE_DOWNGRADE; int dexoptFlags = DexoptOptions.DEXOPT_BOOT_COMPLETE | DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB | DexoptOptions.DEXOPT_DOWNGRADE; long package_size_before = getPackageSize(pm, pkg); // An aggressive downgrade deletes the oat files. boolean aggressive = false; if (isForPrimaryDex) { // This applies for system apps or if packages location is not a directory, i.e. // monolithic install. if (!pm.canHaveOatDir(pkg)) { // For apps that don't have the oat directory, instead of downgrading, // remove their compiler artifacts from dalvik cache. pm.deleteOatArtifactsOfPackage(pkg); aggressive = true; downgradedPrimary = true; } else { downgradedPrimary = performDexOptPrimary(pm, pkg, reason, dexoptFlags); if (supportSecondaryDex()) { performDexOptSecondary(pm, pkg, reason, dexoptFlags); dex_opt_performed = performDexOptPrimary(pm, pkg, reason, dexoptFlags); } } else { dex_opt_performed = performDexOptSecondary(pm, pkg, reason, dexoptFlags); } // This metric aims to log the storage savings when downgrading. // The way disk size is measured using getPackageSize only looks at the primary apks. // Any logs that are due to secondary dex files will show 0% size reduction and pollute // the metrics. if (downgradedPrimary) { if (dex_opt_performed) { StatsLog.write(StatsLog.APP_DOWNGRADED, pkg, package_size_before, getPackageSize(pm, pkg), aggressive); getPackageSize(pm, pkg), /*aggressive=*/ false); } return downgradedPrimary; return dex_opt_performed; } private boolean supportSecondaryDex() { Loading @@ -480,7 +471,7 @@ public class BackgroundDexOptService extends JobService { * concurrent jobs because PackageDexOptimizer.performDexOpt is synchronized. * @param pm An instance of PackageManagerService * @param pkg The package to be downgraded. * @param isForPrimaryDex Apps can have several dex file, primary and secondary. * @param isForPrimaryDex. Apps can have several dex file, primary and secondary. * @return true if the package was downgraded. */ private boolean optimizePackage(PackageManagerService pm, String pkg, Loading Loading @@ -597,6 +588,12 @@ public class BackgroundDexOptService extends JobService { // the checks above. This check is not "live" - the value is determined by a background // restart with a period of ~1 minute. PackageManagerService pm = (PackageManagerService)ServiceManager.getService("package"); if (pm.isStorageLow()) { if (DEBUG_DEXOPT) { Log.i(TAG, "Low storage, skipping this run"); } return false; } final ArraySet<String> pkgs = pm.getOptimizablePackages(); if (pkgs.isEmpty()) { Loading Loading @@ -646,77 +643,17 @@ public class BackgroundDexOptService extends JobService { } private static long getDowngradeUnusedAppsThresholdInMillis() { long defaultValue = Long.MAX_VALUE; if (isDowngradeFeatureEnabled()) { return getInactiveAppsThresholdMillis(); } final String sysPropKey = "pm.dexopt.downgrade_after_inactive_days"; String sysPropValue = SystemProperties.get(sysPropKey); if (sysPropValue == null || sysPropValue.isEmpty()) { Log.w(TAG, "SysProp " + sysPropKey + " not set"); return defaultValue; return Long.MAX_VALUE; } try { return TimeUnit.DAYS.toMillis(Long.parseLong(sysPropValue)); } catch (NumberFormatException e) { Log.w(TAG, "Couldn't parse long for pm.dexopt.downgrade_after_inactive_days: " + sysPropValue + ". Returning default value instead."); return defaultValue; } } private static boolean isBackgroundDexoptDisabled() { return SystemProperties.getBoolean("pm.dexopt.disable_bg_dexopt" /* key */, false /* default */); } private static boolean isDowngradeFeatureEnabled() { // DeviceConfig enables the control of on device features via remotely configurable flags, // compared to SystemProperties which is only a way of sharing info system-widely, but are // not configurable on the server-side. String downgradeUnusedAppsEnabledFlag = DeviceConfig.getProperty( DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE, DOWNGRADE_UNUSED_APPS_ENABLED); return !TextUtils.isEmpty(downgradeUnusedAppsEnabledFlag) && Boolean.parseBoolean(downgradeUnusedAppsEnabledFlag); } private static long getInactiveAppsThresholdMillis() { long defaultValue = TimeUnit.DAYS.toMillis(DEFAULT_INACTIVE_APP_THRESHOLD_DAYS); String inactiveAppThresholdDaysFlag = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE, INACTIVE_APP_THRESHOLD_DAYS); if (!TextUtils.isEmpty(inactiveAppThresholdDaysFlag)) { try { return TimeUnit.DAYS.toMillis(Long.parseLong(inactiveAppThresholdDaysFlag)); } catch (NumberFormatException e) { Log.w(TAG, "Couldn't parse long for " + INACTIVE_APP_THRESHOLD_DAYS + " flag: " + inactiveAppThresholdDaysFlag + ". Returning default value instead."); return defaultValue; } } return defaultValue; } private static int getLowThresholdMultiplierForDowngrade() { int defaultValue = LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE; if (isDowngradeFeatureEnabled()) { String lowStorageThresholdMultiplierFlag = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE, LOW_STORAGE_MULTIPLIER_FOR_DOWNGRADE); if (!TextUtils.isEmpty(lowStorageThresholdMultiplierFlag)) { try { return Integer.parseInt(lowStorageThresholdMultiplierFlag); } catch (NumberFormatException e) { Log.w(TAG, "Couldn't parse long for " + LOW_STORAGE_MULTIPLIER_FOR_DOWNGRADE + " flag: " + lowStorageThresholdMultiplierFlag + ". Returning default value instead."); return defaultValue; } } } return defaultValue; } }
tests/BackgroundDexOptServiceIntegrationTests/AndroidManifest.xml +0 −2 Original line number Diff line number Diff line Loading @@ -28,8 +28,6 @@ <uses-permission android:name="android.permission.SET_TIME" /> <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG" /> <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" /> <application> <uses-library android:name="android.test.runner" /> Loading
tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java +6 −171 Original line number Diff line number Diff line Loading @@ -22,7 +22,6 @@ import android.os.Environment; import android.os.ParcelFileDescriptor; import android.os.SystemProperties; import android.os.storage.StorageManager; import android.provider.DeviceConfig; import android.util.Log; import androidx.test.InstrumentationRegistry; Loading @@ -31,9 +30,7 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; Loading @@ -55,13 +52,6 @@ import java.util.concurrent.TimeUnit; * 3. Under low storage conditions and package is recently used, check * that dexopt upgrades test app to $(getprop pm.dexopt.bg-dexopt). * * When downgrade feature is on (downgrade_unused_apps_enabled flag is set to true): * 4 On low storage, check that the inactive packages are downgraded. * 5. On low storage, check that used packages are upgraded. * 6. On storage completely full, dexopt fails. * 7. Not on low storage, unused packages are upgraded. * 8. Low storage, unused app is downgraded. When app is used again, app is upgraded. * * Each test case runs "cmd package bg-dexopt-job com.android.frameworks.bgdexopttest". * * The setup for these tests make sure this package has been configured to have been recently used Loading @@ -69,10 +59,6 @@ import java.util.concurrent.TimeUnit; * recently used, it sets the time forward more than * `getprop pm.dexopt.downgrade_after_inactive_days` days. * * For some of the tests, the DeviceConfig flags inactive_app_threshold_days and * downgrade_unused_apps_enabled are set. These turn on/off the downgrade unused apps feature for * all devices and set the time threshold for unused apps. * * For tests that require low storage, the phone is filled up. * * Run with "atest BackgroundDexOptServiceIntegrationTests". Loading @@ -94,14 +80,10 @@ public final class BackgroundDexOptServiceIntegrationTests { "pm.dexopt.downgrade_after_inactive_days", 0); // Needs to be between 1.0 and 2.0. private static final double LOW_STORAGE_MULTIPLIER = 1.5; private static final int DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS = 15; // The file used to fill up storage. private File mBigFile; @Rule public ExpectedException mExpectedException = ExpectedException.none(); // Remember start time. @BeforeClass public static void setUpAll() { Loading Loading @@ -214,27 +196,11 @@ public final class BackgroundDexOptServiceIntegrationTests { logSpaceRemaining(); } private void fillUpStorageCompletely() throws IOException { fillUpStorage((getStorageLowBytes())); } // Fill up storage so that device is in low storage condition. private void fillUpToLowStorage() throws IOException { fillUpStorage((long) (getStorageLowBytes() * LOW_STORAGE_MULTIPLIER)); } private void setInactivePackageThreshold(int threshold) { DeviceConfig.setProperty( DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE, "inactive_app_threshold_days", Integer.toString(threshold), false); } private void enableDowngradeFeature(boolean enabled) { DeviceConfig.setProperty( DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE, "downgrade_unused_apps_enabled", Boolean.toString(enabled), false); } // TODO(aeubanks): figure out how to get scheduled bg-dexopt to run private static void runBackgroundDexOpt() throws IOException { String result = runShellCommand("cmd package bg-dexopt-job " + PACKAGE_NAME); Loading Loading @@ -278,7 +244,7 @@ public final class BackgroundDexOptServiceIntegrationTests { // Test that background dexopt under normal conditions succeeds. @Test public void testBackgroundDexOpt_normalConditions_dexOptSucceeds() throws IOException { public void testBackgroundDexOpt() throws IOException { // Set filter to quicken. compilePackageWithFilter(PACKAGE_NAME, "verify"); Assert.assertEquals("verify", getCompilerFilter(PACKAGE_NAME)); Loading @@ -291,16 +257,17 @@ public final class BackgroundDexOptServiceIntegrationTests { // Test that background dexopt under low storage conditions upgrades used packages. @Test public void testBackgroundDexOpt_lowStorage_usedPkgsUpgraded() throws IOException { public void testBackgroundDexOptDowngradeSkipRecentlyUsedPackage() throws IOException { // Should be less than DOWNGRADE_AFTER_DAYS. long deltaDays = DOWNGRADE_AFTER_DAYS - 1; try { enableDowngradeFeature(false); // Set time to future. setTimeFutureDays(deltaDays); // Set filter to quicken. compilePackageWithFilter(PACKAGE_NAME, "quicken"); Assert.assertEquals("quicken", getCompilerFilter(PACKAGE_NAME)); // Fill up storage to trigger low storage threshold. fillUpToLowStorage(); Loading @@ -315,161 +282,29 @@ public final class BackgroundDexOptServiceIntegrationTests { } // Test that background dexopt under low storage conditions downgrades unused packages. // This happens if the system property pm.dexopt.downgrade_after_inactive_days is set // (e.g. on Android Go devices). @Test public void testBackgroundDexOpt_lowStorage_unusedPkgsDowngraded() throws IOException { public void testBackgroundDexOptDowngradeSuccessful() throws IOException { // Should be more than DOWNGRADE_AFTER_DAYS. long deltaDays = DOWNGRADE_AFTER_DAYS + 1; try { enableDowngradeFeature(false); // Set time to future. setTimeFutureDays(deltaDays); // Set filter to quicken. compilePackageWithFilter(PACKAGE_NAME, "quicken"); Assert.assertEquals("quicken", getCompilerFilter(PACKAGE_NAME)); // Fill up storage to trigger low storage threshold. fillUpToLowStorage(); runBackgroundDexOpt(); // Verify that downgrade is successful. Assert.assertEquals(DOWNGRADE_COMPILER_FILTER, getCompilerFilter(PACKAGE_NAME)); } finally { // Reset time. setTimeFutureDays(-deltaDays); } } // Test that the background dexopt downgrades inactive packages when the downgrade feature is // enabled. @Test public void testBackgroundDexOpt_downgradeFeatureEnabled_lowStorage_inactivePkgsDowngraded() throws IOException { // Should be more than DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS. long deltaDays = DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS + 1; try { enableDowngradeFeature(true); setInactivePackageThreshold(DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS); // Set time to future. setTimeFutureDays(deltaDays); // Set filter to quicken. compilePackageWithFilter(PACKAGE_NAME, "quicken"); Assert.assertEquals("quicken", getCompilerFilter(PACKAGE_NAME)); // Fill up storage to trigger low storage threshold. fillUpToLowStorage(); runBackgroundDexOpt(); // Verify that downgrade is successful. Assert.assertEquals(DOWNGRADE_COMPILER_FILTER, getCompilerFilter(PACKAGE_NAME)); } finally { // Reset time. setTimeFutureDays(-deltaDays); } } // Test that the background dexopt upgrades used packages when the downgrade feature is enabled. // This test doesn't fill the device storage completely, but to a multiplier of the low storage // threshold and this is why apps can still be optimized. @Test public void testBackgroundDexOpt_downgradeFeatureEnabled_lowStorage_usedPkgsUpgraded() throws IOException { enableDowngradeFeature(true); // Set filter to quicken. compilePackageWithFilter(PACKAGE_NAME, "quicken"); Assert.assertEquals("quicken", getCompilerFilter(PACKAGE_NAME)); // Fill up storage to trigger low storage threshold. fillUpToLowStorage(); runBackgroundDexOpt(); /// Verify that bg-dexopt is successful in upgrading the used packages. Assert.assertEquals(BG_DEXOPT_COMPILER_FILTER, getCompilerFilter(PACKAGE_NAME)); } // Test that the background dexopt fails and doesn't change the compilation filter of used // packages when the downgrade feature is enabled and the storage is filled up completely. // The bg-dexopt shouldn't optimise nor downgrade these packages. @Test public void testBackgroundDexOpt_downgradeFeatureEnabled_fillUpStorageCompletely_dexOptFails() throws IOException { enableDowngradeFeature(true); String previousCompilerFilter = getCompilerFilter(PACKAGE_NAME); // Fill up storage completely, without using a multiplier for the low storage threshold. fillUpStorageCompletely(); // When the bg dexopt runs with the storage filled up completely, it will fail. mExpectedException.expect(IllegalStateException.class); runBackgroundDexOpt(); /// Verify that bg-dexopt doesn't change the compilation filter of used apps. Assert.assertEquals(previousCompilerFilter, getCompilerFilter(PACKAGE_NAME)); } // Test that the background dexopt upgrades the unused packages when the downgrade feature is // on if the device is not low on storage. @Test public void testBackgroundDexOpt_downgradeFeatureEnabled_notLowStorage_unusedPkgsUpgraded() throws IOException { // Should be more than DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS. long deltaDays = DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS + 1; try { enableDowngradeFeature(true); setInactivePackageThreshold(DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS); // Set time to future. setTimeFutureDays(deltaDays); // Set filter to quicken. compilePackageWithFilter(PACKAGE_NAME, "quicken"); Assert.assertEquals("quicken", getCompilerFilter(PACKAGE_NAME)); runBackgroundDexOpt(); // Verify that bg-dexopt is successful in upgrading the unused packages when the device // is not low on storage. Assert.assertEquals(BG_DEXOPT_COMPILER_FILTER, getCompilerFilter(PACKAGE_NAME)); } finally { // Reset time. setTimeFutureDays(-deltaDays); } } // Test that when an unused package (which was downgraded) is used again, it's re-optimized when // bg-dexopt runs again. @Test public void testBackgroundDexOpt_downgradeFeatureEnabled_downgradedPkgsUpgradedAfterUse() throws IOException { // Should be more than DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS. long deltaDays = DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS + 1; try { enableDowngradeFeature(true); setInactivePackageThreshold(DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS); // Set time to future. setTimeFutureDays(deltaDays); // Fill up storage to trigger low storage threshold. fillUpToLowStorage(); // Set filter to quicken. compilePackageWithFilter(PACKAGE_NAME, "quicken"); Assert.assertEquals("quicken", getCompilerFilter(PACKAGE_NAME)); runBackgroundDexOpt(); // Verify that downgrade is successful. Assert.assertEquals(DOWNGRADE_COMPILER_FILTER, getCompilerFilter(PACKAGE_NAME)); // Reset time. setTimeFutureDays(-deltaDays); deltaDays = 0; runBackgroundDexOpt(); // Verify that bg-dexopt is successful in upgrading the unused packages that were used // again. Assert.assertEquals(BG_DEXOPT_COMPILER_FILTER, getCompilerFilter(PACKAGE_NAME)); } finally { // Reset time. setTimeFutureDays(-deltaDays); } } }