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

Commit fddce524 authored by Suprabh Shukla's avatar Suprabh Shukla Committed by Android (Google) Code Review
Browse files

Merge changes from topic "exact-alarm-default-deny" into tm-mainline-prod

* changes:
  [automerge] Deny SCHEDULE_EXACT_ALARM by default to newer apps 2p: a1adc26c
  Deny SCHEDULE_EXACT_ALARM by default to newer apps
parents f6299b30 cc26719f
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -280,6 +281,18 @@ public class AlarmManager {
    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
    public static final long ENABLE_USE_EXACT_ALARM = 218533173L;

    /**
     * For apps targeting {@link Build.VERSION_CODES#TIRAMISU} or above, the permission
     * {@link Manifest.permission#SCHEDULE_EXACT_ALARM} will be denied, unless the user explicitly
     * allows it from Settings.
     *
     * TODO (b/226439802): change to EnabledSince(T) after SDK finalization.
     * @hide
     */
    @ChangeId
    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S_V2)
    public static final long SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT = 226439802L;

    @UnsupportedAppUsage
    private final IAlarmManager mService;
    private final Context mContext;
+137 −34
Original line number Diff line number Diff line
@@ -66,6 +66,7 @@ import android.app.IAlarmListener;
import android.app.IAlarmManager;
import android.app.PendingIntent;
import android.app.compat.CompatChanges;
import android.app.role.RoleManager;
import android.app.usage.UsageStatsManager;
import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
@@ -164,6 +165,7 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.TimeZone;
@@ -224,6 +226,7 @@ public class AlarmManagerService extends SystemService {
    private ActivityManagerInternal mActivityManagerInternal;
    private final EconomyManagerInternal mEconomyManagerInternal;
    private PackageManagerInternal mPackageManagerInternal;
    private RoleManager mRoleManager;
    private volatile PermissionManagerServiceInternal mLocalPermissionManager;

    final Object mLock = new Object();
@@ -562,6 +565,9 @@ public class AlarmManagerService extends SystemService {
        @VisibleForTesting
        static final String KEY_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED =
                "kill_on_schedule_exact_alarm_revoked";
        @VisibleForTesting
        static final String KEY_SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT =
                "schedule_exact_alarm_denied_by_default";

        private static final long DEFAULT_MIN_FUTURITY = 5 * 1000;
        private static final long DEFAULT_MIN_INTERVAL = 60 * 1000;
@@ -606,6 +612,9 @@ public class AlarmManagerService extends SystemService {

        private static final boolean DEFAULT_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED = true;

        // TODO(b/226439802): Flip to true.
        private static final boolean DEFAULT_SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT = false;

        // Minimum futurity of a new alarm
        public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY;

@@ -693,6 +702,14 @@ public class AlarmManagerService extends SystemService {
        public boolean KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED =
                DEFAULT_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED;

        /**
         * When this is {@code true}, apps with the change
         * {@link AlarmManager#SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT} enabled will not get
         * {@link Manifest.permission#SCHEDULE_EXACT_ALARM} unless the user grants it to them.
         */
        public volatile boolean SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT =
                DEFAULT_SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT;

        public boolean USE_TARE_POLICY = Settings.Global.DEFAULT_ENABLE_TARE == 1;

        private long mLastAllowWhileIdleWhitelistDuration = -1;
@@ -876,6 +893,15 @@ public class AlarmManagerService extends SystemService {
                                    KEY_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED,
                                    DEFAULT_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED);
                            break;
                        case KEY_SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT:
                            final boolean oldValue = SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT;

                            SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT = properties.getBoolean(
                                    KEY_SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT,
                                    DEFAULT_SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT);
                            handleScheduleExactAlarmDeniedByDefaultChange(oldValue,
                                    SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT);
                            break;
                        default:
                            if (name.startsWith(KEY_PREFIX_STANDBY_QUOTA) && !standbyQuotaUpdated) {
                                // The quotas need to be updated in order, so we can't just rely
@@ -946,6 +972,15 @@ public class AlarmManagerService extends SystemService {
            }
        }

        private void handleScheduleExactAlarmDeniedByDefaultChange(boolean oldValue,
                boolean newValue) {
            if (oldValue == newValue) {
                return;
            }
            mHandler.obtainMessage(AlarmHandler.CHECK_EXACT_ALARM_PERMISSION_ON_FEATURE_TOGGLE,
                    newValue).sendToTarget();
        }

        private void migrateAlarmsToNewStoreLocked() {
            final AlarmStore newStore = LAZY_BATCHING ? new LazyAlarmStore()
                    : new BatchingAlarmStore();
@@ -1122,6 +1157,9 @@ public class AlarmManagerService extends SystemService {
            pw.print(KEY_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED,
                    KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED);
            pw.println();
            pw.print(KEY_SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT,
                    SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT);
            pw.println();

            pw.print(Settings.Global.ENABLE_TARE, USE_TARE_POLICY);
            pw.println();
@@ -1892,11 +1930,10 @@ public class AlarmManagerService extends SystemService {
                                if (hasUseExactAlarmInternal(packageName, uid)) {
                                    return;
                                }

                                final boolean requested = mExactAlarmCandidates.contains(
                                        UserHandle.getAppId(uid));
                                final boolean denyListed =
                                        mConstants.EXACT_ALARM_DENY_LIST.contains(packageName);
                                if (!mExactAlarmCandidates.contains(UserHandle.getAppId(uid))) {
                                    // Permission not requested, app op doesn't matter.
                                    return;
                                }

                                final int newMode = mAppOps.checkOpNoThrow(
                                        AppOpsManager.OP_SCHEDULE_EXACT_ALARM, uid, packageName);
@@ -1913,11 +1950,24 @@ public class AlarmManagerService extends SystemService {
                                        mLastOpScheduleExactAlarm.setValueAt(index, newMode);
                                    }
                                }
                                if (oldMode == newMode) {
                                    return;
                                }
                                final boolean allowedByDefault =
                                        isScheduleExactAlarmAllowedByDefault(packageName, uid);

                                final boolean hadPermission = getScheduleExactAlarmState(requested,
                                        denyListed, oldMode);
                                final boolean hasPermission = getScheduleExactAlarmState(requested,
                                        denyListed, newMode);
                                final boolean hadPermission;
                                if (oldMode != AppOpsManager.MODE_DEFAULT) {
                                    hadPermission = (oldMode == AppOpsManager.MODE_ALLOWED);
                                } else {
                                    hadPermission = allowedByDefault;
                                }
                                final boolean hasPermission;
                                if (newMode != AppOpsManager.MODE_DEFAULT) {
                                    hasPermission = (newMode == AppOpsManager.MODE_ALLOWED);
                                } else {
                                    hasPermission = allowedByDefault;
                                }

                                if (hadPermission && !hasPermission) {
                                    mHandler.obtainMessage(AlarmHandler.REMOVE_EXACT_ALARMS,
@@ -1939,6 +1989,8 @@ public class AlarmManagerService extends SystemService {
                    LocalServices.getService(AppStandbyInternal.class);
            appStandbyInternal.addListener(new AppStandbyTracker());

            mRoleManager = getContext().getSystemService(RoleManager.class);

            mMetricsHelper.registerPuller(() -> mAlarmStore);
        }
    }
@@ -2525,19 +2577,6 @@ public class AlarmManagerService extends SystemService {
        }
    }

    private static boolean getScheduleExactAlarmState(boolean requested, boolean denyListed,
            int appOpMode) {
        // This does not account for the state of the USE_EXACT_ALARM permission.
        // The caller should do that separately.
        if (!requested) {
            return false;
        }
        if (appOpMode == AppOpsManager.MODE_DEFAULT) {
            return !denyListed;
        }
        return appOpMode == AppOpsManager.MODE_ALLOWED;
    }

    boolean hasUseExactAlarmInternal(String packageName, int uid) {
        return isUseExactAlarmEnabled(packageName, UserHandle.getUserId(uid))
                && (PermissionChecker.checkPermissionForPreflight(getContext(),
@@ -2545,6 +2584,32 @@ public class AlarmManagerService extends SystemService {
                packageName) == PermissionChecker.PERMISSION_GRANTED);
    }

    /**
     * Returns whether SCHEDULE_EXACT_ALARM is allowed by default.
     */
    boolean isScheduleExactAlarmAllowedByDefault(String packageName, int uid) {
        if (isScheduleExactAlarmDeniedByDefault(packageName, UserHandle.getUserId(uid))) {

            // This is essentially like changing the protection level of the permission to
            // (privileged|signature|role|appop), but have to implement this logic to maintain
            // compatibility for older apps.
            if (mPackageManagerInternal.isPlatformSigned(packageName)
                    || mPackageManagerInternal.isUidPrivileged(uid)) {
                return true;
            }
            final long token = Binder.clearCallingIdentity();
            try {
                final List<String> wellbeingHolders = (mRoleManager != null)
                        ? mRoleManager.getRoleHolders(RoleManager.ROLE_SYSTEM_WELLBEING)
                        : Collections.emptyList();
                return wellbeingHolders.contains(packageName);
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        }
        return !mConstants.EXACT_ALARM_DENY_LIST.contains(packageName);
    }

    boolean hasScheduleExactAlarmInternal(String packageName, int uid) {
        final long start = mStatLogger.getTime();

@@ -2560,7 +2625,7 @@ public class AlarmManagerService extends SystemService {
            final int mode = mAppOps.checkOpNoThrow(AppOpsManager.OP_SCHEDULE_EXACT_ALARM, uid,
                    packageName);
            if (mode == AppOpsManager.MODE_DEFAULT) {
                hasPermission = !mConstants.EXACT_ALARM_DENY_LIST.contains(packageName);
                hasPermission = isScheduleExactAlarmAllowedByDefault(packageName, uid);
            } else {
                hasPermission = (mode == AppOpsManager.MODE_ALLOWED);
            }
@@ -2860,6 +2925,13 @@ public class AlarmManagerService extends SystemService {
                packageName, UserHandle.of(userId));
    }

    private boolean isScheduleExactAlarmDeniedByDefault(String packageName, int userId) {
        return mConstants.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT
                && CompatChanges.isChangeEnabled(
                AlarmManager.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, packageName,
                UserHandle.of(userId));
    }

    @NeverCompile // Avoid size overhead of debugging code.
    void dumpImpl(IndentingPrintWriter pw) {
        synchronized (mLock) {
@@ -3769,26 +3841,27 @@ public class AlarmManagerService extends SystemService {
                if (!isExactAlarmChangeEnabled(changedPackage, userId)) {
                    continue;
                }
                if (isScheduleExactAlarmDeniedByDefault(changedPackage, userId)) {
                    continue;
                }
                if (hasUseExactAlarmInternal(changedPackage, uid)) {
                    continue;
                }
                if (!mExactAlarmCandidates.contains(UserHandle.getAppId(uid))) {
                    // Permission isn't requested, deny list doesn't matter.
                    continue;
                }
                final int appOpMode;
                synchronized (mLock) {
                    appOpMode = mLastOpScheduleExactAlarm.get(uid,
                            AppOpsManager.opToDefaultMode(AppOpsManager.OP_SCHEDULE_EXACT_ALARM));
                }
                final boolean requested = mExactAlarmCandidates.contains(UserHandle.getAppId(uid));

                // added: true => package was added to the deny list
                // added: false => package was removed from the deny list
                final boolean hadPermission = getScheduleExactAlarmState(requested, !added,
                        appOpMode);
                final boolean hasPermission = getScheduleExactAlarmState(requested, added,
                        appOpMode);

                if (hadPermission == hasPermission) {
                if (appOpMode != AppOpsManager.MODE_DEFAULT) {
                    // Deny list doesn't matter.
                    continue;
                }
                // added: true => package was added to the deny list
                // added: false => package was removed from the deny list
                if (added) {
                    synchronized (mLock) {
                        removeExactAlarmsOnPermissionRevokedLocked(uid,
@@ -4634,6 +4707,7 @@ public class AlarmManagerService extends SystemService {
        public static final int REFRESH_EXACT_ALARM_CANDIDATES = 11;
        public static final int TARE_AFFORDABILITY_CHANGED = 12;
        public static final int CHECK_EXACT_ALARM_PERMISSION_ON_UPDATE = 13;
        public static final int CHECK_EXACT_ALARM_PERMISSION_ON_FEATURE_TOGGLE = 14;

        AlarmHandler() {
            super(Looper.myLooper());
@@ -4759,6 +4833,35 @@ public class AlarmManagerService extends SystemService {
                        }
                    }
                    break;
                case CHECK_EXACT_ALARM_PERMISSION_ON_FEATURE_TOGGLE:
                    final boolean defaultDenied = (Boolean) msg.obj;

                    final int[] startedUserIds = mActivityManagerInternal.getStartedUserIds();
                    for (int appId : mExactAlarmCandidates) {
                        for (int userId : startedUserIds) {
                            uid = UserHandle.getUid(userId, appId);

                            final AndroidPackage packageForUid =
                                    mPackageManagerInternal.getPackage(uid);
                            if (packageForUid == null) {
                                continue;
                            }
                            final String pkg = packageForUid.getPackageName();
                            if (defaultDenied) {
                                if (!hasScheduleExactAlarmInternal(pkg, uid)
                                        && !hasUseExactAlarmInternal(pkg, uid)) {
                                    synchronized (mLock) {
                                        removeExactAlarmsOnPermissionRevokedLocked(uid, pkg,
                                                true);
                                    }
                                }
                            } else if (hasScheduleExactAlarmInternal(pkg, uid)) {
                                sendScheduleExactAlarmPermissionStateChangedBroadcast(pkg,
                                        UserHandle.getUserId(uid));
                            }
                        }
                    }
                    break;
                default:
                    // nope, just ignore it
                    break;
+5 −0
Original line number Diff line number Diff line
@@ -1207,6 +1207,11 @@ public abstract class PackageManagerInternal {
    @Nullable
    public abstract SharedUserApi getSharedUserApi(int sharedUserAppId);

    /**
     * Returns if the given uid is privileged or not.
     */
    public abstract boolean isUidPrivileged(int uid);

    /**
     * Initiates a package state mutation request, returning the current state as known by
     * PackageManager. This allows the later commit request to compare the initial values and
+5 −0
Original line number Diff line number Diff line
@@ -722,6 +722,11 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal {
        return snapshot().getSharedUser(sharedUserAppId);
    }

    @Override
    public boolean isUidPrivileged(int uid) {
        return snapshot().isUidPrivileged(uid);
    }

    @NonNull
    @Override
    @Deprecated
+70 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import static android.app.AlarmManager.FLAG_STANDALONE;
import static android.app.AlarmManager.FLAG_WAKE_FROM_IDLE;
import static android.app.AlarmManager.RTC;
import static android.app.AlarmManager.RTC_WAKEUP;
import static android.app.AlarmManager.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT;
import static android.app.AlarmManager.WINDOW_EXACT;
import static android.app.AlarmManager.WINDOW_HEURISTIC;
import static android.app.AppOpsManager.MODE_ALLOWED;
@@ -122,6 +123,7 @@ import android.app.IAlarmListener;
import android.app.IAlarmManager;
import android.app.PendingIntent;
import android.app.compat.CompatChanges;
import android.app.role.RoleManager;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ContentResolver;
import android.content.Context;
@@ -184,6 +186,7 @@ import org.mockito.quality.Strictness;
import org.mockito.stubbing.Answer;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -232,6 +235,8 @@ public class AlarmManagerServiceTest {
    @Mock
    private PackageManagerInternal mPackageManagerInternal;
    @Mock
    private RoleManager mRoleManager;
    @Mock
    private AppStateTrackerImpl mAppStateTracker;
    @Mock
    private AlarmManagerService.ClockReceiver mClockReceiver;
@@ -457,6 +462,7 @@ public class AlarmManagerServiceTest {

        when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager);
        when(mMockContext.getSystemService(BatteryManager.class)).thenReturn(mBatteryManager);
        when(mMockContext.getSystemService(RoleManager.class)).thenReturn(mRoleManager);

        registerAppIds(new String[]{TEST_CALLING_PACKAGE},
                new Integer[]{UserHandle.getAppId(TEST_CALLING_UID)});
@@ -3190,6 +3196,70 @@ public class AlarmManagerServiceTest {
        assertEquals(0, remaining.size());
    }

    @Test
    public void isScheduleExactAlarmAllowedByDefault() {
        final String package1 = "priv";
        final String package2 = "signed";
        final String package3 = "normal";
        final String package4 = "wellbeing";
        final int uid1 = 1294;
        final int uid2 = 8321;
        final int uid3 = 3412;
        final int uid4 = 4591;

        when(mPackageManagerInternal.isUidPrivileged(uid1)).thenReturn(true);
        when(mPackageManagerInternal.isUidPrivileged(uid2)).thenReturn(false);
        when(mPackageManagerInternal.isUidPrivileged(uid3)).thenReturn(false);
        when(mPackageManagerInternal.isUidPrivileged(uid4)).thenReturn(false);

        when(mPackageManagerInternal.isPlatformSigned(package1)).thenReturn(false);
        when(mPackageManagerInternal.isPlatformSigned(package2)).thenReturn(true);
        when(mPackageManagerInternal.isPlatformSigned(package3)).thenReturn(false);
        when(mPackageManagerInternal.isPlatformSigned(package4)).thenReturn(false);

        when(mRoleManager.getRoleHolders(RoleManager.ROLE_SYSTEM_WELLBEING)).thenReturn(
                Arrays.asList(package4));

        mockChangeEnabled(SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, true);
        mService.mConstants.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT = false;
        mService.mConstants.EXACT_ALARM_DENY_LIST = new ArraySet<>(new String[] {
                package1,
                package3,
        });

        // Deny listed packages will be false.
        assertFalse(mService.isScheduleExactAlarmAllowedByDefault(package1, uid1));
        assertTrue(mService.isScheduleExactAlarmAllowedByDefault(package2, uid2));
        assertFalse(mService.isScheduleExactAlarmAllowedByDefault(package3, uid3));
        assertTrue(mService.isScheduleExactAlarmAllowedByDefault(package4, uid4));

        mockChangeEnabled(SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, false);
        mService.mConstants.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT = true;
        mService.mConstants.EXACT_ALARM_DENY_LIST = new ArraySet<>(new String[] {
                package1,
                package3,
        });

        // Same as above, deny listed packages will be false.
        assertFalse(mService.isScheduleExactAlarmAllowedByDefault(package1, uid1));
        assertTrue(mService.isScheduleExactAlarmAllowedByDefault(package2, uid2));
        assertFalse(mService.isScheduleExactAlarmAllowedByDefault(package3, uid3));
        assertTrue(mService.isScheduleExactAlarmAllowedByDefault(package4, uid4));

        mockChangeEnabled(SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, true);
        mService.mConstants.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT = true;
        mService.mConstants.EXACT_ALARM_DENY_LIST = new ArraySet<>(new String[] {
                package1,
                package3,
        });

        // Deny list doesn't matter now, only exemptions should be true.
        assertTrue(mService.isScheduleExactAlarmAllowedByDefault(package1, uid1));
        assertTrue(mService.isScheduleExactAlarmAllowedByDefault(package2, uid2));
        assertFalse(mService.isScheduleExactAlarmAllowedByDefault(package3, uid3));
        assertTrue(mService.isScheduleExactAlarmAllowedByDefault(package4, uid4));
    }

    @Test
    public void alarmScheduledAtomPushed() {
        for (int i = 0; i < 10; i++) {