Merge "Add an API for a prioritized alarm" into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
ea1decfcda
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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) {
|
||||
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();
|
||||
|
||||
@@ -244,6 +244,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";
|
||||
@@ -423,6 +424,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 {
|
||||
|
||||
@@ -4003,6 +4003,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.
|
||||
-->
|
||||
|
||||
@@ -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_UNRESTRICTED;
|
||||
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_WAKE_FROM_IDLE;
|
||||
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_INTERVAL;
|
||||
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.INDEFINITE_DELAY;
|
||||
import static com.android.server.alarm.AlarmManagerService.IS_WAKEUP_MASK;
|
||||
@@ -443,6 +445,12 @@ public class AlarmManagerServiceTest {
|
||||
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,
|
||||
boolean unrestricted) {
|
||||
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_LISTENER_TIMEOUT, 45);
|
||||
setDeviceConfigLong(KEY_MIN_WINDOW, 50);
|
||||
setDeviceConfigLong(KEY_PRIORITY_ALARM_DELAY, 55);
|
||||
assertEquals(5, mService.mConstants.MIN_FUTURITY);
|
||||
assertEquals(10, mService.mConstants.MIN_INTERVAL);
|
||||
assertEquals(15, mService.mConstants.MAX_INTERVAL);
|
||||
@@ -589,6 +598,7 @@ public class AlarmManagerServiceTest {
|
||||
assertEquals(40, mService.mConstants.ALLOW_WHILE_IDLE_WHITELIST_DURATION);
|
||||
assertEquals(45, mService.mConstants.LISTENER_TIMEOUT);
|
||||
assertEquals(50, mService.mConstants.MIN_WINDOW);
|
||||
assertEquals(55, mService.mConstants.PRIORITY_ALARM_DELAY);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1585,6 +1595,89 @@ public class AlarmManagerServiceTest {
|
||||
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
|
||||
public void dispatchOrder() throws Exception {
|
||||
doReturn(0).when(mService).fuzzForDuration(anyLong());
|
||||
|
||||
Reference in New Issue
Block a user