From 2d79ce5f5000640e5f13027ce989e91691653911 Mon Sep 17 00:00:00 2001 From: Kweku Adams Date: Tue, 5 May 2020 12:31:35 -0700 Subject: [PATCH] Add master switch to enable/disable RESTRICTED bucket. Adding a master on/off switch for the new bucket such that when it's off, apps aren't be put into the bucket and any apps already in it are brought out. Bug: 155670438 Test: atest FrameworksServicesTests:AppIdleHistoryTests Test: atest FrameworksServicesTests:AppStandbyControllerTests Test: atest SettingsProviderTest:SettingsBackupTest Change-Id: Iaeabefa14cbaa2d07a13555ecbc29e6fbf11a29c --- .../server/usage/AppStandbyController.java | 40 ++++++++++++-- core/java/android/provider/Settings.java | 18 ++++++- .../android/provider/SettingsBackupTest.java | 1 + .../usage/AppStandbyControllerTests.java | 52 +++++++++++++++++++ 4 files changed, 107 insertions(+), 4 deletions(-) diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index 24728dd8edca4..12e53a85ea022 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -293,6 +293,13 @@ public class AppStandbyController implements AppStandbyInternal { * {@link #setAppStandbyBucket(String, int, int, int, int)} will not be propagated. */ boolean mLinkCrossProfileApps; + /** + * Whether we should allow apps into the + * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket or not. + * If false, any attempts to put an app into the bucket will put the app into the + * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RARE} bucket instead. + */ + private boolean mAllowRestrictedBucket; private volatile boolean mAppIdleEnabled; private boolean mIsCharging; @@ -667,6 +674,10 @@ public class AppStandbyController implements AppStandbyInternal { return; } final int oldBucket = app.currentBucket; + if (oldBucket == STANDBY_BUCKET_NEVER) { + // None of this should bring an app out of the NEVER bucket. + return; + } int newBucket = Math.max(oldBucket, STANDBY_BUCKET_ACTIVE); // Undo EXEMPTED boolean predictionLate = predictionTimedOut(app, elapsedRealtime); // Compute age-based bucket @@ -722,11 +733,18 @@ public class AppStandbyController implements AppStandbyInternal { Slog.d(TAG, "Bringing down to RESTRICTED due to timeout"); } } + if (newBucket == STANDBY_BUCKET_RESTRICTED && !mAllowRestrictedBucket) { + newBucket = STANDBY_BUCKET_RARE; + // Leave the reason alone. + if (DEBUG) { + Slog.d(TAG, "Bringing up from RESTRICTED to RARE due to off switch"); + } + } if (DEBUG) { Slog.d(TAG, " Old bucket=" + oldBucket + ", newBucket=" + newBucket); } - if (oldBucket < newBucket || predictionLate) { + if (oldBucket != newBucket || predictionLate) { mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket, reason); maybeInformListeners(packageName, userId, elapsedRealtime, @@ -1176,8 +1194,8 @@ public class AppStandbyController implements AppStandbyInternal { final int reason = REASON_MAIN_FORCED_BY_SYSTEM | (REASON_SUB_MASK & restrictReason); final long nowElapsed = mInjector.elapsedRealtime(); - setAppStandbyBucket(packageName, userId, STANDBY_BUCKET_RESTRICTED, reason, - nowElapsed, false); + final int bucket = mAllowRestrictedBucket ? STANDBY_BUCKET_RESTRICTED : STANDBY_BUCKET_RARE; + setAppStandbyBucket(packageName, userId, bucket, reason, nowElapsed, false); } @Override @@ -1247,6 +1265,9 @@ public class AppStandbyController implements AppStandbyInternal { Slog.e(TAG, "Tried to set bucket of uninstalled app: " + packageName); return; } + if (newBucket == STANDBY_BUCKET_RESTRICTED && !mAllowRestrictedBucket) { + newBucket = STANDBY_BUCKET_RARE; + } AppIdleHistory.AppUsageHistory app = mAppIdleHistory.getAppUsageHistory(packageName, userId, elapsedRealtime); boolean predicted = (reason & REASON_MAIN_MASK) == REASON_MAIN_PREDICTED; @@ -1365,6 +1386,7 @@ public class AppStandbyController implements AppStandbyInternal { Slog.d(TAG, " Keeping at WORKING_SET due to min timeout"); } } else if (newBucket == STANDBY_BUCKET_RARE + && mAllowRestrictedBucket && getBucketForLocked(packageName, userId, elapsedRealtime) == STANDBY_BUCKET_RESTRICTED) { // Prediction doesn't think the app will be used anytime soon and @@ -1697,6 +1719,8 @@ public class AppStandbyController implements AppStandbyInternal { pw.println(); pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled); + pw.print(" mAllowRestrictedBucket="); + pw.print(mAllowRestrictedBucket); pw.print(" mIsCharging="); pw.print(mIsCharging); pw.println(); @@ -1798,6 +1822,12 @@ public class AppStandbyController implements AppStandbyInternal { return mPowerWhitelistManager.isWhitelisted(packageName, false); } + boolean isRestrictedBucketEnabled() { + return Global.getInt(mContext.getContentResolver(), + Global.ENABLE_RESTRICTED_BUCKET, + Global.DEFAULT_ENABLE_RESTRICTED_BUCKET) == 1; + } + File getDataSystemDirectory() { return Environment.getDataSystemDirectory(); } @@ -2031,6 +2061,8 @@ public class AppStandbyController implements AppStandbyInternal { final ContentResolver cr = mContext.getContentResolver(); cr.registerContentObserver(Global.getUriFor(Global.APP_IDLE_CONSTANTS), false, this); cr.registerContentObserver(Global.getUriFor(Global.APP_STANDBY_ENABLED), false, this); + cr.registerContentObserver(Global.getUriFor(Global.ENABLE_RESTRICTED_BUCKET), + false, this); cr.registerContentObserver(Global.getUriFor(Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED), false, this); } @@ -2129,6 +2161,8 @@ public class AppStandbyController implements AppStandbyInternal { mLinkCrossProfileApps = mParser.getBoolean( KEY_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS, DEFAULT_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS); + + mAllowRestrictedBucket = mInjector.isRestrictedBucketEnabled(); } // Check if app_idle_enabled has changed. Do this after getting the rest of the settings diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index b280c5d63cd7a..e367d75943b4f 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -11947,9 +11947,25 @@ public final class Settings { public static final String ADAPTIVE_BATTERY_MANAGEMENT_ENABLED = "adaptive_battery_management_enabled"; + /** + * Whether or not apps are allowed into the + * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket. + * Type: int (0 for false, 1 for true) + * Default: {@value #DEFAULT_ENABLE_RESTRICTED_BUCKET} + * + * @hide + */ + public static final String ENABLE_RESTRICTED_BUCKET = "enable_restricted_bucket"; + + /** + * @see #ENABLE_RESTRICTED_BUCKET + * @hide + */ + public static final int DEFAULT_ENABLE_RESTRICTED_BUCKET = 1; + /** * Whether or not app auto restriction is enabled. When it is enabled, settings app will - * auto restrict the app if it has bad behavior(e.g. hold wakelock for long time). + * auto restrict the app if it has bad behavior (e.g. hold wakelock for long time). * * Type: boolean (0 for false, 1 for true) * Default: 1 diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index f5589d713bf72..6f53e5774c3d6 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -264,6 +264,7 @@ public class SettingsBackupTest { Settings.Global.ENABLE_DELETION_HELPER_NO_THRESHOLD_TOGGLE, Settings.Global.ENABLE_DISKSTATS_LOGGING, Settings.Global.ENABLE_EPHEMERAL_FEATURE, + Settings.Global.ENABLE_RESTRICTED_BUCKET, Settings.Global.DYNAMIC_POWER_SAVINGS_ENABLED, Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, Settings.Global.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS, diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index 39062f017a737..766349ade4a81 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -165,6 +165,7 @@ public class AppStandbyControllerTests { long mElapsedRealtime; boolean mIsAppIdleEnabled = true; boolean mIsCharging; + boolean mIsRestrictedBucketEnabled = true; List mNonIdleWhitelistApps = new ArrayList<>(); boolean mDisplayOn; DisplayManager.DisplayListener mDisplayListener; @@ -211,6 +212,11 @@ public class AppStandbyControllerTests { return mNonIdleWhitelistApps.contains(packageName); } + @Override + boolean isRestrictedBucketEnabled() { + return mIsRestrictedBucketEnabled; + } + @Override File getDataSystemDirectory() { return new File(getContext().getFilesDir(), Long.toString(Math.randomLongInternal())); @@ -447,6 +453,10 @@ public class AppStandbyControllerTests { assertEquals(bucket, getStandbyBucket(mController, PACKAGE_1)); } + private void assertNotBucket(int bucket) { + assertNotEquals(bucket, getStandbyBucket(mController, PACKAGE_1)); + } + @Test public void testBuckets() throws Exception { assertTimeout(mController, 0, STANDBY_BUCKET_NEVER); @@ -881,6 +891,48 @@ public class AppStandbyControllerTests { assertBucket(STANDBY_BUCKET_RESTRICTED); } + @Test + public void testRestrictedBucketDisabled() { + mInjector.mIsRestrictedBucketEnabled = false; + // Get the controller to read the new value. Capturing the ContentObserver isn't possible + // at the moment. + mController.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY); + + reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1); + mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD; + + // Nothing should be able to put it into the RESTRICTED bucket. + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED, + REASON_MAIN_TIMEOUT); + assertNotBucket(STANDBY_BUCKET_RESTRICTED); + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED, + REASON_MAIN_PREDICTED); + assertNotBucket(STANDBY_BUCKET_RESTRICTED); + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED, + REASON_MAIN_FORCED_BY_SYSTEM); + assertNotBucket(STANDBY_BUCKET_RESTRICTED); + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED, + REASON_MAIN_FORCED_BY_USER); + assertNotBucket(STANDBY_BUCKET_RESTRICTED); + } + + @Test + public void testRestrictedBucket_EnabledToDisabled() { + reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1); + mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD; + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED, + REASON_MAIN_FORCED_BY_SYSTEM); + assertBucket(STANDBY_BUCKET_RESTRICTED); + + mInjector.mIsRestrictedBucketEnabled = false; + // Get the controller to read the new value. Capturing the ContentObserver isn't possible + // at the moment. + mController.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY); + + mController.checkIdleStates(USER_ID); + assertNotBucket(STANDBY_BUCKET_RESTRICTED); + } + @Test public void testPredictionRaiseFromRestrictedTimeout_highBucket() { reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);