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

Commit 73c3d5a2 authored by Suprabh Shukla's avatar Suprabh Shukla
Browse files

Add an API for a prioritized alarm

Adding an API that system apps can use to get alarms while the device is
in doze. These alarms will be prioritized over other alarms in the sense
that the device may wake up to send these alarms and ignore other
pending due alarms at that time.

Test: atest CtsAlarmManagerTestCases
atest FrameworksMockingServicesTests:com.android.server.alarm

CTS-Coverage-Bug: 183661625

Bug: 182843915
Change-Id: Iccae4f532bf145f4e0b130241ca15ff0ee0f106a
parent eb8c8105
Loading
Loading
Loading
Loading
+83 −16
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.app;

import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
@@ -29,6 +30,7 @@ import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
@@ -42,7 +44,9 @@ import com.android.i18n.timezone.ZoneInfoDb;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.Objects;
import java.util.WeakHashMap;
import java.util.concurrent.Executor;

/**
 * This class provides access to the system alarm services.  These allow you
@@ -193,6 +197,15 @@ public class AlarmManager {
     */
    public static final int FLAG_ALLOW_WHILE_IDLE_COMPAT = 1 << 5;

    /**
     * Flag for alarms: Used to mark prioritized alarms. These alarms will get to execute while idle
     * and can be sent separately from other alarms that may be already due at the time.
     * These alarms can be set via
     * {@link #setPrioritized(int, long, long, String, Executor, OnAlarmListener)}
     * @hide
     */
    public static final int FLAG_PRIORITIZE = 1 << 6;

    /**
     * For apps targeting {@link Build.VERSION_CODES#S} or above, APIs
     * {@link #setExactAndAllowWhileIdle(int, long, PendingIntent)} and
@@ -227,15 +240,15 @@ public class AlarmManager {

    final class ListenerWrapper extends IAlarmListener.Stub implements Runnable {
        final OnAlarmListener mListener;
        Handler mHandler;
        Executor mExecutor;
        IAlarmCompleteListener mCompletion;

        public ListenerWrapper(OnAlarmListener listener) {
            mListener = listener;
        }

        public void setHandler(Handler h) {
           mHandler = h;
        void setExecutor(Executor e) {
            mExecutor = e;
        }

        public void cancel() {
@@ -250,7 +263,7 @@ public class AlarmManager {
        public void doAlarm(IAlarmCompleteListener alarmManager) {
            mCompletion = alarmManager;

            mHandler.post(this);
            mExecutor.execute(this);
        }

        @Override
@@ -368,7 +381,7 @@ public class AlarmManager {
     */
    public void set(@AlarmType int type, long triggerAtMillis, PendingIntent operation) {
        setImpl(type, triggerAtMillis, legacyExactLength(), 0, 0, operation, null, null,
                null, null, null);
                (Handler) null, null, null);
    }

    /**
@@ -457,7 +470,7 @@ public class AlarmManager {
    public void setRepeating(@AlarmType int type, long triggerAtMillis,
            long intervalMillis, PendingIntent operation) {
        setImpl(type, triggerAtMillis, legacyExactLength(), intervalMillis, 0, operation,
                null, null, null, null, null);
                null, null, (Handler) null, null, null);
    }

    /**
@@ -507,7 +520,7 @@ public class AlarmManager {
    public void setWindow(@AlarmType int type, long windowStartMillis, long windowLengthMillis,
            PendingIntent operation) {
        setImpl(type, windowStartMillis, windowLengthMillis, 0, 0, operation,
                null, null, null, null, null);
                null, null, (Handler) null, null, null);
    }

    /**
@@ -525,6 +538,53 @@ public class AlarmManager {
                targetHandler, null, null);
    }

    /**
     * Schedule an alarm that is prioritized by the system while the device is in power saving modes
     * such as battery saver and device idle (doze).
     *
     * <p>
     * Apps that use this are not guaranteed to get all alarms as requested during power saving
     * modes, i.e. the system may still impose restrictions on how frequently these alarms will go
     * off for a particular application, like requiring a certain minimum duration be elapsed
     * between consecutive alarms. This duration will be normally be in the order of a few minutes.
     *
     * <p>
     * When the system wakes up to deliver these alarms, it may not deliver any of the other pending
     * alarms set earlier by the calling app, even the special ones set via
     * {@link #setAndAllowWhileIdle(int, long, PendingIntent)} or
     * {@link #setExactAndAllowWhileIdle(int, long, PendingIntent)}. So the caller should not
     * expect these to arrive in any relative order to its other alarms.
     *
     * @param type type of alarm
     * @param windowStartMillis The earliest time, in milliseconds, that the alarm should
     *        be delivered, expressed in the appropriate clock's units (depending on the alarm
     *        type).
     * @param windowLengthMillis The length of the requested delivery window,
     *        in milliseconds.  The alarm will be delivered no later than this many
     *        milliseconds after {@code windowStartMillis}.  Note that this parameter
     *        is a <i>duration,</i> not the timestamp of the end of the window.
     * @param tag string describing the alarm, used for logging and battery-use
     *         attribution
     * @param listener {@link OnAlarmListener} instance whose
     *         {@link OnAlarmListener#onAlarm() onAlarm()} method will be
     *         called when the alarm time is reached.  A given OnAlarmListener instance can
     *         only be the target of a single pending alarm, just as a given PendingIntent
     *         can only be used with one alarm at a time.
     * @param executor {@link Executor} on which to execute the listener's onAlarm()
     *         callback.
     * @hide
     */
    @SystemApi
    @RequiresPermission(Manifest.permission.SCHEDULE_PRIORITIZED_ALARM)
    public void setPrioritized(@AlarmType int type, long windowStartMillis, long windowLengthMillis,
            @NonNull String tag, @NonNull Executor executor, @NonNull OnAlarmListener listener) {
        Objects.requireNonNull(executor);
        Objects.requireNonNull(tag);
        Objects.requireNonNull(listener);
        setImpl(type, windowStartMillis, windowLengthMillis, 0, FLAG_PRIORITIZE, null, listener,
                tag, executor, null, null);
    }

    /**
     * Schedule an alarm to be delivered precisely at the stated time.
     *
@@ -565,7 +625,7 @@ public class AlarmManager {
     */
    @RequiresPermission(value = Manifest.permission.SCHEDULE_EXACT_ALARM, conditional = true)
    public void setExact(@AlarmType int type, long triggerAtMillis, PendingIntent operation) {
        setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, 0, operation, null, null, null,
        setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, 0, operation, null, null, (Handler) null,
                null, null);
    }

@@ -645,7 +705,7 @@ public class AlarmManager {
    @RequiresPermission(Manifest.permission.SCHEDULE_EXACT_ALARM)
    public void setAlarmClock(AlarmClockInfo info, PendingIntent operation) {
        setImpl(RTC_WAKEUP, info.getTriggerTime(), WINDOW_EXACT, 0, 0, operation,
                null, null, null, null, info);
                null, null, (Handler) null, null, info);
    }

    /** @hide */
@@ -654,7 +714,7 @@ public class AlarmManager {
    public void set(@AlarmType int type, long triggerAtMillis, long windowMillis,
            long intervalMillis, PendingIntent operation, WorkSource workSource) {
        setImpl(type, triggerAtMillis, windowMillis, intervalMillis, 0, operation, null, null,
                null, workSource, null);
                (Handler) null, workSource, null);
    }

    /**
@@ -698,6 +758,15 @@ public class AlarmManager {
            long intervalMillis, int flags, PendingIntent operation, final OnAlarmListener listener,
            String listenerTag, Handler targetHandler, WorkSource workSource,
            AlarmClockInfo alarmClock) {
        final Handler handlerToUse = (targetHandler != null) ? targetHandler : mMainThreadHandler;
        setImpl(type, triggerAtMillis, windowMillis, intervalMillis, flags, operation, listener,
                listenerTag, new HandlerExecutor(handlerToUse), workSource, alarmClock);
    }

    private void setImpl(@AlarmType int type, long triggerAtMillis, long windowMillis,
            long intervalMillis, int flags, PendingIntent operation, final OnAlarmListener listener,
            String listenerTag, Executor targetExecutor, WorkSource workSource,
            AlarmClockInfo alarmClock) {
        if (triggerAtMillis < 0) {
            /* NOTYET
            if (mAlwaysExact) {
@@ -726,9 +795,7 @@ public class AlarmManager {
                    sWrappers.put(listener, new WeakReference<>(recipientWrapper));
                }
            }

            final Handler handler = (targetHandler != null) ? targetHandler : mMainThreadHandler;
            recipientWrapper.setHandler(handler);
            recipientWrapper.setExecutor(targetExecutor);
        }

        try {
@@ -834,7 +901,7 @@ public class AlarmManager {
    public void setInexactRepeating(@AlarmType int type, long triggerAtMillis,
            long intervalMillis, PendingIntent operation) {
        setImpl(type, triggerAtMillis, WINDOW_HEURISTIC, intervalMillis, 0, operation, null,
                null, null, null, null);
                null, (Handler) null, null, null);
    }

    /**
@@ -884,7 +951,7 @@ public class AlarmManager {
    public void setAndAllowWhileIdle(@AlarmType int type, long triggerAtMillis,
            PendingIntent operation) {
        setImpl(type, triggerAtMillis, WINDOW_HEURISTIC, 0, FLAG_ALLOW_WHILE_IDLE,
                operation, null, null, null, null, null);
                operation, null, null, (Handler) null, null, null);
    }

    /**
@@ -945,7 +1012,7 @@ public class AlarmManager {
    public void setExactAndAllowWhileIdle(@AlarmType int type, long triggerAtMillis,
            PendingIntent operation) {
        setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, FLAG_ALLOW_WHILE_IDLE, operation,
                null, null, null, null, null);
                null, null, (Handler) null, null, null);
    }

    /**
+77 −6
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE;
import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_COMPAT;
import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
import static android.app.AlarmManager.FLAG_IDLE_UNTIL;
import static android.app.AlarmManager.FLAG_PRIORITIZE;
import static android.app.AlarmManager.FLAG_WAKE_FROM_IDLE;
import static android.app.AlarmManager.INTERVAL_DAY;
import static android.app.AlarmManager.INTERVAL_HOUR;
@@ -100,6 +101,7 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;

@@ -221,6 +223,7 @@ public class AlarmManagerService extends SystemService {
    AlarmHandler mHandler;
    AppWakeupHistory mAppWakeupHistory;
    AppWakeupHistory mAllowWhileIdleHistory;
    private final SparseLongArray mLastPriorityAlarmDispatch = new SparseLongArray();
    ClockReceiver mClockReceiver;
    final DeliveryTracker mDeliveryTracker = new DeliveryTracker();
    IBinder.DeathRecipient mListenerDeathRecipient;
@@ -432,6 +435,8 @@ public class AlarmManagerService extends SystemService {

        @VisibleForTesting
        static final String KEY_CRASH_NON_CLOCK_APPS = "crash_non_clock_apps";
        @VisibleForTesting
        static final String KEY_PRIORITY_ALARM_DELAY = "priority_alarm_delay";

        private static final long DEFAULT_MIN_FUTURITY = 5 * 1000;
        private static final long DEFAULT_MIN_INTERVAL = 60 * 1000;
@@ -470,6 +475,8 @@ public class AlarmManagerService extends SystemService {
        // TODO (b/171306433): Change to true by default.
        private static final boolean DEFAULT_CRASH_NON_CLOCK_APPS = false;

        private static final long DEFAULT_PRIORITY_ALARM_DELAY = 9 * 60_000;

        // Minimum futurity of a new alarm
        public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY;

@@ -525,6 +532,12 @@ public class AlarmManagerService extends SystemService {
         */
        public boolean CRASH_NON_CLOCK_APPS = DEFAULT_CRASH_NON_CLOCK_APPS;

        /**
         * Minimum delay between two slots that an app can get for their prioritized alarms, while
         * the device is in doze.
         */
        public long PRIORITY_ALARM_DELAY = DEFAULT_PRIORITY_ALARM_DELAY;

        private long mLastAllowWhileIdleWhitelistDuration = -1;

        Constants() {
@@ -662,6 +675,10 @@ public class AlarmManagerService extends SystemService {
                            CRASH_NON_CLOCK_APPS = properties.getBoolean(KEY_CRASH_NON_CLOCK_APPS,
                                    DEFAULT_CRASH_NON_CLOCK_APPS);
                            break;
                        case KEY_PRIORITY_ALARM_DELAY:
                            PRIORITY_ALARM_DELAY = properties.getLong(KEY_PRIORITY_ALARM_DELAY,
                                    DEFAULT_PRIORITY_ALARM_DELAY);
                            break;
                        default:
                            if (name.startsWith(KEY_PREFIX_STANDBY_QUOTA) && !standbyQuotaUpdated) {
                                // The quotas need to be updated in order, so we can't just rely
@@ -809,6 +826,11 @@ public class AlarmManagerService extends SystemService {
            pw.print(KEY_CRASH_NON_CLOCK_APPS, CRASH_NON_CLOCK_APPS);
            pw.println();

            pw.print(KEY_PRIORITY_ALARM_DELAY);
            pw.print("=");
            TimeUtils.formatDuration(PRIORITY_ALARM_DELAY, pw);
            pw.println();

            pw.decreaseIndent();
        }

@@ -1794,6 +1816,11 @@ public class AlarmManagerService extends SystemService {
                batterySaverPolicyElapsed = mAllowWhileIdleHistory.getNthLastWakeupForPackage(
                        alarm.sourcePackage, userId, quota) + window;
            }
        } else if ((alarm.flags & FLAG_PRIORITIZE) != 0) {
            final long lastDispatch = mLastPriorityAlarmDispatch.get(alarm.creatorUid, 0);
            batterySaverPolicyElapsed = (lastDispatch == 0)
                    ? nowElapsed
                    : lastDispatch + mConstants.PRIORITY_ALARM_DELAY;
        } else {
            // Not allowed.
            batterySaverPolicyElapsed = nowElapsed + INDEFINITE_DELAY;
@@ -1849,6 +1876,12 @@ public class AlarmManagerService extends SystemService {
                        alarm.sourcePackage, userId, quota) + window;
                deviceIdlePolicyTime = Math.min(whenInQuota, mPendingIdleUntil.getWhenElapsed());
            }
        } else if ((alarm.flags & FLAG_PRIORITIZE) != 0) {
            final long lastDispatch = mLastPriorityAlarmDispatch.get(alarm.creatorUid, 0);
            final long whenAllowed = (lastDispatch == 0)
                    ? nowElapsed
                    : lastDispatch + mConstants.PRIORITY_ALARM_DELAY;
            deviceIdlePolicyTime = Math.min(whenAllowed, mPendingIdleUntil.getWhenElapsed());
        } else {
            // Not allowed.
            deviceIdlePolicyTime = mPendingIdleUntil.getWhenElapsed();
@@ -2025,7 +2058,12 @@ public class AlarmManagerService extends SystemService {
            // make sure the caller is allowed to use the requested kind of alarm, and also
            // decide what quota and broadcast options to use.
            Bundle idleOptions = null;
            if (exact || allowWhileIdle) {
            if ((flags & FLAG_PRIORITIZE) != 0) {
                getContext().enforcePermission(
                        Manifest.permission.SCHEDULE_PRIORITIZED_ALARM,
                        Binder.getCallingPid(), callingUid, "AlarmManager.setPrioritized");
                flags &= ~(FLAG_ALLOW_WHILE_IDLE | FLAG_ALLOW_WHILE_IDLE_COMPAT);
            } else if (exact || allowWhileIdle) {
                final boolean needsPermission;
                boolean lowerQuota;
                if (CompatChanges.isChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION,
@@ -2107,6 +2145,7 @@ public class AlarmManagerService extends SystemService {
                flags |= FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
                flags &= ~FLAG_ALLOW_WHILE_IDLE;
                flags &= ~FLAG_ALLOW_WHILE_IDLE_COMPAT;
                flags &= ~FLAG_PRIORITIZE;
                idleOptions = null;
            }

@@ -2489,6 +2528,19 @@ public class AlarmManagerService extends SystemService {
            pw.println("Allow while idle history:");
            mAllowWhileIdleHistory.dump(pw, nowELAPSED);

            if (mLastPriorityAlarmDispatch.size() > 0) {
                pw.println("Last priority alarm dispatches:");
                pw.increaseIndent();
                for (int i = 0; i < mLastPriorityAlarmDispatch.size(); i++) {
                    pw.print("UID: ");
                    UserHandle.formatUid(pw, mLastPriorityAlarmDispatch.keyAt(i));
                    pw.print(": ");
                    TimeUtils.formatDuration(mLastPriorityAlarmDispatch.valueAt(i), nowELAPSED, pw);
                    pw.println();
                }
                pw.decreaseIndent();
            }

            if (mLog.dump(pw, "Recent problems:")) {
                pw.println();
            }
@@ -3303,6 +3355,11 @@ public class AlarmManagerService extends SystemService {
                mPendingBackgroundAlarms.removeAt(i);
            }
        }
        for (int i = mLastPriorityAlarmDispatch.size() - 1; i >= 0; i--) {
            if (UserHandle.getUserId(mLastPriorityAlarmDispatch.keyAt(i)) == userHandle) {
                mLastPriorityAlarmDispatch.removeAt(i);
            }
        }
        if (mNextWakeFromIdle != null && whichAlarms.test(mNextWakeFromIdle)) {
            mNextWakeFromIdle = mAlarmStore.getNextWakeFromIdleAlarm();
            if (mPendingIdleUntil != null) {
@@ -4103,6 +4160,7 @@ public class AlarmManagerService extends SystemService {
            IntentFilter sdFilter = new IntentFilter();
            sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
            sdFilter.addAction(Intent.ACTION_USER_STOPPED);
            sdFilter.addAction(Intent.ACTION_UID_REMOVED);
            getContext().registerReceiver(this, sdFilter);
        }

@@ -4132,6 +4190,9 @@ public class AlarmManagerService extends SystemService {
                            mAllowWhileIdleHistory.removeForUser(userHandle);
                        }
                        return;
                    case Intent.ACTION_UID_REMOVED:
                        mLastPriorityAlarmDispatch.delete(uid);
                        return;
                    case Intent.ACTION_PACKAGE_REMOVED:
                        if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
                            // This package is being updated; don't kill its alarms.
@@ -4522,11 +4583,11 @@ public class AlarmManagerService extends SystemService {
            if (inflight.isBroadcast()) {
                notifyBroadcastAlarmPendingLocked(alarm.uid);
            }
            if (isAllowedWhileIdleRestricted(alarm)) {
            final boolean doze = (mPendingIdleUntil != null);
            final boolean batterySaver = (mAppStateTracker != null
                    && mAppStateTracker.isForceAllAppsStandbyEnabled());
            if (doze || batterySaver) {
                if (isAllowedWhileIdleRestricted(alarm)) {
                    // Record the last time this uid handled an ALLOW_WHILE_IDLE alarm while the
                    // device was in doze or battery saver.
                    mAllowWhileIdleHistory.recordAlarmForPackage(alarm.sourcePackage,
@@ -4538,6 +4599,16 @@ public class AlarmManagerService extends SystemService {
                        return (doze && adjustDeliveryTimeBasedOnDeviceIdle(a))
                                || (batterySaver && adjustDeliveryTimeBasedOnBatterySaver(a));
                    });
                } else if ((alarm.flags & FLAG_PRIORITIZE) != 0) {
                    mLastPriorityAlarmDispatch.put(alarm.creatorUid, nowELAPSED);
                    mAlarmStore.updateAlarmDeliveries(a -> {
                        if (a.creatorUid != alarm.creatorUid
                                || (alarm.flags & FLAG_PRIORITIZE) == 0) {
                            return false;
                        }
                        return (doze && adjustDeliveryTimeBasedOnDeviceIdle(a))
                                || (batterySaver && adjustDeliveryTimeBasedOnBatterySaver(a));
                    });
                }
                if (RECORD_DEVICE_IDLE_ALARMS) {
                    IdleDispatchEntry ent = new IdleDispatchEntry();
+2 −0
Original line number Diff line number Diff line
@@ -242,6 +242,7 @@ package android {
    field public static final String REVIEW_ACCESSIBILITY_SERVICES = "android.permission.REVIEW_ACCESSIBILITY_SERVICES";
    field public static final String REVOKE_RUNTIME_PERMISSIONS = "android.permission.REVOKE_RUNTIME_PERMISSIONS";
    field public static final String ROTATE_SURFACE_FLINGER = "android.permission.ROTATE_SURFACE_FLINGER";
    field public static final String SCHEDULE_PRIORITIZED_ALARM = "android.permission.SCHEDULE_PRIORITIZED_ALARM";
    field public static final String SCORE_NETWORKS = "android.permission.SCORE_NETWORKS";
    field public static final String SECURE_ELEMENT_PRIVILEGED_OPERATION = "android.permission.SECURE_ELEMENT_PRIVILEGED_OPERATION";
    field public static final String SEND_CATEGORY_CAR_NOTIFICATIONS = "android.permission.SEND_CATEGORY_CAR_NOTIFICATIONS";
@@ -420,6 +421,7 @@ package android.app {
  public class AlarmManager {
    method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void set(int, long, long, long, android.app.PendingIntent, android.os.WorkSource);
    method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void set(int, long, long, long, android.app.AlarmManager.OnAlarmListener, android.os.Handler, android.os.WorkSource);
    method @RequiresPermission(android.Manifest.permission.SCHEDULE_PRIORITIZED_ALARM) public void setPrioritized(int, long, long, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.app.AlarmManager.OnAlarmListener);
  }
  public class AppOpsManager {
+9 −0
Original line number Diff line number Diff line
@@ -3998,6 +3998,15 @@
    <permission android:name="android.permission.SET_KEYBOARD_LAYOUT"
        android:protectionLevel="signature" />

    <!-- Allows an app to schedule a prioritized alarm that can be used to perform
         background work even when the device is in doze.
         <p>Not for use by third-party applications.
         @hide
         @SystemApi
     -->
    <permission android:name="android.permission.SCHEDULE_PRIORITIZED_ALARM"
                android:protectionLevel="signature|privileged"/>

    <!-- Allows an app to use exact alarm scheduling APIs to perform timing
         sensitive background work.
     -->
+93 −0

File changed.

Preview size limit exceeded, changes collapsed.