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

Commit 4ed5b2bb authored by Suprabh Shukla's avatar Suprabh Shukla Committed by Android (Google) Code Review
Browse files

Merge "Adding a policy mechanism to control alarms"

parents 4bf1e3a5 bd8f2ea6
Loading
Loading
Loading
Loading
+164 −61
Original line number Diff line number Diff line
@@ -16,10 +16,13 @@

package com.android.server.alarm;

import static android.app.AlarmManager.ELAPSED_REALTIME;
import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
import static android.app.AlarmManager.RTC;
import static android.app.AlarmManager.RTC_WAKEUP;

import static com.android.server.alarm.AlarmManagerService.clampPositive;

import android.app.AlarmManager;
import android.app.IAlarmListener;
import android.app.PendingIntent;
@@ -32,8 +35,28 @@ import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * Class to describe an alarm that is used to the set the kernel timer that returns when the timer
 * expires. The timer will wake up the device if the alarm is a "wakeup" alarm.
 */
class Alarm {
    private static final int NUM_POLICIES = 2;
    /**
     * Index used to store the time the alarm was requested to expire. To be used with
     * {@link #setPolicyElapsed(int, long)}
     */
    public static final int REQUESTER_POLICY_INDEX = 0;
    /**
     * Index used to store the earliest time the alarm can expire based on app-standby policy.
     * To be used with {@link #setPolicyElapsed(int, long)}
     */
    public static final int APP_STANDBY_POLICY_INDEX = 1;

    public final int type;
    /**
     * The original trigger time supplied by the caller. This can be in the elapsed or rtc time base
     * depending on the type of this alarm
     */
    public final long origWhen;
    public final boolean wakeup;
    public final PendingIntent operation;
@@ -47,42 +70,40 @@ class Alarm {
    public final int creatorUid;
    public final String packageName;
    public final String sourcePackage;
    public final long windowLength;
    public final long repeatInterval;
    public int count;
    public long when;
    public long windowLength;
    public long whenElapsed;    // 'when' in the elapsed time base
    public long maxWhenElapsed; // also in the elapsed time base
    // Expected alarm expiry time before app standby deferring is applied.
    public long expectedWhenElapsed;
    public long expectedMaxWhenElapsed;
    public long repeatInterval;
    /** The earliest time this alarm is eligible to fire according to each policy */
    private long[] mPolicyWhenElapsed;
    /** The ultimate delivery time to be used for this alarm */
    private long mWhenElapsed;
    private long mMaxWhenElapsed;
    public AlarmManagerService.PriorityClass priorityClass;

    Alarm(int _type, long _when, long _whenElapsed, long _windowLength, long _maxWhen,
            long _interval, PendingIntent _op, IAlarmListener _rec, String _listenerTag,
            WorkSource _ws, int _flags, AlarmManager.AlarmClockInfo _info,
            int _uid, String _pkgName) {
        type = _type;
        origWhen = _when;
        wakeup = _type == AlarmManager.ELAPSED_REALTIME_WAKEUP
                || _type == AlarmManager.RTC_WAKEUP;
        when = _when;
        whenElapsed = _whenElapsed;
        expectedWhenElapsed = _whenElapsed;
        windowLength = _windowLength;
        maxWhenElapsed = expectedMaxWhenElapsed = AlarmManagerService.clampPositive(_maxWhen);
        repeatInterval = _interval;
        operation = _op;
        listener = _rec;
        listenerTag = _listenerTag;
        statsTag = makeTag(_op, _listenerTag, _type);
        workSource = _ws;
        flags = _flags;
        alarmClock = _info;
        uid = _uid;
        packageName = _pkgName;
    Alarm(int type, long when, long requestedWhenElapsed, long windowLength, long interval,
            PendingIntent op, IAlarmListener rec, String listenerTag, WorkSource ws, int flags,
            AlarmManager.AlarmClockInfo info, int uid, String pkgName) {
        this.type = type;
        origWhen = when;
        wakeup = type == AlarmManager.ELAPSED_REALTIME_WAKEUP
                || type == AlarmManager.RTC_WAKEUP;
        mPolicyWhenElapsed = new long[NUM_POLICIES];
        mPolicyWhenElapsed[REQUESTER_POLICY_INDEX] = requestedWhenElapsed;
        mWhenElapsed = requestedWhenElapsed;
        this.windowLength = windowLength;
        mMaxWhenElapsed = clampPositive(requestedWhenElapsed + windowLength);
        repeatInterval = interval;
        operation = op;
        listener = rec;
        this.listenerTag = listenerTag;
        statsTag = makeTag(op, listenerTag, type);
        workSource = ws;
        this.flags = flags;
        alarmClock = info;
        this.uid = uid;
        packageName = pkgName;
        sourcePackage = (operation != null) ? operation.getCreatorPackage() : packageName;
        creatorUid = (operation != null) ? operation.getCreatorUid() : uid;
        creatorUid = (operation != null) ? operation.getCreatorUid() : this.uid;
    }

    public static String makeTag(PendingIntent pi, String tag, int type) {
@@ -91,13 +112,6 @@ class Alarm {
        return (pi != null) ? pi.getTag(alarmString) : (alarmString + tag);
    }

    public AlarmManagerService.WakeupEvent makeWakeupEvent(long nowRTC) {
        return new AlarmManagerService.WakeupEvent(nowRTC, creatorUid,
                (operation != null)
                    ? operation.getIntent().getAction()
                    : ("<listener>:" + listenerTag));
    }

    // Returns true if either matches
    public boolean matches(PendingIntent pi, IAlarmListener rec) {
        return (operation != null)
@@ -109,6 +123,65 @@ class Alarm {
        return packageName.equals(sourcePackage);
    }

    /**
     * Get the earliest time this alarm is allowed to expire based on the given policy.
     *
     * @param policyIndex The index of the policy. One of [{@link #REQUESTER_POLICY_INDEX},
     *                    {@link #APP_STANDBY_POLICY_INDEX}].
     */
    public long getPolicyElapsed(int policyIndex) {
        return mPolicyWhenElapsed[policyIndex];
    }

    /**
     * Get the earliest time that this alarm should be delivered to the requesting app.
     */
    public long getWhenElapsed() {
        return mWhenElapsed;
    }

    /**
     * Get the latest time that this alarm should be delivered to the requesting app. Will be equal
     * to {@link #getWhenElapsed()} in case this is an exact alarm.
     */
    public long getMaxWhenElapsed() {
        return mMaxWhenElapsed;
    }

    /**
     * Set the earliest time this alarm can expire based on the passed policy index.
     *
     * @return {@code true} if this change resulted in a change in the ultimate delivery time (or
     * time window in the case of inexact alarms) of this alarm.
     * @see #getWhenElapsed()
     * @see #getMaxWhenElapsed()
     * @see #getPolicyElapsed(int)
     */
    public boolean setPolicyElapsed(int policyIndex, long policyElapsed) {
        mPolicyWhenElapsed[policyIndex] = policyElapsed;
        return updateWhenElapsed();
    }

    /**
     * @return {@code true} if either {@link #mWhenElapsed} or {@link #mMaxWhenElapsed} changes
     * due to this call.
     */
    private boolean updateWhenElapsed() {
        final long oldWhenElapsed = mWhenElapsed;
        mWhenElapsed = 0;
        for (int i = 0; i < NUM_POLICIES; i++) {
            mWhenElapsed = Math.max(mWhenElapsed, mPolicyWhenElapsed[i]);
        }

        final long oldMaxWhenElapsed = mMaxWhenElapsed;
        // windowLength should always be >= 0 here.
        final long maxRequestedElapsed = clampPositive(
                mPolicyWhenElapsed[REQUESTER_POLICY_INDEX] + windowLength);
        mMaxWhenElapsed = Math.max(maxRequestedElapsed, mWhenElapsed);

        return (oldWhenElapsed != mWhenElapsed) || (oldMaxWhenElapsed != mMaxWhenElapsed);
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder(128);
@@ -116,11 +189,11 @@ class Alarm {
        sb.append(Integer.toHexString(System.identityHashCode(this)));
        sb.append(" type ");
        sb.append(type);
        sb.append(" when ");
        sb.append(when);
        sb.append(" origWhen ");
        sb.append(origWhen);
        sb.append(" ");
        sb.append(" whenElapsed ");
        sb.append(whenElapsed);
        sb.append(getWhenElapsed());
        sb.append(" ");
        sb.append(sourcePackage);
        sb.append('}');
@@ -136,29 +209,45 @@ class Alarm {
        dump(ipw, nowELAPSED, sdf);
    }

    private static String policyIndexToString(int index) {
        switch (index) {
            case REQUESTER_POLICY_INDEX:
                return "requester";
            case APP_STANDBY_POLICY_INDEX:
                return "app_standby";
            default:
                return "unknown";
        }
    }

    public static String typeToString(int type) {
        switch (type) {
            case RTC:
                return "RTC";
            case RTC_WAKEUP:
                return "RTC_WAKEUP";
            case ELAPSED_REALTIME:
                return "ELAPSED";
            case ELAPSED_REALTIME_WAKEUP:
                return "ELAPSED_WAKEUP";
            default:
                return "--unknown--";
        }
    }

    public void dump(IndentingPrintWriter ipw, long nowELAPSED, SimpleDateFormat sdf) {
        final boolean isRtc = (type == RTC || type == RTC_WAKEUP);
        ipw.print("tag=");
        ipw.println(statsTag);

        ipw.print("type=");
        ipw.print(type);
        ipw.print(" expectedWhenElapsed=");
        TimeUtils.formatDuration(expectedWhenElapsed, nowELAPSED, ipw);
        ipw.print(" expectedMaxWhenElapsed=");
        TimeUtils.formatDuration(expectedMaxWhenElapsed, nowELAPSED, ipw);
        ipw.print(" whenElapsed=");
        TimeUtils.formatDuration(whenElapsed, nowELAPSED, ipw);
        ipw.print(" maxWhenElapsed=");
        TimeUtils.formatDuration(maxWhenElapsed, nowELAPSED, ipw);
        ipw.print(" when=");
        ipw.print(typeToString(type));
        ipw.print(" origWhen=");
        if (isRtc) {
            ipw.print(sdf.format(new Date(when)));
            ipw.print(sdf.format(new Date(origWhen)));
        } else {
            TimeUtils.formatDuration(when, nowELAPSED, ipw);
            TimeUtils.formatDuration(origWhen, nowELAPSED, ipw);
        }
        ipw.println();

        ipw.print(" window=");
        TimeUtils.formatDuration(windowLength, ipw);
        ipw.print(" repeatInterval=");
@@ -168,6 +257,19 @@ class Alarm {
        ipw.print(" flags=0x");
        ipw.println(Integer.toHexString(flags));

        ipw.print("policyWhenElapsed:");
        for (int i = 0; i < NUM_POLICIES; i++) {
            ipw.print(" " + policyIndexToString(i) + "=");
            TimeUtils.formatDuration(mPolicyWhenElapsed[i], nowELAPSED, ipw);
        }
        ipw.println();

        ipw.print("whenElapsed=");
        TimeUtils.formatDuration(getWhenElapsed(), nowELAPSED, ipw);
        ipw.print(" maxWhenElapsed=");
        TimeUtils.formatDuration(mMaxWhenElapsed, nowELAPSED, ipw);
        ipw.println();

        if (alarmClock != null) {
            ipw.println("Alarm clock:");

@@ -177,9 +279,10 @@ class Alarm {
            ipw.print("  showIntent=");
            ipw.println(alarmClock.getShowIntent());
        }
        if (operation != null) {
            ipw.print("operation=");
            ipw.println(operation);

        }
        if (listener != null) {
            ipw.print("listener=");
            ipw.println(listener.asBinder());
@@ -191,7 +294,7 @@ class Alarm {

        proto.write(AlarmProto.TAG, statsTag);
        proto.write(AlarmProto.TYPE, type);
        proto.write(AlarmProto.TIME_UNTIL_WHEN_ELAPSED_MS, whenElapsed - nowElapsed);
        proto.write(AlarmProto.TIME_UNTIL_WHEN_ELAPSED_MS, getWhenElapsed() - nowElapsed);
        proto.write(AlarmProto.WINDOW_LENGTH_MS, windowLength);
        proto.write(AlarmProto.REPEAT_INTERVAL_MS, repeatInterval);
        proto.write(AlarmProto.COUNT, count);
+119 −159

File changed.

Preview size limit exceeded, changes collapsed.

+14 −3
Original line number Diff line number Diff line
@@ -47,6 +47,15 @@ public interface AlarmStore {
     */
    ArrayList<Alarm> remove(Predicate<Alarm> whichAlarms);

    /**
     * Gets the earliest alarm with the flag {@link android.app.AlarmManager#FLAG_WAKE_FROM_IDLE}
     * based on {@link Alarm#getWhenElapsed()}.
     *
     * @return An alarm object matching the description above or {@code null} if no such alarm was
     *         found.
     */
    Alarm getNextWakeFromIdleAlarm();

    /**
     * Returns the total number of alarms in this store.
     */
@@ -82,7 +91,7 @@ public interface AlarmStore {
     *
     * @return {@code true} if any of the alarm deliveries changed due to this call.
     */
    boolean recalculateAlarmDeliveries(AlarmDeliveryCalculator deliveryCalculator);
    boolean updateAlarmDeliveries(AlarmDeliveryCalculator deliveryCalculator);

    /**
     * Returns all the alarms in the form of a list.
@@ -97,6 +106,7 @@ public interface AlarmStore {
     * Primary useful for debugging. Can be called from the
     * {@link android.os.Binder#dump(FileDescriptor PrintWriter, String[]) dump} method of the
     * caller.
     *
     * @param ipw        The {@link IndentingPrintWriter} to write to.
     * @param nowElapsed the time when the dump is requested in the
     *                   {@link SystemClock#elapsedRealtime()
@@ -112,7 +122,7 @@ public interface AlarmStore {

    /**
     * A functional interface used to update the alarm. Used to describe the update in
     * {@link #recalculateAlarmDeliveries(AlarmDeliveryCalculator)}
     * {@link #updateAlarmDeliveries(AlarmDeliveryCalculator)}
     */
    @FunctionalInterface
    interface AlarmDeliveryCalculator {
@@ -125,3 +135,4 @@ public interface AlarmStore {
        boolean updateAlarmDelivery(Alarm a);
    }
}
+31 −14
Original line number Diff line number Diff line
@@ -66,8 +66,8 @@ public class BatchingAlarmStore implements AlarmStore {
    };

    private static final Comparator<Alarm> sIncreasingTimeOrder = (a1, a2) -> {
        long when1 = a1.whenElapsed;
        long when2 = a2.whenElapsed;
        long when1 = a1.getWhenElapsed();
        long when2 = a2.getWhenElapsed();
        if (when1 > when2) {
            return 1;
        }
@@ -99,11 +99,28 @@ public class BatchingAlarmStore implements AlarmStore {
        }
        if (!removed.isEmpty()) {
            mSize -= removed.size();
            // Not needed if only whole batches were removed, but keeping existing behavior.
            rebatchAllAlarms();
        }
        return removed;
    }

    @Override
    public Alarm getNextWakeFromIdleAlarm() {
        for (final Batch batch : mAlarmBatches) {
            if ((batch.mFlags & AlarmManager.FLAG_WAKE_FROM_IDLE) == 0) {
                continue;
            }
            for (int i = 0; i < batch.size(); i++) {
                final Alarm a = batch.get(i);
                if ((a.flags & AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) {
                    return a;
                }
            }
        }
        return null;
    }

    private void rebatchAllAlarms() {
        final long start = mStatLogger.getTime();
        final ArrayList<Batch> oldBatches = (ArrayList<Batch>) mAlarmBatches.clone();
@@ -157,7 +174,7 @@ public class BatchingAlarmStore implements AlarmStore {
    }

    @Override
    public boolean recalculateAlarmDeliveries(AlarmDeliveryCalculator deliveryCalculator) {
    public boolean updateAlarmDeliveries(AlarmDeliveryCalculator deliveryCalculator) {
        boolean changed = false;
        for (final Batch b : mAlarmBatches) {
            for (int i = 0; i < b.size(); i++) {
@@ -204,7 +221,7 @@ public class BatchingAlarmStore implements AlarmStore {

    private void insertAndBatchAlarm(Alarm alarm) {
        final int whichBatch = ((alarm.flags & AlarmManager.FLAG_STANDALONE) != 0) ? -1
                : attemptCoalesce(alarm.whenElapsed, alarm.maxWhenElapsed);
                : attemptCoalesce(alarm.getWhenElapsed(), alarm.getMaxWhenElapsed());

        if (whichBatch < 0) {
            addBatch(mAlarmBatches, new Batch(alarm));
@@ -247,8 +264,8 @@ public class BatchingAlarmStore implements AlarmStore {
        final ArrayList<Alarm> mAlarms = new ArrayList<>();

        Batch(Alarm seed) {
            mStart = seed.whenElapsed;
            mEnd = clampPositive(seed.maxWhenElapsed);
            mStart = seed.getWhenElapsed();
            mEnd = clampPositive(seed.getMaxWhenElapsed());
            mFlags = seed.flags;
            mAlarms.add(seed);
        }
@@ -276,12 +293,12 @@ public class BatchingAlarmStore implements AlarmStore {
            if (DEBUG_BATCH) {
                Slog.v(TAG, "Adding " + alarm + " to " + this);
            }
            if (alarm.whenElapsed > mStart) {
                mStart = alarm.whenElapsed;
            if (alarm.getWhenElapsed() > mStart) {
                mStart = alarm.getWhenElapsed();
                newStart = true;
            }
            if (alarm.maxWhenElapsed < mEnd) {
                mEnd = alarm.maxWhenElapsed;
            if (alarm.getMaxWhenElapsed() < mEnd) {
                mEnd = alarm.getMaxWhenElapsed();
            }
            mFlags |= alarm.flags;

@@ -309,11 +326,11 @@ public class BatchingAlarmStore implements AlarmStore {
                        Slog.wtf(TAG, "Removed TIME_TICK alarm");
                    }
                } else {
                    if (alarm.whenElapsed > newStart) {
                        newStart = alarm.whenElapsed;
                    if (alarm.getWhenElapsed() > newStart) {
                        newStart = alarm.getWhenElapsed();
                    }
                    if (alarm.maxWhenElapsed < newEnd) {
                        newEnd = alarm.maxWhenElapsed;
                    if (alarm.getMaxWhenElapsed() < newEnd) {
                        newEnd = alarm.getMaxWhenElapsed();
                    }
                    newFlags |= alarm.flags;
                    i++;
+143 −9
Original line number Diff line number Diff line
@@ -48,10 +48,13 @@ import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_INT
import static com.android.server.alarm.AlarmManagerService.IS_WAKEUP_MASK;
import static com.android.server.alarm.AlarmManagerService.TIME_CHANGED_MASK;
import static com.android.server.alarm.AlarmManagerService.WORKING_INDEX;
import static com.android.server.alarm.Constants.TEST_CALLING_PACKAGE;
import static com.android.server.alarm.Constants.TEST_CALLING_UID;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
@@ -82,6 +85,7 @@ import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.SparseArray;

@@ -108,6 +112,8 @@ import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
import org.mockito.stubbing.Answer;

import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.concurrent.Executor;
@@ -116,9 +122,7 @@ import java.util.concurrent.Executor;
@RunWith(AndroidJUnit4.class)
public class AlarmManagerServiceTest {
    private static final String TAG = AlarmManagerServiceTest.class.getSimpleName();
    private static final String TEST_CALLING_PACKAGE = "com.android.framework.test-package";
    private static final int SYSTEM_UI_UID = 12345;
    private static final int TEST_CALLING_UID = 67890;
    private static final int TEST_CALLING_USER = UserHandle.getUserId(TEST_CALLING_UID);

    private long mAppStandbyWindow;
@@ -350,19 +354,31 @@ public class AlarmManagerServiceTest {
    }

    private void setTestAlarm(int type, long triggerTime, PendingIntent operation) {
        setTestAlarm(type, triggerTime, operation, 0, TEST_CALLING_UID);
        setTestAlarm(type, triggerTime, operation, 0, AlarmManager.FLAG_STANDALONE,
                TEST_CALLING_UID);
    }

    private void setRepeatingTestAlarm(int type, long firstTrigger, long interval,
            PendingIntent pi) {
        setTestAlarm(type, firstTrigger, pi, interval, TEST_CALLING_UID);
        setTestAlarm(type, firstTrigger, pi, interval, AlarmManager.FLAG_STANDALONE,
                TEST_CALLING_UID);
    }

    private void setIdleUntilAlarm(int type, long triggerTime, PendingIntent pi) {
        setTestAlarm(type, triggerTime, pi, 0, AlarmManager.FLAG_IDLE_UNTIL, TEST_CALLING_UID);
    }

    private void setWakeFromIdle(int type, long triggerTime, PendingIntent pi) {
        // Note: Only alarm clock alarms are allowed to include this flag in the actual service.
        // But this is a unit test so we'll only test the flag for granularity and convenience.
        setTestAlarm(type, triggerTime, pi, 0,
                AlarmManager.FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_STANDALONE, TEST_CALLING_UID);
    }

    private void setTestAlarm(int type, long triggerTime, PendingIntent operation, long interval,
            int callingUid) {
        mService.setImpl(type, triggerTime, AlarmManager.WINDOW_EXACT, interval,
                operation, null, "test", AlarmManager.FLAG_STANDALONE, null, null,
                callingUid, TEST_CALLING_PACKAGE);
            int flags, int callingUid) {
        mService.setImpl(type, triggerTime, AlarmManager.WINDOW_EXACT, interval, operation, null,
                "test", flags, null, null, callingUid, TEST_CALLING_PACKAGE);
    }

    private void setTestAlarmWithListener(int type, long triggerTime, IAlarmListener listener) {
@@ -1002,7 +1018,7 @@ public class AlarmManagerServiceTest {
        for (int i = 0; i < numAlarms; i++) {
            int mockUid = UserHandle.getUid(mockUserId, 1234 + i);
            setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + i + 10,
                    getNewMockPendingIntent(mockUid), 0, mockUid);
                    getNewMockPendingIntent(mockUid), 0, AlarmManager.FLAG_STANDALONE, mockUid);
        }
        assertEquals(numAlarms, mService.mAlarmsPerUid.size());
        mService.removeUserLocked(mockUserId);
@@ -1142,6 +1158,116 @@ public class AlarmManagerServiceTest {
        }
    }

    @Test
    public void singleIdleUntil() {
        doReturn(0).when(mService).fuzzForDuration(anyLong());

        final PendingIntent idleUntilPi6 = getNewMockPendingIntent();
        setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 6, idleUntilPi6);

        assertTrue(mService.mPendingIdleUntil.matches(idleUntilPi6, null));
        assertEquals(mNowElapsedTest + 6, mTestTimer.getElapsed());
        assertEquals(mNowElapsedTest + 6, mService.mPendingIdleUntil.getWhenElapsed());

        final PendingIntent idleUntilPi2 = getNewMockPendingIntent();
        setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 2, idleUntilPi2);

        // The same mPendingIdleUntil should get updated, even with a different PendingIntent.
        assertTrue(mService.mPendingIdleUntil.matches(idleUntilPi2, null));
        assertEquals(mNowElapsedTest + 2, mTestTimer.getElapsed());
        assertEquals(1, mService.mAlarmStore.size());

        final PendingIntent idleUntilPi10 = getNewMockPendingIntent();
        setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 10, idleUntilPi10);

        // The same thing should happen even when the new alarm is in farther in the future.
        assertTrue(mService.mPendingIdleUntil.matches(idleUntilPi10, null));
        assertEquals(mNowElapsedTest + 10, mTestTimer.getElapsed());
        assertEquals(1, mService.mAlarmStore.size());
    }

    @Test
    public void nextWakeFromIdle() throws Exception {
        assertNull(mService.mNextWakeFromIdle);

        final PendingIntent wakeFromIdle6 = getNewMockPendingIntent();
        final long trigger6 = mNowElapsedTest + 6;
        setWakeFromIdle(ELAPSED_REALTIME_WAKEUP, trigger6, wakeFromIdle6);

        assertTrue(mService.mNextWakeFromIdle.matches(wakeFromIdle6, null));
        assertEquals(trigger6, mService.mNextWakeFromIdle.getWhenElapsed());
        assertEquals(trigger6, mTestTimer.getElapsed());

        final PendingIntent wakeFromIdle10 = getNewMockPendingIntent();
        final long trigger10 = mNowElapsedTest + 10;
        setWakeFromIdle(ELAPSED_REALTIME_WAKEUP, trigger10, wakeFromIdle10);

        // mNextWakeFromIdle should not get updated.
        assertTrue(mService.mNextWakeFromIdle.matches(wakeFromIdle6, null));
        assertEquals(trigger6, mTestTimer.getElapsed());
        assertEquals(trigger6, mService.mNextWakeFromIdle.getWhenElapsed());

        final PendingIntent wakeFromIdle3 = getNewMockPendingIntent();
        final long trigger3 = mNowElapsedTest + 3;
        setWakeFromIdle(ELAPSED_REALTIME_WAKEUP, trigger3, wakeFromIdle3);

        // mNextWakeFromIdle should always reflect the next earliest wake_from_idle alarm.
        assertTrue(mService.mNextWakeFromIdle.matches(wakeFromIdle3, null));
        assertEquals(trigger3, mTestTimer.getElapsed());
        assertEquals(trigger3, mService.mNextWakeFromIdle.getWhenElapsed());

        mNowElapsedTest = trigger3;
        mTestTimer.expire();

        assertTrue(mService.mNextWakeFromIdle.matches(wakeFromIdle6, null));
        assertEquals(trigger6, mTestTimer.getElapsed());
        assertEquals(trigger6, mService.mNextWakeFromIdle.getWhenElapsed());

        mService.removeLocked(wakeFromIdle6, null);

        assertTrue(mService.mNextWakeFromIdle.matches(wakeFromIdle10, null));
        assertEquals(trigger10, mTestTimer.getElapsed());
        assertEquals(trigger10, mService.mNextWakeFromIdle.getWhenElapsed());

        mService.removeLocked(wakeFromIdle10, null);
        assertNull(mService.mNextWakeFromIdle);
    }

    @Test
    public void idleUntilBeforeWakeFromIdle() {
        doReturn(0).when(mService).fuzzForDuration(anyLong());

        final PendingIntent idleUntilPi = getNewMockPendingIntent();
        final long requestedIdleUntil = mNowElapsedTest + 10;
        setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, requestedIdleUntil, idleUntilPi);

        assertEquals(requestedIdleUntil, mService.mPendingIdleUntil.getWhenElapsed());

        final PendingIntent wakeFromIdle5 = getNewMockPendingIntent();
        setWakeFromIdle(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 5, wakeFromIdle5);
        assertEquals(mNowElapsedTest + 5, mService.mPendingIdleUntil.getWhenElapsed());

        final PendingIntent wakeFromIdle8 = getNewMockPendingIntent();
        setWakeFromIdle(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 8, wakeFromIdle8);
        assertEquals(mNowElapsedTest + 5, mService.mPendingIdleUntil.getWhenElapsed());

        final PendingIntent wakeFromIdle12 = getNewMockPendingIntent();
        setWakeFromIdle(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 12, wakeFromIdle12);
        assertEquals(mNowElapsedTest + 5, mService.mPendingIdleUntil.getWhenElapsed());

        mService.removeLocked(wakeFromIdle5, null);
        assertEquals(mNowElapsedTest + 8, mService.mPendingIdleUntil.getWhenElapsed());

        mService.removeLocked(wakeFromIdle8, null);
        assertEquals(requestedIdleUntil, mService.mPendingIdleUntil.getWhenElapsed());

        mService.removeLocked(idleUntilPi, null);
        assertNull(mService.mPendingIdleUntil);

        setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 15, idleUntilPi);
        assertEquals(mNowElapsedTest + 12, mService.mPendingIdleUntil.getWhenElapsed());
    }

    @After
    public void tearDown() {
        if (mMockingSession != null) {
@@ -1149,4 +1275,12 @@ public class AlarmManagerServiceTest {
        }
        LocalServices.removeServiceForTest(AlarmManagerInternal.class);
    }

    private void dumpAllAlarms(String tag, ArrayList<Alarm> alarms) {
        System.out.println(tag + ": ");
        IndentingPrintWriter ipw = new IndentingPrintWriter(new PrintWriter(System.out));
        AlarmManagerService.dumpAlarmList(ipw, alarms, mNowElapsedTest,
                new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"));
        ipw.close();
    }
}
Loading