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

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

Flip the protection level for SCHEDULE_EXACT_ALARM

Updating the protection level to match the permission's behavior: only
apps that are privileged or signed with the platform cert will be
granted this permission statically.

This is still an appop permission so the user can explicitly grant or
deny this to any app that is requesting this via Settings. Similarly,
some roles may grant holders the app-op for this permission.

Permission check for latest apps will be simply delegated to
PermissionChecker.

Since this permission is now a privileged permission, this needs to be
added in the privapp allowlist for all apps currently declaring it to
successfully boot the device.

Older apps (targeting < 33) should not see any changes in how it
behaves - so apps that are not explicitly deny-listed in alarm manager
will see it being statically granted in the absence of user's explicit
choice.

This change is not expected to have any behavorial impact by itself. But
is instead supposed to establish the behavior of SCHEDULE_EXACT_ALARM
going forward. This also allows for simpler documentation and code
maintenance.

Test: Builds, boots.
Test: Manually check that the "Alarms & Reminders" UI works as expected
Test: atest FrameworksMockingServicesTests:AlarmManagerServiceTest

Bug: 270109095
Change-Id: Id18fabb1c3d1215400090540ade1e0257a5434ca
parent f77033c2
Loading
Loading
Loading
Loading
+34 −49
Original line number Diff line number Diff line
@@ -30,6 +30,9 @@ import static android.app.AlarmManager.INTERVAL_DAY;
import static android.app.AlarmManager.INTERVAL_HOUR;
import static android.app.AlarmManager.RTC;
import static android.app.AlarmManager.RTC_WAKEUP;
import static android.content.PermissionChecker.PERMISSION_GRANTED;
import static android.content.PermissionChecker.PID_UNKNOWN;
import static android.content.PermissionChecker.checkPermissionForPreflight;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.os.PowerExemptionManager.REASON_ALARM_MANAGER_ALARM_CLOCK;
import static android.os.PowerExemptionManager.REASON_DENIED;
@@ -87,11 +90,9 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.PermissionChecker;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.UserPackage;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.BatteryManager;
import android.os.BatteryStatsInternal;
@@ -182,7 +183,6 @@ 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;
@@ -269,7 +269,8 @@ public class AlarmManagerService extends SystemService {

    /**
     * A map from uid to the last op-mode we have seen for
     * {@link AppOpsManager#OP_SCHEDULE_EXACT_ALARM}
     * {@link AppOpsManager#OP_SCHEDULE_EXACT_ALARM}. Used for evaluating permission state change
     * when the denylist changes.
     */
    @VisibleForTesting
    @GuardedBy("mLock")
@@ -2097,20 +2098,31 @@ public class AlarmManagerService extends SystemService {
                                if (oldMode == newMode) {
                                    return;
                                }
                                final boolean allowedByDefault =
                                        isScheduleExactAlarmAllowedByDefault(packageName, uid);
                                final boolean deniedByDefault = isScheduleExactAlarmDeniedByDefault(
                                        packageName, UserHandle.getUserId(uid));

                                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);

                                if (deniedByDefault) {
                                    final boolean permissionState = getContext().checkPermission(
                                            Manifest.permission.SCHEDULE_EXACT_ALARM, PID_UNKNOWN,
                                            uid) == PackageManager.PERMISSION_GRANTED;
                                    hadPermission = (oldMode == AppOpsManager.MODE_DEFAULT)
                                            ? permissionState
                                            : (oldMode == AppOpsManager.MODE_ALLOWED);
                                    hasPermission = (newMode == AppOpsManager.MODE_DEFAULT)
                                            ? permissionState
                                            : (newMode == AppOpsManager.MODE_ALLOWED);
                                } else {
                                    hasPermission = allowedByDefault;
                                    final boolean allowedByDefault =
                                            !mConstants.EXACT_ALARM_DENY_LIST.contains(packageName);
                                    hadPermission = (oldMode == AppOpsManager.MODE_DEFAULT)
                                            ? allowedByDefault
                                            : (oldMode == AppOpsManager.MODE_ALLOWED);
                                    hasPermission = (newMode == AppOpsManager.MODE_DEFAULT)
                                            ? allowedByDefault
                                            : (newMode == AppOpsManager.MODE_ALLOWED);
                                }

                                if (hadPermission && !hasPermission) {
@@ -2754,41 +2766,13 @@ public class AlarmManagerService extends SystemService {

    boolean hasUseExactAlarmInternal(String packageName, int uid) {
        return isUseExactAlarmEnabled(packageName, UserHandle.getUserId(uid))
                && (PermissionChecker.checkPermissionForPreflight(getContext(),
                Manifest.permission.USE_EXACT_ALARM, PermissionChecker.PID_UNKNOWN, uid,
                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);
                && (checkPermissionForPreflight(getContext(), Manifest.permission.USE_EXACT_ALARM,
                PID_UNKNOWN, uid, packageName) == PERMISSION_GRANTED);
    }

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

        // Not using getScheduleExactAlarmState as this can avoid some calls to AppOpsService.
        // Not using #mLastOpScheduleExactAlarm as it may contain stale values.
        // No locking needed as all internal containers being queried are immutable.
        final boolean hasPermission;
@@ -2796,11 +2780,16 @@ public class AlarmManagerService extends SystemService {
            hasPermission = false;
        } else if (!isExactAlarmChangeEnabled(packageName, UserHandle.getUserId(uid))) {
            hasPermission = false;
        } else if (isScheduleExactAlarmDeniedByDefault(packageName, UserHandle.getUserId(uid))) {
            hasPermission = (checkPermissionForPreflight(getContext(),
                    Manifest.permission.SCHEDULE_EXACT_ALARM, PID_UNKNOWN, uid, packageName)
                    == PERMISSION_GRANTED);
        } else {
            // Compatibility permission check for older apps.
            final int mode = mAppOps.checkOpNoThrow(AppOpsManager.OP_SCHEDULE_EXACT_ALARM, uid,
                    packageName);
            if (mode == AppOpsManager.MODE_DEFAULT) {
                hasPermission = isScheduleExactAlarmAllowedByDefault(packageName, uid);
                hasPermission = !mConstants.EXACT_ALARM_DENY_LIST.contains(packageName);
            } else {
                hasPermission = (mode == AppOpsManager.MODE_ALLOWED);
            }
@@ -4685,10 +4674,6 @@ public class AlarmManagerService extends SystemService {
            return service.new ClockReceiver();
        }

        void registerContentObserver(ContentObserver contentObserver, Uri uri) {
            mContext.getContentResolver().registerContentObserver(uri, false, contentObserver);
        }

        void registerDeviceConfigListener(DeviceConfig.OnPropertiesChangedListener listener) {
            DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ALARM_MANAGER,
                    AppSchedulingModuleThread.getExecutor(), listener);
+2 −2
Original line number Diff line number Diff line
@@ -5303,12 +5303,12 @@
         {@link android.Manifest.permission#USE_EXACT_ALARM} once it targets API
         {@link android.os.Build.VERSION_CODES#TIRAMISU}. All apps using exact alarms for secondary
         features (which should still be user facing) should continue using this permission.
         <p>Protection level: appop
         <p>Protection level: signature|privileged|appop
     -->
    <permission android:name="android.permission.SCHEDULE_EXACT_ALARM"
        android:label="@string/permlab_schedule_exact_alarm"
        android:description="@string/permdesc_schedule_exact_alarm"
        android:protectionLevel="normal|appop"/>
        android:protectionLevel="signature|privileged|appop"/>

    <!-- Allows apps to use exact alarms just like with {@link
         android.Manifest.permission#SCHEDULE_EXACT_ALARM} but without needing to request this
+1 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@
        <permission name="android.permission.CALL_PRIVILEGED"/>
        <permission name="android.permission.MANAGE_USERS"/>
        <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
        <permission name="android.permission.SCHEDULE_EXACT_ALARM"/>
        <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/>
        <!-- Required to update emergency gesture settings -->
        <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+1 −0
Original line number Diff line number Diff line
@@ -321,6 +321,7 @@ applications that come with the platform
        <permission name="android.permission.REGISTER_CONNECTION_MANAGER"/>
        <permission name="android.permission.REGISTER_SIM_SUBSCRIPTION"/>
        <permission name="android.permission.RETRIEVE_WINDOW_CONTENT"/>
        <permission name="android.permission.SCHEDULE_EXACT_ALARM"/>
        <permission name="android.permission.SET_ALWAYS_FINISH"/>
        <permission name="android.permission.SET_ANIMATION_SCALE"/>
        <permission name="android.permission.SET_DEBUG_APP"/>
+164 −116

File changed.

Preview size limit exceeded, changes collapsed.