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