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: [
|
libs: [
|
||||||
|
"app-compat-annotations",
|
||||||
"framework-minus-apex",
|
"framework-minus-apex",
|
||||||
"unsupportedappusage",
|
"unsupportedappusage",
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -16,11 +16,14 @@
|
|||||||
|
|
||||||
package android.app;
|
package android.app;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
import android.annotation.IntDef;
|
import android.annotation.IntDef;
|
||||||
import android.annotation.RequiresPermission;
|
import android.annotation.RequiresPermission;
|
||||||
import android.annotation.SdkConstant;
|
import android.annotation.SdkConstant;
|
||||||
import android.annotation.SystemApi;
|
import android.annotation.SystemApi;
|
||||||
import android.annotation.SystemService;
|
import android.annotation.SystemService;
|
||||||
|
import android.compat.annotation.ChangeId;
|
||||||
|
import android.compat.annotation.Disabled;
|
||||||
import android.compat.annotation.UnsupportedAppUsage;
|
import android.compat.annotation.UnsupportedAppUsage;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@@ -183,6 +186,25 @@ public class AlarmManager {
|
|||||||
@UnsupportedAppUsage
|
@UnsupportedAppUsage
|
||||||
public static final int FLAG_IDLE_UNTIL = 1<<4;
|
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
|
@UnsupportedAppUsage
|
||||||
private final IAlarmManager mService;
|
private final IAlarmManager mService;
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
@@ -588,6 +610,11 @@ public class AlarmManager {
|
|||||||
* This method is like {@link #setExact(int, long, PendingIntent)}, but implies
|
* This method is like {@link #setExact(int, long, PendingIntent)}, but implies
|
||||||
* {@link #RTC_WAKEUP}.
|
* {@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 info
|
||||||
* @param operation Action to perform when the alarm goes off;
|
* @param operation Action to perform when the alarm goes off;
|
||||||
* typically comes from {@link PendingIntent#getBroadcast
|
* typically comes from {@link PendingIntent#getBroadcast
|
||||||
@@ -603,6 +630,7 @@ public class AlarmManager {
|
|||||||
* @see android.content.Context#registerReceiver
|
* @see android.content.Context#registerReceiver
|
||||||
* @see android.content.Intent#filterEquals
|
* @see android.content.Intent#filterEquals
|
||||||
*/
|
*/
|
||||||
|
@RequiresPermission(Manifest.permission.SCHEDULE_EXACT_ALARM)
|
||||||
public void setAlarmClock(AlarmClockInfo info, PendingIntent operation) {
|
public void setAlarmClock(AlarmClockInfo info, PendingIntent operation) {
|
||||||
setImpl(RTC_WAKEUP, info.getTriggerTime(), WINDOW_EXACT, 0, 0, operation,
|
setImpl(RTC_WAKEUP, info.getTriggerTime(), WINDOW_EXACT, 0, 0, operation,
|
||||||
null, null, null, null, info);
|
null, null, 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
|
* device is idle it may take even more liberties with scheduling in order to optimize
|
||||||
* for battery life.</p>
|
* 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 type type of alarm.
|
||||||
* @param triggerAtMillis time in milliseconds that the alarm should go
|
* @param triggerAtMillis time in milliseconds that the alarm should go
|
||||||
* off, using the appropriate clock (depending on the alarm type).
|
* off, using the appropriate clock (depending on the alarm type).
|
||||||
@@ -895,6 +929,7 @@ public class AlarmManager {
|
|||||||
* @see #RTC
|
* @see #RTC
|
||||||
* @see #RTC_WAKEUP
|
* @see #RTC_WAKEUP
|
||||||
*/
|
*/
|
||||||
|
@RequiresPermission(value = Manifest.permission.SCHEDULE_EXACT_ALARM, conditional = true)
|
||||||
public void setExactAndAllowWhileIdle(@AlarmType int type, long triggerAtMillis,
|
public void setExactAndAllowWhileIdle(@AlarmType int type, long triggerAtMillis,
|
||||||
PendingIntent operation) {
|
PendingIntent operation) {
|
||||||
setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, FLAG_ALLOW_WHILE_IDLE, operation,
|
setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, FLAG_ALLOW_WHILE_IDLE, operation,
|
||||||
@@ -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.
|
* Gets information about the next alarm clock currently scheduled.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -41,4 +41,5 @@ interface IAlarmManager {
|
|||||||
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
|
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
|
||||||
AlarmManager.AlarmClockInfo getNextAlarmClock(int userId);
|
AlarmManager.AlarmClockInfo getNextAlarmClock(int userId);
|
||||||
long currentNetworkTimeMillis();
|
long currentNetworkTimeMillis();
|
||||||
|
boolean canScheduleExactAlarms();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import static com.android.server.alarm.AlarmManagerService.clampPositive;
|
|||||||
import android.app.AlarmManager;
|
import android.app.AlarmManager;
|
||||||
import android.app.IAlarmListener;
|
import android.app.IAlarmListener;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
|
import android.os.Bundle;
|
||||||
import android.os.WorkSource;
|
import android.os.WorkSource;
|
||||||
import android.util.IndentingPrintWriter;
|
import android.util.IndentingPrintWriter;
|
||||||
import android.util.TimeUtils;
|
import android.util.TimeUtils;
|
||||||
@@ -92,10 +93,12 @@ class Alarm {
|
|||||||
private long mWhenElapsed;
|
private long mWhenElapsed;
|
||||||
private long mMaxWhenElapsed;
|
private long mMaxWhenElapsed;
|
||||||
public AlarmManagerService.PriorityClass priorityClass;
|
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,
|
Alarm(int type, long when, long requestedWhenElapsed, long windowLength, long interval,
|
||||||
PendingIntent op, IAlarmListener rec, String listenerTag, WorkSource ws, int flags,
|
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;
|
this.type = type;
|
||||||
origWhen = when;
|
origWhen = when;
|
||||||
wakeup = type == AlarmManager.ELAPSED_REALTIME_WAKEUP
|
wakeup = type == AlarmManager.ELAPSED_REALTIME_WAKEUP
|
||||||
@@ -115,6 +118,7 @@ class Alarm {
|
|||||||
alarmClock = info;
|
alarmClock = info;
|
||||||
this.uid = uid;
|
this.uid = uid;
|
||||||
packageName = pkgName;
|
packageName = pkgName;
|
||||||
|
mIdleOptions = idleOptions;
|
||||||
sourcePackage = (operation != null) ? operation.getCreatorPackage() : packageName;
|
sourcePackage = (operation != null) ? operation.getCreatorPackage() : packageName;
|
||||||
creatorUid = (operation != null) ? operation.getCreatorUid() : this.uid;
|
creatorUid = (operation != null) ? operation.getCreatorUid() : this.uid;
|
||||||
}
|
}
|
||||||
@@ -303,6 +307,10 @@ class Alarm {
|
|||||||
ipw.print("listener=");
|
ipw.print("listener=");
|
||||||
ipw.println(listener.asBinder());
|
ipw.println(listener.asBinder());
|
||||||
}
|
}
|
||||||
|
if (mIdleOptions != null) {
|
||||||
|
ipw.print("idle-options=");
|
||||||
|
ipw.println(mIdleOptions.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dumpDebug(ProtoOutputStream proto, long fieldId, long nowElapsed) {
|
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;
|
||||||
import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
|
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;
|
||||||
|
import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_COMPAT;
|
||||||
import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
|
import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
|
||||||
import static android.app.AlarmManager.FLAG_IDLE_UNTIL;
|
import static android.app.AlarmManager.FLAG_IDLE_UNTIL;
|
||||||
|
import static android.app.AlarmManager.FLAG_WAKE_FROM_IDLE;
|
||||||
|
import static android.app.AlarmManager.INTERVAL_HOUR;
|
||||||
import static android.app.AlarmManager.RTC;
|
import static android.app.AlarmManager.RTC;
|
||||||
import static android.app.AlarmManager.RTC_WAKEUP;
|
import static android.app.AlarmManager.RTC_WAKEUP;
|
||||||
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
|
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 android.os.UserHandle.USER_SYSTEM;
|
||||||
|
|
||||||
import static com.android.server.alarm.Alarm.APP_STANDBY_POLICY_INDEX;
|
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.DEVICE_IDLE_POLICY_INDEX;
|
||||||
import static com.android.server.alarm.Alarm.REQUESTER_POLICY_INDEX;
|
import static com.android.server.alarm.Alarm.REQUESTER_POLICY_INDEX;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.UserIdInt;
|
import android.annotation.UserIdInt;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
@@ -43,12 +48,14 @@ import android.app.IAlarmCompleteListener;
|
|||||||
import android.app.IAlarmListener;
|
import android.app.IAlarmListener;
|
||||||
import android.app.IAlarmManager;
|
import android.app.IAlarmManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
|
import android.app.compat.CompatChanges;
|
||||||
import android.app.usage.UsageStatsManager;
|
import android.app.usage.UsageStatsManager;
|
||||||
import android.app.usage.UsageStatsManagerInternal;
|
import android.app.usage.UsageStatsManagerInternal;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
|
import android.content.PermissionChecker;
|
||||||
import android.content.pm.PackageManagerInternal;
|
import android.content.pm.PackageManagerInternal;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.BatteryManager;
|
import android.os.BatteryManager;
|
||||||
@@ -65,6 +72,7 @@ import android.os.PowerManager;
|
|||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.ResultReceiver;
|
import android.os.ResultReceiver;
|
||||||
|
import android.os.ServiceManager;
|
||||||
import android.os.ShellCallback;
|
import android.os.ShellCallback;
|
||||||
import android.os.ShellCommand;
|
import android.os.ShellCommand;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
@@ -95,6 +103,8 @@ import android.util.proto.ProtoOutputStream;
|
|||||||
|
|
||||||
import com.android.internal.annotations.GuardedBy;
|
import com.android.internal.annotations.GuardedBy;
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
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.DumpUtils;
|
||||||
import com.android.internal.util.FrameworkStatsLog;
|
import com.android.internal.util.FrameworkStatsLog;
|
||||||
import com.android.internal.util.LocalLog;
|
import com.android.internal.util.LocalLog;
|
||||||
@@ -177,6 +187,7 @@ public class AlarmManagerService extends SystemService {
|
|||||||
final LocalLog mLog = new LocalLog(TAG);
|
final LocalLog mLog = new LocalLog(TAG);
|
||||||
|
|
||||||
AppOpsManager mAppOps;
|
AppOpsManager mAppOps;
|
||||||
|
IAppOpsService mAppOpsService;
|
||||||
DeviceIdleInternal mLocalDeviceIdleController;
|
DeviceIdleInternal mLocalDeviceIdleController;
|
||||||
private UsageStatsManagerInternal mUsageStatsManagerInternal;
|
private UsageStatsManagerInternal mUsageStatsManagerInternal;
|
||||||
private ActivityManagerInternal mActivityManagerInternal;
|
private ActivityManagerInternal mActivityManagerInternal;
|
||||||
@@ -253,10 +264,8 @@ public class AlarmManagerService extends SystemService {
|
|||||||
"REORDER_ALARMS_FOR_STANDBY",
|
"REORDER_ALARMS_FOR_STANDBY",
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
BroadcastOptions mOptsWithFgs = BroadcastOptions.makeBasic();
|
||||||
* Broadcast options to use for FLAG_ALLOW_WHILE_IDLE.
|
BroadcastOptions mOptsWithoutFgs = BroadcastOptions.makeBasic();
|
||||||
*/
|
|
||||||
Bundle mIdleOptions;
|
|
||||||
|
|
||||||
// TODO(b/172085676): Move inside alarm store.
|
// TODO(b/172085676): Move inside alarm store.
|
||||||
private final SparseArray<AlarmManager.AlarmClockInfo> mNextAlarmClockForUser =
|
private final SparseArray<AlarmManager.AlarmClockInfo> mNextAlarmClockForUser =
|
||||||
@@ -410,6 +419,10 @@ public class AlarmManagerService extends SystemService {
|
|||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static final String KEY_ALLOW_WHILE_IDLE_QUOTA = "allow_while_idle_quota";
|
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_FUTURITY = 5 * 1000;
|
||||||
private static final long DEFAULT_MIN_INTERVAL = 60 * 1000;
|
private static final long DEFAULT_MIN_INTERVAL = 60 * 1000;
|
||||||
private static final long DEFAULT_MAX_INTERVAL = 365 * DateUtils.DAY_IN_MILLIS;
|
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_LAZY_BATCHING = true;
|
||||||
private static final boolean DEFAULT_TIME_TICK_ALLOWED_WHILE_IDLE = 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
|
// Minimum futurity of a new alarm
|
||||||
public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY;
|
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;
|
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;
|
private long mLastAllowWhileIdleWhitelistDuration = -1;
|
||||||
|
|
||||||
Constants() {
|
Constants() {
|
||||||
@@ -480,9 +512,11 @@ public class AlarmManagerService extends SystemService {
|
|||||||
public void updateAllowWhileIdleWhitelistDurationLocked() {
|
public void updateAllowWhileIdleWhitelistDurationLocked() {
|
||||||
if (mLastAllowWhileIdleWhitelistDuration != ALLOW_WHILE_IDLE_WHITELIST_DURATION) {
|
if (mLastAllowWhileIdleWhitelistDuration != ALLOW_WHILE_IDLE_WHITELIST_DURATION) {
|
||||||
mLastAllowWhileIdleWhitelistDuration = ALLOW_WHILE_IDLE_WHITELIST_DURATION;
|
mLastAllowWhileIdleWhitelistDuration = ALLOW_WHILE_IDLE_WHITELIST_DURATION;
|
||||||
BroadcastOptions opts = BroadcastOptions.makeBasic();
|
|
||||||
opts.setTemporaryAppWhitelistDuration(ALLOW_WHILE_IDLE_WHITELIST_DURATION);
|
mOptsWithFgs.setTemporaryAppWhitelistDuration(ALLOW_WHILE_IDLE_WHITELIST_DURATION);
|
||||||
mIdleOptions = opts.toBundle();
|
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;
|
ALLOW_WHILE_IDLE_QUOTA = 1;
|
||||||
}
|
}
|
||||||
break;
|
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:
|
case KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION:
|
||||||
ALLOW_WHILE_IDLE_WHITELIST_DURATION = properties.getLong(
|
ALLOW_WHILE_IDLE_WHITELIST_DURATION = properties.getLong(
|
||||||
KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION,
|
KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION,
|
||||||
@@ -643,10 +698,14 @@ public class AlarmManagerService extends SystemService {
|
|||||||
TimeUtils.formatDuration(LISTENER_TIMEOUT, pw);
|
TimeUtils.formatDuration(LISTENER_TIMEOUT, pw);
|
||||||
pw.println();
|
pw.println();
|
||||||
|
|
||||||
pw.print("allow_while_idle_window=");
|
pw.print(KEY_ALLOW_WHILE_IDLE_WINDOW);
|
||||||
|
pw.print("=");
|
||||||
TimeUtils.formatDuration(ALLOW_WHILE_IDLE_WINDOW, pw);
|
TimeUtils.formatDuration(ALLOW_WHILE_IDLE_WINDOW, pw);
|
||||||
pw.println();
|
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.print(KEY_ALLOW_WHILE_IDLE_QUOTA, ALLOW_WHILE_IDLE_QUOTA);
|
||||||
pw.println();
|
pw.println();
|
||||||
|
|
||||||
@@ -830,7 +889,15 @@ public class AlarmManagerService extends SystemService {
|
|||||||
if (futurity < MIN_FUZZABLE_INTERVAL) {
|
if (futurity < MIN_FUZZABLE_INTERVAL) {
|
||||||
futurity = 0;
|
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.
|
// 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,
|
setImplLocked(alarm.type, alarm.origWhen + delta, nextElapsed,
|
||||||
nextMaxElapsed - nextElapsed, alarm.repeatInterval, alarm.operation, null,
|
nextMaxElapsed - nextElapsed, alarm.repeatInterval, alarm.operation, null,
|
||||||
null, alarm.flags, alarm.workSource, alarm.alarmClock, alarm.uid,
|
null, alarm.flags, alarm.workSource, alarm.alarmClock, alarm.uid,
|
||||||
alarm.packageName);
|
alarm.packageName, null);
|
||||||
// Kernel alarms will be rescheduled as needed in setImplLocked
|
// Kernel alarms will be rescheduled as needed in setImplLocked
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1241,7 +1308,8 @@ public class AlarmManagerService extends SystemService {
|
|||||||
mAlarmStore.setAlarmClockRemovalListener(mAlarmClockUpdater);
|
mAlarmStore.setAlarmClockRemovalListener(mAlarmClockUpdater);
|
||||||
|
|
||||||
mAppWakeupHistory = new AppWakeupHistory(Constants.DEFAULT_APP_STANDBY_WINDOW);
|
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;
|
mNextWakeup = mNextNonWakeup = 0;
|
||||||
|
|
||||||
@@ -1327,6 +1395,28 @@ public class AlarmManagerService extends SystemService {
|
|||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
mConstants.start();
|
mConstants.start();
|
||||||
mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
|
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 =
|
mLocalDeviceIdleController =
|
||||||
LocalServices.getService(DeviceIdleInternal.class);
|
LocalServices.getService(DeviceIdleInternal.class);
|
||||||
mUsageStatsManagerInternal =
|
mUsageStatsManagerInternal =
|
||||||
@@ -1428,7 +1518,7 @@ public class AlarmManagerService extends SystemService {
|
|||||||
void setImpl(int type, long triggerAtTime, long windowLength, long interval,
|
void setImpl(int type, long triggerAtTime, long windowLength, long interval,
|
||||||
PendingIntent operation, IAlarmListener directReceiver, String listenerTag,
|
PendingIntent operation, IAlarmListener directReceiver, String listenerTag,
|
||||||
int flags, WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock,
|
int flags, WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock,
|
||||||
int callingUid, String callingPackage) {
|
int callingUid, String callingPackage, Bundle idleOptions) {
|
||||||
if ((operation == null && directReceiver == null)
|
if ((operation == null && directReceiver == null)
|
||||||
|| (operation != null && directReceiver != null)) {
|
|| (operation != null && directReceiver != null)) {
|
||||||
Slog.w(TAG, "Alarms must either supply a PendingIntent or an AlarmReceiver");
|
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,
|
setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, interval, operation,
|
||||||
directReceiver, listenerTag, flags, workSource, alarmClock, callingUid,
|
directReceiver, listenerTag, flags, workSource, alarmClock, callingUid,
|
||||||
callingPackage);
|
callingPackage, idleOptions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setImplLocked(int type, long when, long whenElapsed, long windowLength,
|
private void setImplLocked(int type, long when, long whenElapsed, long windowLength,
|
||||||
long interval, PendingIntent operation, IAlarmListener directReceiver,
|
long interval, PendingIntent operation, IAlarmListener directReceiver,
|
||||||
String listenerTag, int flags, WorkSource workSource,
|
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,
|
final Alarm a = new Alarm(type, when, whenElapsed, windowLength, interval,
|
||||||
operation, directReceiver, listenerTag, workSource, flags, alarmClock,
|
operation, directReceiver, listenerTag, workSource, flags, alarmClock,
|
||||||
callingUid, callingPackage);
|
callingUid, callingPackage, idleOptions);
|
||||||
if (mActivityManagerInternal.isAppStartModeDisabled(callingUid, callingPackage)) {
|
if (mActivityManagerInternal.isAppStartModeDisabled(callingUid, callingPackage)) {
|
||||||
Slog.w(TAG, "Not setting alarm from " + callingUid + ":" + a
|
Slog.w(TAG, "Not setting alarm from " + callingUid + ":" + a
|
||||||
+ " -- package not allowed to start");
|
+ " -- package not allowed to start");
|
||||||
@@ -1605,19 +1696,21 @@ public class AlarmManagerService extends SystemService {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(mAppStateTracker != null && mAppStateTracker.areAlarmsRestrictedByBatterySaver(
|
if (mAppStateTracker == null || !mAppStateTracker.areAlarmsRestrictedByBatterySaver(
|
||||||
alarm.creatorUid, alarm.sourcePackage))) {
|
alarm.creatorUid, alarm.sourcePackage)) {
|
||||||
return alarm.setPolicyElapsed(BATTERY_SAVER_POLICY_INDEX, nowElapsed);
|
return alarm.setPolicyElapsed(BATTERY_SAVER_POLICY_INDEX, nowElapsed);
|
||||||
}
|
}
|
||||||
|
|
||||||
final long batterySaverPolicyElapsed;
|
final long batterySaverPolicyElapsed;
|
||||||
if ((alarm.flags & (AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED)) != 0) {
|
if ((alarm.flags & (FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED)) != 0) {
|
||||||
// Unrestricted.
|
// Unrestricted.
|
||||||
batterySaverPolicyElapsed = nowElapsed;
|
batterySaverPolicyElapsed = nowElapsed;
|
||||||
} else if ((alarm.flags & AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) {
|
} else if (isAllowedWhileIdleRestricted(alarm)) {
|
||||||
// Allowed but limited.
|
// Allowed but limited.
|
||||||
final int userId = UserHandle.getUserId(alarm.creatorUid);
|
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(
|
final int dispatchesInWindow = mAllowWhileIdleHistory.getTotalWakeupsInWindow(
|
||||||
alarm.sourcePackage, userId);
|
alarm.sourcePackage, userId);
|
||||||
if (dispatchesInWindow < quota) {
|
if (dispatchesInWindow < quota) {
|
||||||
@@ -1625,7 +1718,7 @@ public class AlarmManagerService extends SystemService {
|
|||||||
batterySaverPolicyElapsed = nowElapsed;
|
batterySaverPolicyElapsed = nowElapsed;
|
||||||
} else {
|
} else {
|
||||||
batterySaverPolicyElapsed = mAllowWhileIdleHistory.getNthLastWakeupForPackage(
|
batterySaverPolicyElapsed = mAllowWhileIdleHistory.getNthLastWakeupForPackage(
|
||||||
alarm.sourcePackage, userId, quota) + Constants.ALLOW_WHILE_IDLE_WINDOW;
|
alarm.sourcePackage, userId, quota) + mConstants.ALLOW_WHILE_IDLE_WINDOW;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Not allowed.
|
// Not allowed.
|
||||||
@@ -1634,6 +1727,16 @@ public class AlarmManagerService extends SystemService {
|
|||||||
return alarm.setPolicyElapsed(BATTERY_SAVER_POLICY_INDEX, batterySaverPolicyElapsed);
|
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.
|
* 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;
|
final long deviceIdlePolicyTime;
|
||||||
if ((alarm.flags & (AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED
|
if ((alarm.flags & (FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED | FLAG_WAKE_FROM_IDLE)) != 0) {
|
||||||
| AlarmManager.FLAG_WAKE_FROM_IDLE)) != 0) {
|
|
||||||
// Unrestricted.
|
// Unrestricted.
|
||||||
deviceIdlePolicyTime = nowElapsed;
|
deviceIdlePolicyTime = nowElapsed;
|
||||||
} else if ((alarm.flags & AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) {
|
} else if (isAllowedWhileIdleRestricted(alarm)) {
|
||||||
// Allowed but limited.
|
// Allowed but limited.
|
||||||
final int userId = UserHandle.getUserId(alarm.creatorUid);
|
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(
|
final int dispatchesInWindow = mAllowWhileIdleHistory.getTotalWakeupsInWindow(
|
||||||
alarm.sourcePackage, userId);
|
alarm.sourcePackage, userId);
|
||||||
if (dispatchesInWindow < quota) {
|
if (dispatchesInWindow < quota) {
|
||||||
@@ -1662,7 +1766,7 @@ public class AlarmManagerService extends SystemService {
|
|||||||
deviceIdlePolicyTime = nowElapsed;
|
deviceIdlePolicyTime = nowElapsed;
|
||||||
} else {
|
} else {
|
||||||
final long whenInQuota = mAllowWhileIdleHistory.getNthLastWakeupForPackage(
|
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());
|
deviceIdlePolicyTime = Math.min(whenInQuota, mPendingIdleUntil.getWhenElapsed());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -1749,7 +1853,7 @@ public class AlarmManagerService extends SystemService {
|
|||||||
} else if (mPendingIdleUntil != null) {
|
} else if (mPendingIdleUntil != null) {
|
||||||
adjustDeliveryTimeBasedOnDeviceIdle(a);
|
adjustDeliveryTimeBasedOnDeviceIdle(a);
|
||||||
}
|
}
|
||||||
if ((a.flags & AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) {
|
if ((a.flags & FLAG_WAKE_FROM_IDLE) != 0) {
|
||||||
if (mNextWakeFromIdle == null || mNextWakeFromIdle.getWhenElapsed()
|
if (mNextWakeFromIdle == null || mNextWakeFromIdle.getWhenElapsed()
|
||||||
> a.getWhenElapsed()) {
|
> a.getWhenElapsed()) {
|
||||||
mNextWakeFromIdle = a;
|
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
|
* Public-facing binder interface
|
||||||
*/
|
*/
|
||||||
@@ -1824,6 +1936,54 @@ public class AlarmManagerService extends SystemService {
|
|||||||
// wakelock time spent in alarm delivery
|
// wakelock time spent in alarm delivery
|
||||||
mAppOps.checkPackage(callingUid, callingPackage);
|
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
|
// Repeating alarms must use PendingIntent, not direct listener
|
||||||
if (interval != 0) {
|
if (interval != 0) {
|
||||||
if (directReceiver != null) {
|
if (directReceiver != null) {
|
||||||
@@ -1840,8 +2000,7 @@ public class AlarmManagerService extends SystemService {
|
|||||||
|
|
||||||
// No incoming callers can request either WAKE_FROM_IDLE or
|
// No incoming callers can request either WAKE_FROM_IDLE or
|
||||||
// ALLOW_WHILE_IDLE_UNRESTRICTED -- we will apply those later as appropriate.
|
// ALLOW_WHILE_IDLE_UNRESTRICTED -- we will apply those later as appropriate.
|
||||||
flags &= ~(AlarmManager.FLAG_WAKE_FROM_IDLE
|
flags &= ~(FLAG_WAKE_FROM_IDLE | FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED);
|
||||||
| AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED);
|
|
||||||
|
|
||||||
// Only the system can use FLAG_IDLE_UNTIL -- this is used to tell the alarm
|
// 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.
|
// 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
|
// 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.
|
// use it to wake early from idle if needed.
|
||||||
if (alarmClock != null) {
|
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
|
// 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.
|
// 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
|
// This means we will allow these alarms to go off as normal even while idle, with no
|
||||||
// timing restrictions.
|
// timing restrictions.
|
||||||
} else if (workSource == null && (callingUid < Process.FIRST_APPLICATION_UID
|
} else if (workSource == null && (UserHandle.isCore(callingUid)
|
||||||
|| UserHandle.isSameApp(callingUid, mSystemUiUid)
|
|| UserHandle.isSameApp(callingUid, mSystemUiUid)
|
||||||
|| ((mAppStateTracker != null)
|
|| ((mAppStateTracker != null)
|
||||||
&& mAppStateTracker.isUidPowerSaveUserExempt(callingUid)))) {
|
&& mAppStateTracker.isUidPowerSaveUserExempt(callingUid)))) {
|
||||||
flags |= AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
|
flags |= FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
|
||||||
flags &= ~AlarmManager.FLAG_ALLOW_WHILE_IDLE;
|
flags &= ~FLAG_ALLOW_WHILE_IDLE;
|
||||||
|
flags &= ~FLAG_ALLOW_WHILE_IDLE_COMPAT;
|
||||||
|
idleOptions = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
setImpl(type, triggerAtTime, windowLength, interval, operation, directReceiver,
|
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
|
@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) {
|
void removeLocked(PendingIntent operation, IAlarmListener directReceiver) {
|
||||||
if (operation == null && directReceiver == null) {
|
if (operation == null && directReceiver == null) {
|
||||||
if (localLOGV) {
|
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) {
|
if (alarm.alarmClock != null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -3142,7 +3382,7 @@ public class AlarmManagerService extends SystemService {
|
|||||||
|
|
||||||
alarm.count = 1;
|
alarm.count = 1;
|
||||||
triggerList.add(alarm);
|
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,
|
EventLogTags.writeDeviceIdleWakeFromIdle(mPendingIdleUntil != null ? 1 : 0,
|
||||||
alarm.statsTag);
|
alarm.statsTag);
|
||||||
}
|
}
|
||||||
@@ -3180,7 +3420,7 @@ public class AlarmManagerService extends SystemService {
|
|||||||
setImplLocked(alarm.type, alarm.origWhen + delta, nextElapsed,
|
setImplLocked(alarm.type, alarm.origWhen + delta, nextElapsed,
|
||||||
nextMaxElapsed - nextElapsed, alarm.repeatInterval, alarm.operation, null,
|
nextMaxElapsed - nextElapsed, alarm.repeatInterval, alarm.operation, null,
|
||||||
null, alarm.flags, alarm.workSource, alarm.alarmClock, alarm.uid,
|
null, alarm.flags, alarm.workSource, alarm.alarmClock, alarm.uid,
|
||||||
alarm.packageName);
|
alarm.packageName, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (alarm.wakeup) {
|
if (alarm.wakeup) {
|
||||||
@@ -3257,7 +3497,6 @@ public class AlarmManagerService extends SystemService {
|
|||||||
mLastAlarmDeliveryTime = nowELAPSED;
|
mLastAlarmDeliveryTime = nowELAPSED;
|
||||||
for (int i = 0; i < triggerList.size(); i++) {
|
for (int i = 0; i < triggerList.size(); i++) {
|
||||||
Alarm alarm = triggerList.get(i);
|
Alarm alarm = triggerList.get(i);
|
||||||
final boolean allowWhileIdle = (alarm.flags & AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0;
|
|
||||||
if (alarm.wakeup) {
|
if (alarm.wakeup) {
|
||||||
Trace.traceBegin(Trace.TRACE_TAG_POWER,
|
Trace.traceBegin(Trace.TRACE_TAG_POWER,
|
||||||
"Dispatch wakeup alarm to " + alarm.packageName);
|
"Dispatch wakeup alarm to " + alarm.packageName);
|
||||||
@@ -3273,7 +3512,7 @@ public class AlarmManagerService extends SystemService {
|
|||||||
mActivityManagerInternal.noteAlarmStart(alarm.operation, alarm.workSource,
|
mActivityManagerInternal.noteAlarmStart(alarm.operation, alarm.workSource,
|
||||||
alarm.uid, alarm.statsTag);
|
alarm.uid, alarm.statsTag);
|
||||||
}
|
}
|
||||||
mDeliveryTracker.deliverLocked(alarm, nowELAPSED, allowWhileIdle);
|
mDeliveryTracker.deliverLocked(alarm, nowELAPSED);
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
Slog.w(TAG, "Failure sending alarm.", 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)
|
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
|
@VisibleForTesting
|
||||||
@@ -3368,6 +3608,11 @@ public class AlarmManagerService extends SystemService {
|
|||||||
MATCH_SYSTEM_ONLY, USER_SYSTEM);
|
MATCH_SYSTEM_ONLY, USER_SYSTEM);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IAppOpsService getAppOpsService() {
|
||||||
|
return IAppOpsService.Stub.asInterface(
|
||||||
|
ServiceManager.getService(Context.APP_OPS_SERVICE));
|
||||||
|
}
|
||||||
|
|
||||||
ClockReceiver getClockReceiver(AlarmManagerService service) {
|
ClockReceiver getClockReceiver(AlarmManagerService service) {
|
||||||
return service.new ClockReceiver();
|
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 APP_STANDBY_BUCKET_CHANGED = 5;
|
||||||
public static final int CHARGING_STATUS_CHANGED = 6;
|
public static final int CHARGING_STATUS_CHANGED = 6;
|
||||||
public static final int REMOVE_FOR_CANCELED = 7;
|
public static final int REMOVE_FOR_CANCELED = 7;
|
||||||
|
public static final int REMOVE_EXACT_ALARMS = 8;
|
||||||
|
|
||||||
AlarmHandler() {
|
AlarmHandler() {
|
||||||
super(Looper.myLooper());
|
super(Looper.myLooper());
|
||||||
@@ -3645,6 +3891,14 @@ public class AlarmManagerService extends SystemService {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case REMOVE_EXACT_ALARMS:
|
||||||
|
final int uid = msg.arg1;
|
||||||
|
final String packageName = (String) msg.obj;
|
||||||
|
synchronized (mLock) {
|
||||||
|
removeExactAlarmsOnPermissionRevokedLocked(uid, packageName);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// nope, just ignore it
|
// nope, just ignore it
|
||||||
break;
|
break;
|
||||||
@@ -3720,7 +3974,7 @@ public class AlarmManagerService extends SystemService {
|
|||||||
|
|
||||||
setImpl(ELAPSED_REALTIME, mInjector.getElapsedRealtime() + tickEventDelay, 0,
|
setImpl(ELAPSED_REALTIME, mInjector.getElapsedRealtime() + tickEventDelay, 0,
|
||||||
0, null, mTimeTickTrigger, TIME_TICK_TAG, flags, workSource, null,
|
0, null, mTimeTickTrigger, TIME_TICK_TAG, flags, workSource, null,
|
||||||
Process.myUid(), "android");
|
Process.myUid(), "android", null);
|
||||||
|
|
||||||
// Finally, remember when we set the tick alarm
|
// Finally, remember when we set the tick alarm
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
@@ -3740,7 +3994,7 @@ public class AlarmManagerService extends SystemService {
|
|||||||
final WorkSource workSource = null; // Let system take blame for date change events.
|
final WorkSource workSource = null; // Let system take blame for date change events.
|
||||||
setImpl(RTC, calendar.getTimeInMillis(), 0, 0, mDateChangeSender, null, null,
|
setImpl(RTC, calendar.getTimeInMillis(), 0, 0, mDateChangeSender, null, null,
|
||||||
AlarmManager.FLAG_STANDALONE, workSource, 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
|
* Deliver an alarm and set up the post-delivery handling appropriately
|
||||||
*/
|
*/
|
||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
public void deliverLocked(Alarm alarm, long nowELAPSED, boolean allowWhileIdle) {
|
public void deliverLocked(Alarm alarm, long nowELAPSED) {
|
||||||
final long workSourceToken = ThreadLocalWorkSource.setUid(
|
final long workSourceToken = ThreadLocalWorkSource.setUid(
|
||||||
getAlarmAttributionUid(alarm));
|
getAlarmAttributionUid(alarm));
|
||||||
try {
|
try {
|
||||||
@@ -4122,10 +4376,8 @@ public class AlarmManagerService extends SystemService {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
alarm.operation.send(getContext(), 0,
|
alarm.operation.send(getContext(), 0,
|
||||||
mBackgroundIntent.putExtra(
|
mBackgroundIntent.putExtra(Intent.EXTRA_ALARM_COUNT, alarm.count),
|
||||||
Intent.EXTRA_ALARM_COUNT, alarm.count),
|
mDeliveryTracker, mHandler, null, alarm.mIdleOptions);
|
||||||
mDeliveryTracker, mHandler, null,
|
|
||||||
allowWhileIdle ? mIdleOptions : null);
|
|
||||||
} catch (PendingIntent.CanceledException e) {
|
} catch (PendingIntent.CanceledException e) {
|
||||||
if (alarm.repeatInterval > 0) {
|
if (alarm.repeatInterval > 0) {
|
||||||
// This IntentSender is no longer valid, but this
|
// This IntentSender is no longer valid, but this
|
||||||
@@ -4194,7 +4446,7 @@ public class AlarmManagerService extends SystemService {
|
|||||||
if (inflight.isBroadcast()) {
|
if (inflight.isBroadcast()) {
|
||||||
notifyBroadcastAlarmPendingLocked(alarm.uid);
|
notifyBroadcastAlarmPendingLocked(alarm.uid);
|
||||||
}
|
}
|
||||||
if (allowWhileIdle) {
|
if (isAllowedWhileIdleRestricted(alarm)) {
|
||||||
final boolean doze = (mPendingIdleUntil != null);
|
final boolean doze = (mPendingIdleUntil != null);
|
||||||
final boolean batterySaver = (mAppStateTracker != null
|
final boolean batterySaver = (mAppStateTracker != null
|
||||||
&& mAppStateTracker.isForceAllAppsStandbyEnabled());
|
&& mAppStateTracker.isForceAllAppsStandbyEnabled());
|
||||||
@@ -4204,8 +4456,7 @@ public class AlarmManagerService extends SystemService {
|
|||||||
mAllowWhileIdleHistory.recordAlarmForPackage(alarm.sourcePackage,
|
mAllowWhileIdleHistory.recordAlarmForPackage(alarm.sourcePackage,
|
||||||
UserHandle.getUserId(alarm.creatorUid), nowELAPSED);
|
UserHandle.getUserId(alarm.creatorUid), nowELAPSED);
|
||||||
mAlarmStore.updateAlarmDeliveries(a -> {
|
mAlarmStore.updateAlarmDeliveries(a -> {
|
||||||
if (a.creatorUid != alarm.creatorUid
|
if (a.creatorUid != alarm.creatorUid || !isAllowedWhileIdleRestricted(a)) {
|
||||||
|| (a.flags & FLAG_ALLOW_WHILE_IDLE) == 0) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return (doze && adjustDeliveryTimeBasedOnDeviceIdle(a))
|
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_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 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 @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_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 SEND_SMS = "android.permission.SEND_SMS";
|
||||||
field public static final String SET_ALARM = "com.android.alarm.permission.SET_ALARM";
|
field public static final String SET_ALARM = "com.android.alarm.permission.SET_ALARM";
|
||||||
@@ -4328,16 +4329,17 @@ package android.app {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public class AlarmManager {
|
public class AlarmManager {
|
||||||
|
method public boolean canScheduleExactAlarms();
|
||||||
method public void cancel(android.app.PendingIntent);
|
method public void cancel(android.app.PendingIntent);
|
||||||
method public void cancel(android.app.AlarmManager.OnAlarmListener);
|
method public void cancel(android.app.AlarmManager.OnAlarmListener);
|
||||||
method public android.app.AlarmManager.AlarmClockInfo getNextAlarmClock();
|
method public android.app.AlarmManager.AlarmClockInfo getNextAlarmClock();
|
||||||
method public void set(int, long, android.app.PendingIntent);
|
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 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 setAndAllowWhileIdle(int, long, android.app.PendingIntent);
|
||||||
method public void setExact(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 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 setInexactRepeating(int, long, long, android.app.PendingIntent);
|
||||||
method public void setRepeating(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);
|
method @RequiresPermission(android.Manifest.permission.SET_TIME) public void setTime(long);
|
||||||
|
|||||||
@@ -795,7 +795,8 @@ public class AppOpsManager {
|
|||||||
// when adding one of these:
|
// when adding one of these:
|
||||||
// - increment _NUM_OP
|
// - increment _NUM_OP
|
||||||
// - define an OPSTR_* constant (marked as @SystemApi)
|
// - 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 descriptive strings to Settings/res/values/arrays.xml
|
||||||
// - add the op to the appropriate template in AppOpsState.OpsTemplate (settings app)
|
// - add the op to the appropriate template in AppOpsState.OpsTemplate (settings app)
|
||||||
|
|
||||||
@@ -1174,13 +1175,19 @@ public class AppOpsManager {
|
|||||||
*
|
*
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
// TODO: Add as AppProtoEnums
|
public static final int OP_RECORD_AUDIO_OUTPUT = AppProtoEnums.APP_OP_RECORD_AUDIO_OUTPUT;
|
||||||
public static final int OP_RECORD_AUDIO_OUTPUT = 106;
|
|
||||||
|
/**
|
||||||
|
* 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 */
|
/** @hide */
|
||||||
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
@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. */
|
/** Access to coarse location information. */
|
||||||
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
|
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";
|
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 */
|
/** {@link #sAppOpsToNote} not initialized yet for this op */
|
||||||
private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
|
private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
|
||||||
/** Should not collect noting of this app-op in {@link #sAppOpsToNote} */
|
/** Should not collect noting of this app-op in {@link #sAppOpsToNote} */
|
||||||
@@ -1633,6 +1647,7 @@ public class AppOpsManager {
|
|||||||
OP_LOADER_USAGE_STATS,
|
OP_LOADER_USAGE_STATS,
|
||||||
OP_MANAGE_ONGOING_CALLS,
|
OP_MANAGE_ONGOING_CALLS,
|
||||||
OP_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER,
|
OP_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER,
|
||||||
|
OP_SCHEDULE_EXACT_ALARM,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1751,6 +1766,7 @@ public class AppOpsManager {
|
|||||||
OP_MANAGE_CREDENTIALS, // MANAGE_CREDENTIALS
|
OP_MANAGE_CREDENTIALS, // MANAGE_CREDENTIALS
|
||||||
OP_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
|
OP_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
|
||||||
OP_RECORD_AUDIO_OUTPUT, // RECORD_AUDIO_OUTPUT
|
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_MANAGE_CREDENTIALS,
|
||||||
OPSTR_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER,
|
OPSTR_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER,
|
||||||
OPSTR_RECORD_AUDIO_OUTPUT,
|
OPSTR_RECORD_AUDIO_OUTPUT,
|
||||||
|
OPSTR_SCHEDULE_EXACT_ALARM,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1978,6 +1995,7 @@ public class AppOpsManager {
|
|||||||
"MANAGE_CREDENTIALS",
|
"MANAGE_CREDENTIALS",
|
||||||
"USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER",
|
"USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER",
|
||||||
"RECORD_AUDIO_OUTPUT",
|
"RECORD_AUDIO_OUTPUT",
|
||||||
|
"SCHEDULE_EXACT_ALARM",
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2093,6 +2111,7 @@ public class AppOpsManager {
|
|||||||
null, // no permission for OP_MANAGE_CREDENTIALS
|
null, // no permission for OP_MANAGE_CREDENTIALS
|
||||||
Manifest.permission.USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER,
|
Manifest.permission.USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER,
|
||||||
null, // no permission for OP_RECORD_AUDIO_OUTPUT
|
null, // no permission for OP_RECORD_AUDIO_OUTPUT
|
||||||
|
Manifest.permission.SCHEDULE_EXACT_ALARM,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2208,6 +2227,7 @@ public class AppOpsManager {
|
|||||||
null, // MANAGE_CREDENTIALS
|
null, // MANAGE_CREDENTIALS
|
||||||
null, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
|
null, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
|
||||||
null, // RECORD_AUDIO_OUTPUT
|
null, // RECORD_AUDIO_OUTPUT
|
||||||
|
null, // SCHEDULE_EXACT_ALARM
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2322,6 +2342,7 @@ public class AppOpsManager {
|
|||||||
null, // MANAGE_CREDENTIALS
|
null, // MANAGE_CREDENTIALS
|
||||||
null, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
|
null, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
|
||||||
null, // RECORD_AUDIO_OUTPUT
|
null, // RECORD_AUDIO_OUTPUT
|
||||||
|
null, // SCHEDULE_EXACT_ALARM
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2435,6 +2456,7 @@ public class AppOpsManager {
|
|||||||
AppOpsManager.MODE_DEFAULT, // MANAGE_CREDENTIALS
|
AppOpsManager.MODE_DEFAULT, // MANAGE_CREDENTIALS
|
||||||
AppOpsManager.MODE_DEFAULT, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
|
AppOpsManager.MODE_DEFAULT, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
|
||||||
AppOpsManager.MODE_ALLOWED, // RECORD_AUDIO_OUTPUT
|
AppOpsManager.MODE_ALLOWED, // RECORD_AUDIO_OUTPUT
|
||||||
|
AppOpsManager.MODE_DEFAULT, // SCHEDULE_EXACT_ALARM
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2552,6 +2574,7 @@ public class AppOpsManager {
|
|||||||
false, // MANAGE_CREDENTIALS
|
false, // MANAGE_CREDENTIALS
|
||||||
true, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
|
true, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
|
||||||
false, // RECORD_AUDIO_OUTPUT
|
false, // RECORD_AUDIO_OUTPUT
|
||||||
|
false, // SCHEDULE_EXACT_ALARM
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -3905,6 +3905,12 @@
|
|||||||
<permission android:name="android.permission.SET_KEYBOARD_LAYOUT"
|
<permission android:name="android.permission.SET_KEYBOARD_LAYOUT"
|
||||||
android:protectionLevel="signature" />
|
android:protectionLevel="signature" />
|
||||||
|
|
||||||
|
<!-- Allows an app to 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
|
<!-- Allows an application to query tablet mode state and monitor changes
|
||||||
in it.
|
in it.
|
||||||
<p>Not for use by third-party applications.
|
<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;
|
||||||
import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
|
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;
|
||||||
|
import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_COMPAT;
|
||||||
import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
|
import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
|
||||||
import static android.app.AlarmManager.FLAG_IDLE_UNTIL;
|
import static android.app.AlarmManager.FLAG_IDLE_UNTIL;
|
||||||
import static android.app.AlarmManager.FLAG_STANDALONE;
|
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;
|
||||||
import static android.app.AlarmManager.RTC_WAKEUP;
|
import static android.app.AlarmManager.RTC_WAKEUP;
|
||||||
import static android.app.AlarmManager.WINDOW_EXACT;
|
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_ACTIVE;
|
||||||
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
|
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_RARE;
|
||||||
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED;
|
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED;
|
||||||
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
|
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.doAnswer;
|
||||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
|
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.APP_STANDBY_BUCKET_CHANGED;
|
||||||
import static com.android.server.alarm.AlarmManagerService.AlarmHandler.CHARGING_STATUS_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.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_QUOTA;
|
||||||
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION;
|
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;
|
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 com.android.server.alarm.Constants.TEST_CALLING_UID;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
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.atLeastOnce;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
import android.app.ActivityManagerInternal;
|
import android.app.ActivityManagerInternal;
|
||||||
|
import android.app.AlarmManager;
|
||||||
|
import android.app.AppOpsManager;
|
||||||
import android.app.IActivityManager;
|
import android.app.IActivityManager;
|
||||||
import android.app.IAlarmCompleteListener;
|
import android.app.IAlarmCompleteListener;
|
||||||
import android.app.IAlarmListener;
|
import android.app.IAlarmListener;
|
||||||
|
import android.app.IAlarmManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
|
import android.app.compat.CompatChanges;
|
||||||
import android.app.usage.UsageStatsManagerInternal;
|
import android.app.usage.UsageStatsManagerInternal;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.PermissionChecker;
|
||||||
import android.os.BatteryManager;
|
import android.os.BatteryManager;
|
||||||
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
import android.os.IBinder;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
|
import android.os.Process;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.ServiceManager;
|
import android.os.ServiceManager;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
@@ -102,9 +116,12 @@ import androidx.test.runner.AndroidJUnit4;
|
|||||||
|
|
||||||
import com.android.dx.mockito.inline.extended.MockedVoidMethod;
|
import com.android.dx.mockito.inline.extended.MockedVoidMethod;
|
||||||
import com.android.internal.annotations.GuardedBy;
|
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.AlarmManagerInternal;
|
||||||
import com.android.server.AppStateTracker;
|
import com.android.server.AppStateTracker;
|
||||||
import com.android.server.AppStateTrackerImpl;
|
import com.android.server.AppStateTrackerImpl;
|
||||||
|
import com.android.server.DeviceIdleInternal;
|
||||||
import com.android.server.LocalServices;
|
import com.android.server.LocalServices;
|
||||||
import com.android.server.SystemService;
|
import com.android.server.SystemService;
|
||||||
import com.android.server.usage.AppStandbyInternal;
|
import com.android.server.usage.AppStandbyInternal;
|
||||||
@@ -125,6 +142,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.function.LongConsumer;
|
||||||
|
|
||||||
@Presubmit
|
@Presubmit
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
@@ -134,14 +152,21 @@ public class AlarmManagerServiceTest {
|
|||||||
private static final int TEST_CALLING_USER = UserHandle.getUserId(TEST_CALLING_UID);
|
private static final int TEST_CALLING_USER = UserHandle.getUserId(TEST_CALLING_UID);
|
||||||
|
|
||||||
private long mAppStandbyWindow;
|
private long mAppStandbyWindow;
|
||||||
|
private long mAllowWhileIdleWindow;
|
||||||
private AlarmManagerService mService;
|
private AlarmManagerService mService;
|
||||||
private AppStandbyInternal.AppIdleStateChangeListener mAppStandbyListener;
|
private AppStandbyInternal.AppIdleStateChangeListener mAppStandbyListener;
|
||||||
private AlarmManagerService.ChargingReceiver mChargingReceiver;
|
private AlarmManagerService.ChargingReceiver mChargingReceiver;
|
||||||
|
private IAppOpsCallback mIAppOpsCallback;
|
||||||
|
private IAlarmManager mBinder;
|
||||||
@Mock
|
@Mock
|
||||||
private Context mMockContext;
|
private Context mMockContext;
|
||||||
@Mock
|
@Mock
|
||||||
private IActivityManager mIActivityManager;
|
private IActivityManager mIActivityManager;
|
||||||
@Mock
|
@Mock
|
||||||
|
private IAppOpsService mIAppOpsService;
|
||||||
|
@Mock
|
||||||
|
private DeviceIdleInternal mDeviceIdleInternal;
|
||||||
|
@Mock
|
||||||
private UsageStatsManagerInternal mUsageStatsManagerInternal;
|
private UsageStatsManagerInternal mUsageStatsManagerInternal;
|
||||||
@Mock
|
@Mock
|
||||||
private AppStandbyInternal mAppStandbyInternal;
|
private AppStandbyInternal mAppStandbyInternal;
|
||||||
@@ -280,6 +305,11 @@ public class AlarmManagerServiceTest {
|
|||||||
// "IllegalStateException: Querying activity state off main thread is not allowed."
|
// "IllegalStateException: Querying activity state off main thread is not allowed."
|
||||||
// when AlarmManager calls DeviceConfig.addOnPropertiesChangedListener().
|
// when AlarmManager calls DeviceConfig.addOnPropertiesChangedListener().
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
IAppOpsService getAppOpsService() {
|
||||||
|
return mIAppOpsService;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@@ -287,15 +317,20 @@ public class AlarmManagerServiceTest {
|
|||||||
mMockingSession = mockitoSession()
|
mMockingSession = mockitoSession()
|
||||||
.initMocks(this)
|
.initMocks(this)
|
||||||
.spyStatic(ActivityManager.class)
|
.spyStatic(ActivityManager.class)
|
||||||
|
.mockStatic(CompatChanges.class)
|
||||||
.spyStatic(DeviceConfig.class)
|
.spyStatic(DeviceConfig.class)
|
||||||
.mockStatic(LocalServices.class)
|
.mockStatic(LocalServices.class)
|
||||||
.spyStatic(Looper.class)
|
.spyStatic(Looper.class)
|
||||||
|
.mockStatic(PermissionChecker.class)
|
||||||
.mockStatic(Settings.Global.class)
|
.mockStatic(Settings.Global.class)
|
||||||
.mockStatic(ServiceManager.class)
|
.mockStatic(ServiceManager.class)
|
||||||
.spyStatic(UserHandle.class)
|
.spyStatic(UserHandle.class)
|
||||||
.strictness(Strictness.WARN)
|
.strictness(Strictness.WARN)
|
||||||
.startMocking();
|
.startMocking();
|
||||||
|
|
||||||
doReturn(mIActivityManager).when(ActivityManager::getService);
|
doReturn(mIActivityManager).when(ActivityManager::getService);
|
||||||
|
doReturn(mDeviceIdleInternal).when(
|
||||||
|
() -> LocalServices.getService(DeviceIdleInternal.class));
|
||||||
doReturn(mActivityManagerInternal).when(
|
doReturn(mActivityManagerInternal).when(
|
||||||
() -> LocalServices.getService(ActivityManagerInternal.class));
|
() -> LocalServices.getService(ActivityManagerInternal.class));
|
||||||
doReturn(mAppStateTracker).when(() -> LocalServices.getService(AppStateTracker.class));
|
doReturn(mAppStateTracker).when(() -> LocalServices.getService(AppStateTracker.class));
|
||||||
@@ -330,6 +365,9 @@ public class AlarmManagerServiceTest {
|
|||||||
() -> DeviceConfig.getProperties(
|
() -> DeviceConfig.getProperties(
|
||||||
eq(DeviceConfig.NAMESPACE_ALARM_MANAGER), ArgumentMatchers.<String>any()));
|
eq(DeviceConfig.NAMESPACE_ALARM_MANAGER), ArgumentMatchers.<String>any()));
|
||||||
|
|
||||||
|
when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(
|
||||||
|
mock(AppOpsManager.class));
|
||||||
|
|
||||||
mInjector = new Injector(mMockContext);
|
mInjector = new Injector(mMockContext);
|
||||||
mService = new AlarmManagerService(mMockContext, mInjector);
|
mService = new AlarmManagerService(mMockContext, mInjector);
|
||||||
spyOn(mService);
|
spyOn(mService);
|
||||||
@@ -346,6 +384,7 @@ public class AlarmManagerServiceTest {
|
|||||||
// Other boot phases don't matter
|
// Other boot phases don't matter
|
||||||
mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
|
mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
|
||||||
mAppStandbyWindow = mService.mConstants.APP_STANDBY_WINDOW;
|
mAppStandbyWindow = mService.mConstants.APP_STANDBY_WINDOW;
|
||||||
|
mAllowWhileIdleWindow = mService.mConstants.ALLOW_WHILE_IDLE_WINDOW;
|
||||||
ArgumentCaptor<AppStandbyInternal.AppIdleStateChangeListener> captor =
|
ArgumentCaptor<AppStandbyInternal.AppIdleStateChangeListener> captor =
|
||||||
ArgumentCaptor.forClass(AppStandbyInternal.AppIdleStateChangeListener.class);
|
ArgumentCaptor.forClass(AppStandbyInternal.AppIdleStateChangeListener.class);
|
||||||
verify(mAppStandbyInternal).addListener(captor.capture());
|
verify(mAppStandbyInternal).addListener(captor.capture());
|
||||||
@@ -358,6 +397,20 @@ public class AlarmManagerServiceTest {
|
|||||||
&& filter.hasAction(BatteryManager.ACTION_DISCHARGING)));
|
&& filter.hasAction(BatteryManager.ACTION_DISCHARGING)));
|
||||||
mChargingReceiver = chargingReceiverCaptor.getValue();
|
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();
|
setTestableQuotas();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -389,13 +442,18 @@ public class AlarmManagerServiceTest {
|
|||||||
|
|
||||||
private void setTestAlarm(int type, long triggerTime, PendingIntent operation, long interval,
|
private void setTestAlarm(int type, long triggerTime, PendingIntent operation, long interval,
|
||||||
int flags, int callingUid) {
|
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,
|
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) {
|
private void setTestAlarmWithListener(int type, long triggerTime, IAlarmListener listener) {
|
||||||
mService.setImpl(type, triggerTime, WINDOW_EXACT, 0, null, listener, "test",
|
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_MIN_INTERVAL, 10);
|
||||||
setDeviceConfigLong(KEY_MAX_INTERVAL, 15);
|
setDeviceConfigLong(KEY_MAX_INTERVAL, 15);
|
||||||
setDeviceConfigInt(KEY_ALLOW_WHILE_IDLE_QUOTA, 20);
|
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_ALLOW_WHILE_IDLE_WHITELIST_DURATION, 30);
|
||||||
setDeviceConfigLong(KEY_LISTENER_TIMEOUT, 35);
|
setDeviceConfigLong(KEY_LISTENER_TIMEOUT, 35);
|
||||||
assertEquals(5, mService.mConstants.MIN_FUTURITY);
|
assertEquals(5, mService.mConstants.MIN_FUTURITY);
|
||||||
assertEquals(10, mService.mConstants.MIN_INTERVAL);
|
assertEquals(10, mService.mConstants.MIN_INTERVAL);
|
||||||
assertEquals(15, mService.mConstants.MAX_INTERVAL);
|
assertEquals(15, mService.mConstants.MAX_INTERVAL);
|
||||||
assertEquals(20, mService.mConstants.ALLOW_WHILE_IDLE_QUOTA);
|
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(30, mService.mConstants.ALLOW_WHILE_IDLE_WHITELIST_DURATION);
|
||||||
assertEquals(35, mService.mConstants.LISTENER_TIMEOUT);
|
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
|
@Test
|
||||||
@@ -559,56 +630,45 @@ public class AlarmManagerServiceTest {
|
|||||||
assertEquals(mNowElapsedTest + 9, mTestTimer.getElapsed());
|
assertEquals(mNowElapsedTest + 9, mTestTimer.getElapsed());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testQuotasDeferralOnSet(int standbyBucket) throws Exception {
|
private void testQuotasDeferralOnSet(LongConsumer alarmSetter, int quota, long window)
|
||||||
final int quota = mService.getQuotaForBucketLocked(standbyBucket);
|
throws Exception {
|
||||||
when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
|
|
||||||
anyLong())).thenReturn(standbyBucket);
|
|
||||||
final long firstTrigger = mNowElapsedTest + 10;
|
final long firstTrigger = mNowElapsedTest + 10;
|
||||||
for (int i = 0; i < quota; i++) {
|
for (int i = 0; i < quota; i++) {
|
||||||
setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
|
alarmSetter.accept(firstTrigger + i);
|
||||||
getNewMockPendingIntent());
|
|
||||||
mNowElapsedTest = mTestTimer.getElapsed();
|
mNowElapsedTest = mTestTimer.getElapsed();
|
||||||
mTestTimer.expire();
|
mTestTimer.expire();
|
||||||
}
|
}
|
||||||
// This one should get deferred on set
|
// This one should get deferred on set
|
||||||
setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota,
|
alarmSetter.accept(firstTrigger + quota);
|
||||||
getNewMockPendingIntent());
|
final long expectedNextTrigger = firstTrigger + window;
|
||||||
final long expectedNextTrigger = firstTrigger + mAppStandbyWindow;
|
|
||||||
assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed());
|
assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testQuotasDeferralOnExpiration(int standbyBucket) throws Exception {
|
private void testQuotasDeferralOnExpiration(LongConsumer alarmSetter, int quota, long window)
|
||||||
final int quota = mService.getQuotaForBucketLocked(standbyBucket);
|
throws Exception {
|
||||||
when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
|
|
||||||
anyLong())).thenReturn(standbyBucket);
|
|
||||||
final long firstTrigger = mNowElapsedTest + 10;
|
final long firstTrigger = mNowElapsedTest + 10;
|
||||||
for (int i = 0; i < quota; i++) {
|
for (int i = 0; i < quota; i++) {
|
||||||
setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
|
alarmSetter.accept(firstTrigger + i);
|
||||||
getNewMockPendingIntent());
|
|
||||||
}
|
}
|
||||||
// This one should get deferred after the latest alarm expires
|
// This one should get deferred after the latest alarm expires.
|
||||||
setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota,
|
alarmSetter.accept(firstTrigger + quota);
|
||||||
getNewMockPendingIntent());
|
|
||||||
for (int i = 0; i < quota; i++) {
|
for (int i = 0; i < quota; i++) {
|
||||||
mNowElapsedTest = mTestTimer.getElapsed();
|
mNowElapsedTest = mTestTimer.getElapsed();
|
||||||
mTestTimer.expire();
|
mTestTimer.expire();
|
||||||
}
|
}
|
||||||
final long expectedNextTrigger = firstTrigger + mAppStandbyWindow;
|
final long expectedNextTrigger = firstTrigger + window;
|
||||||
assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed());
|
assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testQuotasNoDeferral(int standbyBucket) throws Exception {
|
private void testQuotasNoDeferral(LongConsumer alarmSetter, int quota, long window)
|
||||||
final int quota = mService.getQuotaForBucketLocked(standbyBucket);
|
throws Exception {
|
||||||
when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
|
|
||||||
anyLong())).thenReturn(standbyBucket);
|
|
||||||
final long firstTrigger = mNowElapsedTest + 10;
|
final long firstTrigger = mNowElapsedTest + 10;
|
||||||
for (int i = 0; i < quota; i++) {
|
for (int i = 0; i < quota; i++) {
|
||||||
setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 10 + i,
|
alarmSetter.accept(firstTrigger + i);
|
||||||
getNewMockPendingIntent());
|
|
||||||
}
|
}
|
||||||
// This delivery time maintains the quota invariant. Should not be deferred.
|
// This delivery time maintains the quota invariant. Should not be deferred.
|
||||||
final long expectedNextTrigger = firstTrigger + mAppStandbyWindow + 5;
|
final long expectedNextTrigger = firstTrigger + window + 5;
|
||||||
setTestAlarm(ELAPSED_REALTIME_WAKEUP, expectedNextTrigger, getNewMockPendingIntent());
|
alarmSetter.accept(expectedNextTrigger);
|
||||||
for (int i = 0; i < quota; i++) {
|
for (int i = 0; i < quota; i++) {
|
||||||
mNowElapsedTest = mTestTimer.getElapsed();
|
mNowElapsedTest = mTestTimer.getElapsed();
|
||||||
mTestTimer.expire();
|
mTestTimer.expire();
|
||||||
@@ -616,64 +676,88 @@ public class AlarmManagerServiceTest {
|
|||||||
assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed());
|
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
|
@Test
|
||||||
public void testActiveQuota_deferredOnSet() throws Exception {
|
public void testActiveQuota_deferredOnSet() throws Exception {
|
||||||
testQuotasDeferralOnSet(STANDBY_BUCKET_ACTIVE);
|
testStandbyQuotasDeferralOnSet(STANDBY_BUCKET_ACTIVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testActiveQuota_deferredOnExpiration() throws Exception {
|
public void testActiveQuota_deferredOnExpiration() throws Exception {
|
||||||
testQuotasDeferralOnExpiration(STANDBY_BUCKET_ACTIVE);
|
testStandbyQuotasDeferralOnExpiration(STANDBY_BUCKET_ACTIVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testActiveQuota_notDeferred() throws Exception {
|
public void testActiveQuota_notDeferred() throws Exception {
|
||||||
testQuotasNoDeferral(STANDBY_BUCKET_ACTIVE);
|
testStandbyQuotasNoDeferral(STANDBY_BUCKET_ACTIVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWorkingQuota_deferredOnSet() throws Exception {
|
public void testWorkingQuota_deferredOnSet() throws Exception {
|
||||||
testQuotasDeferralOnSet(STANDBY_BUCKET_WORKING_SET);
|
testStandbyQuotasDeferralOnSet(STANDBY_BUCKET_WORKING_SET);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWorkingQuota_deferredOnExpiration() throws Exception {
|
public void testWorkingQuota_deferredOnExpiration() throws Exception {
|
||||||
testQuotasDeferralOnExpiration(STANDBY_BUCKET_WORKING_SET);
|
testStandbyQuotasDeferralOnExpiration(STANDBY_BUCKET_WORKING_SET);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWorkingQuota_notDeferred() throws Exception {
|
public void testWorkingQuota_notDeferred() throws Exception {
|
||||||
testQuotasNoDeferral(STANDBY_BUCKET_WORKING_SET);
|
testStandbyQuotasNoDeferral(STANDBY_BUCKET_WORKING_SET);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFrequentQuota_deferredOnSet() throws Exception {
|
public void testFrequentQuota_deferredOnSet() throws Exception {
|
||||||
testQuotasDeferralOnSet(STANDBY_BUCKET_FREQUENT);
|
testStandbyQuotasDeferralOnSet(STANDBY_BUCKET_FREQUENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFrequentQuota_deferredOnExpiration() throws Exception {
|
public void testFrequentQuota_deferredOnExpiration() throws Exception {
|
||||||
testQuotasDeferralOnExpiration(STANDBY_BUCKET_FREQUENT);
|
testStandbyQuotasDeferralOnExpiration(STANDBY_BUCKET_FREQUENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFrequentQuota_notDeferred() throws Exception {
|
public void testFrequentQuota_notDeferred() throws Exception {
|
||||||
testQuotasNoDeferral(STANDBY_BUCKET_FREQUENT);
|
testStandbyQuotasNoDeferral(STANDBY_BUCKET_FREQUENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRareQuota_deferredOnSet() throws Exception {
|
public void testRareQuota_deferredOnSet() throws Exception {
|
||||||
testQuotasDeferralOnSet(STANDBY_BUCKET_RARE);
|
testStandbyQuotasDeferralOnSet(STANDBY_BUCKET_RARE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRareQuota_deferredOnExpiration() throws Exception {
|
public void testRareQuota_deferredOnExpiration() throws Exception {
|
||||||
testQuotasDeferralOnExpiration(STANDBY_BUCKET_RARE);
|
testStandbyQuotasDeferralOnExpiration(STANDBY_BUCKET_RARE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRareQuota_notDeferred() throws Exception {
|
public void testRareQuota_notDeferred() throws Exception {
|
||||||
testQuotasNoDeferral(STANDBY_BUCKET_RARE);
|
testStandbyQuotasNoDeferral(STANDBY_BUCKET_RARE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -731,7 +815,7 @@ public class AlarmManagerServiceTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testQuotaDowngrade() throws Exception {
|
public void testStandbyQuotaDowngrade() throws Exception {
|
||||||
final int workingQuota = mService.getQuotaForBucketLocked(STANDBY_BUCKET_WORKING_SET);
|
final int workingQuota = mService.getQuotaForBucketLocked(STANDBY_BUCKET_WORKING_SET);
|
||||||
when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
|
when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
|
||||||
anyLong())).thenReturn(STANDBY_BUCKET_WORKING_SET);
|
anyLong())).thenReturn(STANDBY_BUCKET_WORKING_SET);
|
||||||
@@ -758,7 +842,7 @@ public class AlarmManagerServiceTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testQuotaUpgrade() throws Exception {
|
public void testStandbyQuotaUpgrade() throws Exception {
|
||||||
final int frequentQuota = mService.getQuotaForBucketLocked(STANDBY_BUCKET_FREQUENT);
|
final int frequentQuota = mService.getQuotaForBucketLocked(STANDBY_BUCKET_FREQUENT);
|
||||||
when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
|
when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
|
||||||
anyLong())).thenReturn(STANDBY_BUCKET_FREQUENT);
|
anyLong())).thenReturn(STANDBY_BUCKET_FREQUENT);
|
||||||
@@ -1308,7 +1392,7 @@ public class AlarmManagerServiceTest {
|
|||||||
public void allowWhileIdleAlarmsWhileDeviceIdle() throws Exception {
|
public void allowWhileIdleAlarmsWhileDeviceIdle() throws Exception {
|
||||||
doReturn(0).when(mService).fuzzForDuration(anyLong());
|
doReturn(0).when(mService).fuzzForDuration(anyLong());
|
||||||
|
|
||||||
setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + ALLOW_WHILE_IDLE_WINDOW + 1000,
|
setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + mAllowWhileIdleWindow + 1000,
|
||||||
getNewMockPendingIntent());
|
getNewMockPendingIntent());
|
||||||
assertNotNull(mService.mPendingIdleUntil);
|
assertNotNull(mService.mPendingIdleUntil);
|
||||||
|
|
||||||
@@ -1323,7 +1407,7 @@ public class AlarmManagerServiceTest {
|
|||||||
// This one should get deferred on set.
|
// This one should get deferred on set.
|
||||||
setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota,
|
setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota,
|
||||||
getNewMockPendingIntent(), false);
|
getNewMockPendingIntent(), false);
|
||||||
final long expectedNextTrigger = firstTrigger + ALLOW_WHILE_IDLE_WINDOW;
|
final long expectedNextTrigger = firstTrigger + mAllowWhileIdleWindow;
|
||||||
assertEquals("Incorrect trigger when no quota left", expectedNextTrigger,
|
assertEquals("Incorrect trigger when no quota left", expectedNextTrigger,
|
||||||
mTestTimer.getElapsed());
|
mTestTimer.getElapsed());
|
||||||
|
|
||||||
@@ -1449,61 +1533,24 @@ public class AlarmManagerServiceTest {
|
|||||||
when(mAppStateTracker.areAlarmsRestrictedByBatterySaver(TEST_CALLING_UID,
|
when(mAppStateTracker.areAlarmsRestrictedByBatterySaver(TEST_CALLING_UID,
|
||||||
TEST_CALLING_PACKAGE)).thenReturn(true);
|
TEST_CALLING_PACKAGE)).thenReturn(true);
|
||||||
when(mAppStateTracker.isForceAllAppsStandbyEnabled()).thenReturn(true);
|
when(mAppStateTracker.isForceAllAppsStandbyEnabled()).thenReturn(true);
|
||||||
|
|
||||||
final int quota = mService.mConstants.ALLOW_WHILE_IDLE_QUOTA;
|
final int quota = mService.mConstants.ALLOW_WHILE_IDLE_QUOTA;
|
||||||
long firstTrigger = mNowElapsedTest + 10;
|
|
||||||
for (int i = 0; i < quota; i++) {
|
testQuotasDeferralOnSet(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, trigger,
|
||||||
setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
|
getNewMockPendingIntent(), false), quota, mAllowWhileIdleWindow);
|
||||||
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());
|
|
||||||
|
|
||||||
// Refresh the state
|
// Refresh the state
|
||||||
mService.removeLocked(TEST_CALLING_UID);
|
mService.removeLocked(TEST_CALLING_UID);
|
||||||
mService.mAllowWhileIdleHistory.removeForPackage(TEST_CALLING_PACKAGE, TEST_CALLING_USER);
|
mService.mAllowWhileIdleHistory.removeForPackage(TEST_CALLING_PACKAGE, TEST_CALLING_USER);
|
||||||
|
|
||||||
firstTrigger = mNowElapsedTest + 10;
|
testQuotasDeferralOnExpiration(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP,
|
||||||
for (int i = 0; i < quota; i++) {
|
trigger, getNewMockPendingIntent(), false), quota, mAllowWhileIdleWindow);
|
||||||
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());
|
|
||||||
|
|
||||||
// Refresh the state
|
// Refresh the state
|
||||||
mService.removeLocked(TEST_CALLING_UID);
|
mService.removeLocked(TEST_CALLING_UID);
|
||||||
mService.mAllowWhileIdleHistory.removeForPackage(TEST_CALLING_PACKAGE, TEST_CALLING_USER);
|
mService.mAllowWhileIdleHistory.removeForPackage(TEST_CALLING_PACKAGE, TEST_CALLING_USER);
|
||||||
|
|
||||||
firstTrigger = mNowElapsedTest + 10;
|
testQuotasNoDeferral(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, trigger,
|
||||||
for (int i = 0; i < quota; i++) {
|
getNewMockPendingIntent(), false), quota, mAllowWhileIdleWindow);
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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
|
@Test
|
||||||
public void alarmStoreMigration() {
|
public void alarmStoreMigration() {
|
||||||
setDeviceConfigBoolean(KEY_LAZY_BATCHING, false);
|
setDeviceConfigBoolean(KEY_LAZY_BATCHING, false);
|
||||||
|
|||||||
@@ -69,12 +69,12 @@ public class AlarmStoreTest {
|
|||||||
mock(PendingIntent.class));
|
mock(PendingIntent.class));
|
||||||
return new Alarm(ELAPSED_REALTIME_WAKEUP, whenElapsed, whenElapsed, 0, 0,
|
return new Alarm(ELAPSED_REALTIME_WAKEUP, whenElapsed, whenElapsed, 0, 0,
|
||||||
mock(PendingIntent.class), null, null, null, 0, info, TEST_CALLING_UID,
|
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) {
|
private static Alarm createAlarm(int type, long whenElapsed, long windowLength, int flags) {
|
||||||
return new Alarm(type, whenElapsed, whenElapsed, windowLength, 0, mock(PendingIntent.class),
|
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) {
|
private void addAlarmsToStore(Alarm... alarms) {
|
||||||
|
|||||||
@@ -17,10 +17,17 @@
|
|||||||
package com.android.server.alarm;
|
package com.android.server.alarm;
|
||||||
|
|
||||||
import static android.app.AlarmManager.ELAPSED_REALTIME;
|
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.APP_STANDBY_POLICY_INDEX;
|
||||||
import static com.android.server.alarm.Alarm.NUM_POLICIES;
|
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.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_PACKAGE;
|
||||||
import static com.android.server.alarm.Constants.TEST_CALLING_UID;
|
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.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.app.AlarmManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.platform.test.annotations.Presubmit;
|
import android.platform.test.annotations.Presubmit;
|
||||||
|
|
||||||
@@ -43,15 +52,29 @@ import java.util.Random;
|
|||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class AlarmTest {
|
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,
|
return new Alarm(ELAPSED_REALTIME, 0, requestedElapsed, windowLength, 0,
|
||||||
mock(PendingIntent.class), null, null, null, 0, null, TEST_CALLING_UID,
|
createAlarmSender(), null, null, null, flags, null, TEST_CALLING_UID,
|
||||||
TEST_CALLING_PACKAGE);
|
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
|
@Test
|
||||||
public void initSetsOnlyRequesterPolicy() {
|
public void initSetsOnlyRequesterPolicy() {
|
||||||
final Alarm a = createDefaultAlarm(4567, 2);
|
final Alarm a = createDefaultAlarm(4567, 2, 0);
|
||||||
|
|
||||||
for (int i = 0; i < NUM_POLICIES; i++) {
|
for (int i = 0; i < NUM_POLICIES; i++) {
|
||||||
if (i == REQUESTER_POLICY_INDEX) {
|
if (i == REQUESTER_POLICY_INDEX) {
|
||||||
@@ -86,7 +109,7 @@ public class AlarmTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void whenElapsed() {
|
public void whenElapsed() {
|
||||||
final Alarm a = createDefaultAlarm(0, 0);
|
final Alarm a = createDefaultAlarm(0, 0, 0);
|
||||||
|
|
||||||
final long[][] uniqueData = generatePolicyTestMatrix(NUM_POLICIES);
|
final long[][] uniqueData = generatePolicyTestMatrix(NUM_POLICIES);
|
||||||
for (int i = 0; i < NUM_POLICIES; i++) {
|
for (int i = 0; i < NUM_POLICIES; i++) {
|
||||||
@@ -104,7 +127,7 @@ public class AlarmTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void maxWhenElapsed() {
|
public void maxWhenElapsed() {
|
||||||
final Alarm a = createDefaultAlarm(10, 12);
|
final Alarm a = createDefaultAlarm(10, 12, 0);
|
||||||
assertEquals(22, a.getMaxWhenElapsed());
|
assertEquals(22, a.getMaxWhenElapsed());
|
||||||
|
|
||||||
a.setPolicyElapsed(REQUESTER_POLICY_INDEX, 15);
|
a.setPolicyElapsed(REQUESTER_POLICY_INDEX, 15);
|
||||||
@@ -128,7 +151,7 @@ public class AlarmTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void setPolicyElapsedExact() {
|
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(REQUESTER_POLICY_INDEX, 4));
|
||||||
assertTrue(exactAlarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 10));
|
assertTrue(exactAlarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 10));
|
||||||
@@ -143,7 +166,7 @@ public class AlarmTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void setPolicyElapsedInexact() {
|
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(REQUESTER_POLICY_INDEX, 4));
|
||||||
assertTrue(inexactAlarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 10));
|
assertTrue(inexactAlarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 10));
|
||||||
@@ -154,4 +177,20 @@ public class AlarmTest {
|
|||||||
|
|
||||||
assertFalse(inexactAlarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 8));
|
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(
|
uidAlarms.add(new Alarm(
|
||||||
removeIt ? RTC : RTC_WAKEUP,
|
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;
|
return all;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user