diff --git a/services/core/java/com/android/server/am/AppBatteryTracker.java b/services/core/java/com/android/server/am/AppBatteryTracker.java index 690051f47326f..0cbc7ad404f57 100644 --- a/services/core/java/com/android/server/am/AppBatteryTracker.java +++ b/services/core/java/com/android/server/am/AppBatteryTracker.java @@ -20,6 +20,7 @@ import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION; import static android.app.ActivityManager.RESTRICTION_LEVEL_ADAPTIVE_BUCKET; import static android.app.ActivityManager.RESTRICTION_LEVEL_BACKGROUND_RESTRICTED; import static android.app.ActivityManager.RESTRICTION_LEVEL_RESTRICTED_BUCKET; +import static android.app.ActivityManager.RESTRICTION_LEVEL_UNKNOWN; import static android.app.ActivityManager.isLowRamDeviceStatic; import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_SYSTEM; import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE; @@ -1118,6 +1119,13 @@ final class AppBatteryTracker extends BaseAppStateTracker static final String KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_BY_BG_LOCATION = DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "current_drain_high_threshold_by_bg_location"; + /** + * Whether or not the battery usage of the offending app should fulfill the 1st threshold + * before taking actions for the 2nd threshold. + */ + static final String KEY_BG_CURRENT_DRAIN_DECOUPLE_THRESHOLDS = + DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "current_drain_decouple_thresholds"; + /** * Default value to the {@link #INDEX_REGULAR_CURRENT_DRAIN_THRESHOLD} of * the {@link #mBgCurrentDrainRestrictedBucketThreshold}. @@ -1190,6 +1198,11 @@ final class AppBatteryTracker extends BaseAppStateTracker */ final boolean mDefaultBgCurrentDrainHighThresholdByBgLocation; + /** + * Default value to {@link #mBgCurrentDrainDecoupleThresholds}. + */ + static final boolean DEFAULT_BG_CURRENT_DRAIN_DECOUPLE_THRESHOLD = true; + /** * The index to {@link #mBgCurrentDrainRestrictedBucketThreshold} * and {@link #mBgCurrentDrainBgRestrictedThreshold}. @@ -1257,6 +1270,11 @@ final class AppBatteryTracker extends BaseAppStateTracker */ volatile boolean mBgCurrentDrainHighThresholdByBgLocation; + /** + * @see #KEY_BG_CURRENT_DRAIN_DECOUPLE_THRESHOLDS. + */ + volatile boolean mBgCurrentDrainDecoupleThresholds; + /** * The capacity of the battery when fully charged in mAh. */ @@ -1369,6 +1387,9 @@ final class AppBatteryTracker extends BaseAppStateTracker case KEY_BG_CURRENT_DRAIN_EXEMPTED_TYPES: updateCurrentDrainExemptedTypes(); break; + case KEY_BG_CURRENT_DRAIN_DECOUPLE_THRESHOLDS: + updateCurrentDrainDecoupleThresholds(); + break; default: super.onPropertiesChanged(name); break; @@ -1466,6 +1487,13 @@ final class AppBatteryTracker extends BaseAppStateTracker mDefaultBgCurrentDrainExemptedTypes); } + private void updateCurrentDrainDecoupleThresholds() { + mBgCurrentDrainDecoupleThresholds = DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_BG_CURRENT_DRAIN_DECOUPLE_THRESHOLDS, + DEFAULT_BG_CURRENT_DRAIN_DECOUPLE_THRESHOLD); + } + @Override public void onSystemReady() { mBatteryFullChargeMah = @@ -1477,20 +1505,30 @@ final class AppBatteryTracker extends BaseAppStateTracker updateCurrentDrainLocationMinDuration(); updateCurrentDrainEventDurationBasedThresholdEnabled(); updateCurrentDrainExemptedTypes(); + updateCurrentDrainDecoupleThresholds(); } @Override - public @RestrictionLevel int getProposedRestrictionLevel(String packageName, int uid) { + @RestrictionLevel + public int getProposedRestrictionLevel(String packageName, int uid, + @RestrictionLevel int maxLevel) { + if (maxLevel <= RESTRICTION_LEVEL_ADAPTIVE_BUCKET) { + return RESTRICTION_LEVEL_UNKNOWN; + } synchronized (mLock) { - final int index = mHighBgBatteryPackages.indexOfKey(uid); - if (index < 0) { - // Not found, return adaptive as the default one. - return RESTRICTION_LEVEL_ADAPTIVE_BUCKET; + final long[] ts = mHighBgBatteryPackages.get(uid); + if (ts != null) { + final int restrictedLevel = ts[TIME_STAMP_INDEX_RESTRICTED_BUCKET] > 0 + ? RESTRICTION_LEVEL_RESTRICTED_BUCKET + : RESTRICTION_LEVEL_ADAPTIVE_BUCKET; + if (maxLevel > RESTRICTION_LEVEL_BACKGROUND_RESTRICTED) { + return ts[TIME_STAMP_INDEX_BG_RESTRICTED] > 0 + ? RESTRICTION_LEVEL_BACKGROUND_RESTRICTED : restrictedLevel; + } else if (maxLevel == RESTRICTION_LEVEL_BACKGROUND_RESTRICTED) { + return restrictedLevel; + } } - final long[] ts = mHighBgBatteryPackages.valueAt(index); - return ts[TIME_STAMP_INDEX_BG_RESTRICTED] > 0 - ? RESTRICTION_LEVEL_BACKGROUND_RESTRICTED - : RESTRICTION_LEVEL_RESTRICTED_BUCKET; + return RESTRICTION_LEVEL_ADAPTIVE_BUCKET; } } @@ -1573,36 +1611,58 @@ final class AppBatteryTracker extends BaseAppStateTracker final int thresholdIndex = getCurrentDrainThresholdIndex(uid, now, mBgCurrentDrainWindowMs); final int index = mHighBgBatteryPackages.indexOfKey(uid); + final boolean decoupleThresholds = mBgCurrentDrainDecoupleThresholds; + final double rbThreshold = mBgCurrentDrainRestrictedBucketThreshold[thresholdIndex]; + final double brThreshold = mBgCurrentDrainBgRestrictedThreshold[thresholdIndex]; if (index < 0) { - if (rbPercentage >= mBgCurrentDrainRestrictedBucketThreshold[thresholdIndex]) { + long[] ts = null; + if (rbPercentage >= rbThreshold) { // New findings to us, track it and let the controller know. - final long[] ts = new long[TIME_STAMP_INDEX_LAST]; + ts = new long[TIME_STAMP_INDEX_LAST]; ts[TIME_STAMP_INDEX_RESTRICTED_BUCKET] = now; mHighBgBatteryPackages.put(uid, ts); notifyController = excessive = true; } + if (decoupleThresholds && brPercentage >= brThreshold) { + if (ts == null) { + ts = new long[TIME_STAMP_INDEX_LAST]; + mHighBgBatteryPackages.put(uid, ts); + } + ts[TIME_STAMP_INDEX_BG_RESTRICTED] = now; + notifyController = excessive = true; + } } else { final long[] ts = mHighBgBatteryPackages.valueAt(index); - if (rbPercentage < mBgCurrentDrainRestrictedBucketThreshold[thresholdIndex]) { - // it's actually back to normal, but we don't untrack it until - // explicit user interactions. - notifyController = true; - } else { - excessive = true; - if (brPercentage >= mBgCurrentDrainBgRestrictedThreshold[thresholdIndex] - && curLevel == RESTRICTION_LEVEL_RESTRICTED_BUCKET) { - // If we're in the restricted standby bucket but still seeing high - // current drains, tell the controller again. - final long lastResbucket = ts[TIME_STAMP_INDEX_RESTRICTED_BUCKET]; - final long lastBgRes = ts[TIME_STAMP_INDEX_BG_RESTRICTED]; - // If it has been a while since restricting the app and since the last - // time we notify the controller, notify it again. - if ((now >= lastResbucket + mBgCurrentDrainWindowMs) && (lastBgRes == 0 - || (now >= lastBgRes + mBgCurrentDrainWindowMs))) { - ts[TIME_STAMP_INDEX_BG_RESTRICTED] = now; - notifyController = true; - } + final long lastRestrictBucketTs = ts[TIME_STAMP_INDEX_RESTRICTED_BUCKET]; + if (rbPercentage >= rbThreshold) { + if (lastRestrictBucketTs == 0) { + ts[TIME_STAMP_INDEX_RESTRICTED_BUCKET] = now; } + notifyController = excessive = true; + } else { + // It's actually back to normal, but we don't untrack it until + // explicit user interactions, because the restriction could be the cause + // of going back to normal. + } + if (brPercentage >= brThreshold) { + // If either + // a) It's configured to goto threshold 2 directly without threshold 1; + // b) It's already in the restricted standby bucket, but still seeing + // high current drains, and it's been a while since it's restricted; + // tell the controller. + notifyController = decoupleThresholds + || (curLevel == RESTRICTION_LEVEL_RESTRICTED_BUCKET + && (now > lastRestrictBucketTs + mBgCurrentDrainWindowMs)); + if (notifyController) { + ts[TIME_STAMP_INDEX_BG_RESTRICTED] = now; + } + excessive = true; + } else { + // Reset the track now - if it's already background restricted, it requires + // user consent to unrestrict it; or if it's in restricted bucket level, + // resetting this won't lift it from that level. + ts[TIME_STAMP_INDEX_BG_RESTRICTED] = 0; + // Now need to notify the controller. } } } diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java index 3f307fa53d8a8..6c183626fdad8 100644 --- a/services/core/java/com/android/server/am/AppRestrictionController.java +++ b/services/core/java/com/android/server/am/AppRestrictionController.java @@ -24,6 +24,7 @@ import static android.app.ActivityManager.RESTRICTION_LEVEL_ADAPTIVE_BUCKET; import static android.app.ActivityManager.RESTRICTION_LEVEL_BACKGROUND_RESTRICTED; import static android.app.ActivityManager.RESTRICTION_LEVEL_EXEMPTED; import static android.app.ActivityManager.RESTRICTION_LEVEL_HIBERNATION; +import static android.app.ActivityManager.RESTRICTION_LEVEL_MAX; import static android.app.ActivityManager.RESTRICTION_LEVEL_RESTRICTED_BUCKET; import static android.app.ActivityManager.RESTRICTION_LEVEL_UNKNOWN; import static android.app.ActivityManager.UID_OBSERVER_ACTIVE; @@ -1152,7 +1153,8 @@ public final class AppRestrictionController { ? RESTRICTION_LEVEL_RESTRICTED_BUCKET : RESTRICTION_LEVEL_ADAPTIVE_BUCKET; if (calcTrackers) { - @RestrictionLevel int l = calcAppRestrictionLevelFromTackers(uid, packageName); + @RestrictionLevel int l = calcAppRestrictionLevelFromTackers(uid, packageName, + RESTRICTION_LEVEL_MAX); if (l == RESTRICTION_LEVEL_EXEMPTED) { return RESTRICTION_LEVEL_EXEMPTED; } @@ -1164,7 +1166,8 @@ public final class AppRestrictionController { uid, 0, packageName).sendToTarget(); } // Lower the level. - level = RESTRICTION_LEVEL_RESTRICTED_BUCKET; + level = calcAppRestrictionLevelFromTackers(uid, packageName, + RESTRICTION_LEVEL_BACKGROUND_RESTRICTED); } } break; @@ -1181,12 +1184,13 @@ public final class AppRestrictionController { * monitors certain dimensions of the app, the abusive behaviors could be detected in one or * more of these dimensions, but not necessarily all of them.

*/ - private @RestrictionLevel int calcAppRestrictionLevelFromTackers(int uid, String packageName) { + private @RestrictionLevel int calcAppRestrictionLevelFromTackers(int uid, String packageName, + @RestrictionLevel int maxLevel) { @RestrictionLevel int level = RESTRICTION_LEVEL_UNKNOWN; final boolean isRestrictedBucketEnabled = mConstantsObserver.mRestrictedBucketEnabled; for (int i = mAppStateTrackers.size() - 1; i >= 0; i--) { @RestrictionLevel int l = mAppStateTrackers.get(i).getPolicy() - .getProposedRestrictionLevel(packageName, uid); + .getProposedRestrictionLevel(packageName, uid, maxLevel); if (!isRestrictedBucketEnabled && l == RESTRICTION_LEVEL_RESTRICTED_BUCKET) { l = RESTRICTION_LEVEL_ADAPTIVE_BUCKET; } @@ -1475,17 +1479,15 @@ public final class AppRestrictionController { } else if (curLevel >= RESTRICTION_LEVEL_RESTRICTED_BUCKET && level < RESTRICTION_LEVEL_RESTRICTED_BUCKET) { // Moved out of the background-restricted state. - if (curBucket != STANDBY_BUCKET_RARE) { - synchronized (mSettingsLock) { - final int index = mActiveUids.indexOfKey(uid, pkgName); - if (index >= 0) { - mActiveUids.add(uid, pkgName, null); - } + synchronized (mSettingsLock) { + final int index = mActiveUids.indexOfKey(uid, pkgName); + if (index >= 0) { + mActiveUids.add(uid, pkgName, null); } - appStandbyInternal.maybeUnrestrictApp(pkgName, UserHandle.getUserId(uid), - prevReason & REASON_MAIN_MASK, prevReason & REASON_SUB_MASK, - reason, subReason); } + appStandbyInternal.maybeUnrestrictApp(pkgName, UserHandle.getUserId(uid), + prevReason & REASON_MAIN_MASK, prevReason & REASON_SUB_MASK, + reason, subReason); } } diff --git a/services/core/java/com/android/server/am/BaseAppStatePolicy.java b/services/core/java/com/android/server/am/BaseAppStatePolicy.java index 67318a7639079..a55344caea9aa 100644 --- a/services/core/java/com/android/server/am/BaseAppStatePolicy.java +++ b/services/core/java/com/android/server/am/BaseAppStatePolicy.java @@ -87,9 +87,11 @@ public abstract class BaseAppStatePolicy { } /** - * @return The proposed background restriction policy for the given package/uid. + * @return The proposed background restriction policy for the given package/uid, + * the returned level should be capped at {@code maxLevel} (exclusive). */ - public @RestrictionLevel int getProposedRestrictionLevel(String packageName, int uid) { + public @RestrictionLevel int getProposedRestrictionLevel(String packageName, int uid, + @RestrictionLevel int maxLevel) { return RESTRICTION_LEVEL_UNKNOWN; } diff --git a/services/core/java/com/android/server/am/BaseAppStateTimeSlotEventsTracker.java b/services/core/java/com/android/server/am/BaseAppStateTimeSlotEventsTracker.java index 2fbca1fa1724b..b8aee13ccdd70 100644 --- a/services/core/java/com/android/server/am/BaseAppStateTimeSlotEventsTracker.java +++ b/services/core/java/com/android/server/am/BaseAppStateTimeSlotEventsTracker.java @@ -18,6 +18,7 @@ package com.android.server.am; import static android.app.ActivityManager.RESTRICTION_LEVEL_ADAPTIVE_BUCKET; import static android.app.ActivityManager.RESTRICTION_LEVEL_RESTRICTED_BUCKET; +import static android.app.ActivityManager.RESTRICTION_LEVEL_UNKNOWN; import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_SYSTEM; import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE; import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE; @@ -295,11 +296,19 @@ abstract class BaseAppStateTimeSlotEventsTracker } @Override - public @RestrictionLevel int getProposedRestrictionLevel(String packageName, int uid) { + @RestrictionLevel + public int getProposedRestrictionLevel(String packageName, int uid, + @RestrictionLevel int maxLevel) { synchronized (mLock) { - return mExcessiveEventPkgs.get(packageName, uid) == null + final int level = mExcessiveEventPkgs.get(packageName, uid) == null ? RESTRICTION_LEVEL_ADAPTIVE_BUCKET : RESTRICTION_LEVEL_RESTRICTED_BUCKET; + if (maxLevel > RESTRICTION_LEVEL_RESTRICTED_BUCKET) { + return level; + } else if (maxLevel == RESTRICTION_LEVEL_RESTRICTED_BUCKET) { + return RESTRICTION_LEVEL_ADAPTIVE_BUCKET; + } + return RESTRICTION_LEVEL_UNKNOWN; } } diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java index 47d4c8d9af466..32a87b62aeab6 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java @@ -579,6 +579,7 @@ public final class BackgroundRestrictionTest { DeviceConfigSession bgPromptFgsWithNotiToBgRestricted = null; DeviceConfigSession bgNotificationMinInterval = null; DeviceConfigSession bgBatteryExemptionTypes = null; + DeviceConfigSession bgCurrentDrainDecoupleThresholds = null; mBgRestrictionController.addAppBackgroundRestrictionListener(listener); @@ -648,6 +649,13 @@ public final class BackgroundRestrictionTest { R.integer.config_bg_current_drain_exempted_types)); bgBatteryExemptionTypes.set(0); + bgCurrentDrainDecoupleThresholds = new DeviceConfigSession<>( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_DECOUPLE_THRESHOLDS, + DeviceConfig::getBoolean, + AppBatteryPolicy.DEFAULT_BG_CURRENT_DRAIN_DECOUPLE_THRESHOLD); + bgCurrentDrainDecoupleThresholds.set(true); + mCurrentTimeMillis = 10_000L; doReturn(mCurrentTimeMillis - windowMs).when(stats).getStatsStartTimestamp(); doReturn(mCurrentTimeMillis).when(stats).getStatsEndTimestamp(); @@ -785,6 +793,11 @@ public final class BackgroundRestrictionTest { anyInt(), anyInt()); }); + // Pretend we have the standby buckets set above. + doReturn(STANDBY_BUCKET_RESTRICTED) + .when(mAppStandbyInternal) + .getAppStandbyBucket(eq(testPkgName), eq(testUser), anyLong(), anyBoolean()); + // Sleep a while and set a higher drain Thread.sleep(windowMs); clearInvocations(mInjector.getAppStandbyInternal()); @@ -921,6 +934,11 @@ public final class BackgroundRestrictionTest { // Expected. } + // Reset the standby bucket. + doReturn(STANDBY_BUCKET_RARE) + .when(mAppStandbyInternal) + .getAppStandbyBucket(eq(testPkgName), eq(testUser), anyLong(), anyBoolean()); + // Turn OFF the FAS. listener.mLatchHolder[0] = new CountDownLatch(1); clearInvocations(mInjector.getAppStandbyInternal()); @@ -930,6 +948,99 @@ public final class BackgroundRestrictionTest { // It'll go back to restricted bucket because it used to behave poorly. listener.verify(timeout, testUid, testPkgName, RESTRICTION_LEVEL_RESTRICTED_BUCKET); verifyRestrictionLevel(RESTRICTION_LEVEL_RESTRICTED_BUCKET, testPkgName, testUid); + + clearInvocations(mInjector.getAppStandbyInternal()); + // Trigger user interaction. + runTestBgCurrentDrainMonitorOnce(listener, stats, uids, + new double[]{restrictBucketThresholdMah - 1, 0}, + new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros, + () -> { + doReturn(mCurrentTimeMillis).when(stats).getStatsStartTimestamp(); + doReturn(mCurrentTimeMillis + windowMs) + .when(stats).getStatsEndTimestamp(); + mCurrentTimeMillis += windowMs + 1; + mIdleStateListener.onUserInteractionStarted(testPkgName, testUser); + waitForIdleHandler(mBgRestrictionController.getBackgroundHandler()); + // It should have been back to normal. + listener.verify(timeout, testUid, testPkgName, + RESTRICTION_LEVEL_ADAPTIVE_BUCKET); + verify(mInjector.getAppStandbyInternal(), atLeast(1)).maybeUnrestrictApp( + eq(testPkgName), + eq(testUser), + eq(REASON_MAIN_USAGE), + eq(REASON_SUB_USAGE_USER_INTERACTION), + eq(REASON_MAIN_USAGE), + eq(REASON_SUB_USAGE_USER_INTERACTION)); + }); + + bgCurrentDrainDecoupleThresholds.set(true); + clearInvocations(mInjector.getAppStandbyInternal()); + + // Go to the threshold right away. + runTestBgCurrentDrainMonitorOnce(listener, stats, uids, + new double[]{0, restrictBucketThresholdMah - 1}, + new double[]{bgRestrictedThresholdMah + 1, 0}, zeros, zeros, + () -> { + doReturn(mCurrentTimeMillis).when(stats).getStatsStartTimestamp(); + doReturn(mCurrentTimeMillis + windowMs) + .when(stats).getStatsEndTimestamp(); + mCurrentTimeMillis += windowMs + 1; + // We won't change restriction level automatically because it needs + // user consent. + try { + listener.verify(timeout, testUid, testPkgName, + RESTRICTION_LEVEL_BACKGROUND_RESTRICTED); + fail("There shouldn't be level change event like this"); + } catch (Exception e) { + // Expected. + } + verify(mInjector.getAppStandbyInternal(), never()).setAppStandbyBucket( + eq(testPkgName), + eq(STANDBY_BUCKET_RARE), + eq(testUser), + anyInt(), anyInt()); + // We should have requested to goto background restricted level. + verify(mBgRestrictionController, times(1)).handleRequestBgRestricted( + eq(testPkgName), + eq(testUid)); + // Verify we have the notification posted now because its FGS is invisible. + checkNotificationShown(new String[] {testPkgName}, atLeast(1), true); + }); + + bgCurrentDrainDecoupleThresholds.set(false); + clearInvocations(mInjector.getAppStandbyInternal()); + clearInvocations(mBgRestrictionController); + + // Go to the threshold right away, but this time, it shouldn't even request to goto + // bg restricted level because it requires to be in restricted bucket before that. + runTestBgCurrentDrainMonitorOnce(listener, stats, uids, + new double[]{0, restrictBucketThresholdMah - 1}, + new double[]{bgRestrictedThresholdMah + 1, 0}, zeros, zeros, + () -> { + doReturn(mCurrentTimeMillis).when(stats).getStatsStartTimestamp(); + doReturn(mCurrentTimeMillis + windowMs) + .when(stats).getStatsEndTimestamp(); + mCurrentTimeMillis += windowMs + 1; + // We won't change restriction level automatically because it needs + // user consent. + try { + listener.verify(timeout, testUid, testPkgName, + RESTRICTION_LEVEL_BACKGROUND_RESTRICTED); + fail("There shouldn't be level change event like this"); + } catch (Exception e) { + // Expected. + } + verify(mInjector.getAppStandbyInternal(), never()).setAppStandbyBucket( + eq(testPkgName), + eq(STANDBY_BUCKET_RARE), + eq(testUser), + anyInt(), anyInt()); + // We should NOT have requested to goto background restricted level. + verify(mBgRestrictionController, never()).handleRequestBgRestricted( + eq(testPkgName), + eq(testUid)); + }); + } finally { closeIfNotNull(bgCurrentDrainMonitor); closeIfNotNull(bgCurrentDrainWindow); @@ -938,6 +1049,7 @@ public final class BackgroundRestrictionTest { closeIfNotNull(bgPromptFgsWithNotiToBgRestricted); closeIfNotNull(bgNotificationMinInterval); closeIfNotNull(bgBatteryExemptionTypes); + closeIfNotNull(bgCurrentDrainDecoupleThresholds); } } @@ -1441,6 +1553,7 @@ public final class BackgroundRestrictionTest { DeviceConfigSession bgPermissionMonitorEnabled = null; DeviceConfigSession bgPermissionsInMonitor = null; DeviceConfigSession bgCurrentDrainHighThresholdByBgLocation = null; + DeviceConfigSession bgCurrentDrainDecoupleThresholds = null; mBgRestrictionController.addAppBackgroundRestrictionListener(listener); @@ -1572,6 +1685,13 @@ public final class BackgroundRestrictionTest { R.bool.config_bg_current_drain_high_threshold_by_bg_location)); bgCurrentDrainHighThresholdByBgLocation.set(true); + bgCurrentDrainDecoupleThresholds = new DeviceConfigSession<>( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_DECOUPLE_THRESHOLDS, + DeviceConfig::getBoolean, + AppBatteryPolicy.DEFAULT_BG_CURRENT_DRAIN_DECOUPLE_THRESHOLD); + bgCurrentDrainDecoupleThresholds.set(true); + mCurrentTimeMillis = 10_000L; doReturn(mCurrentTimeMillis - windowMs).when(stats).getStatsStartTimestamp(); doReturn(mCurrentTimeMillis).when(stats).getStatsEndTimestamp(); @@ -1990,6 +2110,7 @@ public final class BackgroundRestrictionTest { closeIfNotNull(bgPermissionMonitorEnabled); closeIfNotNull(bgPermissionsInMonitor); closeIfNotNull(bgCurrentDrainHighThresholdByBgLocation); + closeIfNotNull(bgCurrentDrainDecoupleThresholds); } }