Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f1923112 authored by Jing Ji's avatar Jing Ji Committed by Automerger Merge Worker
Browse files

Merge "Decouple the thresholds of the background restrictions" into tm-dev am: 19e584cf

parents ce2b3c1d 19e584cf
Loading
Loading
Loading
Loading
+89 −29
Original line number Original line Diff line number Diff line
@@ -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_ADAPTIVE_BUCKET;
import static android.app.ActivityManager.RESTRICTION_LEVEL_BACKGROUND_RESTRICTED;
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_RESTRICTED_BUCKET;
import static android.app.ActivityManager.RESTRICTION_LEVEL_UNKNOWN;
import static android.app.ActivityManager.isLowRamDeviceStatic;
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_FORCED_BY_SYSTEM;
import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
@@ -1118,6 +1119,13 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy>
        static final String KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_BY_BG_LOCATION =
        static final String KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_BY_BG_LOCATION =
                DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "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
         * Default value to the {@link #INDEX_REGULAR_CURRENT_DRAIN_THRESHOLD} of
         * the {@link #mBgCurrentDrainRestrictedBucketThreshold}.
         * the {@link #mBgCurrentDrainRestrictedBucketThreshold}.
@@ -1190,6 +1198,11 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy>
         */
         */
        final boolean mDefaultBgCurrentDrainHighThresholdByBgLocation;
        final boolean mDefaultBgCurrentDrainHighThresholdByBgLocation;


        /**
         * Default value to {@link #mBgCurrentDrainDecoupleThresholds}.
         */
        static final boolean DEFAULT_BG_CURRENT_DRAIN_DECOUPLE_THRESHOLD = true;

        /**
        /**
         * The index to {@link #mBgCurrentDrainRestrictedBucketThreshold}
         * The index to {@link #mBgCurrentDrainRestrictedBucketThreshold}
         * and {@link #mBgCurrentDrainBgRestrictedThreshold}.
         * and {@link #mBgCurrentDrainBgRestrictedThreshold}.
@@ -1257,6 +1270,11 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy>
         */
         */
        volatile boolean mBgCurrentDrainHighThresholdByBgLocation;
        volatile boolean mBgCurrentDrainHighThresholdByBgLocation;


        /**
         * @see #KEY_BG_CURRENT_DRAIN_DECOUPLE_THRESHOLDS.
         */
        volatile boolean mBgCurrentDrainDecoupleThresholds;

        /**
        /**
         * The capacity of the battery when fully charged in mAh.
         * The capacity of the battery when fully charged in mAh.
         */
         */
@@ -1369,6 +1387,9 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy>
                case KEY_BG_CURRENT_DRAIN_EXEMPTED_TYPES:
                case KEY_BG_CURRENT_DRAIN_EXEMPTED_TYPES:
                    updateCurrentDrainExemptedTypes();
                    updateCurrentDrainExemptedTypes();
                    break;
                    break;
                case KEY_BG_CURRENT_DRAIN_DECOUPLE_THRESHOLDS:
                    updateCurrentDrainDecoupleThresholds();
                    break;
                default:
                default:
                    super.onPropertiesChanged(name);
                    super.onPropertiesChanged(name);
                    break;
                    break;
@@ -1466,6 +1487,13 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy>
                    mDefaultBgCurrentDrainExemptedTypes);
                    mDefaultBgCurrentDrainExemptedTypes);
        }
        }


        private void updateCurrentDrainDecoupleThresholds() {
            mBgCurrentDrainDecoupleThresholds = DeviceConfig.getBoolean(
                    DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                    KEY_BG_CURRENT_DRAIN_DECOUPLE_THRESHOLDS,
                    DEFAULT_BG_CURRENT_DRAIN_DECOUPLE_THRESHOLD);
        }

        @Override
        @Override
        public void onSystemReady() {
        public void onSystemReady() {
            mBatteryFullChargeMah =
            mBatteryFullChargeMah =
@@ -1477,20 +1505,30 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy>
            updateCurrentDrainLocationMinDuration();
            updateCurrentDrainLocationMinDuration();
            updateCurrentDrainEventDurationBasedThresholdEnabled();
            updateCurrentDrainEventDurationBasedThresholdEnabled();
            updateCurrentDrainExemptedTypes();
            updateCurrentDrainExemptedTypes();
            updateCurrentDrainDecoupleThresholds();
        }
        }


        @Override
        @Override
        public @RestrictionLevel int getProposedRestrictionLevel(String packageName, int uid) {
        @RestrictionLevel
            synchronized (mLock) {
        public int getProposedRestrictionLevel(String packageName, int uid,
                final int index = mHighBgBatteryPackages.indexOfKey(uid);
                @RestrictionLevel int maxLevel) {
                if (index < 0) {
            if (maxLevel <= RESTRICTION_LEVEL_ADAPTIVE_BUCKET) {
                    // Not found, return adaptive as the default one.
                return RESTRICTION_LEVEL_UNKNOWN;
                    return RESTRICTION_LEVEL_ADAPTIVE_BUCKET;
            }
            }
                final long[] ts = mHighBgBatteryPackages.valueAt(index);
            synchronized (mLock) {
                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
                        return ts[TIME_STAMP_INDEX_BG_RESTRICTED] > 0
                        ? RESTRICTION_LEVEL_BACKGROUND_RESTRICTED
                                ? RESTRICTION_LEVEL_BACKGROUND_RESTRICTED : restrictedLevel;
                        : RESTRICTION_LEVEL_RESTRICTED_BUCKET;
                    } else if (maxLevel == RESTRICTION_LEVEL_BACKGROUND_RESTRICTED) {
                        return restrictedLevel;
                    }
                }
                return RESTRICTION_LEVEL_ADAPTIVE_BUCKET;
            }
            }
        }
        }


@@ -1573,36 +1611,58 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy>
                final int thresholdIndex = getCurrentDrainThresholdIndex(uid, now,
                final int thresholdIndex = getCurrentDrainThresholdIndex(uid, now,
                        mBgCurrentDrainWindowMs);
                        mBgCurrentDrainWindowMs);
                final int index = mHighBgBatteryPackages.indexOfKey(uid);
                final int index = mHighBgBatteryPackages.indexOfKey(uid);
                final boolean decoupleThresholds = mBgCurrentDrainDecoupleThresholds;
                final double rbThreshold = mBgCurrentDrainRestrictedBucketThreshold[thresholdIndex];
                final double brThreshold = mBgCurrentDrainBgRestrictedThreshold[thresholdIndex];
                if (index < 0) {
                if (index < 0) {
                    if (rbPercentage >= mBgCurrentDrainRestrictedBucketThreshold[thresholdIndex]) {
                    long[] ts = null;
                    if (rbPercentage >= rbThreshold) {
                        // New findings to us, track it and let the controller know.
                        // 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;
                        ts[TIME_STAMP_INDEX_RESTRICTED_BUCKET] = now;
                        mHighBgBatteryPackages.put(uid, ts);
                        mHighBgBatteryPackages.put(uid, ts);
                        notifyController = excessive = true;
                        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 {
                } else {
                    final long[] ts = mHighBgBatteryPackages.valueAt(index);
                    final long[] ts = mHighBgBatteryPackages.valueAt(index);
                    if (rbPercentage < mBgCurrentDrainRestrictedBucketThreshold[thresholdIndex]) {
                    final long lastRestrictBucketTs = ts[TIME_STAMP_INDEX_RESTRICTED_BUCKET];
                        // it's actually back to normal, but we don't untrack it until
                    if (rbPercentage >= rbThreshold) {
                        // explicit user interactions.
                        if (lastRestrictBucketTs == 0) {
                        notifyController = true;
                            ts[TIME_STAMP_INDEX_RESTRICTED_BUCKET] = now;
                        }
                        notifyController = excessive = true;
                    } else {
                    } else {
                        excessive = true;
                        // It's actually back to normal, but we don't untrack it until
                        if (brPercentage >= mBgCurrentDrainBgRestrictedThreshold[thresholdIndex]
                        // explicit user interactions, because the restriction could be the cause
                                && curLevel == RESTRICTION_LEVEL_RESTRICTED_BUCKET) {
                        // of going back to normal.
                            // If we're in the restricted standby bucket but still seeing high
                    }
                            // current drains, tell the controller again.
                    if (brPercentage >= brThreshold) {
                            final long lastResbucket = ts[TIME_STAMP_INDEX_RESTRICTED_BUCKET];
                        // If either
                            final long lastBgRes = ts[TIME_STAMP_INDEX_BG_RESTRICTED];
                        // a) It's configured to goto threshold 2 directly without threshold 1;
                            // If it has been a while since restricting the app and since the last
                        // b) It's already in the restricted standby bucket, but still seeing
                            // time we notify the controller, notify it again.
                        //    high current drains, and it's been a while since it's restricted;
                            if ((now >= lastResbucket + mBgCurrentDrainWindowMs) && (lastBgRes == 0
                        // tell the controller.
                                    || (now >= lastBgRes + mBgCurrentDrainWindowMs))) {
                        notifyController = decoupleThresholds
                                || (curLevel == RESTRICTION_LEVEL_RESTRICTED_BUCKET
                                && (now > lastRestrictBucketTs + mBgCurrentDrainWindowMs));
                        if (notifyController) {
                            ts[TIME_STAMP_INDEX_BG_RESTRICTED] = now;
                            ts[TIME_STAMP_INDEX_BG_RESTRICTED] = now;
                                notifyController = true;
                            }
                        }
                        }
                        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.
                    }
                    }
                }
                }
            }
            }
+15 −13
Original line number Original line Diff line number Diff line
@@ -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_BACKGROUND_RESTRICTED;
import static android.app.ActivityManager.RESTRICTION_LEVEL_EXEMPTED;
import static android.app.ActivityManager.RESTRICTION_LEVEL_EXEMPTED;
import static android.app.ActivityManager.RESTRICTION_LEVEL_HIBERNATION;
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_RESTRICTED_BUCKET;
import static android.app.ActivityManager.RESTRICTION_LEVEL_UNKNOWN;
import static android.app.ActivityManager.RESTRICTION_LEVEL_UNKNOWN;
import static android.app.ActivityManager.UID_OBSERVER_ACTIVE;
import static android.app.ActivityManager.UID_OBSERVER_ACTIVE;
@@ -1152,7 +1153,8 @@ public final class AppRestrictionController {
                        ? RESTRICTION_LEVEL_RESTRICTED_BUCKET
                        ? RESTRICTION_LEVEL_RESTRICTED_BUCKET
                        : RESTRICTION_LEVEL_ADAPTIVE_BUCKET;
                        : RESTRICTION_LEVEL_ADAPTIVE_BUCKET;
                if (calcTrackers) {
                if (calcTrackers) {
                    @RestrictionLevel int l = calcAppRestrictionLevelFromTackers(uid, packageName);
                    @RestrictionLevel int l = calcAppRestrictionLevelFromTackers(uid, packageName,
                            RESTRICTION_LEVEL_MAX);
                    if (l == RESTRICTION_LEVEL_EXEMPTED) {
                    if (l == RESTRICTION_LEVEL_EXEMPTED) {
                        return RESTRICTION_LEVEL_EXEMPTED;
                        return RESTRICTION_LEVEL_EXEMPTED;
                    }
                    }
@@ -1164,7 +1166,8 @@ public final class AppRestrictionController {
                                    uid, 0, packageName).sendToTarget();
                                    uid, 0, packageName).sendToTarget();
                        }
                        }
                        // Lower the level.
                        // Lower the level.
                        level = RESTRICTION_LEVEL_RESTRICTED_BUCKET;
                        level = calcAppRestrictionLevelFromTackers(uid, packageName,
                                RESTRICTION_LEVEL_BACKGROUND_RESTRICTED);
                    }
                    }
                }
                }
                break;
                break;
@@ -1181,12 +1184,13 @@ public final class AppRestrictionController {
     * monitors certain dimensions of the app, the abusive behaviors could be detected in one or
     * 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. </p>
     * more of these dimensions, but not necessarily all of them. </p>
     */
     */
    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;
        @RestrictionLevel int level = RESTRICTION_LEVEL_UNKNOWN;
        final boolean isRestrictedBucketEnabled = mConstantsObserver.mRestrictedBucketEnabled;
        final boolean isRestrictedBucketEnabled = mConstantsObserver.mRestrictedBucketEnabled;
        for (int i = mAppStateTrackers.size() - 1; i >= 0; i--) {
        for (int i = mAppStateTrackers.size() - 1; i >= 0; i--) {
            @RestrictionLevel int l = mAppStateTrackers.get(i).getPolicy()
            @RestrictionLevel int l = mAppStateTrackers.get(i).getPolicy()
                    .getProposedRestrictionLevel(packageName, uid);
                    .getProposedRestrictionLevel(packageName, uid, maxLevel);
            if (!isRestrictedBucketEnabled && l == RESTRICTION_LEVEL_RESTRICTED_BUCKET) {
            if (!isRestrictedBucketEnabled && l == RESTRICTION_LEVEL_RESTRICTED_BUCKET) {
                l = RESTRICTION_LEVEL_ADAPTIVE_BUCKET;
                l = RESTRICTION_LEVEL_ADAPTIVE_BUCKET;
            }
            }
@@ -1475,7 +1479,6 @@ public final class AppRestrictionController {
        } else if (curLevel >= RESTRICTION_LEVEL_RESTRICTED_BUCKET
        } else if (curLevel >= RESTRICTION_LEVEL_RESTRICTED_BUCKET
                && level < RESTRICTION_LEVEL_RESTRICTED_BUCKET) {
                && level < RESTRICTION_LEVEL_RESTRICTED_BUCKET) {
            // Moved out of the background-restricted state.
            // Moved out of the background-restricted state.
            if (curBucket != STANDBY_BUCKET_RARE) {
            synchronized (mSettingsLock) {
            synchronized (mSettingsLock) {
                final int index = mActiveUids.indexOfKey(uid, pkgName);
                final int index = mActiveUids.indexOfKey(uid, pkgName);
                if (index >= 0) {
                if (index >= 0) {
@@ -1487,7 +1490,6 @@ public final class AppRestrictionController {
                    reason, subReason);
                    reason, subReason);
        }
        }
    }
    }
    }


    private void handleBackgroundRestrictionChanged(int uid, String pkgName, boolean restricted) {
    private void handleBackgroundRestrictionChanged(int uid, String pkgName, boolean restricted) {
        // Firstly, notify the trackers.
        // Firstly, notify the trackers.
+4 −2
Original line number Original line Diff line number Diff line
@@ -87,9 +87,11 @@ public abstract class BaseAppStatePolicy<T extends BaseAppStateTracker> {
    }
    }


    /**
    /**
     * @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;
        return RESTRICTION_LEVEL_UNKNOWN;
    }
    }


+11 −2
Original line number Original line Diff line number Diff line
@@ -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_ADAPTIVE_BUCKET;
import static android.app.ActivityManager.RESTRICTION_LEVEL_RESTRICTED_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_FORCED_BY_SYSTEM;
import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE;
import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE;
@@ -295,11 +296,19 @@ abstract class BaseAppStateTimeSlotEventsTracker
        }
        }


        @Override
        @Override
        public @RestrictionLevel int getProposedRestrictionLevel(String packageName, int uid) {
        @RestrictionLevel
        public int getProposedRestrictionLevel(String packageName, int uid,
                @RestrictionLevel int maxLevel) {
            synchronized (mLock) {
            synchronized (mLock) {
                return mExcessiveEventPkgs.get(packageName, uid) == null
                final int level = mExcessiveEventPkgs.get(packageName, uid) == null
                        ? RESTRICTION_LEVEL_ADAPTIVE_BUCKET
                        ? RESTRICTION_LEVEL_ADAPTIVE_BUCKET
                        : RESTRICTION_LEVEL_RESTRICTED_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;
            }
            }
        }
        }


+121 −0
Original line number Original line Diff line number Diff line
@@ -579,6 +579,7 @@ public final class BackgroundRestrictionTest {
        DeviceConfigSession<Boolean> bgPromptFgsWithNotiToBgRestricted = null;
        DeviceConfigSession<Boolean> bgPromptFgsWithNotiToBgRestricted = null;
        DeviceConfigSession<Long> bgNotificationMinInterval = null;
        DeviceConfigSession<Long> bgNotificationMinInterval = null;
        DeviceConfigSession<Integer> bgBatteryExemptionTypes = null;
        DeviceConfigSession<Integer> bgBatteryExemptionTypes = null;
        DeviceConfigSession<Boolean> bgCurrentDrainDecoupleThresholds = null;


        mBgRestrictionController.addAppBackgroundRestrictionListener(listener);
        mBgRestrictionController.addAppBackgroundRestrictionListener(listener);


@@ -648,6 +649,13 @@ public final class BackgroundRestrictionTest {
                            R.integer.config_bg_current_drain_exempted_types));
                            R.integer.config_bg_current_drain_exempted_types));
            bgBatteryExemptionTypes.set(0);
            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;
            mCurrentTimeMillis = 10_000L;
            doReturn(mCurrentTimeMillis - windowMs).when(stats).getStatsStartTimestamp();
            doReturn(mCurrentTimeMillis - windowMs).when(stats).getStatsStartTimestamp();
            doReturn(mCurrentTimeMillis).when(stats).getStatsEndTimestamp();
            doReturn(mCurrentTimeMillis).when(stats).getStatsEndTimestamp();
@@ -785,6 +793,11 @@ public final class BackgroundRestrictionTest {
                                anyInt(), anyInt());
                                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
            // Sleep a while and set a higher drain
            Thread.sleep(windowMs);
            Thread.sleep(windowMs);
            clearInvocations(mInjector.getAppStandbyInternal());
            clearInvocations(mInjector.getAppStandbyInternal());
@@ -921,6 +934,11 @@ public final class BackgroundRestrictionTest {
                // Expected.
                // Expected.
            }
            }


            // Reset the standby bucket.
            doReturn(STANDBY_BUCKET_RARE)
                    .when(mAppStandbyInternal)
                    .getAppStandbyBucket(eq(testPkgName), eq(testUser), anyLong(), anyBoolean());

            // Turn OFF the FAS.
            // Turn OFF the FAS.
            listener.mLatchHolder[0] = new CountDownLatch(1);
            listener.mLatchHolder[0] = new CountDownLatch(1);
            clearInvocations(mInjector.getAppStandbyInternal());
            clearInvocations(mInjector.getAppStandbyInternal());
@@ -930,6 +948,99 @@ public final class BackgroundRestrictionTest {
            // It'll go back to restricted bucket because it used to behave poorly.
            // It'll go back to restricted bucket because it used to behave poorly.
            listener.verify(timeout, testUid, testPkgName, RESTRICTION_LEVEL_RESTRICTED_BUCKET);
            listener.verify(timeout, testUid, testPkgName, RESTRICTION_LEVEL_RESTRICTED_BUCKET);
            verifyRestrictionLevel(RESTRICTION_LEVEL_RESTRICTED_BUCKET, testPkgName, testUid);
            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 {
        } finally {
            closeIfNotNull(bgCurrentDrainMonitor);
            closeIfNotNull(bgCurrentDrainMonitor);
            closeIfNotNull(bgCurrentDrainWindow);
            closeIfNotNull(bgCurrentDrainWindow);
@@ -938,6 +1049,7 @@ public final class BackgroundRestrictionTest {
            closeIfNotNull(bgPromptFgsWithNotiToBgRestricted);
            closeIfNotNull(bgPromptFgsWithNotiToBgRestricted);
            closeIfNotNull(bgNotificationMinInterval);
            closeIfNotNull(bgNotificationMinInterval);
            closeIfNotNull(bgBatteryExemptionTypes);
            closeIfNotNull(bgBatteryExemptionTypes);
            closeIfNotNull(bgCurrentDrainDecoupleThresholds);
        }
        }
    }
    }


@@ -1441,6 +1553,7 @@ public final class BackgroundRestrictionTest {
        DeviceConfigSession<Boolean> bgPermissionMonitorEnabled = null;
        DeviceConfigSession<Boolean> bgPermissionMonitorEnabled = null;
        DeviceConfigSession<String> bgPermissionsInMonitor = null;
        DeviceConfigSession<String> bgPermissionsInMonitor = null;
        DeviceConfigSession<Boolean> bgCurrentDrainHighThresholdByBgLocation = null;
        DeviceConfigSession<Boolean> bgCurrentDrainHighThresholdByBgLocation = null;
        DeviceConfigSession<Boolean> bgCurrentDrainDecoupleThresholds = null;


        mBgRestrictionController.addAppBackgroundRestrictionListener(listener);
        mBgRestrictionController.addAppBackgroundRestrictionListener(listener);


@@ -1572,6 +1685,13 @@ public final class BackgroundRestrictionTest {
                            R.bool.config_bg_current_drain_high_threshold_by_bg_location));
                            R.bool.config_bg_current_drain_high_threshold_by_bg_location));
            bgCurrentDrainHighThresholdByBgLocation.set(true);
            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;
            mCurrentTimeMillis = 10_000L;
            doReturn(mCurrentTimeMillis - windowMs).when(stats).getStatsStartTimestamp();
            doReturn(mCurrentTimeMillis - windowMs).when(stats).getStatsStartTimestamp();
            doReturn(mCurrentTimeMillis).when(stats).getStatsEndTimestamp();
            doReturn(mCurrentTimeMillis).when(stats).getStatsEndTimestamp();
@@ -1990,6 +2110,7 @@ public final class BackgroundRestrictionTest {
            closeIfNotNull(bgPermissionMonitorEnabled);
            closeIfNotNull(bgPermissionMonitorEnabled);
            closeIfNotNull(bgPermissionsInMonitor);
            closeIfNotNull(bgPermissionsInMonitor);
            closeIfNotNull(bgCurrentDrainHighThresholdByBgLocation);
            closeIfNotNull(bgCurrentDrainHighThresholdByBgLocation);
            closeIfNotNull(bgCurrentDrainDecoupleThresholds);
        }
        }
    }
    }