Merge "Enable alarm permission with a soft failure mode" into sc-dev

This commit is contained in:
Suprabh Shukla
2021-02-25 09:47:19 +00:00
committed by Android (Google) Code Review
2 changed files with 36 additions and 11 deletions

View File

@@ -23,7 +23,7 @@ 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.ChangeId;
import android.compat.annotation.Disabled; import android.compat.annotation.EnabledSince;
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;
@@ -202,7 +202,7 @@ public class AlarmManager {
* @hide * @hide
*/ */
@ChangeId @ChangeId
@Disabled // TODO (b/171306433): Enable starting S. @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
public static final long REQUIRE_EXACT_ALARM_PERMISSION = 171306433L; public static final long REQUIRE_EXACT_ALARM_PERMISSION = 171306433L;
@UnsupportedAppUsage @UnsupportedAppUsage

View File

@@ -423,6 +423,8 @@ public class AlarmManagerService extends SystemService {
static final String KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA = "allow_while_idle_compat_quota"; 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 String KEY_ALLOW_WHILE_IDLE_WINDOW = "allow_while_idle_window";
private static final String KEY_CRASH_NON_CLOCK_APPS = "crash_non_clock_apps";
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;
@@ -454,6 +456,8 @@ public class AlarmManagerService extends SystemService {
private static final int DEFAULT_ALLOW_WHILE_IDLE_QUOTA = 72; private static final int DEFAULT_ALLOW_WHILE_IDLE_QUOTA = 72;
private static final long DEFAULT_ALLOW_WHILE_IDLE_WINDOW = 60 * 60 * 1000; // 1 hour. private static final long DEFAULT_ALLOW_WHILE_IDLE_WINDOW = 60 * 60 * 1000; // 1 hour.
// TODO (b/171306433): Change to true by default.
private static final boolean DEFAULT_CRASH_NON_CLOCK_APPS = false;
// 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;
@@ -495,6 +499,13 @@ public class AlarmManagerService extends SystemService {
*/ */
public long ALLOW_WHILE_IDLE_WINDOW = DEFAULT_ALLOW_WHILE_IDLE_WINDOW; public long ALLOW_WHILE_IDLE_WINDOW = DEFAULT_ALLOW_WHILE_IDLE_WINDOW;
/**
* Whether or not to crash callers that use setExactAndAllowWhileIdle or setAlarmClock
* but don't hold the required permission. This is useful to catch broken
* apps and reverting to a softer failure in case of broken apps.
*/
public boolean CRASH_NON_CLOCK_APPS = DEFAULT_CRASH_NON_CLOCK_APPS;
private long mLastAllowWhileIdleWhitelistDuration = -1; private long mLastAllowWhileIdleWhitelistDuration = -1;
Constants() { Constants() {
@@ -607,6 +618,10 @@ public class AlarmManagerService extends SystemService {
KEY_TIME_TICK_ALLOWED_WHILE_IDLE, KEY_TIME_TICK_ALLOWED_WHILE_IDLE,
DEFAULT_TIME_TICK_ALLOWED_WHILE_IDLE); DEFAULT_TIME_TICK_ALLOWED_WHILE_IDLE);
break; break;
case KEY_CRASH_NON_CLOCK_APPS:
CRASH_NON_CLOCK_APPS = properties.getBoolean(KEY_CRASH_NON_CLOCK_APPS,
DEFAULT_CRASH_NON_CLOCK_APPS);
break;
default: default:
if (name.startsWith(KEY_PREFIX_STANDBY_QUOTA) && !standbyQuotaUpdated) { if (name.startsWith(KEY_PREFIX_STANDBY_QUOTA) && !standbyQuotaUpdated) {
// The quotas need to be updated in order, so we can't just rely // The quotas need to be updated in order, so we can't just rely
@@ -741,6 +756,9 @@ public class AlarmManagerService extends SystemService {
pw.print(KEY_TIME_TICK_ALLOWED_WHILE_IDLE, TIME_TICK_ALLOWED_WHILE_IDLE); pw.print(KEY_TIME_TICK_ALLOWED_WHILE_IDLE, TIME_TICK_ALLOWED_WHILE_IDLE);
pw.println(); pw.println();
pw.print(KEY_CRASH_NON_CLOCK_APPS, CRASH_NON_CLOCK_APPS);
pw.println();
pw.decreaseIndent(); pw.decreaseIndent();
} }
@@ -1914,11 +1932,12 @@ public class AlarmManagerService extends SystemService {
} }
/** /**
* Returns true if the given uid is on the system or user's power save exclusion list. * Returns true if the given uid does not require SCHEDULE_EXACT_ALARM to set exact,
* allow-while-idle alarms.
*/ */
boolean isWhitelisted(int uid) { boolean isExemptFromPermission(int uid) {
return (mLocalDeviceIdleController == null || mLocalDeviceIdleController.isAppOnWhitelist( return (UserHandle.isSameApp(mSystemUiUid, uid) || mLocalDeviceIdleController == null
UserHandle.getAppId(uid))); || mLocalDeviceIdleController.isAppOnWhitelist(UserHandle.getAppId(uid)));
} }
/** /**
@@ -1949,7 +1968,7 @@ public class AlarmManagerService extends SystemService {
if (windowLength != AlarmManager.WINDOW_EXACT) { if (windowLength != AlarmManager.WINDOW_EXACT) {
needsPermission = false; needsPermission = false;
lowQuota = true; lowQuota = true;
idleOptions = isWhitelisted(callingUid) ? mOptsWithFgs.toBundle() idleOptions = isExemptFromPermission(callingUid) ? mOptsWithFgs.toBundle()
: mOptsWithoutFgs.toBundle(); : mOptsWithoutFgs.toBundle();
} else if (alarmClock != null) { } else if (alarmClock != null) {
needsPermission = true; needsPermission = true;
@@ -1966,16 +1985,22 @@ public class AlarmManagerService extends SystemService {
idleOptions = allowWhileIdle ? mOptsWithFgs.toBundle() : null; idleOptions = allowWhileIdle ? mOptsWithFgs.toBundle() : null;
} }
if (needsPermission && !canScheduleExactAlarms()) { if (needsPermission && !canScheduleExactAlarms()) {
if (alarmClock == null && isWhitelisted(callingUid)) { if (alarmClock == null && isExemptFromPermission(callingUid)) {
// If the app is on the full system allow-list (not except-idle), we still // 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. // allow the alarms, but with a lower quota to keep pre-S compatibility.
lowQuota = true; lowQuota = true;
} else { } else {
final String errorMessage = "Caller needs to hold " final String errorMessage = "Caller " + callingPackage + " needs to hold "
+ Manifest.permission.SCHEDULE_EXACT_ALARM + " to set " + Manifest.permission.SCHEDULE_EXACT_ALARM + " to set "
+ ((allowWhileIdle) ? "exact, allow-while-idle" : "alarm-clock") + ((allowWhileIdle) ? "exact, allow-while-idle" : "alarm-clock")
+ " alarms."; + " alarms.";
throw new SecurityException(errorMessage); if (mConstants.CRASH_NON_CLOCK_APPS) {
throw new SecurityException(errorMessage);
} else {
Slog.wtf(TAG, errorMessage);
idleOptions = mOptsWithoutFgs.toBundle();
lowQuota = allowWhileIdle;
}
} }
} }
if (lowQuota) { if (lowQuota) {
@@ -2933,7 +2958,7 @@ public class AlarmManagerService extends SystemService {
if (UserHandle.isCore(uid) || uid == mSystemUiUid) { if (UserHandle.isCore(uid) || uid == mSystemUiUid) {
return; return;
} }
if (isWhitelisted(uid)) { if (isExemptFromPermission(uid)) {
return; return;
} }
if (!CompatChanges.isChangeEnabled( if (!CompatChanges.isChangeEnabled(