Merge "Adding a permission to protect exact alarms" into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
48fb8fe9df
@@ -22,6 +22,7 @@ java_library {
|
||||
],
|
||||
},
|
||||
libs: [
|
||||
"app-compat-annotations",
|
||||
"framework-minus-apex",
|
||||
"unsupportedappusage",
|
||||
],
|
||||
|
||||
@@ -16,11 +16,14 @@
|
||||
|
||||
package android.app;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.RequiresPermission;
|
||||
import android.annotation.SdkConstant;
|
||||
import android.annotation.SystemApi;
|
||||
import android.annotation.SystemService;
|
||||
import android.compat.annotation.ChangeId;
|
||||
import android.compat.annotation.Disabled;
|
||||
import android.compat.annotation.UnsupportedAppUsage;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -183,6 +186,25 @@ public class AlarmManager {
|
||||
@UnsupportedAppUsage
|
||||
public static final int FLAG_IDLE_UNTIL = 1<<4;
|
||||
|
||||
/**
|
||||
* Flag for alarms: Used to provide backwards compatibility for apps with targetSdkVersion less
|
||||
* than {@link Build.VERSION_CODES#S}
|
||||
* @hide
|
||||
*/
|
||||
public static final int FLAG_ALLOW_WHILE_IDLE_COMPAT = 1 << 5;
|
||||
|
||||
/**
|
||||
* For apps targeting {@link Build.VERSION_CODES#S} or above, APIs
|
||||
* {@link #setExactAndAllowWhileIdle(int, long, PendingIntent)} and
|
||||
* {@link #setAlarmClock(AlarmClockInfo, PendingIntent)} will require holding a new
|
||||
* permission {@link android.Manifest.permission#SCHEDULE_EXACT_ALARM}
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@ChangeId
|
||||
@Disabled // TODO (b/171306433): Enable starting S.
|
||||
public static final long REQUIRE_EXACT_ALARM_PERMISSION = 171306433L;
|
||||
|
||||
@UnsupportedAppUsage
|
||||
private final IAlarmManager mService;
|
||||
private final Context mContext;
|
||||
@@ -588,6 +610,11 @@ public class AlarmManager {
|
||||
* This method is like {@link #setExact(int, long, PendingIntent)}, but implies
|
||||
* {@link #RTC_WAKEUP}.
|
||||
*
|
||||
* <p>
|
||||
* Starting from API {@link Build.VERSION_CODES#S}, using this method requires the
|
||||
* {@link Manifest.permission#SCHEDULE_EXACT_ALARM} permission. Alarms scheduled via this API
|
||||
* will be allowed to start a foreground service even if the app is in the background.
|
||||
*
|
||||
* @param info
|
||||
* @param operation Action to perform when the alarm goes off;
|
||||
* typically comes from {@link PendingIntent#getBroadcast
|
||||
@@ -603,6 +630,7 @@ public class AlarmManager {
|
||||
* @see android.content.Context#registerReceiver
|
||||
* @see android.content.Intent#filterEquals
|
||||
*/
|
||||
@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);
|
||||
@@ -876,6 +904,12 @@ public class AlarmManager {
|
||||
* device is idle it may take even more liberties with scheduling in order to optimize
|
||||
* for battery life.</p>
|
||||
*
|
||||
* <p>
|
||||
* Starting from API {@link Build.VERSION_CODES#S}, using this method requires the
|
||||
* {@link Manifest.permission#SCHEDULE_EXACT_ALARM} permission, unless the app is exempt from
|
||||
* battery restrictions. Alarms scheduled via this API will be allowed to start a foreground
|
||||
* service even if the app is in the background.
|
||||
*
|
||||
* @param type type of alarm.
|
||||
* @param triggerAtMillis time in milliseconds that the alarm should go
|
||||
* off, using the appropriate clock (depending on the alarm type).
|
||||
@@ -895,6 +929,7 @@ public class AlarmManager {
|
||||
* @see #RTC
|
||||
* @see #RTC_WAKEUP
|
||||
*/
|
||||
@RequiresPermission(value = Manifest.permission.SCHEDULE_EXACT_ALARM, conditional = true)
|
||||
public void setExactAndAllowWhileIdle(@AlarmType int type, long triggerAtMillis,
|
||||
PendingIntent operation) {
|
||||
setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, FLAG_ALLOW_WHILE_IDLE, operation,
|
||||
@@ -1017,6 +1052,18 @@ public class AlarmManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to check if the caller has permission to use alarms set via {@link }
|
||||
* @return
|
||||
*/
|
||||
public boolean canScheduleExactAlarms() {
|
||||
try {
|
||||
return mService.canScheduleExactAlarms();
|
||||
} catch (RemoteException re) {
|
||||
throw re.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets information about the next alarm clock currently scheduled.
|
||||
*
|
||||
|
||||
@@ -41,4 +41,5 @@ interface IAlarmManager {
|
||||
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
|
||||
AlarmManager.AlarmClockInfo getNextAlarmClock(int userId);
|
||||
long currentNetworkTimeMillis();
|
||||
boolean canScheduleExactAlarms();
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import static com.android.server.alarm.AlarmManagerService.clampPositive;
|
||||
import android.app.AlarmManager;
|
||||
import android.app.IAlarmListener;
|
||||
import android.app.PendingIntent;
|
||||
import android.os.Bundle;
|
||||
import android.os.WorkSource;
|
||||
import android.util.IndentingPrintWriter;
|
||||
import android.util.TimeUtils;
|
||||
@@ -92,10 +93,12 @@ class Alarm {
|
||||
private long mWhenElapsed;
|
||||
private long mMaxWhenElapsed;
|
||||
public AlarmManagerService.PriorityClass priorityClass;
|
||||
/** Broadcast options to use when delivering this alarm */
|
||||
public Bundle mIdleOptions;
|
||||
|
||||
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) {
|
||||
AlarmManager.AlarmClockInfo info, int uid, String pkgName, Bundle idleOptions) {
|
||||
this.type = type;
|
||||
origWhen = when;
|
||||
wakeup = type == AlarmManager.ELAPSED_REALTIME_WAKEUP
|
||||
@@ -115,6 +118,7 @@ class Alarm {
|
||||
alarmClock = info;
|
||||
this.uid = uid;
|
||||
packageName = pkgName;
|
||||
mIdleOptions = idleOptions;
|
||||
sourcePackage = (operation != null) ? operation.getCreatorPackage() : packageName;
|
||||
creatorUid = (operation != null) ? operation.getCreatorUid() : this.uid;
|
||||
}
|
||||
@@ -303,6 +307,10 @@ class Alarm {
|
||||
ipw.print("listener=");
|
||||
ipw.println(listener.asBinder());
|
||||
}
|
||||
if (mIdleOptions != null) {
|
||||
ipw.print("idle-options=");
|
||||
ipw.println(mIdleOptions.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public void dumpDebug(ProtoOutputStream proto, long fieldId, long nowElapsed) {
|
||||
|
||||
@@ -20,11 +20,15 @@ import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
|
||||
import static android.app.AlarmManager.ELAPSED_REALTIME;
|
||||
import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
|
||||
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_WAKE_FROM_IDLE;
|
||||
import static android.app.AlarmManager.INTERVAL_HOUR;
|
||||
import static android.app.AlarmManager.RTC;
|
||||
import static android.app.AlarmManager.RTC_WAKEUP;
|
||||
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
|
||||
import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED;
|
||||
import static android.os.UserHandle.USER_SYSTEM;
|
||||
|
||||
import static com.android.server.alarm.Alarm.APP_STANDBY_POLICY_INDEX;
|
||||
@@ -32,6 +36,7 @@ import static com.android.server.alarm.Alarm.BATTERY_SAVER_POLICY_INDEX;
|
||||
import static com.android.server.alarm.Alarm.DEVICE_IDLE_POLICY_INDEX;
|
||||
import static com.android.server.alarm.Alarm.REQUESTER_POLICY_INDEX;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.app.Activity;
|
||||
@@ -43,12 +48,14 @@ import android.app.IAlarmCompleteListener;
|
||||
import android.app.IAlarmListener;
|
||||
import android.app.IAlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.compat.CompatChanges;
|
||||
import android.app.usage.UsageStatsManager;
|
||||
import android.app.usage.UsageStatsManagerInternal;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.PermissionChecker;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.net.Uri;
|
||||
import android.os.BatteryManager;
|
||||
@@ -65,6 +72,7 @@ import android.os.PowerManager;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ResultReceiver;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.ShellCallback;
|
||||
import android.os.ShellCommand;
|
||||
import android.os.SystemClock;
|
||||
@@ -95,6 +103,8 @@ import android.util.proto.ProtoOutputStream;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.app.IAppOpsCallback;
|
||||
import com.android.internal.app.IAppOpsService;
|
||||
import com.android.internal.util.DumpUtils;
|
||||
import com.android.internal.util.FrameworkStatsLog;
|
||||
import com.android.internal.util.LocalLog;
|
||||
@@ -177,6 +187,7 @@ public class AlarmManagerService extends SystemService {
|
||||
final LocalLog mLog = new LocalLog(TAG);
|
||||
|
||||
AppOpsManager mAppOps;
|
||||
IAppOpsService mAppOpsService;
|
||||
DeviceIdleInternal mLocalDeviceIdleController;
|
||||
private UsageStatsManagerInternal mUsageStatsManagerInternal;
|
||||
private ActivityManagerInternal mActivityManagerInternal;
|
||||
@@ -253,10 +264,8 @@ public class AlarmManagerService extends SystemService {
|
||||
"REORDER_ALARMS_FOR_STANDBY",
|
||||
});
|
||||
|
||||
/**
|
||||
* Broadcast options to use for FLAG_ALLOW_WHILE_IDLE.
|
||||
*/
|
||||
Bundle mIdleOptions;
|
||||
BroadcastOptions mOptsWithFgs = BroadcastOptions.makeBasic();
|
||||
BroadcastOptions mOptsWithoutFgs = BroadcastOptions.makeBasic();
|
||||
|
||||
// TODO(b/172085676): Move inside alarm store.
|
||||
private final SparseArray<AlarmManager.AlarmClockInfo> mNextAlarmClockForUser =
|
||||
@@ -410,6 +419,10 @@ public class AlarmManagerService extends SystemService {
|
||||
@VisibleForTesting
|
||||
static final String KEY_ALLOW_WHILE_IDLE_QUOTA = "allow_while_idle_quota";
|
||||
|
||||
@VisibleForTesting
|
||||
static final String KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA = "allow_while_idle_compat_quota";
|
||||
private static final String KEY_ALLOW_WHILE_IDLE_WINDOW = "allow_while_idle_window";
|
||||
|
||||
private static final long DEFAULT_MIN_FUTURITY = 5 * 1000;
|
||||
private static final long DEFAULT_MIN_INTERVAL = 60 * 1000;
|
||||
private static final long DEFAULT_MAX_INTERVAL = 365 * DateUtils.DAY_IN_MILLIS;
|
||||
@@ -433,8 +446,14 @@ public class AlarmManagerService extends SystemService {
|
||||
private static final boolean DEFAULT_LAZY_BATCHING = true;
|
||||
private static final boolean DEFAULT_TIME_TICK_ALLOWED_WHILE_IDLE = true;
|
||||
|
||||
private static final int DEFAULT_ALLOW_WHILE_IDLE_QUOTA = 7;
|
||||
public static final long ALLOW_WHILE_IDLE_WINDOW = 60 * 60 * 1000; // 1 hour.
|
||||
/**
|
||||
* Default quota for pre-S apps. Enough to accommodate the existing policy of an alarm
|
||||
* every ALLOW_WHILE_IDLE_LONG_DELAY, which was 9 minutes.
|
||||
*/
|
||||
private static final int DEFAULT_ALLOW_WHILE_IDLE_COMPAT_QUOTA = 7;
|
||||
private static final int DEFAULT_ALLOW_WHILE_IDLE_QUOTA = 72;
|
||||
|
||||
private static final long DEFAULT_ALLOW_WHILE_IDLE_WINDOW = 60 * 60 * 1000; // 1 hour.
|
||||
|
||||
// Minimum futurity of a new alarm
|
||||
public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY;
|
||||
@@ -463,6 +482,19 @@ public class AlarmManagerService extends SystemService {
|
||||
|
||||
public int ALLOW_WHILE_IDLE_QUOTA = DEFAULT_ALLOW_WHILE_IDLE_QUOTA;
|
||||
|
||||
/**
|
||||
* Used to provide backwards compatibility to pre-S apps with a quota equivalent to the
|
||||
* earlier delay throttling mechanism.
|
||||
*/
|
||||
public int ALLOW_WHILE_IDLE_COMPAT_QUOTA = DEFAULT_ALLOW_WHILE_IDLE_COMPAT_QUOTA;
|
||||
|
||||
/**
|
||||
* The window used for enforcing {@link #ALLOW_WHILE_IDLE_QUOTA} and
|
||||
* {@link #ALLOW_WHILE_IDLE_COMPAT_QUOTA}. Can be configured, but only recommended for
|
||||
* testing.
|
||||
*/
|
||||
public long ALLOW_WHILE_IDLE_WINDOW = DEFAULT_ALLOW_WHILE_IDLE_WINDOW;
|
||||
|
||||
private long mLastAllowWhileIdleWhitelistDuration = -1;
|
||||
|
||||
Constants() {
|
||||
@@ -480,9 +512,11 @@ public class AlarmManagerService extends SystemService {
|
||||
public void updateAllowWhileIdleWhitelistDurationLocked() {
|
||||
if (mLastAllowWhileIdleWhitelistDuration != ALLOW_WHILE_IDLE_WHITELIST_DURATION) {
|
||||
mLastAllowWhileIdleWhitelistDuration = ALLOW_WHILE_IDLE_WHITELIST_DURATION;
|
||||
BroadcastOptions opts = BroadcastOptions.makeBasic();
|
||||
opts.setTemporaryAppWhitelistDuration(ALLOW_WHILE_IDLE_WHITELIST_DURATION);
|
||||
mIdleOptions = opts.toBundle();
|
||||
|
||||
mOptsWithFgs.setTemporaryAppWhitelistDuration(ALLOW_WHILE_IDLE_WHITELIST_DURATION);
|
||||
mOptsWithoutFgs.setTemporaryAppWhitelistDuration(
|
||||
TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED,
|
||||
ALLOW_WHILE_IDLE_WHITELIST_DURATION);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -516,6 +550,27 @@ public class AlarmManagerService extends SystemService {
|
||||
ALLOW_WHILE_IDLE_QUOTA = 1;
|
||||
}
|
||||
break;
|
||||
case KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA:
|
||||
ALLOW_WHILE_IDLE_COMPAT_QUOTA = properties.getInt(
|
||||
KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA,
|
||||
DEFAULT_ALLOW_WHILE_IDLE_COMPAT_QUOTA);
|
||||
if (ALLOW_WHILE_IDLE_COMPAT_QUOTA <= 0) {
|
||||
Slog.w(TAG, "Cannot have quota lower than 1.");
|
||||
ALLOW_WHILE_IDLE_COMPAT_QUOTA = 1;
|
||||
}
|
||||
break;
|
||||
case KEY_ALLOW_WHILE_IDLE_WINDOW:
|
||||
ALLOW_WHILE_IDLE_WINDOW = properties.getLong(
|
||||
KEY_ALLOW_WHILE_IDLE_WINDOW, DEFAULT_ALLOW_WHILE_IDLE_WINDOW);
|
||||
if (ALLOW_WHILE_IDLE_WINDOW > DEFAULT_ALLOW_WHILE_IDLE_WINDOW) {
|
||||
Slog.w(TAG, "Cannot have allow_while_idle_window > "
|
||||
+ DEFAULT_ALLOW_WHILE_IDLE_WINDOW);
|
||||
ALLOW_WHILE_IDLE_WINDOW = DEFAULT_ALLOW_WHILE_IDLE_WINDOW;
|
||||
} else if (ALLOW_WHILE_IDLE_WINDOW < DEFAULT_ALLOW_WHILE_IDLE_WINDOW) {
|
||||
Slog.w(TAG, "Using a non-default allow_while_idle_window = "
|
||||
+ ALLOW_WHILE_IDLE_WINDOW);
|
||||
}
|
||||
break;
|
||||
case KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION:
|
||||
ALLOW_WHILE_IDLE_WHITELIST_DURATION = properties.getLong(
|
||||
KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION,
|
||||
@@ -643,10 +698,14 @@ public class AlarmManagerService extends SystemService {
|
||||
TimeUtils.formatDuration(LISTENER_TIMEOUT, pw);
|
||||
pw.println();
|
||||
|
||||
pw.print("allow_while_idle_window=");
|
||||
pw.print(KEY_ALLOW_WHILE_IDLE_WINDOW);
|
||||
pw.print("=");
|
||||
TimeUtils.formatDuration(ALLOW_WHILE_IDLE_WINDOW, pw);
|
||||
pw.println();
|
||||
|
||||
pw.print(KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA, ALLOW_WHILE_IDLE_COMPAT_QUOTA);
|
||||
pw.println();
|
||||
|
||||
pw.print(KEY_ALLOW_WHILE_IDLE_QUOTA, ALLOW_WHILE_IDLE_QUOTA);
|
||||
pw.println();
|
||||
|
||||
@@ -830,7 +889,15 @@ public class AlarmManagerService extends SystemService {
|
||||
if (futurity < MIN_FUZZABLE_INTERVAL) {
|
||||
futurity = 0;
|
||||
}
|
||||
return clampPositive(triggerAtTime + (long) (.75 * futurity));
|
||||
long maxElapsed = triggerAtTime + (long) (0.75 * futurity);
|
||||
// For non-repeating alarms, window is capped at a maximum of one hour from the requested
|
||||
// delivery time. This allows for inexact-while-idle alarms to be slightly more reliable.
|
||||
// In practice, the delivery window should generally be much smaller than that
|
||||
// when the device is not idling.
|
||||
if (interval == 0) {
|
||||
maxElapsed = Math.min(maxElapsed, triggerAtTime + INTERVAL_HOUR);
|
||||
}
|
||||
return clampPositive(maxElapsed);
|
||||
}
|
||||
|
||||
// The RTC clock has moved arbitrarily, so we need to recalculate all the RTC alarm deliveries.
|
||||
@@ -998,7 +1065,7 @@ public class AlarmManagerService extends SystemService {
|
||||
setImplLocked(alarm.type, alarm.origWhen + delta, nextElapsed,
|
||||
nextMaxElapsed - nextElapsed, alarm.repeatInterval, alarm.operation, null,
|
||||
null, alarm.flags, alarm.workSource, alarm.alarmClock, alarm.uid,
|
||||
alarm.packageName);
|
||||
alarm.packageName, null);
|
||||
// Kernel alarms will be rescheduled as needed in setImplLocked
|
||||
}
|
||||
}
|
||||
@@ -1241,7 +1308,8 @@ public class AlarmManagerService extends SystemService {
|
||||
mAlarmStore.setAlarmClockRemovalListener(mAlarmClockUpdater);
|
||||
|
||||
mAppWakeupHistory = new AppWakeupHistory(Constants.DEFAULT_APP_STANDBY_WINDOW);
|
||||
mAllowWhileIdleHistory = new AppWakeupHistory(Constants.ALLOW_WHILE_IDLE_WINDOW);
|
||||
mAllowWhileIdleHistory = new AppWakeupHistory(
|
||||
Constants.DEFAULT_ALLOW_WHILE_IDLE_WINDOW);
|
||||
|
||||
mNextWakeup = mNextNonWakeup = 0;
|
||||
|
||||
@@ -1327,6 +1395,28 @@ public class AlarmManagerService extends SystemService {
|
||||
synchronized (mLock) {
|
||||
mConstants.start();
|
||||
mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
|
||||
mAppOpsService = mInjector.getAppOpsService();
|
||||
try {
|
||||
mAppOpsService.startWatchingMode(AppOpsManager.OP_SCHEDULE_EXACT_ALARM, null,
|
||||
new IAppOpsCallback.Stub() {
|
||||
@Override
|
||||
public void opChanged(int op, int uid, String packageName)
|
||||
throws RemoteException {
|
||||
if (op != AppOpsManager.OP_SCHEDULE_EXACT_ALARM) {
|
||||
return;
|
||||
}
|
||||
final int mode = mAppOpsService.checkOperation(op, uid,
|
||||
packageName);
|
||||
if (mode != AppOpsManager.MODE_ALLOWED
|
||||
&& mode != AppOpsManager.MODE_DEFAULT) {
|
||||
mHandler.obtainMessage(AlarmHandler.REMOVE_EXACT_ALARMS,
|
||||
uid, 0, packageName).sendToTarget();
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
|
||||
mLocalDeviceIdleController =
|
||||
LocalServices.getService(DeviceIdleInternal.class);
|
||||
mUsageStatsManagerInternal =
|
||||
@@ -1428,7 +1518,7 @@ public class AlarmManagerService extends SystemService {
|
||||
void setImpl(int type, long triggerAtTime, long windowLength, long interval,
|
||||
PendingIntent operation, IAlarmListener directReceiver, String listenerTag,
|
||||
int flags, WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock,
|
||||
int callingUid, String callingPackage) {
|
||||
int callingUid, String callingPackage, Bundle idleOptions) {
|
||||
if ((operation == null && directReceiver == null)
|
||||
|| (operation != null && directReceiver != null)) {
|
||||
Slog.w(TAG, "Alarms must either supply a PendingIntent or an AlarmReceiver");
|
||||
@@ -1519,17 +1609,18 @@ public class AlarmManagerService extends SystemService {
|
||||
}
|
||||
setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, interval, operation,
|
||||
directReceiver, listenerTag, flags, workSource, alarmClock, callingUid,
|
||||
callingPackage);
|
||||
callingPackage, idleOptions);
|
||||
}
|
||||
}
|
||||
|
||||
private void setImplLocked(int type, long when, long whenElapsed, long windowLength,
|
||||
long interval, PendingIntent operation, IAlarmListener directReceiver,
|
||||
String listenerTag, int flags, WorkSource workSource,
|
||||
AlarmManager.AlarmClockInfo alarmClock, int callingUid, String callingPackage) {
|
||||
AlarmManager.AlarmClockInfo alarmClock, int callingUid, String callingPackage,
|
||||
Bundle idleOptions) {
|
||||
final Alarm a = new Alarm(type, when, whenElapsed, windowLength, interval,
|
||||
operation, directReceiver, listenerTag, workSource, flags, alarmClock,
|
||||
callingUid, callingPackage);
|
||||
callingUid, callingPackage, idleOptions);
|
||||
if (mActivityManagerInternal.isAppStartModeDisabled(callingUid, callingPackage)) {
|
||||
Slog.w(TAG, "Not setting alarm from " + callingUid + ":" + a
|
||||
+ " -- package not allowed to start");
|
||||
@@ -1605,19 +1696,21 @@ public class AlarmManagerService extends SystemService {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(mAppStateTracker != null && mAppStateTracker.areAlarmsRestrictedByBatterySaver(
|
||||
alarm.creatorUid, alarm.sourcePackage))) {
|
||||
if (mAppStateTracker == null || !mAppStateTracker.areAlarmsRestrictedByBatterySaver(
|
||||
alarm.creatorUid, alarm.sourcePackage)) {
|
||||
return alarm.setPolicyElapsed(BATTERY_SAVER_POLICY_INDEX, nowElapsed);
|
||||
}
|
||||
|
||||
final long batterySaverPolicyElapsed;
|
||||
if ((alarm.flags & (AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED)) != 0) {
|
||||
if ((alarm.flags & (FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED)) != 0) {
|
||||
// Unrestricted.
|
||||
batterySaverPolicyElapsed = nowElapsed;
|
||||
} else if ((alarm.flags & AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) {
|
||||
} else if (isAllowedWhileIdleRestricted(alarm)) {
|
||||
// Allowed but limited.
|
||||
final int userId = UserHandle.getUserId(alarm.creatorUid);
|
||||
final int quota = mConstants.ALLOW_WHILE_IDLE_QUOTA;
|
||||
final int quota = ((alarm.flags & FLAG_ALLOW_WHILE_IDLE) != 0)
|
||||
? mConstants.ALLOW_WHILE_IDLE_QUOTA
|
||||
: mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA;
|
||||
final int dispatchesInWindow = mAllowWhileIdleHistory.getTotalWakeupsInWindow(
|
||||
alarm.sourcePackage, userId);
|
||||
if (dispatchesInWindow < quota) {
|
||||
@@ -1625,7 +1718,7 @@ public class AlarmManagerService extends SystemService {
|
||||
batterySaverPolicyElapsed = nowElapsed;
|
||||
} else {
|
||||
batterySaverPolicyElapsed = mAllowWhileIdleHistory.getNthLastWakeupForPackage(
|
||||
alarm.sourcePackage, userId, quota) + Constants.ALLOW_WHILE_IDLE_WINDOW;
|
||||
alarm.sourcePackage, userId, quota) + mConstants.ALLOW_WHILE_IDLE_WINDOW;
|
||||
}
|
||||
} else {
|
||||
// Not allowed.
|
||||
@@ -1634,6 +1727,16 @@ public class AlarmManagerService extends SystemService {
|
||||
return alarm.setPolicyElapsed(BATTERY_SAVER_POLICY_INDEX, batterySaverPolicyElapsed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the given alarm has the flag
|
||||
* {@link AlarmManager#FLAG_ALLOW_WHILE_IDLE} or
|
||||
* {@link AlarmManager#FLAG_ALLOW_WHILE_IDLE_COMPAT}
|
||||
*
|
||||
*/
|
||||
private static boolean isAllowedWhileIdleRestricted(Alarm a) {
|
||||
return (a.flags & (FLAG_ALLOW_WHILE_IDLE | FLAG_ALLOW_WHILE_IDLE_COMPAT)) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts the delivery time of the alarm based on device_idle (doze) rules.
|
||||
*
|
||||
@@ -1647,14 +1750,15 @@ public class AlarmManagerService extends SystemService {
|
||||
}
|
||||
|
||||
final long deviceIdlePolicyTime;
|
||||
if ((alarm.flags & (AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED
|
||||
| AlarmManager.FLAG_WAKE_FROM_IDLE)) != 0) {
|
||||
if ((alarm.flags & (FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED | FLAG_WAKE_FROM_IDLE)) != 0) {
|
||||
// Unrestricted.
|
||||
deviceIdlePolicyTime = nowElapsed;
|
||||
} else if ((alarm.flags & AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) {
|
||||
} else if (isAllowedWhileIdleRestricted(alarm)) {
|
||||
// Allowed but limited.
|
||||
final int userId = UserHandle.getUserId(alarm.creatorUid);
|
||||
final int quota = mConstants.ALLOW_WHILE_IDLE_QUOTA;
|
||||
final int quota = ((alarm.flags & FLAG_ALLOW_WHILE_IDLE) != 0)
|
||||
? mConstants.ALLOW_WHILE_IDLE_QUOTA
|
||||
: mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA;
|
||||
final int dispatchesInWindow = mAllowWhileIdleHistory.getTotalWakeupsInWindow(
|
||||
alarm.sourcePackage, userId);
|
||||
if (dispatchesInWindow < quota) {
|
||||
@@ -1662,7 +1766,7 @@ public class AlarmManagerService extends SystemService {
|
||||
deviceIdlePolicyTime = nowElapsed;
|
||||
} else {
|
||||
final long whenInQuota = mAllowWhileIdleHistory.getNthLastWakeupForPackage(
|
||||
alarm.sourcePackage, userId, quota) + Constants.ALLOW_WHILE_IDLE_WINDOW;
|
||||
alarm.sourcePackage, userId, quota) + mConstants.ALLOW_WHILE_IDLE_WINDOW;
|
||||
deviceIdlePolicyTime = Math.min(whenInQuota, mPendingIdleUntil.getWhenElapsed());
|
||||
}
|
||||
} else {
|
||||
@@ -1749,7 +1853,7 @@ public class AlarmManagerService extends SystemService {
|
||||
} else if (mPendingIdleUntil != null) {
|
||||
adjustDeliveryTimeBasedOnDeviceIdle(a);
|
||||
}
|
||||
if ((a.flags & AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) {
|
||||
if ((a.flags & FLAG_WAKE_FROM_IDLE) != 0) {
|
||||
if (mNextWakeFromIdle == null || mNextWakeFromIdle.getWhenElapsed()
|
||||
> a.getWhenElapsed()) {
|
||||
mNextWakeFromIdle = a;
|
||||
@@ -1809,6 +1913,14 @@ public class AlarmManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given uid is on the system or user's power save exclusion list.
|
||||
*/
|
||||
boolean isWhitelisted(int uid) {
|
||||
return (mLocalDeviceIdleController == null || mLocalDeviceIdleController.isAppOnWhitelist(
|
||||
UserHandle.getAppId(uid)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Public-facing binder interface
|
||||
*/
|
||||
@@ -1824,6 +1936,54 @@ public class AlarmManagerService extends SystemService {
|
||||
// wakelock time spent in alarm delivery
|
||||
mAppOps.checkPackage(callingUid, callingPackage);
|
||||
|
||||
final boolean allowWhileIdle = (flags & FLAG_ALLOW_WHILE_IDLE) != 0;
|
||||
|
||||
Bundle idleOptions = null;
|
||||
if (alarmClock != null || allowWhileIdle) {
|
||||
// make sure the caller is allowed to use the requested kind of alarm, and also
|
||||
// decide what broadcast options to use.
|
||||
final boolean needsPermission;
|
||||
boolean lowQuota;
|
||||
if (CompatChanges.isChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION,
|
||||
callingPackage, UserHandle.getUserHandleForUid(callingUid))) {
|
||||
if (windowLength != AlarmManager.WINDOW_EXACT) {
|
||||
needsPermission = false;
|
||||
lowQuota = true;
|
||||
idleOptions = isWhitelisted(callingUid) ? mOptsWithFgs.toBundle()
|
||||
: mOptsWithoutFgs.toBundle();
|
||||
} else if (alarmClock != null) {
|
||||
needsPermission = true;
|
||||
lowQuota = false;
|
||||
idleOptions = mOptsWithFgs.toBundle();
|
||||
} else {
|
||||
needsPermission = true;
|
||||
lowQuota = false;
|
||||
idleOptions = mOptsWithFgs.toBundle();
|
||||
}
|
||||
} else {
|
||||
needsPermission = false;
|
||||
lowQuota = allowWhileIdle;
|
||||
idleOptions = allowWhileIdle ? mOptsWithFgs.toBundle() : null;
|
||||
}
|
||||
if (needsPermission && !canScheduleExactAlarms()) {
|
||||
if (alarmClock == null && isWhitelisted(callingUid)) {
|
||||
// If the app is on the full system allow-list (not except-idle), we still
|
||||
// allow the alarms, but with a lower quota to keep pre-S compatibility.
|
||||
lowQuota = true;
|
||||
} else {
|
||||
final String errorMessage = "Caller needs to hold "
|
||||
+ Manifest.permission.SCHEDULE_EXACT_ALARM + " to set "
|
||||
+ ((allowWhileIdle) ? "exact, allow-while-idle" : "alarm-clock")
|
||||
+ " alarms.";
|
||||
throw new SecurityException(errorMessage);
|
||||
}
|
||||
}
|
||||
if (lowQuota) {
|
||||
flags &= ~FLAG_ALLOW_WHILE_IDLE;
|
||||
flags |= FLAG_ALLOW_WHILE_IDLE_COMPAT;
|
||||
}
|
||||
}
|
||||
|
||||
// Repeating alarms must use PendingIntent, not direct listener
|
||||
if (interval != 0) {
|
||||
if (directReceiver != null) {
|
||||
@@ -1840,8 +2000,7 @@ public class AlarmManagerService extends SystemService {
|
||||
|
||||
// No incoming callers can request either WAKE_FROM_IDLE or
|
||||
// ALLOW_WHILE_IDLE_UNRESTRICTED -- we will apply those later as appropriate.
|
||||
flags &= ~(AlarmManager.FLAG_WAKE_FROM_IDLE
|
||||
| AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED);
|
||||
flags &= ~(FLAG_WAKE_FROM_IDLE | FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED);
|
||||
|
||||
// Only the system can use FLAG_IDLE_UNTIL -- this is used to tell the alarm
|
||||
// manager when to come out of idle mode, which is only for DeviceIdleController.
|
||||
@@ -1857,22 +2016,32 @@ public class AlarmManagerService extends SystemService {
|
||||
// If this alarm is for an alarm clock, then it must be standalone and we will
|
||||
// use it to wake early from idle if needed.
|
||||
if (alarmClock != null) {
|
||||
flags |= AlarmManager.FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_STANDALONE;
|
||||
flags |= FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_STANDALONE;
|
||||
|
||||
// If the caller is a core system component or on the user's whitelist, and not calling
|
||||
// to do work on behalf of someone else, then always set ALLOW_WHILE_IDLE_UNRESTRICTED.
|
||||
// This means we will allow these alarms to go off as normal even while idle, with no
|
||||
// timing restrictions.
|
||||
} else if (workSource == null && (callingUid < Process.FIRST_APPLICATION_UID
|
||||
} else if (workSource == null && (UserHandle.isCore(callingUid)
|
||||
|| UserHandle.isSameApp(callingUid, mSystemUiUid)
|
||||
|| ((mAppStateTracker != null)
|
||||
&& mAppStateTracker.isUidPowerSaveUserExempt(callingUid)))) {
|
||||
flags |= AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
|
||||
flags &= ~AlarmManager.FLAG_ALLOW_WHILE_IDLE;
|
||||
flags |= FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
|
||||
flags &= ~FLAG_ALLOW_WHILE_IDLE;
|
||||
flags &= ~FLAG_ALLOW_WHILE_IDLE_COMPAT;
|
||||
idleOptions = null;
|
||||
}
|
||||
|
||||
setImpl(type, triggerAtTime, windowLength, interval, operation, directReceiver,
|
||||
listenerTag, flags, workSource, alarmClock, callingUid, callingPackage);
|
||||
listenerTag, flags, workSource, alarmClock, callingUid, callingPackage,
|
||||
idleOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canScheduleExactAlarms() {
|
||||
return PermissionChecker.checkCallingOrSelfPermissionForPreflight(getContext(),
|
||||
Manifest.permission.SCHEDULE_EXACT_ALARM)
|
||||
== PermissionChecker.PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2755,6 +2924,77 @@ public class AlarmManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an app loses {@link Manifest.permission#SCHEDULE_EXACT_ALARM} to remove alarms
|
||||
* that the app is no longer eligible to use.
|
||||
* TODO (b/179541791): Revisit and write tests once UX is final.
|
||||
*/
|
||||
void removeExactAlarmsOnPermissionRevokedLocked(int uid, String packageName) {
|
||||
if (UserHandle.isCore(uid) || uid == mSystemUiUid) {
|
||||
return;
|
||||
}
|
||||
if (isWhitelisted(uid)) {
|
||||
return;
|
||||
}
|
||||
if (!CompatChanges.isChangeEnabled(
|
||||
AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION,
|
||||
packageName, UserHandle.getUserHandleForUid(uid))) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Predicate<Alarm> whichAlarms =
|
||||
a -> (a.uid == uid && a.packageName.equals(packageName)
|
||||
&& ((a.flags & FLAG_ALLOW_WHILE_IDLE) != 0 || a.alarmClock != null));
|
||||
final ArrayList<Alarm> removed = mAlarmStore.remove(whichAlarms);
|
||||
final boolean didRemove = !removed.isEmpty();
|
||||
if (didRemove) {
|
||||
decrementAlarmCount(uid, removed.size());
|
||||
}
|
||||
|
||||
for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i--) {
|
||||
final ArrayList<Alarm> alarmsForUid = mPendingBackgroundAlarms.valueAt(i);
|
||||
for (int j = alarmsForUid.size() - 1; j >= 0; j--) {
|
||||
final Alarm alarm = alarmsForUid.get(j);
|
||||
if (whichAlarms.test(alarm)) {
|
||||
// Don't set didRemove, since this doesn't impact the scheduled alarms.
|
||||
alarmsForUid.remove(j);
|
||||
decrementAlarmCount(alarm.uid, 1);
|
||||
}
|
||||
}
|
||||
if (alarmsForUid.size() == 0) {
|
||||
mPendingBackgroundAlarms.removeAt(i);
|
||||
}
|
||||
}
|
||||
for (int i = mPendingNonWakeupAlarms.size() - 1; i >= 0; i--) {
|
||||
final Alarm a = mPendingNonWakeupAlarms.get(i);
|
||||
if (whichAlarms.test(a)) {
|
||||
// Don't set didRemove, since this doesn't impact the scheduled alarms.
|
||||
mPendingNonWakeupAlarms.remove(i);
|
||||
decrementAlarmCount(a.uid, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (didRemove) {
|
||||
if (mNextWakeFromIdle != null && whichAlarms.test(mNextWakeFromIdle)) {
|
||||
mNextWakeFromIdle = mAlarmStore.getNextWakeFromIdleAlarm();
|
||||
if (mPendingIdleUntil != null) {
|
||||
final boolean idleUntilUpdated = mAlarmStore.updateAlarmDeliveries(alarm -> {
|
||||
if (alarm != mPendingIdleUntil) {
|
||||
return false;
|
||||
}
|
||||
return adjustIdleUntilTime(alarm);
|
||||
});
|
||||
if (idleUntilUpdated) {
|
||||
mAlarmStore.updateAlarmDeliveries(
|
||||
alarm -> adjustDeliveryTimeBasedOnDeviceIdle(alarm));
|
||||
}
|
||||
}
|
||||
}
|
||||
rescheduleKernelAlarmsLocked();
|
||||
updateNextAlarmClockLocked();
|
||||
}
|
||||
}
|
||||
|
||||
void removeLocked(PendingIntent operation, IAlarmListener directReceiver) {
|
||||
if (operation == null && directReceiver == null) {
|
||||
if (localLOGV) {
|
||||
@@ -3082,7 +3322,7 @@ public class AlarmManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isExemptFromBatterySaver(Alarm alarm) {
|
||||
private static boolean isExemptFromBatterySaver(Alarm alarm) {
|
||||
if (alarm.alarmClock != null) {
|
||||
return true;
|
||||
}
|
||||
@@ -3142,7 +3382,7 @@ public class AlarmManagerService extends SystemService {
|
||||
|
||||
alarm.count = 1;
|
||||
triggerList.add(alarm);
|
||||
if ((alarm.flags & AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) {
|
||||
if ((alarm.flags & FLAG_WAKE_FROM_IDLE) != 0) {
|
||||
EventLogTags.writeDeviceIdleWakeFromIdle(mPendingIdleUntil != null ? 1 : 0,
|
||||
alarm.statsTag);
|
||||
}
|
||||
@@ -3180,7 +3420,7 @@ public class AlarmManagerService extends SystemService {
|
||||
setImplLocked(alarm.type, alarm.origWhen + delta, nextElapsed,
|
||||
nextMaxElapsed - nextElapsed, alarm.repeatInterval, alarm.operation, null,
|
||||
null, alarm.flags, alarm.workSource, alarm.alarmClock, alarm.uid,
|
||||
alarm.packageName);
|
||||
alarm.packageName, null);
|
||||
}
|
||||
|
||||
if (alarm.wakeup) {
|
||||
@@ -3257,7 +3497,6 @@ public class AlarmManagerService extends SystemService {
|
||||
mLastAlarmDeliveryTime = nowELAPSED;
|
||||
for (int i = 0; i < triggerList.size(); i++) {
|
||||
Alarm alarm = triggerList.get(i);
|
||||
final boolean allowWhileIdle = (alarm.flags & AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0;
|
||||
if (alarm.wakeup) {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_POWER,
|
||||
"Dispatch wakeup alarm to " + alarm.packageName);
|
||||
@@ -3273,7 +3512,7 @@ public class AlarmManagerService extends SystemService {
|
||||
mActivityManagerInternal.noteAlarmStart(alarm.operation, alarm.workSource,
|
||||
alarm.uid, alarm.statsTag);
|
||||
}
|
||||
mDeliveryTracker.deliverLocked(alarm, nowELAPSED, allowWhileIdle);
|
||||
mDeliveryTracker.deliverLocked(alarm, nowELAPSED);
|
||||
} catch (RuntimeException e) {
|
||||
Slog.w(TAG, "Failure sending alarm.", e);
|
||||
}
|
||||
@@ -3282,9 +3521,10 @@ public class AlarmManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isExemptFromAppStandby(Alarm a) {
|
||||
@VisibleForTesting
|
||||
static boolean isExemptFromAppStandby(Alarm a) {
|
||||
return a.alarmClock != null || UserHandle.isCore(a.creatorUid)
|
||||
|| (a.flags & FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED) != 0;
|
||||
|| (a.flags & (FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED | FLAG_ALLOW_WHILE_IDLE)) != 0;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -3368,6 +3608,11 @@ public class AlarmManagerService extends SystemService {
|
||||
MATCH_SYSTEM_ONLY, USER_SYSTEM);
|
||||
}
|
||||
|
||||
IAppOpsService getAppOpsService() {
|
||||
return IAppOpsService.Stub.asInterface(
|
||||
ServiceManager.getService(Context.APP_OPS_SERVICE));
|
||||
}
|
||||
|
||||
ClockReceiver getClockReceiver(AlarmManagerService service) {
|
||||
return service.new ClockReceiver();
|
||||
}
|
||||
@@ -3566,6 +3811,7 @@ public class AlarmManagerService extends SystemService {
|
||||
public static final int APP_STANDBY_BUCKET_CHANGED = 5;
|
||||
public static final int CHARGING_STATUS_CHANGED = 6;
|
||||
public static final int REMOVE_FOR_CANCELED = 7;
|
||||
public static final int REMOVE_EXACT_ALARMS = 8;
|
||||
|
||||
AlarmHandler() {
|
||||
super(Looper.myLooper());
|
||||
@@ -3645,6 +3891,14 @@ public class AlarmManagerService extends SystemService {
|
||||
}
|
||||
break;
|
||||
|
||||
case REMOVE_EXACT_ALARMS:
|
||||
final int uid = msg.arg1;
|
||||
final String packageName = (String) msg.obj;
|
||||
synchronized (mLock) {
|
||||
removeExactAlarmsOnPermissionRevokedLocked(uid, packageName);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// nope, just ignore it
|
||||
break;
|
||||
@@ -3720,7 +3974,7 @@ public class AlarmManagerService extends SystemService {
|
||||
|
||||
setImpl(ELAPSED_REALTIME, mInjector.getElapsedRealtime() + tickEventDelay, 0,
|
||||
0, null, mTimeTickTrigger, TIME_TICK_TAG, flags, workSource, null,
|
||||
Process.myUid(), "android");
|
||||
Process.myUid(), "android", null);
|
||||
|
||||
// Finally, remember when we set the tick alarm
|
||||
synchronized (mLock) {
|
||||
@@ -3740,7 +3994,7 @@ public class AlarmManagerService extends SystemService {
|
||||
final WorkSource workSource = null; // Let system take blame for date change events.
|
||||
setImpl(RTC, calendar.getTimeInMillis(), 0, 0, mDateChangeSender, null, null,
|
||||
AlarmManager.FLAG_STANDALONE, workSource, null,
|
||||
Process.myUid(), "android");
|
||||
Process.myUid(), "android", null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4112,7 +4366,7 @@ public class AlarmManagerService extends SystemService {
|
||||
* Deliver an alarm and set up the post-delivery handling appropriately
|
||||
*/
|
||||
@GuardedBy("mLock")
|
||||
public void deliverLocked(Alarm alarm, long nowELAPSED, boolean allowWhileIdle) {
|
||||
public void deliverLocked(Alarm alarm, long nowELAPSED) {
|
||||
final long workSourceToken = ThreadLocalWorkSource.setUid(
|
||||
getAlarmAttributionUid(alarm));
|
||||
try {
|
||||
@@ -4122,10 +4376,8 @@ public class AlarmManagerService extends SystemService {
|
||||
|
||||
try {
|
||||
alarm.operation.send(getContext(), 0,
|
||||
mBackgroundIntent.putExtra(
|
||||
Intent.EXTRA_ALARM_COUNT, alarm.count),
|
||||
mDeliveryTracker, mHandler, null,
|
||||
allowWhileIdle ? mIdleOptions : null);
|
||||
mBackgroundIntent.putExtra(Intent.EXTRA_ALARM_COUNT, alarm.count),
|
||||
mDeliveryTracker, mHandler, null, alarm.mIdleOptions);
|
||||
} catch (PendingIntent.CanceledException e) {
|
||||
if (alarm.repeatInterval > 0) {
|
||||
// This IntentSender is no longer valid, but this
|
||||
@@ -4194,7 +4446,7 @@ public class AlarmManagerService extends SystemService {
|
||||
if (inflight.isBroadcast()) {
|
||||
notifyBroadcastAlarmPendingLocked(alarm.uid);
|
||||
}
|
||||
if (allowWhileIdle) {
|
||||
if (isAllowedWhileIdleRestricted(alarm)) {
|
||||
final boolean doze = (mPendingIdleUntil != null);
|
||||
final boolean batterySaver = (mAppStateTracker != null
|
||||
&& mAppStateTracker.isForceAllAppsStandbyEnabled());
|
||||
@@ -4204,8 +4456,7 @@ public class AlarmManagerService extends SystemService {
|
||||
mAllowWhileIdleHistory.recordAlarmForPackage(alarm.sourcePackage,
|
||||
UserHandle.getUserId(alarm.creatorUid), nowELAPSED);
|
||||
mAlarmStore.updateAlarmDeliveries(a -> {
|
||||
if (a.creatorUid != alarm.creatorUid
|
||||
|| (a.flags & FLAG_ALLOW_WHILE_IDLE) == 0) {
|
||||
if (a.creatorUid != alarm.creatorUid || !isAllowedWhileIdleRestricted(a)) {
|
||||
return false;
|
||||
}
|
||||
return (doze && adjustDeliveryTimeBasedOnDeviceIdle(a))
|
||||
|
||||
@@ -145,6 +145,7 @@ package android {
|
||||
field public static final String REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE = "android.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE";
|
||||
field public static final String REQUEST_PASSWORD_COMPLEXITY = "android.permission.REQUEST_PASSWORD_COMPLEXITY";
|
||||
field @Deprecated public static final String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES";
|
||||
field public static final String SCHEDULE_EXACT_ALARM = "android.permission.SCHEDULE_EXACT_ALARM";
|
||||
field public static final String SEND_RESPOND_VIA_MESSAGE = "android.permission.SEND_RESPOND_VIA_MESSAGE";
|
||||
field public static final String SEND_SMS = "android.permission.SEND_SMS";
|
||||
field public static final String SET_ALARM = "com.android.alarm.permission.SET_ALARM";
|
||||
@@ -4328,16 +4329,17 @@ package android.app {
|
||||
}
|
||||
|
||||
public class AlarmManager {
|
||||
method public boolean canScheduleExactAlarms();
|
||||
method public void cancel(android.app.PendingIntent);
|
||||
method public void cancel(android.app.AlarmManager.OnAlarmListener);
|
||||
method public android.app.AlarmManager.AlarmClockInfo getNextAlarmClock();
|
||||
method public void set(int, long, android.app.PendingIntent);
|
||||
method public void set(int, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler);
|
||||
method public void setAlarmClock(android.app.AlarmManager.AlarmClockInfo, android.app.PendingIntent);
|
||||
method @RequiresPermission(android.Manifest.permission.SCHEDULE_EXACT_ALARM) public void setAlarmClock(android.app.AlarmManager.AlarmClockInfo, android.app.PendingIntent);
|
||||
method public void setAndAllowWhileIdle(int, long, android.app.PendingIntent);
|
||||
method public void setExact(int, long, android.app.PendingIntent);
|
||||
method public void setExact(int, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler);
|
||||
method public void setExactAndAllowWhileIdle(int, long, android.app.PendingIntent);
|
||||
method @RequiresPermission(value=android.Manifest.permission.SCHEDULE_EXACT_ALARM, conditional=true) public void setExactAndAllowWhileIdle(int, long, android.app.PendingIntent);
|
||||
method public void setInexactRepeating(int, long, long, android.app.PendingIntent);
|
||||
method public void setRepeating(int, long, long, android.app.PendingIntent);
|
||||
method @RequiresPermission(android.Manifest.permission.SET_TIME) public void setTime(long);
|
||||
|
||||
@@ -795,7 +795,8 @@ public class AppOpsManager {
|
||||
// when adding one of these:
|
||||
// - increment _NUM_OP
|
||||
// - define an OPSTR_* constant (marked as @SystemApi)
|
||||
// - add rows to sOpToSwitch, sOpToString, sOpNames, sOpToPerms, sOpDefault
|
||||
// - add rows to sOpToSwitch, sOpToString, sOpNames, sOpPerms, sOpDefaultMode, sOpDisableReset,
|
||||
// sOpRestrictions, sOpAllowSystemRestrictionBypass
|
||||
// - add descriptive strings to Settings/res/values/arrays.xml
|
||||
// - add the op to the appropriate template in AppOpsState.OpsTemplate (settings app)
|
||||
|
||||
@@ -1174,13 +1175,19 @@ public class AppOpsManager {
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
// TODO: Add as AppProtoEnums
|
||||
public static final int OP_RECORD_AUDIO_OUTPUT = 106;
|
||||
public static final int OP_RECORD_AUDIO_OUTPUT = AppProtoEnums.APP_OP_RECORD_AUDIO_OUTPUT;
|
||||
|
||||
/**
|
||||
* App can schedule exact alarm to perform timing based background work
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final int OP_SCHEDULE_EXACT_ALARM = AppProtoEnums.APP_OP_SCHEDULE_EXACT_ALARM;
|
||||
|
||||
|
||||
/** @hide */
|
||||
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
||||
public static final int _NUM_OP = 107;
|
||||
public static final int _NUM_OP = 108;
|
||||
|
||||
/** Access to coarse location information. */
|
||||
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
|
||||
@@ -1553,6 +1560,13 @@ public class AppOpsManager {
|
||||
*/
|
||||
public static final String OPSTR_RECORD_AUDIO_OUTPUT = "android:record_audio_output";
|
||||
|
||||
/**
|
||||
* App can schedule exact alarm to perform timing based background work.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final String OPSTR_SCHEDULE_EXACT_ALARM = "android:schedule_exact_alarm";
|
||||
|
||||
/** {@link #sAppOpsToNote} not initialized yet for this op */
|
||||
private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
|
||||
/** Should not collect noting of this app-op in {@link #sAppOpsToNote} */
|
||||
@@ -1633,6 +1647,7 @@ public class AppOpsManager {
|
||||
OP_LOADER_USAGE_STATS,
|
||||
OP_MANAGE_ONGOING_CALLS,
|
||||
OP_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER,
|
||||
OP_SCHEDULE_EXACT_ALARM,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1751,6 +1766,7 @@ public class AppOpsManager {
|
||||
OP_MANAGE_CREDENTIALS, // MANAGE_CREDENTIALS
|
||||
OP_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
|
||||
OP_RECORD_AUDIO_OUTPUT, // RECORD_AUDIO_OUTPUT
|
||||
OP_SCHEDULE_EXACT_ALARM, // SCHEDULE_EXACT_ALARM
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1864,6 +1880,7 @@ public class AppOpsManager {
|
||||
OPSTR_MANAGE_CREDENTIALS,
|
||||
OPSTR_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER,
|
||||
OPSTR_RECORD_AUDIO_OUTPUT,
|
||||
OPSTR_SCHEDULE_EXACT_ALARM,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1978,6 +1995,7 @@ public class AppOpsManager {
|
||||
"MANAGE_CREDENTIALS",
|
||||
"USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER",
|
||||
"RECORD_AUDIO_OUTPUT",
|
||||
"SCHEDULE_EXACT_ALARM",
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -2093,6 +2111,7 @@ public class AppOpsManager {
|
||||
null, // no permission for OP_MANAGE_CREDENTIALS
|
||||
Manifest.permission.USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER,
|
||||
null, // no permission for OP_RECORD_AUDIO_OUTPUT
|
||||
Manifest.permission.SCHEDULE_EXACT_ALARM,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -2208,6 +2227,7 @@ public class AppOpsManager {
|
||||
null, // MANAGE_CREDENTIALS
|
||||
null, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
|
||||
null, // RECORD_AUDIO_OUTPUT
|
||||
null, // SCHEDULE_EXACT_ALARM
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -2322,6 +2342,7 @@ public class AppOpsManager {
|
||||
null, // MANAGE_CREDENTIALS
|
||||
null, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
|
||||
null, // RECORD_AUDIO_OUTPUT
|
||||
null, // SCHEDULE_EXACT_ALARM
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -2435,6 +2456,7 @@ public class AppOpsManager {
|
||||
AppOpsManager.MODE_DEFAULT, // MANAGE_CREDENTIALS
|
||||
AppOpsManager.MODE_DEFAULT, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
|
||||
AppOpsManager.MODE_ALLOWED, // RECORD_AUDIO_OUTPUT
|
||||
AppOpsManager.MODE_DEFAULT, // SCHEDULE_EXACT_ALARM
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -2552,6 +2574,7 @@ public class AppOpsManager {
|
||||
false, // MANAGE_CREDENTIALS
|
||||
true, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
|
||||
false, // RECORD_AUDIO_OUTPUT
|
||||
false, // SCHEDULE_EXACT_ALARM
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -3905,6 +3905,12 @@
|
||||
<permission android:name="android.permission.SET_KEYBOARD_LAYOUT"
|
||||
android:protectionLevel="signature" />
|
||||
|
||||
<!-- Allows an app to use exact alarm scheduling APIs to perform timing
|
||||
sensitive background work.
|
||||
-->
|
||||
<permission android:name="android.permission.SCHEDULE_EXACT_ALARM"
|
||||
android:protectionLevel="normal|appop"/>
|
||||
|
||||
<!-- Allows an application to query tablet mode state and monitor changes
|
||||
in it.
|
||||
<p>Not for use by third-party applications.
|
||||
|
||||
@@ -18,6 +18,7 @@ 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.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_STANDALONE;
|
||||
@@ -25,11 +26,14 @@ import static android.app.AlarmManager.FLAG_WAKE_FROM_IDLE;
|
||||
import static android.app.AlarmManager.RTC;
|
||||
import static android.app.AlarmManager.RTC_WAKEUP;
|
||||
import static android.app.AlarmManager.WINDOW_EXACT;
|
||||
import static android.app.AlarmManager.WINDOW_HEURISTIC;
|
||||
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
|
||||
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
|
||||
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
|
||||
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED;
|
||||
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
|
||||
import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
|
||||
import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED;
|
||||
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
|
||||
@@ -44,7 +48,7 @@ import static com.android.server.alarm.AlarmManagerService.ACTIVE_INDEX;
|
||||
import static com.android.server.alarm.AlarmManagerService.AlarmHandler.APP_STANDBY_BUCKET_CHANGED;
|
||||
import static com.android.server.alarm.AlarmManagerService.AlarmHandler.CHARGING_STATUS_CHANGED;
|
||||
import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REMOVE_FOR_CANCELED;
|
||||
import static com.android.server.alarm.AlarmManagerService.Constants.ALLOW_WHILE_IDLE_WINDOW;
|
||||
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA;
|
||||
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_QUOTA;
|
||||
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION;
|
||||
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_LAZY_BATCHING;
|
||||
@@ -61,6 +65,7 @@ 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.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
@@ -75,20 +80,29 @@ import static org.mockito.ArgumentMatchers.isNull;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.never;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManagerInternal;
|
||||
import android.app.AlarmManager;
|
||||
import android.app.AppOpsManager;
|
||||
import android.app.IActivityManager;
|
||||
import android.app.IAlarmCompleteListener;
|
||||
import android.app.IAlarmListener;
|
||||
import android.app.IAlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.compat.CompatChanges;
|
||||
import android.app.usage.UsageStatsManagerInternal;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.PermissionChecker;
|
||||
import android.os.BatteryManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.PowerManager;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.UserHandle;
|
||||
@@ -102,9 +116,12 @@ import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.dx.mockito.inline.extended.MockedVoidMethod;
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.app.IAppOpsCallback;
|
||||
import com.android.internal.app.IAppOpsService;
|
||||
import com.android.server.AlarmManagerInternal;
|
||||
import com.android.server.AppStateTracker;
|
||||
import com.android.server.AppStateTrackerImpl;
|
||||
import com.android.server.DeviceIdleInternal;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.SystemService;
|
||||
import com.android.server.usage.AppStandbyInternal;
|
||||
@@ -125,6 +142,7 @@ import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.LongConsumer;
|
||||
|
||||
@Presubmit
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@@ -134,14 +152,21 @@ public class AlarmManagerServiceTest {
|
||||
private static final int TEST_CALLING_USER = UserHandle.getUserId(TEST_CALLING_UID);
|
||||
|
||||
private long mAppStandbyWindow;
|
||||
private long mAllowWhileIdleWindow;
|
||||
private AlarmManagerService mService;
|
||||
private AppStandbyInternal.AppIdleStateChangeListener mAppStandbyListener;
|
||||
private AlarmManagerService.ChargingReceiver mChargingReceiver;
|
||||
private IAppOpsCallback mIAppOpsCallback;
|
||||
private IAlarmManager mBinder;
|
||||
@Mock
|
||||
private Context mMockContext;
|
||||
@Mock
|
||||
private IActivityManager mIActivityManager;
|
||||
@Mock
|
||||
private IAppOpsService mIAppOpsService;
|
||||
@Mock
|
||||
private DeviceIdleInternal mDeviceIdleInternal;
|
||||
@Mock
|
||||
private UsageStatsManagerInternal mUsageStatsManagerInternal;
|
||||
@Mock
|
||||
private AppStandbyInternal mAppStandbyInternal;
|
||||
@@ -280,6 +305,11 @@ public class AlarmManagerServiceTest {
|
||||
// "IllegalStateException: Querying activity state off main thread is not allowed."
|
||||
// when AlarmManager calls DeviceConfig.addOnPropertiesChangedListener().
|
||||
}
|
||||
|
||||
@Override
|
||||
IAppOpsService getAppOpsService() {
|
||||
return mIAppOpsService;
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
@@ -287,15 +317,20 @@ public class AlarmManagerServiceTest {
|
||||
mMockingSession = mockitoSession()
|
||||
.initMocks(this)
|
||||
.spyStatic(ActivityManager.class)
|
||||
.mockStatic(CompatChanges.class)
|
||||
.spyStatic(DeviceConfig.class)
|
||||
.mockStatic(LocalServices.class)
|
||||
.spyStatic(Looper.class)
|
||||
.mockStatic(PermissionChecker.class)
|
||||
.mockStatic(Settings.Global.class)
|
||||
.mockStatic(ServiceManager.class)
|
||||
.spyStatic(UserHandle.class)
|
||||
.strictness(Strictness.WARN)
|
||||
.startMocking();
|
||||
|
||||
doReturn(mIActivityManager).when(ActivityManager::getService);
|
||||
doReturn(mDeviceIdleInternal).when(
|
||||
() -> LocalServices.getService(DeviceIdleInternal.class));
|
||||
doReturn(mActivityManagerInternal).when(
|
||||
() -> LocalServices.getService(ActivityManagerInternal.class));
|
||||
doReturn(mAppStateTracker).when(() -> LocalServices.getService(AppStateTracker.class));
|
||||
@@ -330,6 +365,9 @@ public class AlarmManagerServiceTest {
|
||||
() -> DeviceConfig.getProperties(
|
||||
eq(DeviceConfig.NAMESPACE_ALARM_MANAGER), ArgumentMatchers.<String>any()));
|
||||
|
||||
when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(
|
||||
mock(AppOpsManager.class));
|
||||
|
||||
mInjector = new Injector(mMockContext);
|
||||
mService = new AlarmManagerService(mMockContext, mInjector);
|
||||
spyOn(mService);
|
||||
@@ -346,6 +384,7 @@ public class AlarmManagerServiceTest {
|
||||
// Other boot phases don't matter
|
||||
mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
|
||||
mAppStandbyWindow = mService.mConstants.APP_STANDBY_WINDOW;
|
||||
mAllowWhileIdleWindow = mService.mConstants.ALLOW_WHILE_IDLE_WINDOW;
|
||||
ArgumentCaptor<AppStandbyInternal.AppIdleStateChangeListener> captor =
|
||||
ArgumentCaptor.forClass(AppStandbyInternal.AppIdleStateChangeListener.class);
|
||||
verify(mAppStandbyInternal).addListener(captor.capture());
|
||||
@@ -358,6 +397,20 @@ public class AlarmManagerServiceTest {
|
||||
&& filter.hasAction(BatteryManager.ACTION_DISCHARGING)));
|
||||
mChargingReceiver = chargingReceiverCaptor.getValue();
|
||||
|
||||
ArgumentCaptor<IBinder> binderCaptor = ArgumentCaptor.forClass(IBinder.class);
|
||||
verify(() -> ServiceManager.addService(eq(Context.ALARM_SERVICE), binderCaptor.capture(),
|
||||
anyBoolean(), anyInt()));
|
||||
mBinder = IAlarmManager.Stub.asInterface(binderCaptor.getValue());
|
||||
|
||||
ArgumentCaptor<IAppOpsCallback> appOpsCallbackCaptor = ArgumentCaptor.forClass(
|
||||
IAppOpsCallback.class);
|
||||
try {
|
||||
verify(mIAppOpsService).startWatchingMode(eq(AppOpsManager.OP_SCHEDULE_EXACT_ALARM),
|
||||
isNull(), appOpsCallbackCaptor.capture());
|
||||
} catch (RemoteException e) {
|
||||
// Not expected on a mock.
|
||||
}
|
||||
mIAppOpsCallback = appOpsCallbackCaptor.getValue();
|
||||
setTestableQuotas();
|
||||
}
|
||||
|
||||
@@ -389,13 +442,18 @@ public class AlarmManagerServiceTest {
|
||||
|
||||
private void setTestAlarm(int type, long triggerTime, PendingIntent operation, long interval,
|
||||
int flags, int callingUid) {
|
||||
setTestAlarm(type, triggerTime, operation, interval, flags, callingUid, null);
|
||||
}
|
||||
|
||||
private void setTestAlarm(int type, long triggerTime, PendingIntent operation, long interval,
|
||||
int flags, int callingUid, Bundle idleOptions) {
|
||||
mService.setImpl(type, triggerTime, WINDOW_EXACT, interval, operation, null, "test", flags,
|
||||
null, null, callingUid, TEST_CALLING_PACKAGE);
|
||||
null, null, callingUid, TEST_CALLING_PACKAGE, idleOptions);
|
||||
}
|
||||
|
||||
private void setTestAlarmWithListener(int type, long triggerTime, IAlarmListener listener) {
|
||||
mService.setImpl(type, triggerTime, WINDOW_EXACT, 0, null, listener, "test",
|
||||
FLAG_STANDALONE, null, null, TEST_CALLING_UID, TEST_CALLING_PACKAGE);
|
||||
FLAG_STANDALONE, null, null, TEST_CALLING_UID, TEST_CALLING_PACKAGE, null);
|
||||
}
|
||||
|
||||
|
||||
@@ -506,14 +564,27 @@ public class AlarmManagerServiceTest {
|
||||
setDeviceConfigLong(KEY_MIN_INTERVAL, 10);
|
||||
setDeviceConfigLong(KEY_MAX_INTERVAL, 15);
|
||||
setDeviceConfigInt(KEY_ALLOW_WHILE_IDLE_QUOTA, 20);
|
||||
setDeviceConfigInt(KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA, 25);
|
||||
setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION, 30);
|
||||
setDeviceConfigLong(KEY_LISTENER_TIMEOUT, 35);
|
||||
assertEquals(5, mService.mConstants.MIN_FUTURITY);
|
||||
assertEquals(10, mService.mConstants.MIN_INTERVAL);
|
||||
assertEquals(15, mService.mConstants.MAX_INTERVAL);
|
||||
assertEquals(20, mService.mConstants.ALLOW_WHILE_IDLE_QUOTA);
|
||||
assertEquals(25, mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA);
|
||||
assertEquals(30, mService.mConstants.ALLOW_WHILE_IDLE_WHITELIST_DURATION);
|
||||
assertEquals(35, mService.mConstants.LISTENER_TIMEOUT);
|
||||
|
||||
// Test safeguards.
|
||||
setDeviceConfigInt(KEY_ALLOW_WHILE_IDLE_QUOTA, -3);
|
||||
assertEquals(1, mService.mConstants.ALLOW_WHILE_IDLE_QUOTA);
|
||||
setDeviceConfigInt(KEY_ALLOW_WHILE_IDLE_QUOTA, 0);
|
||||
assertEquals(1, mService.mConstants.ALLOW_WHILE_IDLE_QUOTA);
|
||||
|
||||
setDeviceConfigInt(KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA, -8);
|
||||
assertEquals(1, mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA);
|
||||
setDeviceConfigInt(KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA, 0);
|
||||
assertEquals(1, mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -559,56 +630,45 @@ public class AlarmManagerServiceTest {
|
||||
assertEquals(mNowElapsedTest + 9, mTestTimer.getElapsed());
|
||||
}
|
||||
|
||||
private void testQuotasDeferralOnSet(int standbyBucket) throws Exception {
|
||||
final int quota = mService.getQuotaForBucketLocked(standbyBucket);
|
||||
when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
|
||||
anyLong())).thenReturn(standbyBucket);
|
||||
private void testQuotasDeferralOnSet(LongConsumer alarmSetter, int quota, long window)
|
||||
throws Exception {
|
||||
final long firstTrigger = mNowElapsedTest + 10;
|
||||
for (int i = 0; i < quota; i++) {
|
||||
setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
|
||||
getNewMockPendingIntent());
|
||||
alarmSetter.accept(firstTrigger + i);
|
||||
mNowElapsedTest = mTestTimer.getElapsed();
|
||||
mTestTimer.expire();
|
||||
}
|
||||
// This one should get deferred on set
|
||||
setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota,
|
||||
getNewMockPendingIntent());
|
||||
final long expectedNextTrigger = firstTrigger + mAppStandbyWindow;
|
||||
alarmSetter.accept(firstTrigger + quota);
|
||||
final long expectedNextTrigger = firstTrigger + window;
|
||||
assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed());
|
||||
}
|
||||
|
||||
private void testQuotasDeferralOnExpiration(int standbyBucket) throws Exception {
|
||||
final int quota = mService.getQuotaForBucketLocked(standbyBucket);
|
||||
when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
|
||||
anyLong())).thenReturn(standbyBucket);
|
||||
private void testQuotasDeferralOnExpiration(LongConsumer alarmSetter, int quota, long window)
|
||||
throws Exception {
|
||||
final long firstTrigger = mNowElapsedTest + 10;
|
||||
for (int i = 0; i < quota; i++) {
|
||||
setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
|
||||
getNewMockPendingIntent());
|
||||
alarmSetter.accept(firstTrigger + i);
|
||||
}
|
||||
// This one should get deferred after the latest alarm expires
|
||||
setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota,
|
||||
getNewMockPendingIntent());
|
||||
// This one should get deferred after the latest alarm expires.
|
||||
alarmSetter.accept(firstTrigger + quota);
|
||||
for (int i = 0; i < quota; i++) {
|
||||
mNowElapsedTest = mTestTimer.getElapsed();
|
||||
mTestTimer.expire();
|
||||
}
|
||||
final long expectedNextTrigger = firstTrigger + mAppStandbyWindow;
|
||||
final long expectedNextTrigger = firstTrigger + window;
|
||||
assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed());
|
||||
}
|
||||
|
||||
private void testQuotasNoDeferral(int standbyBucket) throws Exception {
|
||||
final int quota = mService.getQuotaForBucketLocked(standbyBucket);
|
||||
when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
|
||||
anyLong())).thenReturn(standbyBucket);
|
||||
private void testQuotasNoDeferral(LongConsumer alarmSetter, int quota, long window)
|
||||
throws Exception {
|
||||
final long firstTrigger = mNowElapsedTest + 10;
|
||||
for (int i = 0; i < quota; i++) {
|
||||
setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 10 + i,
|
||||
getNewMockPendingIntent());
|
||||
alarmSetter.accept(firstTrigger + i);
|
||||
}
|
||||
// This delivery time maintains the quota invariant. Should not be deferred.
|
||||
final long expectedNextTrigger = firstTrigger + mAppStandbyWindow + 5;
|
||||
setTestAlarm(ELAPSED_REALTIME_WAKEUP, expectedNextTrigger, getNewMockPendingIntent());
|
||||
final long expectedNextTrigger = firstTrigger + window + 5;
|
||||
alarmSetter.accept(expectedNextTrigger);
|
||||
for (int i = 0; i < quota; i++) {
|
||||
mNowElapsedTest = mTestTimer.getElapsed();
|
||||
mTestTimer.expire();
|
||||
@@ -616,64 +676,88 @@ public class AlarmManagerServiceTest {
|
||||
assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed());
|
||||
}
|
||||
|
||||
private void testStandbyQuotasDeferralOnSet(int standbyBucket) throws Exception {
|
||||
final int quota = mService.getQuotaForBucketLocked(standbyBucket);
|
||||
when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
|
||||
anyLong())).thenReturn(standbyBucket);
|
||||
testQuotasDeferralOnSet(trigger -> setTestAlarm(ELAPSED_REALTIME_WAKEUP, trigger,
|
||||
getNewMockPendingIntent()), quota, mAppStandbyWindow);
|
||||
}
|
||||
|
||||
private void testStandbyQuotasDeferralOnExpiration(int standbyBucket) throws Exception {
|
||||
final int quota = mService.getQuotaForBucketLocked(standbyBucket);
|
||||
when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
|
||||
anyLong())).thenReturn(standbyBucket);
|
||||
testQuotasDeferralOnExpiration(trigger -> setTestAlarm(ELAPSED_REALTIME_WAKEUP, trigger,
|
||||
getNewMockPendingIntent()), quota, mAppStandbyWindow);
|
||||
}
|
||||
|
||||
private void testStandbyQuotasNoDeferral(int standbyBucket) throws Exception {
|
||||
final int quota = mService.getQuotaForBucketLocked(standbyBucket);
|
||||
when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
|
||||
anyLong())).thenReturn(standbyBucket);
|
||||
testQuotasNoDeferral(trigger -> setTestAlarm(ELAPSED_REALTIME_WAKEUP, trigger,
|
||||
getNewMockPendingIntent()), quota, mAppStandbyWindow);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActiveQuota_deferredOnSet() throws Exception {
|
||||
testQuotasDeferralOnSet(STANDBY_BUCKET_ACTIVE);
|
||||
testStandbyQuotasDeferralOnSet(STANDBY_BUCKET_ACTIVE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActiveQuota_deferredOnExpiration() throws Exception {
|
||||
testQuotasDeferralOnExpiration(STANDBY_BUCKET_ACTIVE);
|
||||
testStandbyQuotasDeferralOnExpiration(STANDBY_BUCKET_ACTIVE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActiveQuota_notDeferred() throws Exception {
|
||||
testQuotasNoDeferral(STANDBY_BUCKET_ACTIVE);
|
||||
testStandbyQuotasNoDeferral(STANDBY_BUCKET_ACTIVE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWorkingQuota_deferredOnSet() throws Exception {
|
||||
testQuotasDeferralOnSet(STANDBY_BUCKET_WORKING_SET);
|
||||
testStandbyQuotasDeferralOnSet(STANDBY_BUCKET_WORKING_SET);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWorkingQuota_deferredOnExpiration() throws Exception {
|
||||
testQuotasDeferralOnExpiration(STANDBY_BUCKET_WORKING_SET);
|
||||
testStandbyQuotasDeferralOnExpiration(STANDBY_BUCKET_WORKING_SET);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWorkingQuota_notDeferred() throws Exception {
|
||||
testQuotasNoDeferral(STANDBY_BUCKET_WORKING_SET);
|
||||
testStandbyQuotasNoDeferral(STANDBY_BUCKET_WORKING_SET);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFrequentQuota_deferredOnSet() throws Exception {
|
||||
testQuotasDeferralOnSet(STANDBY_BUCKET_FREQUENT);
|
||||
testStandbyQuotasDeferralOnSet(STANDBY_BUCKET_FREQUENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFrequentQuota_deferredOnExpiration() throws Exception {
|
||||
testQuotasDeferralOnExpiration(STANDBY_BUCKET_FREQUENT);
|
||||
testStandbyQuotasDeferralOnExpiration(STANDBY_BUCKET_FREQUENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFrequentQuota_notDeferred() throws Exception {
|
||||
testQuotasNoDeferral(STANDBY_BUCKET_FREQUENT);
|
||||
testStandbyQuotasNoDeferral(STANDBY_BUCKET_FREQUENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRareQuota_deferredOnSet() throws Exception {
|
||||
testQuotasDeferralOnSet(STANDBY_BUCKET_RARE);
|
||||
testStandbyQuotasDeferralOnSet(STANDBY_BUCKET_RARE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRareQuota_deferredOnExpiration() throws Exception {
|
||||
testQuotasDeferralOnExpiration(STANDBY_BUCKET_RARE);
|
||||
testStandbyQuotasDeferralOnExpiration(STANDBY_BUCKET_RARE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRareQuota_notDeferred() throws Exception {
|
||||
testQuotasNoDeferral(STANDBY_BUCKET_RARE);
|
||||
testStandbyQuotasNoDeferral(STANDBY_BUCKET_RARE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -731,7 +815,7 @@ public class AlarmManagerServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQuotaDowngrade() throws Exception {
|
||||
public void testStandbyQuotaDowngrade() throws Exception {
|
||||
final int workingQuota = mService.getQuotaForBucketLocked(STANDBY_BUCKET_WORKING_SET);
|
||||
when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
|
||||
anyLong())).thenReturn(STANDBY_BUCKET_WORKING_SET);
|
||||
@@ -758,7 +842,7 @@ public class AlarmManagerServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQuotaUpgrade() throws Exception {
|
||||
public void testStandbyQuotaUpgrade() throws Exception {
|
||||
final int frequentQuota = mService.getQuotaForBucketLocked(STANDBY_BUCKET_FREQUENT);
|
||||
when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
|
||||
anyLong())).thenReturn(STANDBY_BUCKET_FREQUENT);
|
||||
@@ -1308,7 +1392,7 @@ public class AlarmManagerServiceTest {
|
||||
public void allowWhileIdleAlarmsWhileDeviceIdle() throws Exception {
|
||||
doReturn(0).when(mService).fuzzForDuration(anyLong());
|
||||
|
||||
setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + ALLOW_WHILE_IDLE_WINDOW + 1000,
|
||||
setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + mAllowWhileIdleWindow + 1000,
|
||||
getNewMockPendingIntent());
|
||||
assertNotNull(mService.mPendingIdleUntil);
|
||||
|
||||
@@ -1323,7 +1407,7 @@ public class AlarmManagerServiceTest {
|
||||
// This one should get deferred on set.
|
||||
setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota,
|
||||
getNewMockPendingIntent(), false);
|
||||
final long expectedNextTrigger = firstTrigger + ALLOW_WHILE_IDLE_WINDOW;
|
||||
final long expectedNextTrigger = firstTrigger + mAllowWhileIdleWindow;
|
||||
assertEquals("Incorrect trigger when no quota left", expectedNextTrigger,
|
||||
mTestTimer.getElapsed());
|
||||
|
||||
@@ -1449,61 +1533,24 @@ public class AlarmManagerServiceTest {
|
||||
when(mAppStateTracker.areAlarmsRestrictedByBatterySaver(TEST_CALLING_UID,
|
||||
TEST_CALLING_PACKAGE)).thenReturn(true);
|
||||
when(mAppStateTracker.isForceAllAppsStandbyEnabled()).thenReturn(true);
|
||||
|
||||
final int quota = mService.mConstants.ALLOW_WHILE_IDLE_QUOTA;
|
||||
long firstTrigger = mNowElapsedTest + 10;
|
||||
for (int i = 0; i < quota; i++) {
|
||||
setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
|
||||
getNewMockPendingIntent(), false);
|
||||
mNowElapsedTest = mTestTimer.getElapsed();
|
||||
mTestTimer.expire();
|
||||
}
|
||||
// This one should get deferred on set.
|
||||
setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota,
|
||||
getNewMockPendingIntent(), false);
|
||||
long expectedNextTrigger = firstTrigger + ALLOW_WHILE_IDLE_WINDOW;
|
||||
assertEquals("Incorrect trigger when no quota available", expectedNextTrigger,
|
||||
mTestTimer.getElapsed());
|
||||
|
||||
testQuotasDeferralOnSet(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, trigger,
|
||||
getNewMockPendingIntent(), false), quota, mAllowWhileIdleWindow);
|
||||
|
||||
// Refresh the state
|
||||
mService.removeLocked(TEST_CALLING_UID);
|
||||
mService.mAllowWhileIdleHistory.removeForPackage(TEST_CALLING_PACKAGE, TEST_CALLING_USER);
|
||||
|
||||
firstTrigger = mNowElapsedTest + 10;
|
||||
for (int i = 0; i < quota; i++) {
|
||||
setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
|
||||
getNewMockPendingIntent(), false);
|
||||
}
|
||||
// This one should get deferred after the latest alarm expires.
|
||||
setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota,
|
||||
getNewMockPendingIntent(), false);
|
||||
for (int i = 0; i < quota; i++) {
|
||||
mNowElapsedTest = mTestTimer.getElapsed();
|
||||
mTestTimer.expire();
|
||||
}
|
||||
expectedNextTrigger = firstTrigger + ALLOW_WHILE_IDLE_WINDOW;
|
||||
assertEquals("Incorrect trigger when no quota available", expectedNextTrigger,
|
||||
mTestTimer.getElapsed());
|
||||
testQuotasDeferralOnExpiration(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP,
|
||||
trigger, getNewMockPendingIntent(), false), quota, mAllowWhileIdleWindow);
|
||||
|
||||
// Refresh the state
|
||||
mService.removeLocked(TEST_CALLING_UID);
|
||||
mService.mAllowWhileIdleHistory.removeForPackage(TEST_CALLING_PACKAGE, TEST_CALLING_USER);
|
||||
|
||||
firstTrigger = mNowElapsedTest + 10;
|
||||
for (int i = 0; i < quota; i++) {
|
||||
setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
|
||||
getNewMockPendingIntent(), false);
|
||||
}
|
||||
// This delivery time maintains the quota invariant. Should not be deferred.
|
||||
expectedNextTrigger = firstTrigger + ALLOW_WHILE_IDLE_WINDOW + 5;
|
||||
setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, expectedNextTrigger,
|
||||
getNewMockPendingIntent(), false);
|
||||
for (int i = 0; i < quota; i++) {
|
||||
mNowElapsedTest = mTestTimer.getElapsed();
|
||||
mTestTimer.expire();
|
||||
}
|
||||
assertEquals("Incorrect trigger when no quota available", expectedNextTrigger,
|
||||
mTestTimer.getElapsed());
|
||||
testQuotasNoDeferral(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, trigger,
|
||||
getNewMockPendingIntent(), false), quota, mAllowWhileIdleWindow);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1544,6 +1591,259 @@ public class AlarmManagerServiceTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canScheduleExactAlarms() throws RemoteException {
|
||||
doReturn(PermissionChecker.PERMISSION_GRANTED).when(
|
||||
() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
|
||||
Manifest.permission.SCHEDULE_EXACT_ALARM));
|
||||
assertTrue(mBinder.canScheduleExactAlarms());
|
||||
|
||||
doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when(
|
||||
() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
|
||||
Manifest.permission.SCHEDULE_EXACT_ALARM));
|
||||
assertFalse(mBinder.canScheduleExactAlarms());
|
||||
|
||||
doReturn(PermissionChecker.PERMISSION_SOFT_DENIED).when(
|
||||
() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
|
||||
Manifest.permission.SCHEDULE_EXACT_ALARM));
|
||||
assertFalse(mBinder.canScheduleExactAlarms());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noPermissionCheckWhenChangeDisabled() throws RemoteException {
|
||||
doReturn(false).when(
|
||||
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
|
||||
anyString(), any(UserHandle.class)));
|
||||
|
||||
// alarm clock
|
||||
mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0, 0,
|
||||
getNewMockPendingIntent(), null, null, null,
|
||||
mock(AlarmManager.AlarmClockInfo.class));
|
||||
|
||||
// exact, allow-while-idle
|
||||
mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
|
||||
FLAG_ALLOW_WHILE_IDLE, getNewMockPendingIntent(), null, null, null, null);
|
||||
|
||||
// inexact, allow-while-idle
|
||||
mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_HEURISTIC, 0,
|
||||
FLAG_ALLOW_WHILE_IDLE, getNewMockPendingIntent(), null, null, null, null);
|
||||
|
||||
verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
|
||||
Manifest.permission.SCHEDULE_EXACT_ALARM), never());
|
||||
verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void exactAllowWhileIdleBinderCallWhenChangeDisabled() throws Exception {
|
||||
doReturn(false).when(
|
||||
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
|
||||
anyString(), any(UserHandle.class)));
|
||||
|
||||
final PendingIntent alarmPi = getNewMockPendingIntent();
|
||||
mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
|
||||
FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null);
|
||||
|
||||
final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
|
||||
verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L),
|
||||
eq(alarmPi), isNull(), isNull(),
|
||||
eq(FLAG_ALLOW_WHILE_IDLE_COMPAT | FLAG_STANDALONE), isNull(), isNull(),
|
||||
eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture());
|
||||
|
||||
final Bundle idleOptions = bundleCaptor.getValue();
|
||||
final int type = idleOptions.getInt("android:broadcast.temporaryAppWhitelistType");
|
||||
assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inexactAllowWhileIdleBinderCallWhenChangeDisabled() throws Exception {
|
||||
doReturn(false).when(
|
||||
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
|
||||
anyString(), any(UserHandle.class)));
|
||||
|
||||
final PendingIntent alarmPi = getNewMockPendingIntent();
|
||||
mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_HEURISTIC, 0,
|
||||
FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null);
|
||||
|
||||
final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
|
||||
verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), anyLong(), eq(0L),
|
||||
eq(alarmPi), isNull(), isNull(), eq(FLAG_ALLOW_WHILE_IDLE_COMPAT), isNull(),
|
||||
isNull(), eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture());
|
||||
|
||||
final Bundle idleOptions = bundleCaptor.getValue();
|
||||
final int type = idleOptions.getInt("android:broadcast.temporaryAppWhitelistType");
|
||||
assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void alarmClockBinderCallWhenChangeDisabled() throws Exception {
|
||||
doReturn(false).when(
|
||||
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
|
||||
anyString(), any(UserHandle.class)));
|
||||
|
||||
final PendingIntent alarmPi = getNewMockPendingIntent();
|
||||
final AlarmManager.AlarmClockInfo alarmClock = mock(AlarmManager.AlarmClockInfo.class);
|
||||
mBinder.set(TEST_CALLING_PACKAGE, RTC_WAKEUP, 1234, WINDOW_EXACT, 0, 0,
|
||||
alarmPi, null, null, null, alarmClock);
|
||||
|
||||
verify(mService).setImpl(eq(RTC_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L),
|
||||
eq(alarmPi), isNull(), isNull(), eq(FLAG_STANDALONE | FLAG_WAKE_FROM_IDLE),
|
||||
isNull(), eq(alarmClock), eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), isNull());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void alarmClockBinderCall() throws RemoteException {
|
||||
doReturn(true).when(
|
||||
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
|
||||
anyString(), any(UserHandle.class)));
|
||||
|
||||
doReturn(PermissionChecker.PERMISSION_GRANTED).when(
|
||||
() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
|
||||
Manifest.permission.SCHEDULE_EXACT_ALARM));
|
||||
|
||||
final PendingIntent alarmPi = getNewMockPendingIntent();
|
||||
final AlarmManager.AlarmClockInfo alarmClock = mock(AlarmManager.AlarmClockInfo.class);
|
||||
mBinder.set(TEST_CALLING_PACKAGE, RTC_WAKEUP, 1234, WINDOW_EXACT, 0, 0,
|
||||
alarmPi, null, null, null, alarmClock);
|
||||
|
||||
// Correct permission checks are invoked.
|
||||
verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
|
||||
Manifest.permission.SCHEDULE_EXACT_ALARM));
|
||||
verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt());
|
||||
|
||||
final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
|
||||
verify(mService).setImpl(eq(RTC_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L),
|
||||
eq(alarmPi), isNull(), isNull(), eq(FLAG_STANDALONE | FLAG_WAKE_FROM_IDLE),
|
||||
isNull(), eq(alarmClock), eq(Process.myUid()), eq(TEST_CALLING_PACKAGE),
|
||||
bundleCaptor.capture());
|
||||
|
||||
final Bundle idleOptions = bundleCaptor.getValue();
|
||||
final int type = idleOptions.getInt("android:broadcast.temporaryAppWhitelistType");
|
||||
assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void exactAllowWhileIdleBinderCallWithPermission() throws RemoteException {
|
||||
doReturn(true).when(
|
||||
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
|
||||
anyString(), any(UserHandle.class)));
|
||||
|
||||
// Permission check is granted by default by the mock.
|
||||
final PendingIntent alarmPi = getNewMockPendingIntent();
|
||||
mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
|
||||
FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null);
|
||||
|
||||
verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
|
||||
Manifest.permission.SCHEDULE_EXACT_ALARM));
|
||||
verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt());
|
||||
|
||||
final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
|
||||
verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L),
|
||||
eq(alarmPi), isNull(), isNull(),
|
||||
eq(FLAG_ALLOW_WHILE_IDLE | FLAG_STANDALONE), isNull(), isNull(),
|
||||
eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture());
|
||||
|
||||
final Bundle idleOptions = bundleCaptor.getValue();
|
||||
final int type = idleOptions.getInt("android:broadcast.temporaryAppWhitelistType");
|
||||
assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void exactAllowWhileIdleBinderCallWithAllowlist() throws RemoteException {
|
||||
doReturn(true).when(
|
||||
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
|
||||
anyString(), any(UserHandle.class)));
|
||||
// If permission is denied, only then allowlist will be checked.
|
||||
doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when(
|
||||
() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
|
||||
Manifest.permission.SCHEDULE_EXACT_ALARM));
|
||||
when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true);
|
||||
|
||||
final PendingIntent alarmPi = getNewMockPendingIntent();
|
||||
mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
|
||||
FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null);
|
||||
|
||||
verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
|
||||
Manifest.permission.SCHEDULE_EXACT_ALARM));
|
||||
verify(mDeviceIdleInternal).isAppOnWhitelist(UserHandle.getAppId(Process.myUid()));
|
||||
|
||||
final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
|
||||
verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L),
|
||||
eq(alarmPi), isNull(), isNull(),
|
||||
eq(FLAG_ALLOW_WHILE_IDLE_COMPAT | FLAG_STANDALONE), isNull(), isNull(),
|
||||
eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture());
|
||||
|
||||
final Bundle idleOptions = bundleCaptor.getValue();
|
||||
final int type = idleOptions.getInt("android:broadcast.temporaryAppWhitelistType");
|
||||
assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inexactAllowWhileIdleBinderCallWithAllowlist() throws RemoteException {
|
||||
doReturn(true).when(
|
||||
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
|
||||
anyString(), any(UserHandle.class)));
|
||||
|
||||
when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true);
|
||||
final PendingIntent alarmPi = getNewMockPendingIntent();
|
||||
mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 4321, WINDOW_HEURISTIC, 0,
|
||||
FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null);
|
||||
|
||||
verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
|
||||
Manifest.permission.SCHEDULE_EXACT_ALARM), never());
|
||||
verify(mDeviceIdleInternal).isAppOnWhitelist(UserHandle.getAppId(Process.myUid()));
|
||||
|
||||
final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
|
||||
verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(4321L), anyLong(), eq(0L),
|
||||
eq(alarmPi), isNull(), isNull(), eq(FLAG_ALLOW_WHILE_IDLE_COMPAT), isNull(),
|
||||
isNull(), eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture());
|
||||
|
||||
final Bundle idleOptions = bundleCaptor.getValue();
|
||||
final int type = idleOptions.getInt("android:broadcast.temporaryAppWhitelistType");
|
||||
assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inexactAllowWhileIdleBinderCallWithoutAllowlist() throws RemoteException {
|
||||
doReturn(true).when(
|
||||
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
|
||||
anyString(), any(UserHandle.class)));
|
||||
|
||||
when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(false);
|
||||
final PendingIntent alarmPi = getNewMockPendingIntent();
|
||||
mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 4321, WINDOW_HEURISTIC, 0,
|
||||
FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null);
|
||||
|
||||
verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
|
||||
Manifest.permission.SCHEDULE_EXACT_ALARM), never());
|
||||
verify(mDeviceIdleInternal).isAppOnWhitelist(UserHandle.getAppId(Process.myUid()));
|
||||
|
||||
final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
|
||||
verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(4321L), anyLong(), eq(0L),
|
||||
eq(alarmPi), isNull(), isNull(), eq(FLAG_ALLOW_WHILE_IDLE_COMPAT), isNull(),
|
||||
isNull(), eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture());
|
||||
|
||||
final Bundle idleOptions = bundleCaptor.getValue();
|
||||
final int type = idleOptions.getInt("android:broadcast.temporaryAppWhitelistType");
|
||||
assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED, type);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void idleOptionsSentOnExpiration() throws Exception {
|
||||
final long triggerTime = mNowElapsedTest + 5000;
|
||||
final PendingIntent alarmPi = getNewMockPendingIntent();
|
||||
final Bundle idleOptions = new Bundle();
|
||||
idleOptions.putChar("TEST_CHAR_KEY", 'x');
|
||||
idleOptions.putInt("TEST_INT_KEY", 53);
|
||||
setTestAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime, alarmPi, 0, 0, TEST_CALLING_UID,
|
||||
idleOptions);
|
||||
|
||||
mNowElapsedTest = mTestTimer.getElapsed();
|
||||
mTestTimer.expire();
|
||||
|
||||
verify(alarmPi).send(eq(mMockContext), eq(0), any(Intent.class),
|
||||
any(), any(Handler.class), isNull(), eq(idleOptions));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void alarmStoreMigration() {
|
||||
setDeviceConfigBoolean(KEY_LAZY_BATCHING, false);
|
||||
|
||||
@@ -69,12 +69,12 @@ public class AlarmStoreTest {
|
||||
mock(PendingIntent.class));
|
||||
return new Alarm(ELAPSED_REALTIME_WAKEUP, whenElapsed, whenElapsed, 0, 0,
|
||||
mock(PendingIntent.class), null, null, null, 0, info, TEST_CALLING_UID,
|
||||
TEST_CALLING_PACKAGE);
|
||||
TEST_CALLING_PACKAGE, null);
|
||||
}
|
||||
|
||||
private static Alarm createAlarm(int type, long whenElapsed, long windowLength, int flags) {
|
||||
return new Alarm(type, whenElapsed, whenElapsed, windowLength, 0, mock(PendingIntent.class),
|
||||
null, null, null, flags, null, TEST_CALLING_UID, TEST_CALLING_PACKAGE);
|
||||
null, null, null, flags, null, TEST_CALLING_UID, TEST_CALLING_PACKAGE, null);
|
||||
}
|
||||
|
||||
private void addAlarmsToStore(Alarm... alarms) {
|
||||
|
||||
@@ -17,10 +17,17 @@
|
||||
package com.android.server.alarm;
|
||||
|
||||
import static android.app.AlarmManager.ELAPSED_REALTIME;
|
||||
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_STANDALONE;
|
||||
import static android.app.AlarmManager.FLAG_WAKE_FROM_IDLE;
|
||||
import static android.app.AlarmManager.RTC_WAKEUP;
|
||||
|
||||
import static com.android.server.alarm.Alarm.APP_STANDBY_POLICY_INDEX;
|
||||
import static com.android.server.alarm.Alarm.NUM_POLICIES;
|
||||
import static com.android.server.alarm.Alarm.REQUESTER_POLICY_INDEX;
|
||||
import static com.android.server.alarm.AlarmManagerService.isExemptFromAppStandby;
|
||||
import static com.android.server.alarm.Constants.TEST_CALLING_PACKAGE;
|
||||
import static com.android.server.alarm.Constants.TEST_CALLING_UID;
|
||||
|
||||
@@ -28,7 +35,9 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
|
||||
@@ -43,15 +52,29 @@ import java.util.Random;
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class AlarmTest {
|
||||
|
||||
private Alarm createDefaultAlarm(long requestedElapsed, long windowLength) {
|
||||
private Alarm createDefaultAlarm(long requestedElapsed, long windowLength, int flags) {
|
||||
return new Alarm(ELAPSED_REALTIME, 0, requestedElapsed, windowLength, 0,
|
||||
mock(PendingIntent.class), null, null, null, 0, null, TEST_CALLING_UID,
|
||||
TEST_CALLING_PACKAGE);
|
||||
createAlarmSender(), null, null, null, flags, null, TEST_CALLING_UID,
|
||||
TEST_CALLING_PACKAGE, null);
|
||||
}
|
||||
|
||||
private Alarm createAlarmClock(long requestedRtc) {
|
||||
final AlarmManager.AlarmClockInfo info = mock(AlarmManager.AlarmClockInfo.class);
|
||||
return new Alarm(RTC_WAKEUP, requestedRtc, requestedRtc, 0, 0, createAlarmSender(),
|
||||
null, null, null, FLAG_WAKE_FROM_IDLE | FLAG_STANDALONE, info, TEST_CALLING_UID,
|
||||
TEST_CALLING_PACKAGE, null);
|
||||
}
|
||||
|
||||
private PendingIntent createAlarmSender() {
|
||||
final PendingIntent alarmPi = mock(PendingIntent.class);
|
||||
when(alarmPi.getCreatorPackage()).thenReturn(TEST_CALLING_PACKAGE);
|
||||
when(alarmPi.getCreatorUid()).thenReturn(TEST_CALLING_UID);
|
||||
return alarmPi;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void initSetsOnlyRequesterPolicy() {
|
||||
final Alarm a = createDefaultAlarm(4567, 2);
|
||||
final Alarm a = createDefaultAlarm(4567, 2, 0);
|
||||
|
||||
for (int i = 0; i < NUM_POLICIES; i++) {
|
||||
if (i == REQUESTER_POLICY_INDEX) {
|
||||
@@ -86,7 +109,7 @@ public class AlarmTest {
|
||||
|
||||
@Test
|
||||
public void whenElapsed() {
|
||||
final Alarm a = createDefaultAlarm(0, 0);
|
||||
final Alarm a = createDefaultAlarm(0, 0, 0);
|
||||
|
||||
final long[][] uniqueData = generatePolicyTestMatrix(NUM_POLICIES);
|
||||
for (int i = 0; i < NUM_POLICIES; i++) {
|
||||
@@ -104,7 +127,7 @@ public class AlarmTest {
|
||||
|
||||
@Test
|
||||
public void maxWhenElapsed() {
|
||||
final Alarm a = createDefaultAlarm(10, 12);
|
||||
final Alarm a = createDefaultAlarm(10, 12, 0);
|
||||
assertEquals(22, a.getMaxWhenElapsed());
|
||||
|
||||
a.setPolicyElapsed(REQUESTER_POLICY_INDEX, 15);
|
||||
@@ -128,7 +151,7 @@ public class AlarmTest {
|
||||
|
||||
@Test
|
||||
public void setPolicyElapsedExact() {
|
||||
final Alarm exactAlarm = createDefaultAlarm(10, 0);
|
||||
final Alarm exactAlarm = createDefaultAlarm(10, 0, 0);
|
||||
|
||||
assertTrue(exactAlarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, 4));
|
||||
assertTrue(exactAlarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 10));
|
||||
@@ -143,7 +166,7 @@ public class AlarmTest {
|
||||
|
||||
@Test
|
||||
public void setPolicyElapsedInexact() {
|
||||
final Alarm inexactAlarm = createDefaultAlarm(10, 5);
|
||||
final Alarm inexactAlarm = createDefaultAlarm(10, 5, 0);
|
||||
|
||||
assertTrue(inexactAlarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, 4));
|
||||
assertTrue(inexactAlarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 10));
|
||||
@@ -154,4 +177,20 @@ public class AlarmTest {
|
||||
|
||||
assertFalse(inexactAlarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 8));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isExemptFromStandby() {
|
||||
final long anything = 35412; // Arbitrary number, doesn't matter for this test.
|
||||
|
||||
assertFalse("Basic alarm exempt", isExemptFromAppStandby(
|
||||
createDefaultAlarm(anything, anything, 0)));
|
||||
assertFalse("FLAG_ALLOW_WHILE_IDLE_COMPAT exempt", isExemptFromAppStandby(
|
||||
createDefaultAlarm(anything, anything, FLAG_ALLOW_WHILE_IDLE_COMPAT)));
|
||||
|
||||
assertTrue("ALLOW_WHILE_IDLE not exempt", isExemptFromAppStandby(
|
||||
createDefaultAlarm(anything, anything, FLAG_ALLOW_WHILE_IDLE)));
|
||||
assertTrue("ALLOW_WHILE_IDLE_UNRESTRICTED not exempt", isExemptFromAppStandby(
|
||||
createDefaultAlarm(anything, anything, FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED)));
|
||||
assertTrue("Alarm clock not exempt", isExemptFromAppStandby(createAlarmClock(anything)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ public class BackgroundRestrictedAlarmsTest {
|
||||
}
|
||||
uidAlarms.add(new Alarm(
|
||||
removeIt ? RTC : RTC_WAKEUP,
|
||||
0, 0, 0, 0, null, null, null, null, 0, null, uid, name));
|
||||
0, 0, 0, 0, null, null, null, null, 0, null, uid, name, null));
|
||||
return all;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user