Merge "Add an API for a prioritized alarm" into sc-dev

This commit is contained in:
Suprabh Shukla
2021-03-25 09:01:57 +00:00
committed by Android (Google) Code Review
5 changed files with 264 additions and 22 deletions

View File

@@ -18,6 +18,7 @@ package android.app;
import android.Manifest; import android.Manifest;
import android.annotation.IntDef; import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission; import android.annotation.RequiresPermission;
import android.annotation.SdkConstant; import android.annotation.SdkConstant;
import android.annotation.SystemApi; import android.annotation.SystemApi;
@@ -29,6 +30,7 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Build; import android.os.Build;
import android.os.Handler; import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.os.RemoteException; import android.os.RemoteException;
@@ -42,7 +44,9 @@ import com.android.i18n.timezone.ZoneInfoDb;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.Objects;
import java.util.WeakHashMap; import java.util.WeakHashMap;
import java.util.concurrent.Executor;
/** /**
* This class provides access to the system alarm services. These allow you * 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; 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 * For apps targeting {@link Build.VERSION_CODES#S} or above, APIs
* {@link #setExactAndAllowWhileIdle(int, long, PendingIntent)} and * {@link #setExactAndAllowWhileIdle(int, long, PendingIntent)} and
@@ -227,15 +240,15 @@ public class AlarmManager {
final class ListenerWrapper extends IAlarmListener.Stub implements Runnable { final class ListenerWrapper extends IAlarmListener.Stub implements Runnable {
final OnAlarmListener mListener; final OnAlarmListener mListener;
Handler mHandler; Executor mExecutor;
IAlarmCompleteListener mCompletion; IAlarmCompleteListener mCompletion;
public ListenerWrapper(OnAlarmListener listener) { public ListenerWrapper(OnAlarmListener listener) {
mListener = listener; mListener = listener;
} }
public void setHandler(Handler h) { void setExecutor(Executor e) {
mHandler = h; mExecutor = e;
} }
public void cancel() { public void cancel() {
@@ -250,7 +263,7 @@ public class AlarmManager {
public void doAlarm(IAlarmCompleteListener alarmManager) { public void doAlarm(IAlarmCompleteListener alarmManager) {
mCompletion = alarmManager; mCompletion = alarmManager;
mHandler.post(this); mExecutor.execute(this);
} }
@Override @Override
@@ -368,7 +381,7 @@ public class AlarmManager {
*/ */
public void set(@AlarmType int type, long triggerAtMillis, PendingIntent operation) { public void set(@AlarmType int type, long triggerAtMillis, PendingIntent operation) {
setImpl(type, triggerAtMillis, legacyExactLength(), 0, 0, operation, null, null, 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, public void setRepeating(@AlarmType int type, long triggerAtMillis,
long intervalMillis, PendingIntent operation) { long intervalMillis, PendingIntent operation) {
setImpl(type, triggerAtMillis, legacyExactLength(), intervalMillis, 0, 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, public void setWindow(@AlarmType int type, long windowStartMillis, long windowLengthMillis,
PendingIntent operation) { PendingIntent operation) {
setImpl(type, windowStartMillis, windowLengthMillis, 0, 0, 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); 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. * 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) @RequiresPermission(value = Manifest.permission.SCHEDULE_EXACT_ALARM, conditional = true)
public void setExact(@AlarmType int type, long triggerAtMillis, PendingIntent operation) { 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); null, null);
} }
@@ -645,7 +705,7 @@ public class AlarmManager {
@RequiresPermission(Manifest.permission.SCHEDULE_EXACT_ALARM) @RequiresPermission(Manifest.permission.SCHEDULE_EXACT_ALARM)
public void setAlarmClock(AlarmClockInfo info, PendingIntent operation) { public void setAlarmClock(AlarmClockInfo info, PendingIntent operation) {
setImpl(RTC_WAKEUP, info.getTriggerTime(), WINDOW_EXACT, 0, 0, operation, setImpl(RTC_WAKEUP, info.getTriggerTime(), WINDOW_EXACT, 0, 0, operation,
null, null, null, null, info); null, null, (Handler) null, null, info);
} }
/** @hide */ /** @hide */
@@ -654,7 +714,7 @@ public class AlarmManager {
public void set(@AlarmType int type, long triggerAtMillis, long windowMillis, public void set(@AlarmType int type, long triggerAtMillis, long windowMillis,
long intervalMillis, PendingIntent operation, WorkSource workSource) { long intervalMillis, PendingIntent operation, WorkSource workSource) {
setImpl(type, triggerAtMillis, windowMillis, intervalMillis, 0, operation, null, null, 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, long intervalMillis, int flags, PendingIntent operation, final OnAlarmListener listener,
String listenerTag, Handler targetHandler, WorkSource workSource, String listenerTag, Handler targetHandler, WorkSource workSource,
AlarmClockInfo alarmClock) { 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) { if (triggerAtMillis < 0) {
/* NOTYET /* NOTYET
if (mAlwaysExact) { if (mAlwaysExact) {
@@ -726,9 +795,7 @@ public class AlarmManager {
sWrappers.put(listener, new WeakReference<>(recipientWrapper)); sWrappers.put(listener, new WeakReference<>(recipientWrapper));
} }
} }
recipientWrapper.setExecutor(targetExecutor);
final Handler handler = (targetHandler != null) ? targetHandler : mMainThreadHandler;
recipientWrapper.setHandler(handler);
} }
try { try {
@@ -834,7 +901,7 @@ public class AlarmManager {
public void setInexactRepeating(@AlarmType int type, long triggerAtMillis, public void setInexactRepeating(@AlarmType int type, long triggerAtMillis,
long intervalMillis, PendingIntent operation) { long intervalMillis, PendingIntent operation) {
setImpl(type, triggerAtMillis, WINDOW_HEURISTIC, intervalMillis, 0, operation, null, 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, public void setAndAllowWhileIdle(@AlarmType int type, long triggerAtMillis,
PendingIntent operation) { PendingIntent operation) {
setImpl(type, triggerAtMillis, WINDOW_HEURISTIC, 0, FLAG_ALLOW_WHILE_IDLE, 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, public void setExactAndAllowWhileIdle(@AlarmType int type, long triggerAtMillis,
PendingIntent operation) { PendingIntent operation) {
setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, FLAG_ALLOW_WHILE_IDLE, operation, setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, FLAG_ALLOW_WHILE_IDLE, operation,
null, null, null, null, null); null, null, (Handler) null, null, null);
} }
/** /**

View File

@@ -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_COMPAT;
import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED; import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
import static android.app.AlarmManager.FLAG_IDLE_UNTIL; 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.FLAG_WAKE_FROM_IDLE;
import static android.app.AlarmManager.INTERVAL_DAY; import static android.app.AlarmManager.INTERVAL_DAY;
import static android.app.AlarmManager.INTERVAL_HOUR; import static android.app.AlarmManager.INTERVAL_HOUR;
@@ -100,6 +101,7 @@ import android.util.Slog;
import android.util.SparseArray; import android.util.SparseArray;
import android.util.SparseBooleanArray; import android.util.SparseBooleanArray;
import android.util.SparseIntArray; import android.util.SparseIntArray;
import android.util.SparseLongArray;
import android.util.TimeUtils; import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream; import android.util.proto.ProtoOutputStream;
@@ -221,6 +223,7 @@ public class AlarmManagerService extends SystemService {
AlarmHandler mHandler; AlarmHandler mHandler;
AppWakeupHistory mAppWakeupHistory; AppWakeupHistory mAppWakeupHistory;
AppWakeupHistory mAllowWhileIdleHistory; AppWakeupHistory mAllowWhileIdleHistory;
private final SparseLongArray mLastPriorityAlarmDispatch = new SparseLongArray();
ClockReceiver mClockReceiver; ClockReceiver mClockReceiver;
final DeliveryTracker mDeliveryTracker = new DeliveryTracker(); final DeliveryTracker mDeliveryTracker = new DeliveryTracker();
IBinder.DeathRecipient mListenerDeathRecipient; IBinder.DeathRecipient mListenerDeathRecipient;
@@ -432,6 +435,8 @@ public class AlarmManagerService extends SystemService {
@VisibleForTesting @VisibleForTesting
static final String KEY_CRASH_NON_CLOCK_APPS = "crash_non_clock_apps"; 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_FUTURITY = 5 * 1000;
private static final long DEFAULT_MIN_INTERVAL = 60 * 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. // TODO (b/171306433): Change to true by default.
private static final boolean DEFAULT_CRASH_NON_CLOCK_APPS = false; 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 // Minimum futurity of a new alarm
public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY; 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; 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; private long mLastAllowWhileIdleWhitelistDuration = -1;
Constants() { Constants() {
@@ -662,6 +675,10 @@ public class AlarmManagerService extends SystemService {
CRASH_NON_CLOCK_APPS = properties.getBoolean(KEY_CRASH_NON_CLOCK_APPS, CRASH_NON_CLOCK_APPS = properties.getBoolean(KEY_CRASH_NON_CLOCK_APPS,
DEFAULT_CRASH_NON_CLOCK_APPS); DEFAULT_CRASH_NON_CLOCK_APPS);
break; break;
case KEY_PRIORITY_ALARM_DELAY:
PRIORITY_ALARM_DELAY = properties.getLong(KEY_PRIORITY_ALARM_DELAY,
DEFAULT_PRIORITY_ALARM_DELAY);
break;
default: default:
if (name.startsWith(KEY_PREFIX_STANDBY_QUOTA) && !standbyQuotaUpdated) { if (name.startsWith(KEY_PREFIX_STANDBY_QUOTA) && !standbyQuotaUpdated) {
// The quotas need to be updated in order, so we can't just rely // 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.print(KEY_CRASH_NON_CLOCK_APPS, CRASH_NON_CLOCK_APPS);
pw.println(); pw.println();
pw.print(KEY_PRIORITY_ALARM_DELAY);
pw.print("=");
TimeUtils.formatDuration(PRIORITY_ALARM_DELAY, pw);
pw.println();
pw.decreaseIndent(); pw.decreaseIndent();
} }
@@ -1794,6 +1816,11 @@ public class AlarmManagerService extends SystemService {
batterySaverPolicyElapsed = mAllowWhileIdleHistory.getNthLastWakeupForPackage( batterySaverPolicyElapsed = mAllowWhileIdleHistory.getNthLastWakeupForPackage(
alarm.sourcePackage, userId, quota) + window; 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 { } else {
// Not allowed. // Not allowed.
batterySaverPolicyElapsed = nowElapsed + INDEFINITE_DELAY; batterySaverPolicyElapsed = nowElapsed + INDEFINITE_DELAY;
@@ -1849,6 +1876,12 @@ public class AlarmManagerService extends SystemService {
alarm.sourcePackage, userId, quota) + window; alarm.sourcePackage, userId, quota) + window;
deviceIdlePolicyTime = Math.min(whenInQuota, mPendingIdleUntil.getWhenElapsed()); 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 { } else {
// Not allowed. // Not allowed.
deviceIdlePolicyTime = mPendingIdleUntil.getWhenElapsed(); 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 // make sure the caller is allowed to use the requested kind of alarm, and also
// decide what quota and broadcast options to use. // decide what quota and broadcast options to use.
Bundle idleOptions = null; 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; final boolean needsPermission;
boolean lowerQuota; boolean lowerQuota;
if (CompatChanges.isChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, 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_UNRESTRICTED;
flags &= ~FLAG_ALLOW_WHILE_IDLE; flags &= ~FLAG_ALLOW_WHILE_IDLE;
flags &= ~FLAG_ALLOW_WHILE_IDLE_COMPAT; flags &= ~FLAG_ALLOW_WHILE_IDLE_COMPAT;
flags &= ~FLAG_PRIORITIZE;
idleOptions = null; idleOptions = null;
} }
@@ -2489,6 +2528,19 @@ public class AlarmManagerService extends SystemService {
pw.println("Allow while idle history:"); pw.println("Allow while idle history:");
mAllowWhileIdleHistory.dump(pw, nowELAPSED); 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:")) { if (mLog.dump(pw, "Recent problems:")) {
pw.println(); pw.println();
} }
@@ -3303,6 +3355,11 @@ public class AlarmManagerService extends SystemService {
mPendingBackgroundAlarms.removeAt(i); 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)) { if (mNextWakeFromIdle != null && whichAlarms.test(mNextWakeFromIdle)) {
mNextWakeFromIdle = mAlarmStore.getNextWakeFromIdleAlarm(); mNextWakeFromIdle = mAlarmStore.getNextWakeFromIdleAlarm();
if (mPendingIdleUntil != null) { if (mPendingIdleUntil != null) {
@@ -4103,6 +4160,7 @@ public class AlarmManagerService extends SystemService {
IntentFilter sdFilter = new IntentFilter(); IntentFilter sdFilter = new IntentFilter();
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
sdFilter.addAction(Intent.ACTION_USER_STOPPED); sdFilter.addAction(Intent.ACTION_USER_STOPPED);
sdFilter.addAction(Intent.ACTION_UID_REMOVED);
getContext().registerReceiver(this, sdFilter); getContext().registerReceiver(this, sdFilter);
} }
@@ -4132,6 +4190,9 @@ public class AlarmManagerService extends SystemService {
mAllowWhileIdleHistory.removeForUser(userHandle); mAllowWhileIdleHistory.removeForUser(userHandle);
} }
return; return;
case Intent.ACTION_UID_REMOVED:
mLastPriorityAlarmDispatch.delete(uid);
return;
case Intent.ACTION_PACKAGE_REMOVED: case Intent.ACTION_PACKAGE_REMOVED:
if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
// This package is being updated; don't kill its alarms. // This package is being updated; don't kill its alarms.
@@ -4522,11 +4583,11 @@ public class AlarmManagerService extends SystemService {
if (inflight.isBroadcast()) { if (inflight.isBroadcast()) {
notifyBroadcastAlarmPendingLocked(alarm.uid); notifyBroadcastAlarmPendingLocked(alarm.uid);
} }
if (isAllowedWhileIdleRestricted(alarm)) {
final boolean doze = (mPendingIdleUntil != null); final boolean doze = (mPendingIdleUntil != null);
final boolean batterySaver = (mAppStateTracker != null final boolean batterySaver = (mAppStateTracker != null
&& mAppStateTracker.isForceAllAppsStandbyEnabled()); && mAppStateTracker.isForceAllAppsStandbyEnabled());
if (doze || batterySaver) { if (doze || batterySaver) {
if (isAllowedWhileIdleRestricted(alarm)) {
// Record the last time this uid handled an ALLOW_WHILE_IDLE alarm while the // Record the last time this uid handled an ALLOW_WHILE_IDLE alarm while the
// device was in doze or battery saver. // device was in doze or battery saver.
mAllowWhileIdleHistory.recordAlarmForPackage(alarm.sourcePackage, mAllowWhileIdleHistory.recordAlarmForPackage(alarm.sourcePackage,
@@ -4538,6 +4599,16 @@ public class AlarmManagerService extends SystemService {
return (doze && adjustDeliveryTimeBasedOnDeviceIdle(a)) return (doze && adjustDeliveryTimeBasedOnDeviceIdle(a))
|| (batterySaver && adjustDeliveryTimeBasedOnBatterySaver(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) { if (RECORD_DEVICE_IDLE_ALARMS) {
IdleDispatchEntry ent = new IdleDispatchEntry(); IdleDispatchEntry ent = new IdleDispatchEntry();

View File

@@ -244,6 +244,7 @@ package android {
field public static final String REVIEW_ACCESSIBILITY_SERVICES = "android.permission.REVIEW_ACCESSIBILITY_SERVICES"; 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 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 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 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 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"; field public static final String SEND_CATEGORY_CAR_NOTIFICATIONS = "android.permission.SEND_CATEGORY_CAR_NOTIFICATIONS";
@@ -423,6 +424,7 @@ package android.app {
public class AlarmManager { 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.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.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 { public class AppOpsManager {

View File

@@ -4003,6 +4003,15 @@
<permission android:name="android.permission.SET_KEYBOARD_LAYOUT" <permission android:name="android.permission.SET_KEYBOARD_LAYOUT"
android:protectionLevel="signature" /> 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 <!-- Allows an app to use exact alarm scheduling APIs to perform timing
sensitive background work. sensitive background work.
--> -->

View File

@@ -21,6 +21,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_COMPAT;
import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED; import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
import static android.app.AlarmManager.FLAG_IDLE_UNTIL; import static android.app.AlarmManager.FLAG_IDLE_UNTIL;
import static android.app.AlarmManager.FLAG_PRIORITIZE;
import static android.app.AlarmManager.FLAG_STANDALONE; import static android.app.AlarmManager.FLAG_STANDALONE;
import static android.app.AlarmManager.FLAG_WAKE_FROM_IDLE; import static android.app.AlarmManager.FLAG_WAKE_FROM_IDLE;
import static android.app.AlarmManager.RTC; import static android.app.AlarmManager.RTC;
@@ -62,6 +63,7 @@ import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MAX_INT
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_FUTURITY; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_FUTURITY;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_INTERVAL; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_INTERVAL;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_WINDOW; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_WINDOW;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_PRIORITY_ALARM_DELAY;
import static com.android.server.alarm.AlarmManagerService.FREQUENT_INDEX; import static com.android.server.alarm.AlarmManagerService.FREQUENT_INDEX;
import static com.android.server.alarm.AlarmManagerService.INDEFINITE_DELAY; import static com.android.server.alarm.AlarmManagerService.INDEFINITE_DELAY;
import static com.android.server.alarm.AlarmManagerService.IS_WAKEUP_MASK; import static com.android.server.alarm.AlarmManagerService.IS_WAKEUP_MASK;
@@ -443,6 +445,12 @@ public class AlarmManagerServiceTest {
TEST_CALLING_UID); TEST_CALLING_UID);
} }
private void setPrioritizedAlarm(int type, long triggerTime, IAlarmListener listener) {
mService.setImpl(type, triggerTime, WINDOW_EXACT, 0, null, listener, "test",
FLAG_STANDALONE | FLAG_PRIORITIZE, null, null, TEST_CALLING_UID,
TEST_CALLING_PACKAGE, null);
}
private void setAllowWhileIdleAlarm(int type, long triggerTime, PendingIntent pi, private void setAllowWhileIdleAlarm(int type, long triggerTime, PendingIntent pi,
boolean unrestricted) { boolean unrestricted) {
final int flags = unrestricted ? FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED : FLAG_ALLOW_WHILE_IDLE; final int flags = unrestricted ? FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED : FLAG_ALLOW_WHILE_IDLE;
@@ -579,6 +587,7 @@ public class AlarmManagerServiceTest {
setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION, 40); setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION, 40);
setDeviceConfigLong(KEY_LISTENER_TIMEOUT, 45); setDeviceConfigLong(KEY_LISTENER_TIMEOUT, 45);
setDeviceConfigLong(KEY_MIN_WINDOW, 50); setDeviceConfigLong(KEY_MIN_WINDOW, 50);
setDeviceConfigLong(KEY_PRIORITY_ALARM_DELAY, 55);
assertEquals(5, mService.mConstants.MIN_FUTURITY); assertEquals(5, mService.mConstants.MIN_FUTURITY);
assertEquals(10, mService.mConstants.MIN_INTERVAL); assertEquals(10, mService.mConstants.MIN_INTERVAL);
assertEquals(15, mService.mConstants.MAX_INTERVAL); assertEquals(15, mService.mConstants.MAX_INTERVAL);
@@ -589,6 +598,7 @@ public class AlarmManagerServiceTest {
assertEquals(40, mService.mConstants.ALLOW_WHILE_IDLE_WHITELIST_DURATION); assertEquals(40, mService.mConstants.ALLOW_WHILE_IDLE_WHITELIST_DURATION);
assertEquals(45, mService.mConstants.LISTENER_TIMEOUT); assertEquals(45, mService.mConstants.LISTENER_TIMEOUT);
assertEquals(50, mService.mConstants.MIN_WINDOW); assertEquals(50, mService.mConstants.MIN_WINDOW);
assertEquals(55, mService.mConstants.PRIORITY_ALARM_DELAY);
} }
@Test @Test
@@ -1585,6 +1595,89 @@ public class AlarmManagerServiceTest {
getNewMockPendingIntent(), false), quota, mAllowWhileIdleWindow); getNewMockPendingIntent(), false), quota, mAllowWhileIdleWindow);
} }
@Test
public void prioritizedAlarmsInBatterySaver() throws Exception {
when(mAppStateTracker.areAlarmsRestrictedByBatterySaver(TEST_CALLING_UID,
TEST_CALLING_PACKAGE)).thenReturn(true);
when(mAppStateTracker.isForceAllAppsStandbyEnabled()).thenReturn(true);
final long minDelay = 5;
setDeviceConfigLong(KEY_PRIORITY_ALARM_DELAY, minDelay);
final long firstTrigger = mNowElapsedTest + 4;
final AtomicInteger alarmsFired = new AtomicInteger(0);
final int numAlarms = 10;
for (int i = 0; i < numAlarms; i++) {
setPrioritizedAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
new IAlarmListener.Stub() {
@Override
public void doAlarm(IAlarmCompleteListener callback)
throws RemoteException {
alarmsFired.incrementAndGet();
}
});
}
assertEquals(firstTrigger, mTestTimer.getElapsed());
mNowElapsedTest = firstTrigger;
mTestTimer.expire();
while (alarmsFired.get() < numAlarms) {
assertEquals(mNowElapsedTest + minDelay, mTestTimer.getElapsed());
mNowElapsedTest = mTestTimer.getElapsed();
mTestTimer.expire();
}
assertEquals(numAlarms, alarmsFired.get());
}
@Test
public void prioritizedAlarmsInDeviceIdle() throws Exception {
doReturn(0).when(mService).fuzzForDuration(anyLong());
final long minDelay = 5;
setDeviceConfigLong(KEY_PRIORITY_ALARM_DELAY, minDelay);
final long idleUntil = mNowElapsedTest + 1000;
setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, idleUntil, getNewMockPendingIntent());
assertNotNull(mService.mPendingIdleUntil);
final long firstTrigger = mNowElapsedTest + 4;
final AtomicInteger alarmsFired = new AtomicInteger(0);
final int numAlarms = 10;
for (int i = 0; i < numAlarms; i++) {
setPrioritizedAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
new IAlarmListener.Stub() {
@Override
public void doAlarm(IAlarmCompleteListener callback)
throws RemoteException {
alarmsFired.incrementAndGet();
}
});
}
assertEquals(firstTrigger, mTestTimer.getElapsed());
mNowElapsedTest = firstTrigger;
mTestTimer.expire();
while (alarmsFired.get() < numAlarms) {
assertEquals(mNowElapsedTest + minDelay, mTestTimer.getElapsed());
mNowElapsedTest = mTestTimer.getElapsed();
mTestTimer.expire();
}
assertEquals(numAlarms, alarmsFired.get());
setPrioritizedAlarm(ELAPSED_REALTIME_WAKEUP, idleUntil - 3, new IAlarmListener.Stub() {
@Override
public void doAlarm(IAlarmCompleteListener callback) throws RemoteException {
}
});
setPrioritizedAlarm(ELAPSED_REALTIME_WAKEUP, idleUntil - 2, new IAlarmListener.Stub() {
@Override
public void doAlarm(IAlarmCompleteListener callback) throws RemoteException {
}
});
assertEquals(idleUntil - 3, mTestTimer.getElapsed());
mNowElapsedTest = mTestTimer.getElapsed();
mTestTimer.expire();
assertEquals(idleUntil, mTestTimer.getElapsed());
}
@Test @Test
public void dispatchOrder() throws Exception { public void dispatchOrder() throws Exception {
doReturn(0).when(mService).fuzzForDuration(anyLong()); doReturn(0).when(mService).fuzzForDuration(anyLong());