Loading apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +46 −6 Original line number Original line Diff line number Diff line Loading @@ -72,6 +72,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Context; import android.content.Intent; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentFilter; import android.content.PermissionChecker; import android.content.pm.PackageManager; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.PackageManagerInternal; import android.database.ContentObserver; import android.database.ContentObserver; Loading Loading @@ -1839,6 +1840,9 @@ public class AlarmManagerService extends SystemService { if (!isExactAlarmChangeEnabled(a.packageName, UserHandle.getUserId(a.uid))) { if (!isExactAlarmChangeEnabled(a.packageName, UserHandle.getUserId(a.uid))) { return false; return false; } } if (hasUseExactAlarmPermission(a.packageName, a.uid)) { return false; } return !isExemptFromExactAlarmPermission(a.uid); return !isExemptFromExactAlarmPermission(a.uid); }; }; removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_EXACT_PERMISSION_REVOKED); removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_EXACT_PERMISSION_REVOKED); Loading Loading @@ -1900,6 +1904,9 @@ public class AlarmManagerService extends SystemService { || !isExactAlarmChangeEnabled(packageName, userId)) { || !isExactAlarmChangeEnabled(packageName, userId)) { return; return; } } if (hasUseExactAlarmPermission(packageName, uid)) { return; } final boolean requested = mExactAlarmCandidates.contains( final boolean requested = mExactAlarmCandidates.contains( UserHandle.getAppId(uid)); UserHandle.getAppId(uid)); Loading Loading @@ -2534,6 +2541,8 @@ public class AlarmManagerService extends SystemService { private static boolean getScheduleExactAlarmState(boolean requested, boolean denyListed, private static boolean getScheduleExactAlarmState(boolean requested, boolean denyListed, int appOpMode) { int appOpMode) { // This does not account for the state of the USE_EXACT_ALARM permission. // The caller should do that separately. if (!requested) { if (!requested) { return false; return false; } } Loading @@ -2543,7 +2552,16 @@ public class AlarmManagerService extends SystemService { return appOpMode == AppOpsManager.MODE_ALLOWED; return appOpMode == AppOpsManager.MODE_ALLOWED; } } boolean hasUseExactAlarmPermission(String packageName, int uid) { return PermissionChecker.checkPermissionForPreflight(getContext(), Manifest.permission.USE_EXACT_ALARM, PermissionChecker.PID_UNKNOWN, uid, packageName) == PermissionChecker.PERMISSION_GRANTED; } boolean hasScheduleExactAlarmInternal(String packageName, int uid) { boolean hasScheduleExactAlarmInternal(String packageName, int uid) { if (hasUseExactAlarmPermission(packageName, uid)) { return true; } // Not using getScheduleExactAlarmState as this can avoid some calls to AppOpsService. // Not using getScheduleExactAlarmState as this can avoid some calls to AppOpsService. // Not using #mLastOpScheduleExactAlarm as it may contain stale values. // Not using #mLastOpScheduleExactAlarm as it may contain stale values. // No locking needed as all internal containers being queried are immutable. // No locking needed as all internal containers being queried are immutable. Loading Loading @@ -3759,6 +3777,9 @@ public class AlarmManagerService extends SystemService { if (!isExactAlarmChangeEnabled(changedPackage, userId)) { if (!isExactAlarmChangeEnabled(changedPackage, userId)) { continue; continue; } } if (hasUseExactAlarmPermission(changedPackage, uid)) { continue; } final int appOpMode; final int appOpMode; synchronized (mLock) { synchronized (mLock) { appOpMode = mLastOpScheduleExactAlarm.get(uid, appOpMode = mLastOpScheduleExactAlarm.get(uid, Loading @@ -3778,7 +3799,8 @@ public class AlarmManagerService extends SystemService { } } if (added) { if (added) { synchronized (mLock) { synchronized (mLock) { removeExactAlarmsOnPermissionRevokedLocked(uid, changedPackage); removeExactAlarmsOnPermissionRevokedLocked(uid, changedPackage, /*killUid = */ true); } } } else { } else { sendScheduleExactAlarmPermissionStateChangedBroadcast(changedPackage, userId); sendScheduleExactAlarmPermissionStateChangedBroadcast(changedPackage, userId); Loading @@ -3794,7 +3816,7 @@ public class AlarmManagerService extends SystemService { * This is not expected to get called frequently. * This is not expected to get called frequently. */ */ @GuardedBy("mLock") @GuardedBy("mLock") void removeExactAlarmsOnPermissionRevokedLocked(int uid, String packageName) { void removeExactAlarmsOnPermissionRevokedLocked(int uid, String packageName, boolean killUid) { if (isExemptFromExactAlarmPermission(uid) if (isExemptFromExactAlarmPermission(uid) || !isExactAlarmChangeEnabled(packageName, UserHandle.getUserId(uid))) { || !isExactAlarmChangeEnabled(packageName, UserHandle.getUserId(uid))) { return; return; Loading @@ -3805,7 +3827,7 @@ public class AlarmManagerService extends SystemService { && a.windowLength == 0); && a.windowLength == 0); removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_EXACT_PERMISSION_REVOKED); removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_EXACT_PERMISSION_REVOKED); if (mConstants.KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED) { if (killUid && mConstants.KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED) { PermissionManagerService.killUid(UserHandle.getAppId(uid), UserHandle.getUserId(uid), PermissionManagerService.killUid(UserHandle.getAppId(uid), UserHandle.getUserId(uid), "schedule_exact_alarm revoked"); "schedule_exact_alarm revoked"); } } Loading Loading @@ -4617,6 +4639,7 @@ public class AlarmManagerService extends SystemService { public static final int EXACT_ALARM_DENY_LIST_PACKAGES_REMOVED = 10; public static final int EXACT_ALARM_DENY_LIST_PACKAGES_REMOVED = 10; public static final int REFRESH_EXACT_ALARM_CANDIDATES = 11; public static final int REFRESH_EXACT_ALARM_CANDIDATES = 11; public static final int TARE_AFFORDABILITY_CHANGED = 12; public static final int TARE_AFFORDABILITY_CHANGED = 12; public static final int CHECK_EXACT_ALARM_PERMISSION_ON_UPDATE = 13; AlarmHandler() { AlarmHandler() { super(Looper.myLooper()); super(Looper.myLooper()); Loading Loading @@ -4715,10 +4738,11 @@ public class AlarmManagerService extends SystemService { break; break; case REMOVE_EXACT_ALARMS: case REMOVE_EXACT_ALARMS: final int uid = msg.arg1; int uid = msg.arg1; final String packageName = (String) msg.obj; String packageName = (String) msg.obj; synchronized (mLock) { synchronized (mLock) { removeExactAlarmsOnPermissionRevokedLocked(uid, packageName); removeExactAlarmsOnPermissionRevokedLocked(uid, packageName, /*killUid = */ true); } } break; break; case EXACT_ALARM_DENY_LIST_PACKAGES_ADDED: case EXACT_ALARM_DENY_LIST_PACKAGES_ADDED: Loading @@ -4730,6 +4754,16 @@ public class AlarmManagerService extends SystemService { case REFRESH_EXACT_ALARM_CANDIDATES: case REFRESH_EXACT_ALARM_CANDIDATES: refreshExactAlarmCandidates(); refreshExactAlarmCandidates(); break; break; case CHECK_EXACT_ALARM_PERMISSION_ON_UPDATE: packageName = (String) msg.obj; uid = msg.arg1; if (!hasScheduleExactAlarmInternal(packageName, uid)) { synchronized (mLock) { removeExactAlarmsOnPermissionRevokedLocked(uid, packageName, /*killUid = */false); } } break; default: default: // nope, just ignore it // nope, just ignore it break; break; Loading Loading @@ -4914,6 +4948,12 @@ public class AlarmManagerService extends SystemService { } } break; break; case Intent.ACTION_PACKAGE_ADDED: case Intent.ACTION_PACKAGE_ADDED: if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { final String packageUpdated = intent.getData().getSchemeSpecificPart(); mHandler.obtainMessage( AlarmHandler.CHECK_EXACT_ALARM_PERMISSION_ON_UPDATE, uid, -1, packageUpdated).sendToTarget(); } mHandler.sendEmptyMessage(AlarmHandler.REFRESH_EXACT_ALARM_CANDIDATES); mHandler.sendEmptyMessage(AlarmHandler.REFRESH_EXACT_ALARM_CANDIDATES); return; return; } } Loading core/api/current.txt +1 −0 Original line number Original line Diff line number Diff line Loading @@ -194,6 +194,7 @@ package android { field public static final String UPDATE_DEVICE_STATS = "android.permission.UPDATE_DEVICE_STATS"; field public static final String UPDATE_DEVICE_STATS = "android.permission.UPDATE_DEVICE_STATS"; field public static final String UPDATE_PACKAGES_WITHOUT_USER_ACTION = "android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION"; field public static final String UPDATE_PACKAGES_WITHOUT_USER_ACTION = "android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION"; field public static final String USE_BIOMETRIC = "android.permission.USE_BIOMETRIC"; field public static final String USE_BIOMETRIC = "android.permission.USE_BIOMETRIC"; field public static final String USE_EXACT_ALARM = "android.permission.USE_EXACT_ALARM"; field @Deprecated public static final String USE_FINGERPRINT = "android.permission.USE_FINGERPRINT"; field @Deprecated public static final String USE_FINGERPRINT = "android.permission.USE_FINGERPRINT"; field public static final String USE_FULL_SCREEN_INTENT = "android.permission.USE_FULL_SCREEN_INTENT"; field public static final String USE_FULL_SCREEN_INTENT = "android.permission.USE_FULL_SCREEN_INTENT"; field public static final String USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER = "android.permission.USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER"; field public static final String USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER = "android.permission.USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER"; core/res/AndroidManifest.xml +10 −0 Original line number Original line Diff line number Diff line Loading @@ -4403,6 +4403,16 @@ <permission android:name="android.permission.SCHEDULE_EXACT_ALARM" <permission android:name="android.permission.SCHEDULE_EXACT_ALARM" android:protectionLevel="normal|appop"/> android:protectionLevel="normal|appop"/> <!-- Allows apps to use exact alarms just like with SCHEDULE_EXACT_ALARM but without needing to request this permission from the user. <p><b>This is only for apps that rely on exact alarms for their core functionality.</b> App stores may enforce policies to audit and review the use of this permission. Any app that requests this but is found to not require exact alarms for its primary function may be removed from the app store. --> <permission android:name="android.permission.USE_EXACT_ALARM" android:protectionLevel="normal"/> <!-- Allows an application to query tablet mode state and monitor changes <!-- Allows an application to query tablet mode state and monitor changes in it. in it. <p>Not for use by third-party applications. <p>Not for use by third-party applications. Loading services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java +26 −8 Original line number Original line Diff line number Diff line Loading @@ -108,6 +108,7 @@ import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.never; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.times; import android.Manifest; import android.app.ActivityManager; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.ActivityManagerInternal; import android.app.ActivityOptions; import android.app.ActivityOptions; Loading @@ -124,6 +125,7 @@ import android.app.usage.UsageStatsManagerInternal; import android.content.ContentResolver; import android.content.ContentResolver; import android.content.Context; import android.content.Context; import android.content.Intent; import android.content.Intent; import android.content.PermissionChecker; import android.content.pm.PackageManagerInternal; import android.content.pm.PackageManagerInternal; import android.database.ContentObserver; import android.database.ContentObserver; import android.net.Uri; import android.net.Uri; Loading Loading @@ -389,6 +391,7 @@ public class AlarmManagerServiceTest { .mockStatic(LocalServices.class) .mockStatic(LocalServices.class) .spyStatic(Looper.class) .spyStatic(Looper.class) .mockStatic(MetricsHelper.class) .mockStatic(MetricsHelper.class) .mockStatic(PermissionChecker.class) .mockStatic(PermissionManagerService.class) .mockStatic(PermissionManagerService.class) .mockStatic(ServiceManager.class) .mockStatic(ServiceManager.class) .mockStatic(Settings.Global.class) .mockStatic(Settings.Global.class) Loading Loading @@ -445,6 +448,10 @@ public class AlarmManagerServiceTest { doReturn(true) doReturn(true) .when(() -> DateFormat.is24HourFormat(eq(mMockContext), anyInt())); .when(() -> DateFormat.is24HourFormat(eq(mMockContext), anyInt())); doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when( () -> PermissionChecker.checkPermissionForPreflight(any(), eq(Manifest.permission.USE_EXACT_ALARM), anyInt(), anyInt(), anyString())); when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager); when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager); registerAppIds(new String[]{TEST_CALLING_PACKAGE}, registerAppIds(new String[]{TEST_CALLING_PACKAGE}, Loading Loading @@ -2158,6 +2165,7 @@ public class AlarmManagerServiceTest { @Test @Test public void canScheduleExactAlarmsBinderCall() throws RemoteException { public void canScheduleExactAlarmsBinderCall() throws RemoteException { // Policy permission is denied in setUp(). mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); // No permission, no exemption. // No permission, no exemption. Loading @@ -2168,6 +2176,14 @@ public class AlarmManagerServiceTest { mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); assertFalse(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); assertFalse(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); // Policy permission only, no exemption. mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); doReturn(PermissionChecker.PERMISSION_GRANTED).when( () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), eq(Manifest.permission.USE_EXACT_ALARM), anyInt(), eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); // Permission, no exemption. // Permission, no exemption. mockExactAlarmPermissionGrant(true, false, MODE_DEFAULT); mockExactAlarmPermissionGrant(true, false, MODE_DEFAULT); assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); Loading Loading @@ -2699,7 +2715,8 @@ public class AlarmManagerServiceTest { mService.handleChangesToExactAlarmDenyList(new ArraySet<>(packages), false); mService.handleChangesToExactAlarmDenyList(new ArraySet<>(packages), false); // No permission revoked. // No permission revoked. verify(mService, never()).removeExactAlarmsOnPermissionRevokedLocked(anyInt(), anyString()); verify(mService, never()).removeExactAlarmsOnPermissionRevokedLocked(anyInt(), anyString(), anyBoolean()); // Permission got granted only for (appId1, userId2). // Permission got granted only for (appId1, userId2). final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); Loading Loading @@ -2754,14 +2771,14 @@ public class AlarmManagerServiceTest { // Permission got revoked only for (appId1, userId2) // Permission got revoked only for (appId1, userId2) verify(mService, never()).removeExactAlarmsOnPermissionRevokedLocked( verify(mService, never()).removeExactAlarmsOnPermissionRevokedLocked( eq(UserHandle.getUid(userId1, appId1)), eq(packages[0])); eq(UserHandle.getUid(userId1, appId1)), eq(packages[0]), eq(true)); verify(mService, never()).removeExactAlarmsOnPermissionRevokedLocked( verify(mService, never()).removeExactAlarmsOnPermissionRevokedLocked( eq(UserHandle.getUid(userId1, appId2)), eq(packages[1])); eq(UserHandle.getUid(userId1, appId2)), eq(packages[1]), eq(true)); verify(mService, never()).removeExactAlarmsOnPermissionRevokedLocked( verify(mService, never()).removeExactAlarmsOnPermissionRevokedLocked( eq(UserHandle.getUid(userId2, appId2)), eq(packages[1])); eq(UserHandle.getUid(userId2, appId2)), eq(packages[1]), eq(true)); verify(mService).removeExactAlarmsOnPermissionRevokedLocked( verify(mService).removeExactAlarmsOnPermissionRevokedLocked( eq(UserHandle.getUid(userId2, appId1)), eq(packages[0])); eq(UserHandle.getUid(userId2, appId1)), eq(packages[0]), eq(true)); } } @Test @Test Loading @@ -2774,7 +2791,7 @@ public class AlarmManagerServiceTest { mIAppOpsCallback.opChanged(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, TEST_CALLING_PACKAGE); mIAppOpsCallback.opChanged(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, TEST_CALLING_PACKAGE); assertAndHandleMessageSync(REMOVE_EXACT_ALARMS); assertAndHandleMessageSync(REMOVE_EXACT_ALARMS); verify(mService).removeExactAlarmsOnPermissionRevokedLocked(TEST_CALLING_UID, verify(mService).removeExactAlarmsOnPermissionRevokedLocked(TEST_CALLING_UID, TEST_CALLING_PACKAGE); TEST_CALLING_PACKAGE, true); } } @Test @Test Loading Loading @@ -2859,7 +2876,8 @@ public class AlarmManagerServiceTest { null); null); assertEquals(6, mService.mAlarmStore.size()); assertEquals(6, mService.mAlarmStore.size()); mService.removeExactAlarmsOnPermissionRevokedLocked(TEST_CALLING_UID, TEST_CALLING_PACKAGE); mService.removeExactAlarmsOnPermissionRevokedLocked(TEST_CALLING_UID, TEST_CALLING_PACKAGE, true); final ArrayList<Alarm> remaining = mService.mAlarmStore.asList(); final ArrayList<Alarm> remaining = mService.mAlarmStore.asList(); assertEquals(3, remaining.size()); assertEquals(3, remaining.size()); Loading Loading @@ -3080,7 +3098,7 @@ public class AlarmManagerServiceTest { SCHEDULE_EXACT_ALARM)).thenReturn(exactAlarmRequesters); SCHEDULE_EXACT_ALARM)).thenReturn(exactAlarmRequesters); final Intent packageAdded = new Intent(Intent.ACTION_PACKAGE_ADDED) final Intent packageAdded = new Intent(Intent.ACTION_PACKAGE_ADDED) .setPackage(TEST_CALLING_PACKAGE) .setData(Uri.fromParts("package", TEST_CALLING_PACKAGE, null)) .putExtra(Intent.EXTRA_REPLACING, true); .putExtra(Intent.EXTRA_REPLACING, true); mPackageChangesReceiver.onReceive(mMockContext, packageAdded); mPackageChangesReceiver.onReceive(mMockContext, packageAdded); Loading Loading
apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +46 −6 Original line number Original line Diff line number Diff line Loading @@ -72,6 +72,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Context; import android.content.Intent; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentFilter; import android.content.PermissionChecker; import android.content.pm.PackageManager; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.PackageManagerInternal; import android.database.ContentObserver; import android.database.ContentObserver; Loading Loading @@ -1839,6 +1840,9 @@ public class AlarmManagerService extends SystemService { if (!isExactAlarmChangeEnabled(a.packageName, UserHandle.getUserId(a.uid))) { if (!isExactAlarmChangeEnabled(a.packageName, UserHandle.getUserId(a.uid))) { return false; return false; } } if (hasUseExactAlarmPermission(a.packageName, a.uid)) { return false; } return !isExemptFromExactAlarmPermission(a.uid); return !isExemptFromExactAlarmPermission(a.uid); }; }; removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_EXACT_PERMISSION_REVOKED); removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_EXACT_PERMISSION_REVOKED); Loading Loading @@ -1900,6 +1904,9 @@ public class AlarmManagerService extends SystemService { || !isExactAlarmChangeEnabled(packageName, userId)) { || !isExactAlarmChangeEnabled(packageName, userId)) { return; return; } } if (hasUseExactAlarmPermission(packageName, uid)) { return; } final boolean requested = mExactAlarmCandidates.contains( final boolean requested = mExactAlarmCandidates.contains( UserHandle.getAppId(uid)); UserHandle.getAppId(uid)); Loading Loading @@ -2534,6 +2541,8 @@ public class AlarmManagerService extends SystemService { private static boolean getScheduleExactAlarmState(boolean requested, boolean denyListed, private static boolean getScheduleExactAlarmState(boolean requested, boolean denyListed, int appOpMode) { int appOpMode) { // This does not account for the state of the USE_EXACT_ALARM permission. // The caller should do that separately. if (!requested) { if (!requested) { return false; return false; } } Loading @@ -2543,7 +2552,16 @@ public class AlarmManagerService extends SystemService { return appOpMode == AppOpsManager.MODE_ALLOWED; return appOpMode == AppOpsManager.MODE_ALLOWED; } } boolean hasUseExactAlarmPermission(String packageName, int uid) { return PermissionChecker.checkPermissionForPreflight(getContext(), Manifest.permission.USE_EXACT_ALARM, PermissionChecker.PID_UNKNOWN, uid, packageName) == PermissionChecker.PERMISSION_GRANTED; } boolean hasScheduleExactAlarmInternal(String packageName, int uid) { boolean hasScheduleExactAlarmInternal(String packageName, int uid) { if (hasUseExactAlarmPermission(packageName, uid)) { return true; } // Not using getScheduleExactAlarmState as this can avoid some calls to AppOpsService. // Not using getScheduleExactAlarmState as this can avoid some calls to AppOpsService. // Not using #mLastOpScheduleExactAlarm as it may contain stale values. // Not using #mLastOpScheduleExactAlarm as it may contain stale values. // No locking needed as all internal containers being queried are immutable. // No locking needed as all internal containers being queried are immutable. Loading Loading @@ -3759,6 +3777,9 @@ public class AlarmManagerService extends SystemService { if (!isExactAlarmChangeEnabled(changedPackage, userId)) { if (!isExactAlarmChangeEnabled(changedPackage, userId)) { continue; continue; } } if (hasUseExactAlarmPermission(changedPackage, uid)) { continue; } final int appOpMode; final int appOpMode; synchronized (mLock) { synchronized (mLock) { appOpMode = mLastOpScheduleExactAlarm.get(uid, appOpMode = mLastOpScheduleExactAlarm.get(uid, Loading @@ -3778,7 +3799,8 @@ public class AlarmManagerService extends SystemService { } } if (added) { if (added) { synchronized (mLock) { synchronized (mLock) { removeExactAlarmsOnPermissionRevokedLocked(uid, changedPackage); removeExactAlarmsOnPermissionRevokedLocked(uid, changedPackage, /*killUid = */ true); } } } else { } else { sendScheduleExactAlarmPermissionStateChangedBroadcast(changedPackage, userId); sendScheduleExactAlarmPermissionStateChangedBroadcast(changedPackage, userId); Loading @@ -3794,7 +3816,7 @@ public class AlarmManagerService extends SystemService { * This is not expected to get called frequently. * This is not expected to get called frequently. */ */ @GuardedBy("mLock") @GuardedBy("mLock") void removeExactAlarmsOnPermissionRevokedLocked(int uid, String packageName) { void removeExactAlarmsOnPermissionRevokedLocked(int uid, String packageName, boolean killUid) { if (isExemptFromExactAlarmPermission(uid) if (isExemptFromExactAlarmPermission(uid) || !isExactAlarmChangeEnabled(packageName, UserHandle.getUserId(uid))) { || !isExactAlarmChangeEnabled(packageName, UserHandle.getUserId(uid))) { return; return; Loading @@ -3805,7 +3827,7 @@ public class AlarmManagerService extends SystemService { && a.windowLength == 0); && a.windowLength == 0); removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_EXACT_PERMISSION_REVOKED); removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_EXACT_PERMISSION_REVOKED); if (mConstants.KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED) { if (killUid && mConstants.KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED) { PermissionManagerService.killUid(UserHandle.getAppId(uid), UserHandle.getUserId(uid), PermissionManagerService.killUid(UserHandle.getAppId(uid), UserHandle.getUserId(uid), "schedule_exact_alarm revoked"); "schedule_exact_alarm revoked"); } } Loading Loading @@ -4617,6 +4639,7 @@ public class AlarmManagerService extends SystemService { public static final int EXACT_ALARM_DENY_LIST_PACKAGES_REMOVED = 10; public static final int EXACT_ALARM_DENY_LIST_PACKAGES_REMOVED = 10; public static final int REFRESH_EXACT_ALARM_CANDIDATES = 11; public static final int REFRESH_EXACT_ALARM_CANDIDATES = 11; public static final int TARE_AFFORDABILITY_CHANGED = 12; public static final int TARE_AFFORDABILITY_CHANGED = 12; public static final int CHECK_EXACT_ALARM_PERMISSION_ON_UPDATE = 13; AlarmHandler() { AlarmHandler() { super(Looper.myLooper()); super(Looper.myLooper()); Loading Loading @@ -4715,10 +4738,11 @@ public class AlarmManagerService extends SystemService { break; break; case REMOVE_EXACT_ALARMS: case REMOVE_EXACT_ALARMS: final int uid = msg.arg1; int uid = msg.arg1; final String packageName = (String) msg.obj; String packageName = (String) msg.obj; synchronized (mLock) { synchronized (mLock) { removeExactAlarmsOnPermissionRevokedLocked(uid, packageName); removeExactAlarmsOnPermissionRevokedLocked(uid, packageName, /*killUid = */ true); } } break; break; case EXACT_ALARM_DENY_LIST_PACKAGES_ADDED: case EXACT_ALARM_DENY_LIST_PACKAGES_ADDED: Loading @@ -4730,6 +4754,16 @@ public class AlarmManagerService extends SystemService { case REFRESH_EXACT_ALARM_CANDIDATES: case REFRESH_EXACT_ALARM_CANDIDATES: refreshExactAlarmCandidates(); refreshExactAlarmCandidates(); break; break; case CHECK_EXACT_ALARM_PERMISSION_ON_UPDATE: packageName = (String) msg.obj; uid = msg.arg1; if (!hasScheduleExactAlarmInternal(packageName, uid)) { synchronized (mLock) { removeExactAlarmsOnPermissionRevokedLocked(uid, packageName, /*killUid = */false); } } break; default: default: // nope, just ignore it // nope, just ignore it break; break; Loading Loading @@ -4914,6 +4948,12 @@ public class AlarmManagerService extends SystemService { } } break; break; case Intent.ACTION_PACKAGE_ADDED: case Intent.ACTION_PACKAGE_ADDED: if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { final String packageUpdated = intent.getData().getSchemeSpecificPart(); mHandler.obtainMessage( AlarmHandler.CHECK_EXACT_ALARM_PERMISSION_ON_UPDATE, uid, -1, packageUpdated).sendToTarget(); } mHandler.sendEmptyMessage(AlarmHandler.REFRESH_EXACT_ALARM_CANDIDATES); mHandler.sendEmptyMessage(AlarmHandler.REFRESH_EXACT_ALARM_CANDIDATES); return; return; } } Loading
core/api/current.txt +1 −0 Original line number Original line Diff line number Diff line Loading @@ -194,6 +194,7 @@ package android { field public static final String UPDATE_DEVICE_STATS = "android.permission.UPDATE_DEVICE_STATS"; field public static final String UPDATE_DEVICE_STATS = "android.permission.UPDATE_DEVICE_STATS"; field public static final String UPDATE_PACKAGES_WITHOUT_USER_ACTION = "android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION"; field public static final String UPDATE_PACKAGES_WITHOUT_USER_ACTION = "android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION"; field public static final String USE_BIOMETRIC = "android.permission.USE_BIOMETRIC"; field public static final String USE_BIOMETRIC = "android.permission.USE_BIOMETRIC"; field public static final String USE_EXACT_ALARM = "android.permission.USE_EXACT_ALARM"; field @Deprecated public static final String USE_FINGERPRINT = "android.permission.USE_FINGERPRINT"; field @Deprecated public static final String USE_FINGERPRINT = "android.permission.USE_FINGERPRINT"; field public static final String USE_FULL_SCREEN_INTENT = "android.permission.USE_FULL_SCREEN_INTENT"; field public static final String USE_FULL_SCREEN_INTENT = "android.permission.USE_FULL_SCREEN_INTENT"; field public static final String USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER = "android.permission.USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER"; field public static final String USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER = "android.permission.USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER";
core/res/AndroidManifest.xml +10 −0 Original line number Original line Diff line number Diff line Loading @@ -4403,6 +4403,16 @@ <permission android:name="android.permission.SCHEDULE_EXACT_ALARM" <permission android:name="android.permission.SCHEDULE_EXACT_ALARM" android:protectionLevel="normal|appop"/> android:protectionLevel="normal|appop"/> <!-- Allows apps to use exact alarms just like with SCHEDULE_EXACT_ALARM but without needing to request this permission from the user. <p><b>This is only for apps that rely on exact alarms for their core functionality.</b> App stores may enforce policies to audit and review the use of this permission. Any app that requests this but is found to not require exact alarms for its primary function may be removed from the app store. --> <permission android:name="android.permission.USE_EXACT_ALARM" android:protectionLevel="normal"/> <!-- Allows an application to query tablet mode state and monitor changes <!-- Allows an application to query tablet mode state and monitor changes in it. in it. <p>Not for use by third-party applications. <p>Not for use by third-party applications. Loading
services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java +26 −8 Original line number Original line Diff line number Diff line Loading @@ -108,6 +108,7 @@ import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.never; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.times; import android.Manifest; import android.app.ActivityManager; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.ActivityManagerInternal; import android.app.ActivityOptions; import android.app.ActivityOptions; Loading @@ -124,6 +125,7 @@ import android.app.usage.UsageStatsManagerInternal; import android.content.ContentResolver; import android.content.ContentResolver; import android.content.Context; import android.content.Context; import android.content.Intent; import android.content.Intent; import android.content.PermissionChecker; import android.content.pm.PackageManagerInternal; import android.content.pm.PackageManagerInternal; import android.database.ContentObserver; import android.database.ContentObserver; import android.net.Uri; import android.net.Uri; Loading Loading @@ -389,6 +391,7 @@ public class AlarmManagerServiceTest { .mockStatic(LocalServices.class) .mockStatic(LocalServices.class) .spyStatic(Looper.class) .spyStatic(Looper.class) .mockStatic(MetricsHelper.class) .mockStatic(MetricsHelper.class) .mockStatic(PermissionChecker.class) .mockStatic(PermissionManagerService.class) .mockStatic(PermissionManagerService.class) .mockStatic(ServiceManager.class) .mockStatic(ServiceManager.class) .mockStatic(Settings.Global.class) .mockStatic(Settings.Global.class) Loading Loading @@ -445,6 +448,10 @@ public class AlarmManagerServiceTest { doReturn(true) doReturn(true) .when(() -> DateFormat.is24HourFormat(eq(mMockContext), anyInt())); .when(() -> DateFormat.is24HourFormat(eq(mMockContext), anyInt())); doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when( () -> PermissionChecker.checkPermissionForPreflight(any(), eq(Manifest.permission.USE_EXACT_ALARM), anyInt(), anyInt(), anyString())); when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager); when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager); registerAppIds(new String[]{TEST_CALLING_PACKAGE}, registerAppIds(new String[]{TEST_CALLING_PACKAGE}, Loading Loading @@ -2158,6 +2165,7 @@ public class AlarmManagerServiceTest { @Test @Test public void canScheduleExactAlarmsBinderCall() throws RemoteException { public void canScheduleExactAlarmsBinderCall() throws RemoteException { // Policy permission is denied in setUp(). mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); // No permission, no exemption. // No permission, no exemption. Loading @@ -2168,6 +2176,14 @@ public class AlarmManagerServiceTest { mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); assertFalse(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); assertFalse(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); // Policy permission only, no exemption. mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); doReturn(PermissionChecker.PERMISSION_GRANTED).when( () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), eq(Manifest.permission.USE_EXACT_ALARM), anyInt(), eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); // Permission, no exemption. // Permission, no exemption. mockExactAlarmPermissionGrant(true, false, MODE_DEFAULT); mockExactAlarmPermissionGrant(true, false, MODE_DEFAULT); assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); Loading Loading @@ -2699,7 +2715,8 @@ public class AlarmManagerServiceTest { mService.handleChangesToExactAlarmDenyList(new ArraySet<>(packages), false); mService.handleChangesToExactAlarmDenyList(new ArraySet<>(packages), false); // No permission revoked. // No permission revoked. verify(mService, never()).removeExactAlarmsOnPermissionRevokedLocked(anyInt(), anyString()); verify(mService, never()).removeExactAlarmsOnPermissionRevokedLocked(anyInt(), anyString(), anyBoolean()); // Permission got granted only for (appId1, userId2). // Permission got granted only for (appId1, userId2). final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); Loading Loading @@ -2754,14 +2771,14 @@ public class AlarmManagerServiceTest { // Permission got revoked only for (appId1, userId2) // Permission got revoked only for (appId1, userId2) verify(mService, never()).removeExactAlarmsOnPermissionRevokedLocked( verify(mService, never()).removeExactAlarmsOnPermissionRevokedLocked( eq(UserHandle.getUid(userId1, appId1)), eq(packages[0])); eq(UserHandle.getUid(userId1, appId1)), eq(packages[0]), eq(true)); verify(mService, never()).removeExactAlarmsOnPermissionRevokedLocked( verify(mService, never()).removeExactAlarmsOnPermissionRevokedLocked( eq(UserHandle.getUid(userId1, appId2)), eq(packages[1])); eq(UserHandle.getUid(userId1, appId2)), eq(packages[1]), eq(true)); verify(mService, never()).removeExactAlarmsOnPermissionRevokedLocked( verify(mService, never()).removeExactAlarmsOnPermissionRevokedLocked( eq(UserHandle.getUid(userId2, appId2)), eq(packages[1])); eq(UserHandle.getUid(userId2, appId2)), eq(packages[1]), eq(true)); verify(mService).removeExactAlarmsOnPermissionRevokedLocked( verify(mService).removeExactAlarmsOnPermissionRevokedLocked( eq(UserHandle.getUid(userId2, appId1)), eq(packages[0])); eq(UserHandle.getUid(userId2, appId1)), eq(packages[0]), eq(true)); } } @Test @Test Loading @@ -2774,7 +2791,7 @@ public class AlarmManagerServiceTest { mIAppOpsCallback.opChanged(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, TEST_CALLING_PACKAGE); mIAppOpsCallback.opChanged(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, TEST_CALLING_PACKAGE); assertAndHandleMessageSync(REMOVE_EXACT_ALARMS); assertAndHandleMessageSync(REMOVE_EXACT_ALARMS); verify(mService).removeExactAlarmsOnPermissionRevokedLocked(TEST_CALLING_UID, verify(mService).removeExactAlarmsOnPermissionRevokedLocked(TEST_CALLING_UID, TEST_CALLING_PACKAGE); TEST_CALLING_PACKAGE, true); } } @Test @Test Loading Loading @@ -2859,7 +2876,8 @@ public class AlarmManagerServiceTest { null); null); assertEquals(6, mService.mAlarmStore.size()); assertEquals(6, mService.mAlarmStore.size()); mService.removeExactAlarmsOnPermissionRevokedLocked(TEST_CALLING_UID, TEST_CALLING_PACKAGE); mService.removeExactAlarmsOnPermissionRevokedLocked(TEST_CALLING_UID, TEST_CALLING_PACKAGE, true); final ArrayList<Alarm> remaining = mService.mAlarmStore.asList(); final ArrayList<Alarm> remaining = mService.mAlarmStore.asList(); assertEquals(3, remaining.size()); assertEquals(3, remaining.size()); Loading Loading @@ -3080,7 +3098,7 @@ public class AlarmManagerServiceTest { SCHEDULE_EXACT_ALARM)).thenReturn(exactAlarmRequesters); SCHEDULE_EXACT_ALARM)).thenReturn(exactAlarmRequesters); final Intent packageAdded = new Intent(Intent.ACTION_PACKAGE_ADDED) final Intent packageAdded = new Intent(Intent.ACTION_PACKAGE_ADDED) .setPackage(TEST_CALLING_PACKAGE) .setData(Uri.fromParts("package", TEST_CALLING_PACKAGE, null)) .putExtra(Intent.EXTRA_REPLACING, true); .putExtra(Intent.EXTRA_REPLACING, true); mPackageChangesReceiver.onReceive(mMockContext, packageAdded); mPackageChangesReceiver.onReceive(mMockContext, packageAdded); Loading