Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 190b4311 authored by Suprabh Shukla's avatar Suprabh Shukla Committed by Automerger Merge Worker
Browse files

Merge "All exact alarms now require SCHEDULE_EXACT_ALARM" into sc-dev am: 235fae7f

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/13851991

Change-Id: I32138528fb5db725d059d4066809301ef221cf68
parents 0bb56fd1 235fae7f
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -539,6 +539,11 @@ public class AlarmManager {
     * scheduled as exact.  Applications are strongly discouraged from using exact
     * alarms unnecessarily as they reduce the OS's ability to minimize battery use.
     *
     * <p>
     * Starting with {@link Build.VERSION_CODES#S}, apps require the
     * {@link Manifest.permission#SCHEDULE_EXACT_ALARM SCHEDULE_EXACT_ALARM} permission to use this
     * API.
     *
     * @param type type of alarm.
     * @param triggerAtMillis time in milliseconds that the alarm should go
     *        off, using the appropriate clock (depending on the alarm type).
@@ -558,6 +563,7 @@ public class AlarmManager {
     * @see #RTC
     * @see #RTC_WAKEUP
     */
    @RequiresPermission(value = Manifest.permission.SCHEDULE_EXACT_ALARM, conditional = true)
    public void setExact(@AlarmType int type, long triggerAtMillis, PendingIntent operation) {
        setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, 0, operation, null, null, null,
                null, null);
@@ -571,7 +577,13 @@ public class AlarmManager {
     * The OnAlarmListener's {@link OnAlarmListener#onAlarm() onAlarm()} method will be
     * invoked via the specified target Handler, or on the application's main looper
     * if {@code null} is passed as the {@code targetHandler} parameter.
     *
     * <p>
     * Starting with {@link Build.VERSION_CODES#S}, apps require the
     * {@link Manifest.permission#SCHEDULE_EXACT_ALARM SCHEDULE_EXACT_ALARM} permission to use this
     * API.
     */
    @RequiresPermission(value = Manifest.permission.SCHEDULE_EXACT_ALARM, conditional = true)
    public void setExact(@AlarmType int type, long triggerAtMillis, String tag,
            OnAlarmListener listener, Handler targetHandler) {
        setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, 0, null, listener, tag,
+43 −38
Original line number Diff line number Diff line
@@ -388,6 +388,8 @@ public class AlarmManagerService extends SystemService {
        @VisibleForTesting
        static final String KEY_MAX_INTERVAL = "max_interval";
        @VisibleForTesting
        static final String KEY_MIN_WINDOW = "min_window";
        @VisibleForTesting
        static final String KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION
                = "allow_while_idle_whitelist_duration";
        @VisibleForTesting
@@ -428,11 +430,13 @@ public class AlarmManagerService extends SystemService {
        @VisibleForTesting
        static final String KEY_ALLOW_WHILE_IDLE_COMPAT_WINDOW = "allow_while_idle_compat_window";

        private static final String KEY_CRASH_NON_CLOCK_APPS = "crash_non_clock_apps";
        @VisibleForTesting
        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_INTERVAL = 60 * 1000;
        private static final long DEFAULT_MAX_INTERVAL = 365 * INTERVAL_DAY;
        private static final long DEFAULT_MIN_WINDOW = 10_000;
        private static final long DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION = 10 * 1000;
        private static final long DEFAULT_LISTENER_TIMEOUT = 5 * 1000;
        private static final int DEFAULT_MAX_ALARMS_PER_UID = 500;
@@ -475,6 +479,9 @@ public class AlarmManagerService extends SystemService {
        // Maximum alarm recurrence interval
        public long MAX_INTERVAL = DEFAULT_MAX_INTERVAL;

        // Minimum window size for inexact alarms
        public long MIN_WINDOW = DEFAULT_MIN_WINDOW;

        // BroadcastOptions.setTemporaryAppWhitelistDuration() to use for FLAG_ALLOW_WHILE_IDLE.
        public long ALLOW_WHILE_IDLE_WHITELIST_DURATION
                = DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION;
@@ -575,6 +582,9 @@ public class AlarmManagerService extends SystemService {
                                ALLOW_WHILE_IDLE_QUOTA = 1;
                            }
                            break;
                        case KEY_MIN_WINDOW:
                            MIN_WINDOW = properties.getLong(KEY_MIN_WINDOW, DEFAULT_MIN_WINDOW);
                            break;
                        case KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA:
                            ALLOW_WHILE_IDLE_COMPAT_QUOTA = properties.getInt(
                                    KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA,
@@ -738,6 +748,11 @@ public class AlarmManagerService extends SystemService {
            TimeUtils.formatDuration(MAX_INTERVAL, pw);
            pw.println();

            pw.print(KEY_MIN_WINDOW);
            pw.print("=");
            TimeUtils.formatDuration(MIN_WINDOW, pw);
            pw.println();

            pw.print(KEY_LISTENER_TIMEOUT);
            pw.print("=");
            TimeUtils.formatDuration(LISTENER_TIMEOUT, pw);
@@ -1642,6 +1657,7 @@ public class AlarmManagerService extends SystemService {
            // Fix this window in place, so that as time approaches we don't collapse it.
            windowLength = maxElapsed - triggerElapsed;
        } else {
            windowLength = Math.max(windowLength, mConstants.MIN_WINDOW);
            maxElapsed = triggerElapsed + windowLength;
        }
        synchronized (mLock) {
@@ -1981,8 +1997,10 @@ public class AlarmManagerService extends SystemService {
     * Returns true if the given uid does not require SCHEDULE_EXACT_ALARM to set exact,
     * allow-while-idle alarms.
     */
    boolean isExemptFromPermission(int uid) {
        return (UserHandle.isSameApp(mSystemUiUid, uid) || mLocalDeviceIdleController == null
    boolean isExemptFromExactAlarmPermission(int uid) {
        return (UserHandle.isSameApp(mSystemUiUid, uid)
                || UserHandle.isCore(uid)
                || mLocalDeviceIdleController == null
                || mLocalDeviceIdleController.isAppOnWhitelist(UserHandle.getAppId(uid)));
    }

@@ -2002,54 +2020,43 @@ public class AlarmManagerService extends SystemService {
            mAppOps.checkPackage(callingUid, callingPackage);

            final boolean allowWhileIdle = (flags & FLAG_ALLOW_WHILE_IDLE) != 0;
            final boolean exact = (windowLength == AlarmManager.WINDOW_EXACT);

            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.
            // decide what quota and broadcast options to use.
            Bundle idleOptions = null;
            if (exact || allowWhileIdle) {
                final boolean needsPermission;
                boolean lowQuota;
                boolean lowerQuota;
                if (CompatChanges.isChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION,
                        callingPackage, UserHandle.getUserHandleForUid(callingUid))) {
                    if (windowLength != AlarmManager.WINDOW_EXACT) {
                        needsPermission = false;
                        lowQuota = true;
                        idleOptions = isExemptFromPermission(callingUid) ? mOptsWithFgs.toBundle()
                                : mOptsWithoutFgs.toBundle();
                    } else if (alarmClock != null) {
                        needsPermission = true;
                        lowQuota = false;
                        idleOptions = mOptsWithFgs.toBundle();
                    } else {
                        needsPermission = true;
                        lowQuota = false;
                        idleOptions = mOptsWithFgs.toBundle();
                    }
                    needsPermission = exact;
                    lowerQuota = !exact;
                    idleOptions = exact ? mOptsWithFgs.toBundle() : mOptsWithoutFgs.toBundle();
                } else {
                    needsPermission = false;
                    lowQuota = allowWhileIdle;
                    lowerQuota = allowWhileIdle;
                    idleOptions = allowWhileIdle ? mOptsWithFgs.toBundle() : null;
                }
                if (needsPermission && !canScheduleExactAlarms()) {
                    if (alarmClock == null && isExemptFromPermission(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 {
                    if (alarmClock != null || !isExemptFromExactAlarmPermission(callingUid)) {
                        final String errorMessage = "Caller " + callingPackage + " needs to hold "
                                + Manifest.permission.SCHEDULE_EXACT_ALARM + " to set "
                                + ((allowWhileIdle) ? "exact, allow-while-idle" : "alarm-clock")
                                + " alarms.";
                                + "exact alarms.";
                        if (mConstants.CRASH_NON_CLOCK_APPS) {
                            throw new SecurityException(errorMessage);
                        } else {
                            Slog.wtf(TAG, errorMessage);
                            idleOptions = mOptsWithoutFgs.toBundle();
                            lowQuota = allowWhileIdle;
                        }
                    }
                    // If the app is on the full system power allow-list (not except-idle), or we're
                    // in a soft failure mode, we still allow the alarms.
                    // We give temporary allowlist to allow-while-idle alarms but without FGS
                    // capability. Note that apps that are in the power allow-list do not need it.
                    idleOptions = allowWhileIdle ? mOptsWithoutFgs.toBundle() : null;
                    lowerQuota = allowWhileIdle;
                }
                if (lowQuota) {
                if (lowerQuota) {
                    flags &= ~FLAG_ALLOW_WHILE_IDLE;
                    flags |= FLAG_ALLOW_WHILE_IDLE_COMPAT;
                }
@@ -2998,13 +3005,10 @@ 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.
     * TODO (b/179541791): Add revocation history to dumpsys.
     */
    void removeExactAlarmsOnPermissionRevokedLocked(int uid, String packageName) {
        if (UserHandle.isCore(uid) || uid == mSystemUiUid) {
            return;
        }
        if (isExemptFromPermission(uid)) {
        if (isExemptFromExactAlarmPermission(uid)) {
            return;
        }
        if (!CompatChanges.isChangeEnabled(
@@ -3015,7 +3019,7 @@ public class AlarmManagerService extends SystemService {

        final Predicate<Alarm> whichAlarms =
                a -> (a.uid == uid && a.packageName.equals(packageName)
                        && ((a.flags & FLAG_ALLOW_WHILE_IDLE) != 0 || a.alarmClock != null));
                        && a.windowLength == AlarmManager.WINDOW_EXACT);
        final ArrayList<Alarm> removed = mAlarmStore.remove(whichAlarms);
        final boolean didRemove = !removed.isEmpty();
        if (didRemove) {
@@ -3873,6 +3877,7 @@ public class AlarmManagerService extends SystemService {
        return alarm.creatorUid;
    }


    @VisibleForTesting
    class AlarmHandler extends Handler {
        public static final int ALARM_EVENT = 1;
+5 −0
Original line number Diff line number Diff line
@@ -133,6 +133,11 @@ public interface AlarmStore {
     */
    void dumpProto(ProtoOutputStream pos, long nowElapsed);

    /**
     * @return a name for this alarm store that can be used for debugging and tests.
     */
    String getName();

    /**
     * A functional interface used to update the alarm. Used to describe the update in
     * {@link #updateAlarmDeliveries(AlarmDeliveryCalculator)}
+9 −1
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.StatLogger;

import java.text.SimpleDateFormat;
@@ -40,6 +41,8 @@ import java.util.function.Predicate;
 * This keeps the alarms in batches, which are sorted on the start time of their delivery window.
 */
public class BatchingAlarmStore implements AlarmStore {
    @VisibleForTesting
    static final String TAG = BatchingAlarmStore.class.getSimpleName();

    private final ArrayList<Batch> mAlarmBatches = new ArrayList<>();
    private int mSize;
@@ -49,7 +52,7 @@ public class BatchingAlarmStore implements AlarmStore {
        int REBATCH_ALL_ALARMS = 0;
    }

    final StatLogger mStatLogger = new StatLogger("BatchingAlarmStore stats", new String[]{
    final StatLogger mStatLogger = new StatLogger(TAG + " stats", new String[]{
            "REBATCH_ALL_ALARMS",
    });

@@ -211,6 +214,11 @@ public class BatchingAlarmStore implements AlarmStore {
        }
    }

    @Override
    public String getName() {
        return TAG;
    }

    private void insertAndBatchAlarm(Alarm alarm) {
        final int whichBatch = ((alarm.flags & AlarmManager.FLAG_STANDALONE) != 0) ? -1
                : attemptCoalesce(alarm.getWhenElapsed(), alarm.getMaxWhenElapsed());
+9 −1
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.StatLogger;

import java.text.SimpleDateFormat;
@@ -38,6 +39,8 @@ import java.util.function.Predicate;
 * This keeps the alarms in a sorted list, and only batches them at the time of delivery.
 */
public class LazyAlarmStore implements AlarmStore {
    @VisibleForTesting
    static final String TAG = LazyAlarmStore.class.getSimpleName();

    private final ArrayList<Alarm> mAlarms = new ArrayList<>();
    private Runnable mOnAlarmClockRemoved;
@@ -47,7 +50,7 @@ public class LazyAlarmStore implements AlarmStore {
        int GET_NEXT_WAKEUP_DELIVERY_TIME = 1;
    }

    final StatLogger mStatLogger = new StatLogger("LazyAlarmStore stats", new String[]{
    final StatLogger mStatLogger = new StatLogger(TAG + " stats", new String[]{
            "GET_NEXT_DELIVERY_TIME",
            "GET_NEXT_WAKEUP_DELIVERY_TIME",
    });
@@ -214,4 +217,9 @@ public class LazyAlarmStore implements AlarmStore {
            a.dumpDebug(pos, AlarmManagerServiceDumpProto.PENDING_ALARMS, nowElapsed);
        }
    }

    @Override
    public String getName() {
        return TAG;
    }
}
Loading