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

Commit 4efee2f7 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Limiting alarms of apps in the RESTRICTED bucket."

parents 7fa9abc9 610aee76
Loading
Loading
Loading
Loading
+67 −19
Original line number Diff line number Diff line
@@ -331,12 +331,16 @@ class AlarmManagerService extends SystemService {
            return (history == null) ? 0 : history.size();
        }

        long getLastWakeupForPackage(String packageName, int userId, int positionFromEnd) {
        /**
         * @param n The desired nth-last wakeup
         *                (1=1st-last=the ultimate wakeup and 2=2nd-last=the penultimate wakeup)
         */
        long getNthLastWakeupForPackage(String packageName, int userId, int n) {
            final LongArrayQueue history = mPackageHistory.get(Pair.create(packageName, userId));
            if (history == null) {
                return 0;
            }
            final int i = history.size() - positionFromEnd;
            final int i = history.size() - n;
            return (i < 0) ? 0 : history.get(i);
        }

@@ -400,6 +404,11 @@ class AlarmManagerService extends SystemService {
                "standby_rare_quota",
                "standby_never_quota",
        };
        // Not putting this in the KEYS_APP_STANDBY_QUOTAS array because this uses a different
        // window size.
        private static final String KEY_APP_STANDBY_RESTRICTED_QUOTA = "standby_restricted_quota";
        private static final String KEY_APP_STANDBY_RESTRICTED_WINDOW =
                "app_standby_restricted_window";

        private static final long DEFAULT_MIN_FUTURITY = 5 * 1000;
        private static final long DEFAULT_MIN_INTERVAL = 60 * 1000;
@@ -420,6 +429,8 @@ class AlarmManagerService extends SystemService {
                1,      // Rare
                0       // Never
        };
        private static final int DEFAULT_APP_STANDBY_RESTRICTED_QUOTA = 1;
        private static final long DEFAULT_APP_STANDBY_RESTRICTED_WINDOW = MILLIS_IN_DAY;

        // Minimum futurity of a new alarm
        public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY;
@@ -446,6 +457,8 @@ class AlarmManagerService extends SystemService {

        public long APP_STANDBY_WINDOW = DEFAULT_APP_STANDBY_WINDOW;
        public int[] APP_STANDBY_QUOTAS = new int[DEFAULT_APP_STANDBY_QUOTAS.length];
        public int APP_STANDBY_RESTRICTED_QUOTA = DEFAULT_APP_STANDBY_RESTRICTED_QUOTA;
        public long APP_STANDBY_RESTRICTED_WINDOW = DEFAULT_APP_STANDBY_RESTRICTED_WINDOW;

        private ContentResolver mResolver;
        private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -520,6 +533,14 @@ class AlarmManagerService extends SystemService {
                            Math.min(APP_STANDBY_QUOTAS[i - 1], DEFAULT_APP_STANDBY_QUOTAS[i]));
                }

                APP_STANDBY_RESTRICTED_QUOTA = Math.max(1,
                        mParser.getInt(KEY_APP_STANDBY_RESTRICTED_QUOTA,
                                DEFAULT_APP_STANDBY_RESTRICTED_QUOTA));

                APP_STANDBY_RESTRICTED_WINDOW = Math.max(APP_STANDBY_WINDOW,
                        mParser.getLong(KEY_APP_STANDBY_RESTRICTED_WINDOW,
                                DEFAULT_APP_STANDBY_RESTRICTED_WINDOW));

                MAX_ALARMS_PER_UID = mParser.getInt(KEY_MAX_ALARMS_PER_UID,
                        DEFAULT_MAX_ALARMS_PER_UID);
                if (MAX_ALARMS_PER_UID < DEFAULT_MAX_ALARMS_PER_UID) {
@@ -581,6 +602,14 @@ class AlarmManagerService extends SystemService {
                pw.println(APP_STANDBY_QUOTAS[i]);
            }

            pw.print(KEY_APP_STANDBY_RESTRICTED_QUOTA); pw.print("=");
            TimeUtils.formatDuration(APP_STANDBY_RESTRICTED_QUOTA, pw);
            pw.println();

            pw.print(KEY_APP_STANDBY_RESTRICTED_WINDOW); pw.print("=");
            TimeUtils.formatDuration(APP_STANDBY_RESTRICTED_WINDOW, pw);
            pw.println();

            pw.decreaseIndent();
        }

@@ -1814,10 +1843,28 @@ class AlarmManagerService extends SystemService {
                sourcePackage, sourceUserId, mInjector.getElapsedRealtime());

        // Quota deferring implementation:
        boolean deferred = false;
        final int wakeupsInWindow = mAppWakeupHistory.getTotalWakeupsInWindow(sourcePackage,
                sourceUserId);
        if (standbyBucket == UsageStatsManager.STANDBY_BUCKET_RESTRICTED) {
            // Special case because it's 1/day instead of 1/hour.
            // AppWakeupHistory doesn't delete old wakeup times until a new one is logged, so we
            // should always have the last wakeup available.
            if (wakeupsInWindow > 0) {
                final long lastWakeupTime = mAppWakeupHistory.getNthLastWakeupForPackage(
                        sourcePackage, sourceUserId, mConstants.APP_STANDBY_RESTRICTED_QUOTA);
                if (mInjector.getElapsedRealtime() - lastWakeupTime
                        < mConstants.APP_STANDBY_RESTRICTED_WINDOW) {
                    final long minElapsed =
                            lastWakeupTime + mConstants.APP_STANDBY_RESTRICTED_WINDOW;
                    if (alarm.expectedWhenElapsed < minElapsed) {
                        alarm.whenElapsed = alarm.maxWhenElapsed = minElapsed;
                        deferred = true;
                    }
                }
            }
        } else {
            final int quotaForBucket = getQuotaForBucketLocked(standbyBucket);
        boolean deferred = false;
            if (wakeupsInWindow >= quotaForBucket) {
                final long minElapsed;
                if (quotaForBucket <= 0) {
@@ -1826,8 +1873,8 @@ class AlarmManagerService extends SystemService {
                } else {
                    // Suppose the quota for window was q, and the qth last delivery time for this
                    // package was t(q) then the next delivery must be after t(q) + <window_size>
                final long t = mAppWakeupHistory.getLastWakeupForPackage(sourcePackage,
                        sourceUserId, quotaForBucket);
                    final long t = mAppWakeupHistory.getNthLastWakeupForPackage(
                            sourcePackage, sourceUserId, quotaForBucket);
                    minElapsed = t + 1 + mConstants.APP_STANDBY_WINDOW;
                }
                if (alarm.expectedWhenElapsed < minElapsed) {
@@ -1835,6 +1882,7 @@ class AlarmManagerService extends SystemService {
                    deferred = true;
                }
            }
        }
        if (!deferred) {
            // Restore original requirements in case they were changed earlier.
            alarm.whenElapsed = alarm.expectedWhenElapsed;
+44 −4
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import static android.app.AlarmManager.RTC_WAKEUP;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;

import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
@@ -45,6 +46,7 @@ import static com.android.server.AlarmManagerService.Constants.KEY_MAX_INTERVAL;
import static com.android.server.AlarmManagerService.Constants.KEY_MIN_FUTURITY;
import static com.android.server.AlarmManagerService.Constants.KEY_MIN_INTERVAL;
import static com.android.server.AlarmManagerService.IS_WAKEUP_MASK;
import static com.android.server.AlarmManagerService.MILLIS_IN_DAY;
import static com.android.server.AlarmManagerService.TIME_CHANGED_MASK;
import static com.android.server.AlarmManagerService.WORKING_INDEX;

@@ -107,6 +109,7 @@ public class AlarmManagerServiceTest {
    private static final String TEST_CALLING_PACKAGE = "com.android.framework.test-package";
    private static final int SYSTEM_UI_UID = 123456789;
    private static final int TEST_CALLING_UID = 12345;
    private static final long RESTRICTED_WINDOW_MS = MILLIS_IN_DAY;

    private long mAppStandbyWindow;
    private AlarmManagerService mService;
@@ -485,13 +488,13 @@ public class AlarmManagerServiceTest {
                anyLong())).thenReturn(standbyBucket);
        final long firstTrigger = mNowElapsedTest + 10;
        for (int i = 0; i < quota; i++) {
            setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 10 + i,
            setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
                    getNewMockPendingIntent());
            mNowElapsedTest = mTestTimer.getElapsed();
            mTestTimer.expire();
        }
        // This one should get deferred on set
        setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + quota + 10,
        setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota,
                getNewMockPendingIntent());
        final long expectedNextTrigger = firstTrigger + 1 + mAppStandbyWindow;
        assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed());
@@ -503,11 +506,11 @@ public class AlarmManagerServiceTest {
                anyLong())).thenReturn(standbyBucket);
        final long firstTrigger = mNowElapsedTest + 10;
        for (int i = 0; i < quota; i++) {
            setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 10 + i,
            setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
                    getNewMockPendingIntent());
        }
        // This one should get deferred after the latest alarm expires
        setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + quota + 10,
        setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota,
                getNewMockPendingIntent());
        for (int i = 0; i < quota; i++) {
            mNowElapsedTest = mTestTimer.getElapsed();
@@ -596,6 +599,43 @@ public class AlarmManagerServiceTest {
        testQuotasNoDeferral(STANDBY_BUCKET_RARE);
    }

    @Test
    public void testRestrictedBucketAlarmsDeferredOnSet() throws Exception {
        when(mUsageStatsManagerInternal
                .getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(), anyLong()))
                .thenReturn(STANDBY_BUCKET_RESTRICTED);
        // This one should go off
        final long firstTrigger = mNowElapsedTest + 10;
        setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger, getNewMockPendingIntent());
        mNowElapsedTest = mTestTimer.getElapsed();
        mTestTimer.expire();

        // This one should get deferred on set
        setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + 1, getNewMockPendingIntent());
        final long expectedNextTrigger =
                firstTrigger + mService.mConstants.APP_STANDBY_RESTRICTED_WINDOW;
        assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed());
    }

    @Test
    public void testRestrictedBucketAlarmsDeferredOnExpiration() throws Exception {
        when(mUsageStatsManagerInternal
                .getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(), anyLong()))
                .thenReturn(STANDBY_BUCKET_RESTRICTED);
        // This one should go off
        final long firstTrigger = mNowElapsedTest + 10;
        setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger, getNewMockPendingIntent());

        // This one should get deferred after the latest alarm expires
        setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + 1, getNewMockPendingIntent());

        mNowElapsedTest = mTestTimer.getElapsed();
        mTestTimer.expire();
        final long expectedNextTrigger =
                firstTrigger + mService.mConstants.APP_STANDBY_RESTRICTED_WINDOW;
        assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed());
    }

    private void assertAndHandleBucketChanged(int bucket) {
        when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
                anyLong())).thenReturn(bucket);