From 51ec8010c5632ad0a0a3bc6f35b01651d63f733c Mon Sep 17 00:00:00 2001 From: Suprabh Shukla Date: Wed, 4 Jan 2023 12:26:13 -0800 Subject: [PATCH] Exempt exact alarms using listener from permission Listener alarms rely on the caller staying in valid lifecycle till the alarm expires. In this case the caller should be encouraged to use exact alarms for use-cases requiring timing precisions without needing additional user consent. Adding explicit reason codes for listener and prioritized alarms for logging when these alarms are also exact. Also reducing the quota of listener allow-while-idle alarms to match other alarms that do not require permission. Test: atest CtsAlarmManagerTestCases:ExactAlarmsTest Bug: 228125912 Change-Id: I7636126c411b174a71ad91583c9fbd9d9457623d --- .../java/android/app/AlarmManager.java | 29 +++++-------------- .../java/com/android/server/alarm/Alarm.java | 8 +++++ .../server/alarm/AlarmManagerService.java | 25 +++++++++++----- .../android/server/alarm/MetricsHelper.java | 10 +++++-- core/api/current.txt | 2 +- core/api/system-current.txt | 4 +-- 6 files changed, 45 insertions(+), 33 deletions(-) diff --git a/apex/jobscheduler/framework/java/android/app/AlarmManager.java b/apex/jobscheduler/framework/java/android/app/AlarmManager.java index 439f54c6328b5..7ed4d35b3ccc5 100644 --- a/apex/jobscheduler/framework/java/android/app/AlarmManager.java +++ b/apex/jobscheduler/framework/java/android/app/AlarmManager.java @@ -800,22 +800,12 @@ public class AlarmManager { * if {@code null} is passed as the {@code targetHandler} parameter. * *

Note: - * Starting with {@link Build.VERSION_CODES#S}, apps targeting SDK level 31 or higher - * need to request the - * {@link Manifest.permission#SCHEDULE_EXACT_ALARM SCHEDULE_EXACT_ALARM} permission to use this - * API, unless the app is exempt from battery restrictions. - * The user and the system can revoke this permission via the special app access screen in - * Settings. + * On previous android versions {@link Build.VERSION_CODES#S} and + * {@link Build.VERSION_CODES#TIRAMISU}, apps targeting SDK level 31 or higher needed to hold + * the {@link Manifest.permission#SCHEDULE_EXACT_ALARM SCHEDULE_EXACT_ALARM} permission to use + * this API, unless the app was exempt from battery restrictions. * - *

Note: - * Exact alarms should only be used for user-facing features. - * For more details, see - * Exact alarm permission. - * - * @see Manifest.permission#SCHEDULE_EXACT_ALARM SCHEDULE_EXACT_ALARM */ - @RequiresPermission(value = Manifest.permission.SCHEDULE_EXACT_ALARM, conditional = true) public void setExact(@AlarmType int type, long triggerAtMillis, @Nullable String tag, @NonNull OnAlarmListener listener, @Nullable Handler targetHandler) { setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, 0, null, listener, tag, @@ -949,7 +939,8 @@ public class AlarmManager { * {@link #setExact(int, long, String, OnAlarmListener, Handler)} instead. * *

- * Note that using this API requires you to hold + * Note that on previous Android versions {@link Build.VERSION_CODES#S} and + * {@link Build.VERSION_CODES#TIRAMISU}, using this API required you to hold * {@link Manifest.permission#SCHEDULE_EXACT_ALARM}, unless you are on the system's power * allowlist. This can be set, for example, by marking the app as {@code } * within the system config. @@ -970,9 +961,7 @@ public class AlarmManager { * @hide */ @SystemApi - @RequiresPermission(allOf = { - Manifest.permission.UPDATE_DEVICE_STATS, - Manifest.permission.SCHEDULE_EXACT_ALARM}, conditional = true) + @RequiresPermission(Manifest.permission.UPDATE_DEVICE_STATS) public void setExact(@AlarmType int type, long triggerAtMillis, @Nullable String tag, @NonNull Executor executor, @NonNull WorkSource workSource, @NonNull OnAlarmListener listener) { @@ -1283,9 +1272,7 @@ public class AlarmManager { * @hide */ @SystemApi - @RequiresPermission(allOf = { - Manifest.permission.UPDATE_DEVICE_STATS, - Manifest.permission.SCHEDULE_EXACT_ALARM}, conditional = true) + @RequiresPermission(Manifest.permission.UPDATE_DEVICE_STATS) public void setExactAndAllowWhileIdle(@AlarmType int type, long triggerAtMillis, @Nullable String tag, @NonNull Executor executor, @Nullable WorkSource workSource, @NonNull OnAlarmListener listener) { diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java b/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java index fd2bb1347fd36..69fe85e37b4bf 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java @@ -92,6 +92,14 @@ class Alarm { * Caller had USE_EXACT_ALARM permission. */ static final int EXACT_ALLOW_REASON_POLICY_PERMISSION = 3; + /** + * Caller used a listener alarm, which does not need permission to be exact. + */ + static final int EXACT_ALLOW_REASON_LISTENER = 4; + /** + * Caller used a prioritized alarm, which does not need permission to be exact. + */ + static final int EXACT_ALLOW_REASON_PRIORITIZED = 5; public final int type; /** diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java index d6d51e0cf3fe2..e41eb0071f6a8 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -47,9 +47,11 @@ 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.EXACT_ALLOW_REASON_ALLOW_LIST; import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_COMPAT; +import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_LISTENER; import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_NOT_APPLICABLE; import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_PERMISSION; import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_POLICY_PERMISSION; +import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_PRIORITIZED; import static com.android.server.alarm.Alarm.REQUESTER_POLICY_INDEX; import static com.android.server.alarm.Alarm.TARE_POLICY_INDEX; import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_ALARM_CANCELLED; @@ -2890,12 +2892,23 @@ public class AlarmManagerService extends SystemService { // The API doesn't allow using both together. flags &= ~FLAG_ALLOW_WHILE_IDLE; // Prioritized alarms don't need any extra permission to be exact. + if (exact) { + exactAllowReason = EXACT_ALLOW_REASON_PRIORITIZED; + } } else if (exact || allowWhileIdle) { final boolean needsPermission; boolean lowerQuota; if (isExactAlarmChangeEnabled(callingPackage, callingUserId)) { - needsPermission = exact; - lowerQuota = !exact; + if (directReceiver == null) { + needsPermission = exact; + lowerQuota = !exact; + } else { + needsPermission = false; + lowerQuota = allowWhileIdle; + if (exact) { + exactAllowReason = EXACT_ALLOW_REASON_LISTENER; + } + } if (exact) { idleOptions = (alarmClock != null) ? mOptsWithFgsForAlarmClock.toBundle() : mOptsWithFgs.toBundle(); @@ -2931,11 +2944,9 @@ public class AlarmManagerService extends SystemService { throw new SecurityException(errorMessage); } // If the app is on the full system power allow-list (not except-idle), - // or the user-elected allow-list, or we're in a soft failure mode, we still - // allow the alarms. - // In both cases, ALLOW_WHILE_IDLE alarms get a lower quota equivalent to - // what pre-S apps got. Note that user-allow-listed apps don't use the flag - // ALLOW_WHILE_IDLE. + // or the user-elected allow-list, we allow exact alarms. + // ALLOW_WHILE_IDLE alarms get a lower quota equivalent to what pre-S apps + // got. Note that user-allow-listed apps don't use FLAG_ALLOW_WHILE_IDLE. // We grant temporary allow-list to allow-while-idle alarms but without FGS // capability. AlarmClock alarms do not get the temporary allow-list. // This is consistent with pre-S behavior. Note that apps that are in diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/MetricsHelper.java b/apex/jobscheduler/service/java/com/android/server/alarm/MetricsHelper.java index 75ed616e2d964..28acb451c5f83 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/MetricsHelper.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/MetricsHelper.java @@ -18,9 +18,11 @@ package com.android.server.alarm; import static com.android.internal.util.FrameworkStatsLog.ALARM_SCHEDULED__EXACT_ALARM_ALLOWED_REASON__ALLOW_LIST; import static com.android.internal.util.FrameworkStatsLog.ALARM_SCHEDULED__EXACT_ALARM_ALLOWED_REASON__CHANGE_DISABLED; +import static com.android.internal.util.FrameworkStatsLog.ALARM_SCHEDULED__EXACT_ALARM_ALLOWED_REASON__LISTENER; import static com.android.internal.util.FrameworkStatsLog.ALARM_SCHEDULED__EXACT_ALARM_ALLOWED_REASON__NOT_APPLICABLE; import static com.android.internal.util.FrameworkStatsLog.ALARM_SCHEDULED__EXACT_ALARM_ALLOWED_REASON__PERMISSION; import static com.android.internal.util.FrameworkStatsLog.ALARM_SCHEDULED__EXACT_ALARM_ALLOWED_REASON__POLICY_PERMISSION; +import static com.android.internal.util.FrameworkStatsLog.ALARM_SCHEDULED__EXACT_ALARM_ALLOWED_REASON__PRIORITIZED; import static com.android.server.alarm.AlarmManagerService.INDEFINITE_DELAY; import android.app.ActivityManager; @@ -84,14 +86,18 @@ class MetricsHelper { private static int reasonToStatsReason(int reasonCode) { switch (reasonCode) { - case Alarm.EXACT_ALLOW_REASON_ALLOW_LIST: - return ALARM_SCHEDULED__EXACT_ALARM_ALLOWED_REASON__ALLOW_LIST; case Alarm.EXACT_ALLOW_REASON_PERMISSION: return ALARM_SCHEDULED__EXACT_ALARM_ALLOWED_REASON__PERMISSION; + case Alarm.EXACT_ALLOW_REASON_ALLOW_LIST: + return ALARM_SCHEDULED__EXACT_ALARM_ALLOWED_REASON__ALLOW_LIST; case Alarm.EXACT_ALLOW_REASON_COMPAT: return ALARM_SCHEDULED__EXACT_ALARM_ALLOWED_REASON__CHANGE_DISABLED; case Alarm.EXACT_ALLOW_REASON_POLICY_PERMISSION: return ALARM_SCHEDULED__EXACT_ALARM_ALLOWED_REASON__POLICY_PERMISSION; + case Alarm.EXACT_ALLOW_REASON_LISTENER: + return ALARM_SCHEDULED__EXACT_ALARM_ALLOWED_REASON__LISTENER; + case Alarm.EXACT_ALLOW_REASON_PRIORITIZED: + return ALARM_SCHEDULED__EXACT_ALARM_ALLOWED_REASON__PRIORITIZED; default: return ALARM_SCHEDULED__EXACT_ALARM_ALLOWED_REASON__NOT_APPLICABLE; } diff --git a/core/api/current.txt b/core/api/current.txt index 4b1335e0d6d60..db96c33f9584d 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -4673,7 +4673,7 @@ package android.app { method @RequiresPermission(android.Manifest.permission.SCHEDULE_EXACT_ALARM) public void setAlarmClock(@NonNull android.app.AlarmManager.AlarmClockInfo, @NonNull android.app.PendingIntent); method public void setAndAllowWhileIdle(int, long, @NonNull android.app.PendingIntent); method @RequiresPermission(value=android.Manifest.permission.SCHEDULE_EXACT_ALARM, conditional=true) public void setExact(int, long, @NonNull android.app.PendingIntent); - method @RequiresPermission(value=android.Manifest.permission.SCHEDULE_EXACT_ALARM, conditional=true) public void setExact(int, long, @Nullable String, @NonNull android.app.AlarmManager.OnAlarmListener, @Nullable android.os.Handler); + method public void setExact(int, long, @Nullable String, @NonNull android.app.AlarmManager.OnAlarmListener, @Nullable android.os.Handler); method @RequiresPermission(value=android.Manifest.permission.SCHEDULE_EXACT_ALARM, conditional=true) public void setExactAndAllowWhileIdle(int, long, @NonNull android.app.PendingIntent); method public void setInexactRepeating(int, long, long, @NonNull android.app.PendingIntent); method public void setRepeating(int, long, long, @NonNull android.app.PendingIntent); diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 0fce784b61665..2b583c5f3ecdc 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -541,8 +541,8 @@ package android.app { public class AlarmManager { method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void set(int, long, long, long, @NonNull android.app.PendingIntent, @Nullable android.os.WorkSource); method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void set(int, long, long, long, @NonNull android.app.AlarmManager.OnAlarmListener, @Nullable android.os.Handler, @Nullable android.os.WorkSource); - method @RequiresPermission(allOf={android.Manifest.permission.UPDATE_DEVICE_STATS, android.Manifest.permission.SCHEDULE_EXACT_ALARM}, conditional=true) public void setExact(int, long, @Nullable String, @NonNull java.util.concurrent.Executor, @NonNull android.os.WorkSource, @NonNull android.app.AlarmManager.OnAlarmListener); - method @RequiresPermission(allOf={android.Manifest.permission.UPDATE_DEVICE_STATS, android.Manifest.permission.SCHEDULE_EXACT_ALARM}, conditional=true) public void setExactAndAllowWhileIdle(int, long, @Nullable String, @NonNull java.util.concurrent.Executor, @Nullable android.os.WorkSource, @NonNull android.app.AlarmManager.OnAlarmListener); + method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void setExact(int, long, @Nullable String, @NonNull java.util.concurrent.Executor, @NonNull android.os.WorkSource, @NonNull android.app.AlarmManager.OnAlarmListener); + method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void setExactAndAllowWhileIdle(int, long, @Nullable String, @NonNull java.util.concurrent.Executor, @Nullable android.os.WorkSource, @NonNull android.app.AlarmManager.OnAlarmListener); method @RequiresPermission(android.Manifest.permission.SCHEDULE_PRIORITIZED_ALARM) public void setPrioritized(int, long, long, @Nullable String, @NonNull java.util.concurrent.Executor, @NonNull android.app.AlarmManager.OnAlarmListener); method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void setWindow(int, long, long, @Nullable String, @NonNull java.util.concurrent.Executor, @Nullable android.os.WorkSource, @NonNull android.app.AlarmManager.OnAlarmListener); }