From c2cb5944f0db334586fbf088032a1e1350bd03c2 Mon Sep 17 00:00:00 2001 From: Suprabh Shukla Date: Tue, 7 Mar 2023 18:49:10 -0800 Subject: [PATCH] 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 --- .../server/alarm/AlarmManagerService.java | 83 ++--- core/res/AndroidManifest.xml | 4 +- data/etc/com.android.emergency.xml | 1 + data/etc/privapp-permissions-platform.xml | 1 + .../server/alarm/AlarmManagerServiceTest.java | 316 ++++++++++-------- 5 files changed, 220 insertions(+), 185 deletions(-) diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java index 1151bb7d0e6a5..f60fae064a7b1 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -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 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); diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 611035e1a4ace..c901232034521 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -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. -

Protection level: appop +

Protection level: signature|privileged|appop --> + android:protectionLevel="signature|privileged|appop"/> diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 5d303cfb2a85e..0faf62e03b145 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -321,6 +321,7 @@ applications that come with the platform + diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java index b395f42478b15..3927bcc16a77d 100644 --- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java @@ -28,7 +28,6 @@ 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; @@ -126,7 +125,6 @@ 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.tare.EconomyManager; import android.app.usage.UsageStatsManagerInternal; import android.content.ContentResolver; @@ -134,7 +132,6 @@ import android.content.Context; import android.content.Intent; import android.content.PermissionChecker; import android.content.pm.PackageManagerInternal; -import android.database.ContentObserver; import android.net.Uri; import android.os.BatteryManager; import android.os.Bundle; @@ -192,7 +189,6 @@ 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; @@ -246,8 +242,6 @@ public final class AlarmManagerServiceTest { @Mock private PackageManagerInternal mPackageManagerInternal; @Mock - private RoleManager mRoleManager; - @Mock private AppStateTrackerImpl mAppStateTracker; @Mock private AlarmManagerService.ClockReceiver mClockReceiver; @@ -392,11 +386,6 @@ public final class AlarmManagerServiceTest { return mWakeLock; } - @Override - void registerContentObserver(ContentObserver observer, Uri uri) { - // Do nothing. - } - @Override void registerDeviceConfigListener(DeviceConfig.OnPropertiesChangedListener listener) { // Do nothing. @@ -484,10 +473,12 @@ public final class AlarmManagerServiceTest { doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when( () -> PermissionChecker.checkPermissionForPreflight(any(), eq(Manifest.permission.USE_EXACT_ALARM), anyInt(), anyInt(), anyString())); + doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when( + () -> PermissionChecker.checkPermissionForPreflight(any(), eq(SCHEDULE_EXACT_ALARM), + anyInt(), anyInt(), anyString())); 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)}); @@ -2180,40 +2171,6 @@ public final class AlarmManagerServiceTest { } } - @Test - public void hasScheduleExactAlarmBinderCallNotDenyListed() throws RemoteException { - mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); - - mockScheduleExactAlarmState(true, false, MODE_DEFAULT); - assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); - - mockScheduleExactAlarmState(true, false, MODE_ALLOWED); - assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); - - mockScheduleExactAlarmState(true, false, MODE_ERRORED); - assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); - - mockScheduleExactAlarmState(true, false, MODE_IGNORED); - assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); - } - - @Test - public void hasScheduleExactAlarmBinderCallDenyListed() throws RemoteException { - mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); - - mockScheduleExactAlarmState(true, true, MODE_ERRORED); - assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); - - mockScheduleExactAlarmState(true, true, MODE_DEFAULT); - assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); - - mockScheduleExactAlarmState(true, true, MODE_IGNORED); - assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); - - mockScheduleExactAlarmState(true, true, MODE_ALLOWED); - assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); - } - private void mockChangeEnabled(long changeId, boolean enabled) { doReturn(enabled).when(() -> CompatChanges.isChangeEnabled(eq(changeId), anyString(), any(UserHandle.class))); @@ -2221,16 +2178,62 @@ public final class AlarmManagerServiceTest { } @Test - public void hasScheduleExactAlarmBinderCallNotDeclared() throws RemoteException { + public void hasScheduleExactAlarmBinderCall() throws RemoteException { + mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); + mockChangeEnabled(AlarmManager.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, true); + + mockScheduleExactAlarmState(true); + assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); + + mockScheduleExactAlarmState(false); + assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); + } + + @Test + public void hasScheduleExactAlarmBinderCallNotDenyListedPreT() throws RemoteException { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); - mockScheduleExactAlarmState(false, false, MODE_DEFAULT); + mockScheduleExactAlarmStatePreT(true, false, MODE_DEFAULT); + assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); + + mockScheduleExactAlarmStatePreT(true, false, MODE_ALLOWED); + assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); + + mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED); assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); - mockScheduleExactAlarmState(false, false, MODE_ALLOWED); + mockScheduleExactAlarmStatePreT(true, false, MODE_IGNORED); + assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); + } + + @Test + public void hasScheduleExactAlarmBinderCallDenyListedPreT() throws RemoteException { + mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); + + mockScheduleExactAlarmStatePreT(true, true, MODE_ERRORED); assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); - mockScheduleExactAlarmState(false, true, MODE_ALLOWED); + mockScheduleExactAlarmStatePreT(true, true, MODE_DEFAULT); + assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); + + mockScheduleExactAlarmStatePreT(true, true, MODE_IGNORED); + assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); + + mockScheduleExactAlarmStatePreT(true, true, MODE_ALLOWED); + assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); + } + + @Test + public void hasScheduleExactAlarmBinderCallNotDeclaredPreT() throws RemoteException { + mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); + + mockScheduleExactAlarmStatePreT(false, false, MODE_DEFAULT); + assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); + + mockScheduleExactAlarmStatePreT(false, false, MODE_ALLOWED); + assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); + + mockScheduleExactAlarmStatePreT(false, true, MODE_ALLOWED); assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); } @@ -2239,61 +2242,94 @@ public final class AlarmManagerServiceTest { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false); // canScheduleExactAlarms should be true regardless of any permission state. - mockUseExactAlarmState(true); + // Both SEA and UEA are denied in setUp. assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); mockUseExactAlarmState(false); assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); - mockScheduleExactAlarmState(false, true, MODE_DEFAULT); - assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); - - mockScheduleExactAlarmState(true, false, MODE_ERRORED); + mockScheduleExactAlarmState(false); assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); } @Test - public void canScheduleExactAlarmsBinderCall() throws RemoteException { + public void canScheduleExactAlarmsBinderCallPreT() throws RemoteException { // Policy permission is denied in setUp(). mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); mockChangeEnabled(AlarmManager.ENABLE_USE_EXACT_ALARM, true); // No permission, no exemption. - mockScheduleExactAlarmState(true, true, MODE_DEFAULT); + mockScheduleExactAlarmStatePreT(true, true, MODE_DEFAULT); assertFalse(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); // No permission, no exemption. - mockScheduleExactAlarmState(true, false, MODE_ERRORED); + mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED); assertFalse(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); // Policy permission only, no exemption. - mockScheduleExactAlarmState(true, false, MODE_ERRORED); + mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED); mockUseExactAlarmState(true); assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); mockUseExactAlarmState(false); // User permission only, no exemption. - mockScheduleExactAlarmState(true, false, MODE_DEFAULT); + mockScheduleExactAlarmStatePreT(true, false, MODE_DEFAULT); assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); // User permission only, no exemption. - mockScheduleExactAlarmState(true, true, MODE_ALLOWED); + mockScheduleExactAlarmStatePreT(true, true, MODE_ALLOWED); assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); // No permission, exemption. - mockScheduleExactAlarmState(true, false, MODE_ERRORED); + mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED); when(mDeviceIdleInternal.isAppOnWhitelist(TEST_CALLING_UID)).thenReturn(true); assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); // No permission, exemption. - mockScheduleExactAlarmState(true, false, MODE_ERRORED); + mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED); when(mDeviceIdleInternal.isAppOnWhitelist(TEST_CALLING_UID)).thenReturn(false); doReturn(true).when(() -> UserHandle.isCore(TEST_CALLING_UID)); assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); // Both permissions and exemption. - mockScheduleExactAlarmState(true, false, MODE_ALLOWED); + mockScheduleExactAlarmStatePreT(true, false, MODE_ALLOWED); + mockUseExactAlarmState(true); + assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); + } + + @Test + public void canScheduleExactAlarmsBinderCall() throws RemoteException { + // Both permissions are denied in setUp(). + mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); + mockChangeEnabled(AlarmManager.ENABLE_USE_EXACT_ALARM, true); + mockChangeEnabled(AlarmManager.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, true); + + // No permission, no exemption. + assertFalse(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); + + // Policy permission only, no exemption. + mockUseExactAlarmState(true); + assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); + + mockUseExactAlarmState(false); + + // User permission only, no exemption. + mockScheduleExactAlarmState(true); + assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); + + // No permission, exemption. + mockScheduleExactAlarmState(false); + when(mDeviceIdleInternal.isAppOnWhitelist(TEST_CALLING_UID)).thenReturn(true); + assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); + + // No permission, core uid exemption. + when(mDeviceIdleInternal.isAppOnWhitelist(TEST_CALLING_UID)).thenReturn(false); + doReturn(true).when(() -> UserHandle.isCore(TEST_CALLING_UID)); + assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); + + // Both permissions and core uid exemption. + mockScheduleExactAlarmState(true); mockUseExactAlarmState(true); assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); } @@ -2403,8 +2439,9 @@ public final class AlarmManagerServiceTest { @Test public void alarmClockBinderCallWithSEAPermission() throws RemoteException { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); + mockChangeEnabled(AlarmManager.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, true); - mockScheduleExactAlarmState(true, false, MODE_ALLOWED); + mockScheduleExactAlarmState(true); final PendingIntent alarmPi = getNewMockPendingIntent(); final AlarmManager.AlarmClockInfo alarmClock = mock(AlarmManager.AlarmClockInfo.class); @@ -2430,9 +2467,10 @@ public final class AlarmManagerServiceTest { public void alarmClockBinderCallWithUEAPermission() throws RemoteException { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); mockChangeEnabled(AlarmManager.ENABLE_USE_EXACT_ALARM, true); + mockChangeEnabled(AlarmManager.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, true); mockUseExactAlarmState(true); - mockScheduleExactAlarmState(false, false, MODE_ERRORED); + mockScheduleExactAlarmState(false); final PendingIntent alarmPi = getNewMockPendingIntent(); final AlarmManager.AlarmClockInfo alarmClock = mock(AlarmManager.AlarmClockInfo.class); @@ -2454,7 +2492,7 @@ public final class AlarmManagerServiceTest { assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type); } - private void mockScheduleExactAlarmState(boolean declared, boolean denyList, int mode) { + private void mockScheduleExactAlarmStatePreT(boolean declared, boolean denyList, int mode) { String[] requesters = declared ? new String[]{TEST_CALLING_PACKAGE} : EmptyArray.STRING; when(mPermissionManagerInternal.getAppOpPermissionPackages(SCHEDULE_EXACT_ALARM)) .thenReturn(requesters); @@ -2469,6 +2507,20 @@ public final class AlarmManagerServiceTest { TEST_CALLING_PACKAGE)).thenReturn(mode); } + private void mockScheduleExactAlarmState(boolean granted) { + String[] requesters = granted ? new String[]{TEST_CALLING_PACKAGE} : EmptyArray.STRING; + when(mPermissionManagerInternal.getAppOpPermissionPackages(SCHEDULE_EXACT_ALARM)) + .thenReturn(requesters); + mService.refreshExactAlarmCandidates(); + + final int result = granted ? PermissionChecker.PERMISSION_GRANTED + : PermissionChecker.PERMISSION_HARD_DENIED; + doReturn(result).when( + () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), + eq(SCHEDULE_EXACT_ALARM), anyInt(), eq(TEST_CALLING_UID), + eq(TEST_CALLING_PACKAGE))); + } + private void mockUseExactAlarmState(boolean granted) { final int result = granted ? PermissionChecker.PERMISSION_GRANTED : PermissionChecker.PERMISSION_HARD_DENIED; @@ -2482,7 +2534,7 @@ public final class AlarmManagerServiceTest { public void alarmClockBinderCallWithoutPermission() throws RemoteException { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); - mockScheduleExactAlarmState(true, false, MODE_ERRORED); + mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED); when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true); final PendingIntent alarmPi = getNewMockPendingIntent(); @@ -2503,8 +2555,9 @@ public final class AlarmManagerServiceTest { @Test public void exactBinderCallWithSEAPermission() throws RemoteException { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); + mockChangeEnabled(AlarmManager.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, true); - mockScheduleExactAlarmState(true, false, MODE_ALLOWED); + mockScheduleExactAlarmState(true); final PendingIntent alarmPi = getNewMockPendingIntent(); mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0, 0, alarmPi, null, null, null, null); @@ -2528,9 +2581,10 @@ public final class AlarmManagerServiceTest { public void exactBinderCallWithUEAPermission() throws RemoteException { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); mockChangeEnabled(AlarmManager.ENABLE_USE_EXACT_ALARM, true); + mockChangeEnabled(AlarmManager.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, true); mockUseExactAlarmState(true); - mockScheduleExactAlarmState(false, false, MODE_ERRORED); + mockScheduleExactAlarmState(false); final PendingIntent alarmPi = getNewMockPendingIntent(); mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0, 0, alarmPi, null, null, null, null); @@ -2554,7 +2608,7 @@ public final class AlarmManagerServiceTest { public void exactBinderCallWithAllowlist() throws RemoteException { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); // If permission is denied, only then allowlist will be checked. - mockScheduleExactAlarmState(true, false, MODE_ERRORED); + mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED); when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true); final PendingIntent alarmPi = getNewMockPendingIntent(); @@ -2574,7 +2628,7 @@ public final class AlarmManagerServiceTest { public void exactAllowWhileIdleBinderCallWithSEAPermission() throws RemoteException { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); - mockScheduleExactAlarmState(true, false, MODE_ALLOWED); + mockScheduleExactAlarmStatePreT(true, false, MODE_ALLOWED); final PendingIntent alarmPi = getNewMockPendingIntent(); mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0, FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null); @@ -2600,7 +2654,7 @@ public final class AlarmManagerServiceTest { mockChangeEnabled(AlarmManager.ENABLE_USE_EXACT_ALARM, true); mockUseExactAlarmState(true); - mockScheduleExactAlarmState(false, false, MODE_ERRORED); + mockScheduleExactAlarmStatePreT(false, false, MODE_ERRORED); final PendingIntent alarmPi = getNewMockPendingIntent(); mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0, FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null); @@ -2624,7 +2678,7 @@ public final class AlarmManagerServiceTest { public void exactAllowWhileIdleBinderCallWithAllowlist() throws RemoteException { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); // If permission is denied, only then allowlist will be checked. - mockScheduleExactAlarmState(true, false, MODE_ERRORED); + mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED); when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true); final PendingIntent alarmPi = getNewMockPendingIntent(); @@ -2650,7 +2704,7 @@ public final class AlarmManagerServiceTest { public void exactBinderCallsWithoutPermissionWithoutAllowlist() throws RemoteException { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); - mockScheduleExactAlarmState(true, false, MODE_ERRORED); + mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED); when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(false); final PendingIntent alarmPi = getNewMockPendingIntent(); @@ -2700,7 +2754,7 @@ public final class AlarmManagerServiceTest { public void binderCallWithUserAllowlist() throws RemoteException { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); - mockScheduleExactAlarmState(true, false, MODE_ERRORED); + mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED); when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true); when(mAppStateTracker.isUidPowerSaveUserExempt(TEST_CALLING_UID)).thenReturn(true); @@ -3025,7 +3079,7 @@ public final class AlarmManagerServiceTest { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); mService.mLastOpScheduleExactAlarm.put(TEST_CALLING_UID, MODE_ALLOWED); - mockScheduleExactAlarmState(true, false, MODE_ERRORED); + mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED); mIAppOpsCallback.opChanged(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, TEST_CALLING_PACKAGE); assertAndHandleMessageSync(REMOVE_EXACT_ALARMS); @@ -3038,7 +3092,7 @@ public final class AlarmManagerServiceTest { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false); mService.mLastOpScheduleExactAlarm.put(TEST_CALLING_UID, MODE_ALLOWED); - mockScheduleExactAlarmState(true, false, MODE_ERRORED); + mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED); mIAppOpsCallback.opChanged(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, TEST_CALLING_PACKAGE); @@ -3051,7 +3105,7 @@ public final class AlarmManagerServiceTest { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false); mService.mLastOpScheduleExactAlarm.put(TEST_CALLING_UID, MODE_ERRORED); - mockScheduleExactAlarmState(true, true, MODE_DEFAULT); + mockScheduleExactAlarmStatePreT(true, true, MODE_DEFAULT); mIAppOpsCallback.opChanged(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, TEST_CALLING_PACKAGE); @@ -3067,7 +3121,7 @@ public final class AlarmManagerServiceTest { when(mActivityManagerInternal.getBootTimeTempAllowListDuration()).thenReturn(durationMs); mService.mLastOpScheduleExactAlarm.put(TEST_CALLING_UID, MODE_ERRORED); - mockScheduleExactAlarmState(true, false, MODE_ALLOWED); + mockScheduleExactAlarmStatePreT(true, false, MODE_ALLOWED); mIAppOpsCallback.opChanged(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, TEST_CALLING_PACKAGE); final ArgumentCaptor intentCaptor = ArgumentCaptor.forClass(Intent.class); @@ -3327,7 +3381,7 @@ public final class AlarmManagerServiceTest { .putExtra(Intent.EXTRA_REPLACING, true); mockUseExactAlarmState(false); - mockScheduleExactAlarmState(true, false, MODE_ALLOWED); + mockScheduleExactAlarmStatePreT(true, false, MODE_ALLOWED); mPackageChangesReceiver.onReceive(mMockContext, packageReplacedIntent); assertAndHandleMessageSync(CHECK_EXACT_ALARM_PERMISSION_ON_UPDATE); @@ -3335,7 +3389,7 @@ public final class AlarmManagerServiceTest { assertEquals(5, mService.mAlarmStore.size()); mockUseExactAlarmState(true); - mockScheduleExactAlarmState(true, false, MODE_ERRORED); + mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED); mPackageChangesReceiver.onReceive(mMockContext, packageReplacedIntent); assertAndHandleMessageSync(CHECK_EXACT_ALARM_PERMISSION_ON_UPDATE); @@ -3343,7 +3397,7 @@ public final class AlarmManagerServiceTest { assertEquals(5, mService.mAlarmStore.size()); mockUseExactAlarmState(false); - mockScheduleExactAlarmState(true, false, MODE_ERRORED); + mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED); mPackageChangesReceiver.onReceive(mMockContext, packageReplacedIntent); assertAndHandleMessageSync(CHECK_EXACT_ALARM_PERMISSION_ON_UPDATE); @@ -3361,55 +3415,6 @@ public final 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, 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, 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++) { @@ -3509,7 +3514,7 @@ public final class AlarmManagerServiceTest { } @Test - public void hasUseExactAlarmPermission() { + public void hasUseExactAlarmInternal() { mockChangeEnabled(AlarmManager.ENABLE_USE_EXACT_ALARM, true); mockUseExactAlarmState(true); @@ -3520,7 +3525,7 @@ public final class AlarmManagerServiceTest { } @Test - public void hasUseExactAlarmPermissionChangeDisabled() { + public void hasUseExactAlarmInternalChangeDisabled() { mockChangeEnabled(AlarmManager.ENABLE_USE_EXACT_ALARM, false); mockUseExactAlarmState(true); @@ -3530,6 +3535,49 @@ public final class AlarmManagerServiceTest { assertFalse(mService.hasUseExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID)); } + @Test + public void hasScheduleExactAlarmInternal() { + mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); + mockChangeEnabled(AlarmManager.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, true); + + mockScheduleExactAlarmState(false); + assertFalse(mService.hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID)); + + mockScheduleExactAlarmState(true); + assertTrue(mService.hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID)); + } + + @Test + public void hasScheduleExactAlarmInternalPreT() { + mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); + mockChangeEnabled(AlarmManager.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, false); + + mockScheduleExactAlarmStatePreT(true, true, MODE_DEFAULT); + assertFalse(mService.hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID)); + + mockScheduleExactAlarmStatePreT(false, false, MODE_ALLOWED); + assertFalse(mService.hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID)); + + mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED); + assertFalse(mService.hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID)); + + mockScheduleExactAlarmStatePreT(true, false, MODE_ALLOWED); + assertTrue(mService.hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID)); + } + + @Test + public void hasScheduleExactAlarmInternalPreS() { + mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false); + + mockScheduleExactAlarmState(true); + mockScheduleExactAlarmStatePreT(true, false, MODE_ALLOWED); + assertFalse(mService.hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID)); + + mockScheduleExactAlarmState(false); + mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED); + assertFalse(mService.hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID)); + } + @Test public void temporaryQuotaReserve_hasQuota() { final int quotaToFill = 5;