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

Commit d0bbe0b9 authored by Suprabh Shukla's avatar Suprabh Shukla
Browse files

Adding a permission to protect exact alarms

Added a new permission to protect the alarm manager APIs setAlarmClock
and setExactAndAllowWhileIdle. The change will apply to apps targeting
API S and above.
- The permission will be granted by default to apps that request it.
- This will be a user facing permission as these APIs allow for exact
scheduling, and an app should generally only use them when scheduling
work on behalf of the user. This means the user can revoke it when they
want.
- Alarms set with this API will also be allowed to start FGS from the
background.
- Quota on exact, allow-while-idle alarms has been relaxed generously,
while alarm-clocks remain totally uninhibited.
- Apps that are exempted from battery via <allow-in-power-save> in the
sysconfig, can still use exact, allow-while-idle alarms without the
permission, but with lower quota.
- Inexact, allow-while-idle alarms do not require this permission, but
will *not* be allowed to start FGS from the background.
- A new API to query the state of this permission is included.

Test: atest FrameworksMockingServicesTests:com.android.server.alarm
atest CtsAlarmManagerTestCases

BYPASS_INCLUSIVE_LANGUAGE_REASON=Existing APIs

Bug: 171306433
Change-Id: Ifb6a3f3c42316b1c83fe6960920501f5e0ee51f2
parent 0e05abff
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ java_library {
        ],
    },
    libs: [
        "app-compat-annotations",
        "framework-minus-apex",
        "unsupportedappusage",
    ],
+47 −0
Original line number Diff line number Diff line
@@ -16,11 +16,14 @@

package android.app;

import android.Manifest;
import android.annotation.IntDef;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
@@ -183,6 +186,25 @@ public class AlarmManager {
    @UnsupportedAppUsage
    public static final int FLAG_IDLE_UNTIL = 1<<4;

    /**
     * Flag for alarms: Used to provide backwards compatibility for apps with targetSdkVersion less
     * than {@link Build.VERSION_CODES#S}
     * @hide
     */
    public static final int FLAG_ALLOW_WHILE_IDLE_COMPAT = 1 << 5;

    /**
     * For apps targeting {@link Build.VERSION_CODES#S} or above, APIs
     * {@link #setExactAndAllowWhileIdle(int, long, PendingIntent)} and
     * {@link #setAlarmClock(AlarmClockInfo, PendingIntent)} will require holding a new
     * permission {@link android.Manifest.permission#SCHEDULE_EXACT_ALARM}
     *
     * @hide
     */
    @ChangeId
    @Disabled // TODO (b/171306433): Enable starting S.
    public static final long REQUIRE_EXACT_ALARM_PERMISSION = 171306433L;

    @UnsupportedAppUsage
    private final IAlarmManager mService;
    private final Context mContext;
@@ -588,6 +610,11 @@ public class AlarmManager {
     * This method is like {@link #setExact(int, long, PendingIntent)}, but implies
     * {@link #RTC_WAKEUP}.
     *
     * <p>
     * Starting from API {@link Build.VERSION_CODES#S}, using this method requires the
     * {@link Manifest.permission#SCHEDULE_EXACT_ALARM} permission. Alarms scheduled via this API
     * will be allowed to start a foreground service even if the app is in the background.
     *
     * @param info
     * @param operation Action to perform when the alarm goes off;
     *        typically comes from {@link PendingIntent#getBroadcast
@@ -603,6 +630,7 @@ public class AlarmManager {
     * @see android.content.Context#registerReceiver
     * @see android.content.Intent#filterEquals
     */
    @RequiresPermission(Manifest.permission.SCHEDULE_EXACT_ALARM)
    public void setAlarmClock(AlarmClockInfo info, PendingIntent operation) {
        setImpl(RTC_WAKEUP, info.getTriggerTime(), WINDOW_EXACT, 0, 0, operation,
                null, null, null, null, info);
@@ -876,6 +904,12 @@ public class AlarmManager {
     * device is idle it may take even more liberties with scheduling in order to optimize
     * for battery life.</p>
     *
     * <p>
     * Starting from API {@link Build.VERSION_CODES#S}, using this method requires the
     * {@link Manifest.permission#SCHEDULE_EXACT_ALARM} permission, unless the app is exempt from
     * battery restrictions. Alarms scheduled via this API will be allowed to start a foreground
     * service even if the app is in the background.
     *
     * @param type type of alarm.
     * @param triggerAtMillis time in milliseconds that the alarm should go
     *        off, using the appropriate clock (depending on the alarm type).
@@ -895,6 +929,7 @@ public class AlarmManager {
     * @see #RTC
     * @see #RTC_WAKEUP
     */
    @RequiresPermission(value = Manifest.permission.SCHEDULE_EXACT_ALARM, conditional = true)
    public void setExactAndAllowWhileIdle(@AlarmType int type, long triggerAtMillis,
            PendingIntent operation) {
        setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, FLAG_ALLOW_WHILE_IDLE, operation,
@@ -1017,6 +1052,18 @@ public class AlarmManager {
        }
    }

    /**
     * Called to check if the caller has permission to use alarms set via {@link }
     * @return
     */
    public boolean canScheduleExactAlarms() {
        try {
            return mService.canScheduleExactAlarms();
        } catch (RemoteException re) {
            throw re.rethrowFromSystemServer();
        }
    }

    /**
     * Gets information about the next alarm clock currently scheduled.
     *
+1 −0
Original line number Diff line number Diff line
@@ -41,4 +41,5 @@ interface IAlarmManager {
    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
    AlarmManager.AlarmClockInfo getNextAlarmClock(int userId);
    long currentNetworkTimeMillis();
    boolean canScheduleExactAlarms();
}
+9 −1
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import static com.android.server.alarm.AlarmManagerService.clampPositive;
import android.app.AlarmManager;
import android.app.IAlarmListener;
import android.app.PendingIntent;
import android.os.Bundle;
import android.os.WorkSource;
import android.util.IndentingPrintWriter;
import android.util.TimeUtils;
@@ -92,10 +93,12 @@ class Alarm {
    private long mWhenElapsed;
    private long mMaxWhenElapsed;
    public AlarmManagerService.PriorityClass priorityClass;
    /** Broadcast options to use when delivering this alarm */
    public Bundle mIdleOptions;

    Alarm(int type, long when, long requestedWhenElapsed, long windowLength, long interval,
            PendingIntent op, IAlarmListener rec, String listenerTag, WorkSource ws, int flags,
            AlarmManager.AlarmClockInfo info, int uid, String pkgName) {
            AlarmManager.AlarmClockInfo info, int uid, String pkgName, Bundle idleOptions) {
        this.type = type;
        origWhen = when;
        wakeup = type == AlarmManager.ELAPSED_REALTIME_WAKEUP
@@ -115,6 +118,7 @@ class Alarm {
        alarmClock = info;
        this.uid = uid;
        packageName = pkgName;
        mIdleOptions = idleOptions;
        sourcePackage = (operation != null) ? operation.getCreatorPackage() : packageName;
        creatorUid = (operation != null) ? operation.getCreatorUid() : this.uid;
    }
@@ -303,6 +307,10 @@ class Alarm {
            ipw.print("listener=");
            ipw.println(listener.asBinder());
        }
        if (mIdleOptions != null) {
            ipw.print("idle-options=");
            ipw.println(mIdleOptions.toString());
        }
    }

    public void dumpDebug(ProtoOutputStream proto, long fieldId, long nowElapsed) {
+304 −53

File changed.

Preview size limit exceeded, changes collapsed.

Loading