diff options
9 files changed, 549 insertions, 286 deletions
diff --git a/apex/jobscheduler/framework/java/android/app/AlarmManager.java b/apex/jobscheduler/framework/java/android/app/AlarmManager.java index 3ea1922547d5..88f3df8fa2c4 100644 --- a/apex/jobscheduler/framework/java/android/app/AlarmManager.java +++ b/apex/jobscheduler/framework/java/android/app/AlarmManager.java @@ -23,6 +23,7 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SystemApi; import android.annotation.SystemService; +import android.annotation.TestApi; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledSince; import android.compat.annotation.UnsupportedAppUsage; @@ -1132,12 +1133,31 @@ public class AlarmManager { } /** - * Called to check if the caller has permission to use alarms set via {@link } - * @return + * Called to check if the caller has the permission + * {@link Manifest.permission#SCHEDULE_EXACT_ALARM}. + * + * Apps can start {@link android.provider.Settings#ACTION_REQUEST_SCHEDULE_EXACT_ALARM} to + * request this from the user. + * + * @return {@code true} if the caller has the permission, {@code false} otherwise. + * @see android.provider.Settings#ACTION_REQUEST_SCHEDULE_EXACT_ALARM */ public boolean canScheduleExactAlarms() { + return hasScheduleExactAlarm(mContext.getOpPackageName(), mContext.getUserId()); + } + + /** + * Called to check if the given package in the given user has the permission + * {@link Manifest.permission#SCHEDULE_EXACT_ALARM}. + * + * <p><em>Note: This is only for use by system components.</em> + * + * @hide + */ + @TestApi + public boolean hasScheduleExactAlarm(@NonNull String packageName, int userId) { try { - return mService.canScheduleExactAlarms(); + return mService.hasScheduleExactAlarm(packageName, userId); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } diff --git a/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl b/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl index a10cbbee19d1..cd7c1e8ab4fd 100644 --- a/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl +++ b/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl @@ -41,6 +41,6 @@ interface IAlarmManager { @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) AlarmManager.AlarmClockInfo getNextAlarmClock(int userId); long currentNetworkTimeMillis(); - boolean canScheduleExactAlarms(); + boolean hasScheduleExactAlarm(String packageName, int userId); int getConfigVersion(); } 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 08e1a952603c..7a3614172dff 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -126,6 +126,8 @@ import com.android.server.SystemServiceManager; import com.android.server.usage.AppStandbyInternal; import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener; +import libcore.util.EmptyArray; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.text.SimpleDateFormat; @@ -139,6 +141,7 @@ import java.util.Date; import java.util.HashMap; import java.util.Locale; import java.util.Random; +import java.util.Set; import java.util.TimeZone; import java.util.TreeSet; import java.util.function.Predicate; @@ -194,6 +197,7 @@ public class AlarmManagerService extends SystemService { DeviceIdleInternal mLocalDeviceIdleController; private UsageStatsManagerInternal mUsageStatsManagerInternal; private ActivityManagerInternal mActivityManagerInternal; + private PackageManagerInternal mPackageManagerInternal; final Object mLock = new Object(); @@ -262,10 +266,12 @@ public class AlarmManagerService extends SystemService { interface Stats { int REORDER_ALARMS_FOR_STANDBY = 0; + int HAS_SCHEDULE_EXACT_ALARM = 1; } private final StatLogger mStatLogger = new StatLogger("Alarm manager stats", new String[]{ "REORDER_ALARMS_FOR_STANDBY", + "HAS_SCHEDULE_EXACT_ALARM", }); BroadcastOptions mOptsWithFgs = BroadcastOptions.makeBasic(); @@ -383,6 +389,9 @@ public class AlarmManagerService extends SystemService { */ @VisibleForTesting final class Constants implements DeviceConfig.OnPropertiesChangedListener { + @VisibleForTesting + static final int MAX_EXACT_ALARM_DENY_LIST_SIZE = 250; + // Key names stored in the settings value. @VisibleForTesting static final String KEY_MIN_FUTURITY = "min_futurity"; @@ -437,6 +446,8 @@ public class AlarmManagerService extends SystemService { static final String KEY_CRASH_NON_CLOCK_APPS = "crash_non_clock_apps"; @VisibleForTesting static final String KEY_PRIORITY_ALARM_DELAY = "priority_alarm_delay"; + @VisibleForTesting + static final String KEY_EXACT_ALARM_DENY_LIST = "exact_alarm_deny_list"; private static final long DEFAULT_MIN_FUTURITY = 5 * 1000; private static final long DEFAULT_MIN_INTERVAL = 60 * 1000; @@ -537,6 +548,12 @@ public class AlarmManagerService extends SystemService { */ public long PRIORITY_ALARM_DELAY = DEFAULT_PRIORITY_ALARM_DELAY; + /** + * Set of apps that won't get SCHEDULE_EXACT_ALARM when the app-op mode for + * OP_SCHEDULE_EXACT_ALARM is MODE_DEFAULT. + */ + public Set<String> EXACT_ALARM_DENY_LIST = Collections.emptySet(); + private long mLastAllowWhileIdleWhitelistDuration = -1; private int mVersion = 0; @@ -686,6 +703,21 @@ public class AlarmManagerService extends SystemService { PRIORITY_ALARM_DELAY = properties.getLong(KEY_PRIORITY_ALARM_DELAY, DEFAULT_PRIORITY_ALARM_DELAY); break; + case KEY_EXACT_ALARM_DENY_LIST: + final String rawValue = properties.getString(KEY_EXACT_ALARM_DENY_LIST, + ""); + final String[] values = rawValue.isEmpty() + ? EmptyArray.STRING + : rawValue.split(",", MAX_EXACT_ALARM_DENY_LIST_SIZE + 1); + if (values.length > MAX_EXACT_ALARM_DENY_LIST_SIZE) { + Slog.w(TAG, "Deny list too long, truncating to " + + MAX_EXACT_ALARM_DENY_LIST_SIZE + " elements."); + updateExactAlarmDenyList( + Arrays.copyOf(values, MAX_EXACT_ALARM_DENY_LIST_SIZE)); + } else { + updateExactAlarmDenyList(values); + } + break; default: if (name.startsWith(KEY_PREFIX_STANDBY_QUOTA) && !standbyQuotaUpdated) { // The quotas need to be updated in order, so we can't just rely @@ -699,6 +731,21 @@ public class AlarmManagerService extends SystemService { } } + private void updateExactAlarmDenyList(String[] newDenyList) { + if (newDenyList.length == 0) { + EXACT_ALARM_DENY_LIST = Collections.emptySet(); + } else { + final Set<String> oldSet = EXACT_ALARM_DENY_LIST; + final Set<String> newlyAdded = new ArraySet<>(newDenyList); + EXACT_ALARM_DENY_LIST = Collections.unmodifiableSet(new ArraySet<>(newlyAdded)); + newlyAdded.removeAll(oldSet); + if (newlyAdded.size() > 0) { + mHandler.obtainMessage(AlarmHandler.EXACT_ALARM_DENY_LIST_CHANGED, newlyAdded) + .sendToTarget(); + } + } + } + private void migrateAlarmsToNewStoreLocked() { final AlarmStore newStore = LAZY_BATCHING ? new LazyAlarmStore() : new BatchingAlarmStore(); @@ -841,6 +888,9 @@ public class AlarmManagerService extends SystemService { TimeUtils.formatDuration(PRIORITY_ALARM_DELAY, pw); pw.println(); + pw.print(KEY_EXACT_ALARM_DENY_LIST, EXACT_ALARM_DENY_LIST); + pw.println(); + pw.decreaseIndent(); } @@ -1428,8 +1478,9 @@ public class AlarmManagerService extends SystemService { mInjector.setKernelTime(systemBuildTime); } + mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); // Determine SysUI's uid - mSystemUiUid = mInjector.getSystemUiUid(); + mSystemUiUid = mInjector.getSystemUiUid(mPackageManagerInternal); if (mSystemUiUid <= 0) { Slog.wtf(TAG, "SysUI package not found!"); } @@ -1504,10 +1555,7 @@ public class AlarmManagerService extends SystemService { if (op != AppOpsManager.OP_SCHEDULE_EXACT_ALARM) { return; } - final int mode = mAppOpsService.checkOperation(op, uid, - packageName); - if (mode != AppOpsManager.MODE_ALLOWED - && mode != AppOpsManager.MODE_DEFAULT) { + if (!hasScheduleExactAlarmInternal(packageName, uid)) { mHandler.obtainMessage(AlarmHandler.REMOVE_EXACT_ALARMS, uid, 0, packageName).sendToTarget(); } @@ -2029,6 +2077,11 @@ public class AlarmManagerService extends SystemService { } @Override + public boolean hasScheduleExactAlarm(String packageName, int uid) { + return hasScheduleExactAlarmInternal(packageName, uid); + } + + @Override public void registerInFlightListener(InFlightListener callback) { synchronized (mLock) { mInFlightListeners.add(callback); @@ -2036,6 +2089,21 @@ public class AlarmManagerService extends SystemService { } } + boolean hasScheduleExactAlarmInternal(String packageName, int uid) { + final long start = mStatLogger.getTime(); + // No locking needed as EXACT_ALARM_DENY_LIST is immutable. + final boolean isOnDenyList = mConstants.EXACT_ALARM_DENY_LIST.contains(packageName); + if (isOnDenyList && mAppOps.checkOpNoThrow(AppOpsManager.OP_SCHEDULE_EXACT_ALARM, uid, + packageName) != AppOpsManager.MODE_ALLOWED) { + return false; + } + final boolean has = PermissionChecker.checkPermissionForPreflight(getContext(), + Manifest.permission.SCHEDULE_EXACT_ALARM, -1, uid, packageName) + == PermissionChecker.PERMISSION_GRANTED; + mStatLogger.logDurationStat(Stats.HAS_SCHEDULE_EXACT_ALARM, start); + return has; + } + /** * Returns true if the given uid does not require SCHEDULE_EXACT_ALARM to set exact, * allow-while-idle alarms. @@ -2056,11 +2124,16 @@ public class AlarmManagerService extends SystemService { int type, long triggerAtTime, long windowLength, long interval, int flags, PendingIntent operation, IAlarmListener directReceiver, String listenerTag, WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock) { - final int callingUid = Binder.getCallingUid(); + final int callingUid = mInjector.getCallingUid(); + final int callingUserId = UserHandle.getUserId(callingUid); // make sure the caller is not lying about which package should be blamed for // wakelock time spent in alarm delivery - mAppOps.checkPackage(callingUid, callingPackage); + if (callingUid != mPackageManagerInternal.getPackageUid(callingPackage, 0, + callingUserId)) { + throw new SecurityException("Package " + callingPackage + + " does not belong to the calling uid " + callingUid); + } final boolean allowWhileIdle = (flags & FLAG_ALLOW_WHILE_IDLE) != 0; final boolean exact = (windowLength == AlarmManager.WINDOW_EXACT); @@ -2077,7 +2150,7 @@ public class AlarmManagerService extends SystemService { final boolean needsPermission; boolean lowerQuota; if (CompatChanges.isChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, - callingPackage, UserHandle.getUserHandleForUid(callingUid))) { + callingPackage, UserHandle.of(callingUserId))) { needsPermission = exact; lowerQuota = !exact; idleOptions = exact ? mOptsWithFgs.toBundle() : mOptsWithoutFgs.toBundle(); @@ -2086,7 +2159,7 @@ public class AlarmManagerService extends SystemService { lowerQuota = allowWhileIdle; idleOptions = allowWhileIdle ? mOptsWithFgs.toBundle() : null; } - if (needsPermission && !canScheduleExactAlarms()) { + if (needsPermission && !hasScheduleExactAlarmInternal(callingPackage, callingUid)) { if (alarmClock != null || !isExemptFromExactAlarmPermission(callingUid)) { final String errorMessage = "Caller " + callingPackage + " needs to hold " + Manifest.permission.SCHEDULE_EXACT_ALARM + " to set " @@ -2165,10 +2238,18 @@ public class AlarmManagerService extends SystemService { } @Override - public boolean canScheduleExactAlarms() { - return PermissionChecker.checkCallingOrSelfPermissionForPreflight(getContext(), - Manifest.permission.SCHEDULE_EXACT_ALARM) - == PermissionChecker.PERMISSION_GRANTED; + public boolean hasScheduleExactAlarm(String packageName, int userId) { + final int callingUid = mInjector.getCallingUid(); + if (UserHandle.getUserId(callingUid) != userId) { + getContext().enforceCallingOrSelfPermission( + Manifest.permission.INTERACT_ACROSS_USERS_FULL, "hasScheduleExactAlarm"); + } + final int uid = mPackageManagerInternal.getPackageUid(packageName, 0, userId); + if (callingUid != uid && !UserHandle.isCore(callingUid)) { + throw new SecurityException("Uid " + callingUid + + " cannot query hasScheduleExactAlarm for uid " + uid); + } + return (uid > 0) ? hasScheduleExactAlarmInternal(packageName, uid) : false; } @Override @@ -3072,27 +3153,63 @@ public class AlarmManagerService extends SystemService { } /** + * Called when some packages are added to the {@link Constants#EXACT_ALARM_DENY_LIST}, as this + * may cause some of them to lose their permission. + * + * Note that these packages don't need to be installed on the device, but if they do have an + * exact alarm scheduled and they lose the permission, this alarm will be canceled. + * + * This is not expected to get called frequently. + */ + void handlePackagesAddedToExactAlarmsDenyListLocked(ArraySet<String> packageNames) { + Slog.w(TAG, "Packages " + packageNames + " added to the exact alarm deny list."); + final Predicate<Alarm> whichAlarms = a -> { + if (!packageNames.contains(a.packageName) || a.windowLength != 0) { + return false; + } + if (!CompatChanges.isChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, + a.packageName, UserHandle.getUserHandleForUid(a.uid))) { + return false; + } + if (a.alarmClock == null && isExemptFromExactAlarmPermission(a.uid)) { + return false; + } + return !hasScheduleExactAlarmInternal(a.packageName, a.uid); + }; + removeAlarmsInternalLocked(whichAlarms); + } + + /** * Called when an app loses {@link Manifest.permission#SCHEDULE_EXACT_ALARM} to remove alarms * that the app is no longer eligible to use. + * + * This is not expected to get called frequently. * TODO (b/179541791): Add revocation history to dumpsys. */ void removeExactAlarmsOnPermissionRevokedLocked(int uid, String packageName) { - if (isExemptFromExactAlarmPermission(uid)) { - return; - } + Slog.w(TAG, "Package " + packageName + ", uid " + uid + " lost SCHEDULE_EXACT_ALARM!"); if (!CompatChanges.isChangeEnabled( AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, packageName, UserHandle.getUserHandleForUid(uid))) { return; } - final Predicate<Alarm> whichAlarms = - a -> (a.uid == uid && a.packageName.equals(packageName) - && a.windowLength == AlarmManager.WINDOW_EXACT); - final ArrayList<Alarm> removed = mAlarmStore.remove(whichAlarms); - final boolean didRemove = !removed.isEmpty(); + final Predicate<Alarm> whichAlarms = a -> { + if (a.uid == uid && a.packageName.equals(packageName) && a.windowLength == 0) { + return a.alarmClock != null || !isExemptFromExactAlarmPermission(uid); + } + return false; + }; + removeAlarmsInternalLocked(whichAlarms); + } + + private void removeAlarmsInternalLocked(Predicate<Alarm> whichAlarms) { + final ArrayList<Alarm> removedAlarms = mAlarmStore.remove(whichAlarms); + final boolean didRemove = !removedAlarms.isEmpty(); if (didRemove) { - decrementAlarmCount(uid, removed.size()); + for (final Alarm removed : removedAlarms) { + decrementAlarmCount(removed.uid, 1); + } } for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i--) { @@ -3119,21 +3236,22 @@ public class AlarmManagerService extends SystemService { } if (didRemove) { + boolean idleUntilUpdated = false; + if (mPendingIdleUntil != null && whichAlarms.test(mPendingIdleUntil)) { + mPendingIdleUntil = null; + idleUntilUpdated = true; + } if (mNextWakeFromIdle != null && whichAlarms.test(mNextWakeFromIdle)) { mNextWakeFromIdle = mAlarmStore.getNextWakeFromIdleAlarm(); if (mPendingIdleUntil != null) { - final boolean idleUntilUpdated = mAlarmStore.updateAlarmDeliveries(alarm -> { - if (alarm != mPendingIdleUntil) { - return false; - } - return adjustIdleUntilTime(alarm); - }); - if (idleUntilUpdated) { - mAlarmStore.updateAlarmDeliveries( - alarm -> adjustDeliveryTimeBasedOnDeviceIdle(alarm)); - } + idleUntilUpdated |= mAlarmStore.updateAlarmDeliveries(alarm -> + (alarm == mPendingIdleUntil && adjustIdleUntilTime(alarm))); } } + if (idleUntilUpdated) { + mAlarmStore.updateAlarmDeliveries( + alarm -> adjustDeliveryTimeBasedOnDeviceIdle(alarm)); + } rescheduleKernelAlarmsLocked(); updateNextAlarmClockLocked(); } @@ -3147,63 +3265,7 @@ public class AlarmManagerService extends SystemService { } return; } - - final ArrayList<Alarm> removedAlarms = mAlarmStore.remove( - a -> a.matches(operation, directReceiver)); - for (final Alarm removed : removedAlarms) { - decrementAlarmCount(removed.uid, 1); - } - final boolean didRemove = !removedAlarms.isEmpty(); - - for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i--) { - final ArrayList<Alarm> alarmsForUid = mPendingBackgroundAlarms.valueAt(i); - for (int j = alarmsForUid.size() - 1; j >= 0; j--) { - final Alarm alarm = alarmsForUid.get(j); - if (alarm.matches(operation, directReceiver)) { - // Don't set didRemove, since this doesn't impact the scheduled alarms. - alarmsForUid.remove(j); - decrementAlarmCount(alarm.uid, 1); - } - } - if (alarmsForUid.size() == 0) { - mPendingBackgroundAlarms.removeAt(i); - } - } - for (int i = mPendingNonWakeupAlarms.size() - 1; i >= 0; i--) { - final Alarm a = mPendingNonWakeupAlarms.get(i); - if (a.matches(operation, directReceiver)) { - // Don't set didRemove, since this doesn't impact the scheduled alarms. - mPendingNonWakeupAlarms.remove(i); - decrementAlarmCount(a.uid, 1); - } - } - if (didRemove) { - if (DEBUG_BATCH) { - Slog.v(TAG, "remove(operation) changed bounds; rebatching"); - } - boolean idleUntilUpdated = false; - if (mPendingIdleUntil != null && mPendingIdleUntil.matches(operation, directReceiver)) { - mPendingIdleUntil = null; - idleUntilUpdated = true; - } - if (mNextWakeFromIdle != null && mNextWakeFromIdle.matches(operation, directReceiver)) { - mNextWakeFromIdle = mAlarmStore.getNextWakeFromIdleAlarm(); - if (mPendingIdleUntil != null) { - idleUntilUpdated |= mAlarmStore.updateAlarmDeliveries(alarm -> { - if (alarm != mPendingIdleUntil) { - return false; - } - return adjustIdleUntilTime(alarm); - }); - } - } - if (idleUntilUpdated) { - mAlarmStore.updateAlarmDeliveries( - alarm -> adjustDeliveryTimeBasedOnDeviceIdle(alarm)); - } - rescheduleKernelAlarmsLocked(); - updateNextAlarmClockLocked(); - } + removeAlarmsInternalLocked(a -> a.matches(operation, directReceiver)); } void removeLocked(final int uid) { @@ -3211,55 +3273,7 @@ public class AlarmManagerService extends SystemService { // If a force-stop occurs for a system-uid package, ignore it. return; } - - final ArrayList<Alarm> removed = mAlarmStore.remove(a -> a.uid == uid); - final boolean didRemove = !removed.isEmpty(); - if (didRemove) { - decrementAlarmCount(uid, removed.size()); - } - - for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i--) { - final ArrayList<Alarm> alarmsForUid = mPendingBackgroundAlarms.valueAt(i); - for (int j = alarmsForUid.size() - 1; j >= 0; j--) { - if (alarmsForUid.get(j).uid == uid) { - alarmsForUid.remove(j); - decrementAlarmCount(uid, 1); - } - } - if (alarmsForUid.size() == 0) { - mPendingBackgroundAlarms.removeAt(i); - } - } - // If we're currently using this app's alarms to come out of doze, - // make sure to reset to any remaining WAKE_FROM_IDLE alarms. - boolean idleUntilUpdated = false; - if (mNextWakeFromIdle != null && mNextWakeFromIdle.uid == uid) { - mNextWakeFromIdle = mAlarmStore.getNextWakeFromIdleAlarm(); - if (mPendingIdleUntil != null) { - idleUntilUpdated |= mAlarmStore.updateAlarmDeliveries(alarm -> { - if (alarm != mPendingIdleUntil) { - return false; - } - return adjustIdleUntilTime(alarm); - }); - } - } - if (mPendingIdleUntil != null && mPendingIdleUntil.uid == uid) { - // Should never happen - only the system uid is allowed to set idle-until alarms - Slog.wtf(TAG, "Removed app uid " + uid + " set idle-until alarm!"); - mPendingIdleUntil = null; - idleUntilUpdated = true; - } - if (idleUntilUpdated) { - mAlarmStore.updateAlarmDeliveries(alarm -> adjustDeliveryTimeBasedOnDeviceIdle(alarm)); - } - if (didRemove) { - if (DEBUG_BATCH) { - Slog.v(TAG, "remove(uid) changed bounds; rebatching"); - } - rescheduleKernelAlarmsLocked(); - updateNextAlarmClockLocked(); - } + removeAlarmsInternalLocked(a -> a.uid == uid); } void removeLocked(final String packageName) { @@ -3270,50 +3284,7 @@ public class AlarmManagerService extends SystemService { } return; } - - final ArrayList<Alarm> removed = mAlarmStore.remove(a -> a.matches(packageName)); - final boolean didRemove = !removed.isEmpty(); - if (didRemove) { - decrementAlarmCount(removed.get(0).uid, removed.size()); - } - - for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i--) { - final ArrayList<Alarm> alarmsForUid = mPendingBackgroundAlarms.valueAt(i); - for (int j = alarmsForUid.size() - 1; j >= 0; j--) { - final Alarm alarm = alarmsForUid.get(j); - if (alarm.matches(packageName)) { - alarmsForUid.remove(j); - decrementAlarmCount(alarm.uid, 1); - } - } - if (alarmsForUid.size() == 0) { - mPendingBackgroundAlarms.removeAt(i); - } - } - // If we're currently using this app's alarms to come out of doze, - // make sure to reset to any remaining WAKE_FROM_IDLE alarms. - if (mNextWakeFromIdle != null && mNextWakeFromIdle.matches(packageName)) { - mNextWakeFromIdle = mAlarmStore.getNextWakeFromIdleAlarm(); - if (mPendingIdleUntil != null) { - final boolean updated = mAlarmStore.updateAlarmDeliveries(alarm -> { - if (alarm != mPendingIdleUntil) { - return false; - } - return adjustIdleUntilTime(alarm); - }); - if (updated) { - mAlarmStore.updateAlarmDeliveries( - alarm -> adjustDeliveryTimeBasedOnDeviceIdle(alarm)); - } - } - } - if (didRemove) { - if (DEBUG_BATCH) { - Slog.v(TAG, "remove(package) changed bounds; rebatching"); - } - rescheduleKernelAlarmsLocked(); - updateNextAlarmClockLocked(); - } + removeAlarmsInternalLocked(a -> a.matches(packageName)); } // Only called for ephemeral apps @@ -3324,28 +3295,7 @@ public class AlarmManagerService extends SystemService { } final Predicate<Alarm> whichAlarms = (a) -> (a.uid == uid && mActivityManagerInternal.isAppStartModeDisabled(uid, a.packageName)); - final ArrayList<Alarm> removed = mAlarmStore.remove(whichAlarms); - final boolean didRemove = !removed.isEmpty(); - if (didRemove) { - decrementAlarmCount(uid, removed.size()); - } - - for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i--) { - if (mPendingBackgroundAlarms.keyAt(i) == uid) { - final ArrayList<Alarm> toRemove = mPendingBackgroundAlarms.valueAt(i); - if (toRemove != null) { - decrementAlarmCount(uid, toRemove.size()); - } - mPendingBackgroundAlarms.removeAt(i); - } - } - if (didRemove) { - if (DEBUG_BATCH) { - Slog.v(TAG, "remove(package) changed bounds; rebatching"); - } - rescheduleKernelAlarmsLocked(); - updateNextAlarmClockLocked(); - } + removeAlarmsInternalLocked(whichAlarms); } void removeUserLocked(int userHandle) { @@ -3720,6 +3670,10 @@ public class AlarmManagerService extends SystemService { } } + int getCallingUid() { + return Binder.getCallingUid(); + } + long getNextAlarm(int type) { return AlarmManagerService.getNextAlarm(mNativeData, type); } @@ -3751,8 +3705,7 @@ public class AlarmManagerService extends SystemService { return pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*alarm*"); } - int getSystemUiUid() { - PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class); + int getSystemUiUid(PackageManagerInternal pm) { return pm.getPackageUid(pm.getSystemUiServiceComponent().getPackageName(), MATCH_SYSTEM_ONLY, USER_SYSTEM); } @@ -3962,6 +3915,7 @@ public class AlarmManagerService extends SystemService { public static final int CHARGING_STATUS_CHANGED = 6; public static final int REMOVE_FOR_CANCELED = 7; public static final int REMOVE_EXACT_ALARMS = 8; + public static final int EXACT_ALARM_DENY_LIST_CHANGED = 9; AlarmHandler() { super(Looper.myLooper()); @@ -4048,7 +4002,11 @@ public class AlarmManagerService extends SystemService { removeExactAlarmsOnPermissionRevokedLocked(uid, packageName); } break; - + case EXACT_ALARM_DENY_LIST_CHANGED: + synchronized (mLock) { + handlePackagesAddedToExactAlarmsDenyListLocked((ArraySet<String>) msg.obj); + } + break; default: // nope, just ignore it break; diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index a62e258153a0..d17df63c4363 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -111,6 +111,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; import com.android.internal.util.ArrayUtils; import com.android.internal.util.ConcurrentUtils; +import com.android.server.AlarmManagerInternal; import com.android.server.JobSchedulerBackgroundThread; import com.android.server.LocalServices; import com.android.server.pm.parsing.pkg.AndroidPackage; @@ -1191,6 +1192,10 @@ public class AppStandbyController if (mInjector.isWellbeingPackage(packageName)) { return STANDBY_BUCKET_WORKING_SET; } + + if (mInjector.hasScheduleExactAlarm(packageName, UserHandle.getUid(userId, appId))) { + return STANDBY_BUCKET_WORKING_SET; + } } // Check this last, as it can be the most expensive check @@ -2007,6 +2012,7 @@ public class AppStandbyController private PowerManager mPowerManager; private IDeviceIdleController mDeviceIdleController; private CrossProfileAppsInternal mCrossProfileAppsInternal; + private AlarmManagerInternal mAlarmManagerInternal; int mBootPhase; /** * The minimum amount of time required since the last user interaction before an app can be @@ -2047,6 +2053,7 @@ public class AppStandbyController mBatteryManager = mContext.getSystemService(BatteryManager.class); mCrossProfileAppsInternal = LocalServices.getService( CrossProfileAppsInternal.class); + mAlarmManagerInternal = LocalServices.getService(AlarmManagerInternal.class); final ActivityManager activityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); @@ -2110,6 +2117,10 @@ public class AppStandbyController return mWellbeingApp != null && mWellbeingApp.equals(packageName); } + boolean hasScheduleExactAlarm(String packageName, int uid) { + return mAlarmManagerInternal.hasScheduleExactAlarm(packageName, uid); + } + void updatePowerWhitelistCache() { try { // Don't call out to DeviceIdleController with the lock held. diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 9931bf9d78c2..f207c039d5a2 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -204,6 +204,10 @@ package android.app { method public void onTaskRemovalStarted(int); } + public class AlarmManager { + method public boolean hasScheduleExactAlarm(@NonNull String, int); + } + public class AppDetailsActivity extends android.app.Activity { ctor public AppDetailsActivity(); } diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index bcc22f6605a5..bba1e86c437d 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -1275,18 +1275,32 @@ <!-- Label for the settings activity for controlling apps that can schedule alarms [CHAR LIMIT=30] --> <string name="alarms_and_reminders_label">Alarms and reminders</string> <!-- Label for the switch to toggler the permission for scheduling alarms [CHAR LIMIT=50] --> - <string name="alarms_and_reminders_switch_title">Allow to set alarms or reminders</string> + <string name="alarms_and_reminders_switch_title">Allow setting alarms and reminders</string> <!-- Title for the setting screen for controlling apps that can schedule alarms [CHAR LIMIT=30] --> - <string name="alarms_and_reminders_title">Alarms and reminders</string> + <string name="alarms_and_reminders_title">Alarms & reminders</string> <!-- Description that appears below the alarms_and_reminders switch [CHAR LIMIT=NONE] --> - <string name="alarms_and_reminders_footer_title"> - Allow this app to schedule alarms or other timing based events. - This will allow the app to wake up and run even when you are not using the device. - Note that revoking this permission may cause the app to malfunction, specifically any alarms - that the app has scheduled will no longer work. + <string name="alarms_and_reminders_footer_title" product="default"> + Allow this app to set alarms and schedule other actions. + This app may be used when you\u2019re not using your phone, which may use more battery. + If this permission is off, this app may not function normally, + and its alarms won\u2019t work as scheduled. + </string> + <!-- Description that appears below the alarms_and_reminders switch [CHAR LIMIT=NONE] --> + <string name="alarms_and_reminders_footer_title" product="tablet"> + Allow this app to set alarms and schedule other actions. + This app may be used when you\u2019re not using your tablet, which may use more battery. + If this permission is off, this app may not function normally, + and its alarms won\u2019t work as scheduled. + </string> + <!-- Description that appears below the alarms_and_reminders switch [CHAR LIMIT=NONE] --> + <string name="alarms_and_reminders_footer_title" product="device"> + Allow this app to set alarms and schedule other actions. + This app may be used when you\u2019re not using your device, which may use more battery. + If this permission is off, this app may not function normally, + and its alarms won\u2019t work as scheduled. </string> <!-- Keywords for setting screen for controlling apps that can schedule alarms [CHAR LIMIT=100] --> - <string name="keywords_alarms_and_reminders">schedule, alarm, reminder, event</string> + <string name="keywords_alarms_and_reminders">schedule, alarm, reminder, clock</string> <!-- Do not disturb: Label for button in enable zen dialog that will turn on zen mode. [CHAR LIMIT=30] --> <string name="zen_mode_enable_dialog_turn_on">Turn on</string> diff --git a/services/core/java/com/android/server/AlarmManagerInternal.java b/services/core/java/com/android/server/AlarmManagerInternal.java index 0a735029eead..e5ae77a26246 100644 --- a/services/core/java/com/android/server/AlarmManagerInternal.java +++ b/services/core/java/com/android/server/AlarmManagerInternal.java @@ -24,13 +24,16 @@ public interface AlarmManagerInternal { public interface InFlightListener { /** There is now an alarm pending delivery to the given app */ void broadcastAlarmPending(int recipientUid); + /** A broadcast alarm targeted to the given app has completed delivery */ void broadcastAlarmComplete(int recipientUid); } /** Returns true if AlarmManager is delaying alarms due to device idle. */ boolean isIdling(); + public void removeAlarmsForUid(int uid); + public void registerInFlightListener(InFlightListener callback); /** @@ -38,4 +41,10 @@ public interface AlarmManagerInternal { * {@link android.app.PendingIntent#equals(java.lang.Object) PendingIntent.equals} */ void remove(PendingIntent rec); + + /** + * Returns if the given package in the given user holds + * {@link android.Manifest.permission#SCHEDULE_EXACT_ALARM} + */ + boolean hasScheduleExactAlarm(String packageName, int uid); } 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 84819619ad79..edfc21d47767 100644 --- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java @@ -28,6 +28,10 @@ import static android.app.AlarmManager.RTC; import static android.app.AlarmManager.RTC_WAKEUP; import static android.app.AlarmManager.WINDOW_EXACT; import static android.app.AlarmManager.WINDOW_HEURISTIC; +import static android.app.AppOpsManager.MODE_ALLOWED; +import static android.app.AppOpsManager.MODE_DEFAULT; +import static android.app.AppOpsManager.MODE_ERRORED; +import static android.app.AppOpsManager.MODE_IGNORED; import static android.app.AppOpsManager.OP_SCHEDULE_EXACT_ALARM; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT; @@ -49,6 +53,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; import static com.android.server.alarm.AlarmManagerService.ACTIVE_INDEX; import static com.android.server.alarm.AlarmManagerService.AlarmHandler.APP_STANDBY_BUCKET_CHANGED; import static com.android.server.alarm.AlarmManagerService.AlarmHandler.CHARGING_STATUS_CHANGED; +import static com.android.server.alarm.AlarmManagerService.AlarmHandler.EXACT_ALARM_DENY_LIST_CHANGED; import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REMOVE_EXACT_ALARMS; import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REMOVE_FOR_CANCELED; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA; @@ -57,6 +62,7 @@ import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_W import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_WINDOW; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_CRASH_NON_CLOCK_APPS; +import static com.android.server.alarm.AlarmManagerService.Constants.KEY_EXACT_ALARM_DENY_LIST; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_LAZY_BATCHING; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_LISTENER_TIMEOUT; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MAX_INTERVAL; @@ -64,6 +70,7 @@ import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_FUT import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_INTERVAL; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_WINDOW; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_PRIORITY_ALARM_DELAY; +import static com.android.server.alarm.AlarmManagerService.Constants.MAX_EXACT_ALARM_DENY_LIST_SIZE; import static com.android.server.alarm.AlarmManagerService.FREQUENT_INDEX; import static com.android.server.alarm.AlarmManagerService.INDEFINITE_DELAY; import static com.android.server.alarm.AlarmManagerService.IS_WAKEUP_MASK; @@ -87,8 +94,10 @@ import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verifyNoMoreInteractions; import android.Manifest; import android.app.ActivityManager; @@ -106,6 +115,7 @@ import android.app.usage.UsageStatsManagerInternal; import android.content.Context; import android.content.Intent; import android.content.PermissionChecker; +import android.content.pm.PackageManagerInternal; import android.os.BatteryManager; import android.os.Bundle; import android.os.Handler; @@ -113,13 +123,13 @@ import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.PowerManager; -import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; import android.provider.DeviceConfig; import android.provider.Settings; +import android.util.ArraySet; import android.util.Log; import android.util.SparseArray; @@ -176,6 +186,8 @@ public class AlarmManagerServiceTest { @Mock private IAppOpsService mIAppOpsService; @Mock + private AppOpsManager mAppOpsManager; + @Mock private DeviceIdleInternal mDeviceIdleInternal; @Mock private UsageStatsManagerInternal mUsageStatsManagerInternal; @@ -184,6 +196,8 @@ public class AlarmManagerServiceTest { @Mock private ActivityManagerInternal mActivityManagerInternal; @Mock + private PackageManagerInternal mPackageManagerInternal; + @Mock private AppStateTrackerImpl mAppStateTracker; @Mock private AlarmManagerService.ClockReceiver mClockReceiver; @@ -270,6 +284,11 @@ public class AlarmManagerServiceTest { } @Override + int getCallingUid() { + return TEST_CALLING_UID; + } + + @Override void setAlarm(int type, long millis) { mTestTimer.set(type, millis); } @@ -279,7 +298,7 @@ public class AlarmManagerServiceTest { } @Override - int getSystemUiUid() { + int getSystemUiUid(PackageManagerInternal unused) { return SYSTEM_UI_UID; } @@ -344,6 +363,8 @@ public class AlarmManagerServiceTest { () -> LocalServices.getService(DeviceIdleInternal.class)); doReturn(mActivityManagerInternal).when( () -> LocalServices.getService(ActivityManagerInternal.class)); + doReturn(mPackageManagerInternal).when( + () -> LocalServices.getService(PackageManagerInternal.class)); doReturn(mAppStateTracker).when(() -> LocalServices.getService(AppStateTracker.class)); doReturn(mAppStandbyInternal).when( () -> LocalServices.getService(AppStandbyInternal.class)); @@ -376,8 +397,10 @@ public class AlarmManagerServiceTest { () -> DeviceConfig.getProperties( eq(DeviceConfig.NAMESPACE_ALARM_MANAGER), ArgumentMatchers.<String>any())); - when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn( - mock(AppOpsManager.class)); + when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager); + + when(mPackageManagerInternal.getPackageUid(eq(TEST_CALLING_PACKAGE), anyInt(), + eq(TEST_CALLING_USER))).thenReturn(TEST_CALLING_UID); mInjector = new Injector(mMockContext); mService = new AlarmManagerService(mMockContext, mInjector); @@ -464,8 +487,15 @@ public class AlarmManagerServiceTest { private void setTestAlarm(int type, long triggerTime, long windowLength, PendingIntent operation, long interval, int flags, int callingUid, Bundle idleOptions) { + setTestAlarm(type, triggerTime, windowLength, operation, interval, flags, callingUid, + TEST_CALLING_PACKAGE, idleOptions); + } + + private void setTestAlarm(int type, long triggerTime, long windowLength, + PendingIntent operation, long interval, int flags, int callingUid, + String callingPackage, Bundle idleOptions) { mService.setImpl(type, triggerTime, windowLength, interval, operation, null, "test", flags, - null, null, callingUid, TEST_CALLING_PACKAGE, idleOptions); + null, null, callingUid, callingPackage, idleOptions); } private void setTestAlarmWithListener(int type, long triggerTime, IAlarmListener listener) { @@ -503,6 +533,12 @@ public class AlarmManagerServiceTest { mService.mConstants.onPropertiesChanged(mDeviceConfigProperties); } + private void setDeviceConfigString(String key, String val) { + mDeviceConfigKeys.add(key); + doReturn(val).when(mDeviceConfigProperties).getString(eq(key), anyString()); + mService.mConstants.onPropertiesChanged(mDeviceConfigProperties); + } + /** * Lowers quotas to make testing feasible. Careful while calling as this will replace any * existing settings for the calling test. @@ -602,6 +638,47 @@ public class AlarmManagerServiceTest { } @Test + public void updatingExactAlarmDenyList() { + ArraySet<String> denyListed = new ArraySet<>(new String[]{ + "com.example.package1", + "com.example.package2", + "com.example.package3", + }); + setDeviceConfigString(KEY_EXACT_ALARM_DENY_LIST, + "com.example.package1,com.example.package2,com.example.package3"); + assertEquals(denyListed, mService.mConstants.EXACT_ALARM_DENY_LIST); + + + denyListed = new ArraySet<>(new String[]{ + "com.example.package1", + "com.example.package4", + }); + setDeviceConfigString(KEY_EXACT_ALARM_DENY_LIST, + "com.example.package1,com.example.package4"); + assertEquals(denyListed, mService.mConstants.EXACT_ALARM_DENY_LIST); + + setDeviceConfigString(KEY_EXACT_ALARM_DENY_LIST, ""); + assertEquals(0, mService.mConstants.EXACT_ALARM_DENY_LIST.size()); + } + + @Test + public void exactAlarmDenyListMaxSize() { + final ArraySet<String> expectedSet = new ArraySet<>(); + final StringBuilder sb = new StringBuilder("package1"); + expectedSet.add("package1"); + for (int i = 2; i <= 2 * MAX_EXACT_ALARM_DENY_LIST_SIZE; i++) { + sb.append(",package"); + sb.append(i); + if (i <= MAX_EXACT_ALARM_DENY_LIST_SIZE) { + expectedSet.add("package" + i); + } + } + assertEquals(MAX_EXACT_ALARM_DENY_LIST_SIZE, expectedSet.size()); + setDeviceConfigString(KEY_EXACT_ALARM_DENY_LIST, sb.toString()); + assertEquals(expectedSet, mService.mConstants.EXACT_ALARM_DENY_LIST); + } + + @Test public void positiveWhileIdleQuotas() { setDeviceConfigInt(KEY_ALLOW_WHILE_IDLE_QUOTA, -3); assertEquals(1, mService.mConstants.ALLOW_WHILE_IDLE_QUOTA); @@ -1717,21 +1794,70 @@ public class AlarmManagerServiceTest { } @Test - public void canScheduleExactAlarms() throws RemoteException { + public void hasScheduleExactAlarmBinderCallEmptyDenyList() throws RemoteException { doReturn(PermissionChecker.PERMISSION_GRANTED).when( - () -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, - Manifest.permission.SCHEDULE_EXACT_ALARM)); - assertTrue(mBinder.canScheduleExactAlarms()); + () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), + eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), + eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); + assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when( - () -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, - Manifest.permission.SCHEDULE_EXACT_ALARM)); - assertFalse(mBinder.canScheduleExactAlarms()); + () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), + eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), + eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); + assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); + } + + @Test + public void hasScheduleExactAlarmBinderCallWithDenyList() throws RemoteException { + setDeviceConfigString(KEY_EXACT_ALARM_DENY_LIST, TEST_CALLING_PACKAGE); + + when(mAppOpsManager.checkOpNoThrow(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, + TEST_CALLING_PACKAGE)).thenReturn(MODE_ERRORED); + + assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); + verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), + eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), + eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)), never()); + + when(mAppOpsManager.checkOpNoThrow(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, + TEST_CALLING_PACKAGE)).thenReturn(MODE_DEFAULT); - doReturn(PermissionChecker.PERMISSION_SOFT_DENIED).when( - () -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, - Manifest.permission.SCHEDULE_EXACT_ALARM)); - assertFalse(mBinder.canScheduleExactAlarms()); + assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); + verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), + eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), + eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)), never()); + + when(mAppOpsManager.checkOpNoThrow(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, + TEST_CALLING_PACKAGE)).thenReturn(MODE_IGNORED); + + assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); + verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), + eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), + eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)), never()); + + when(mAppOpsManager.checkOpNoThrow(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, + TEST_CALLING_PACKAGE)).thenReturn(MODE_ALLOWED); + + doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when( + () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), + eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), + eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); + + assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); + verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), + eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), + eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); + + doReturn(PermissionChecker.PERMISSION_GRANTED).when( + () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), + eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), + eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); + + assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); + verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), + eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), + eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)), times(2)); } @Test @@ -1757,8 +1883,9 @@ public class AlarmManagerServiceTest { mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_HEURISTIC, 0, FLAG_ALLOW_WHILE_IDLE, getNewMockPendingIntent(), null, null, null, null); - verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, - Manifest.permission.SCHEDULE_EXACT_ALARM), never()); + verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), + eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), + eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)), never()); verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt()); } @@ -1775,7 +1902,7 @@ public class AlarmManagerServiceTest { verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L), eq(alarmPi), isNull(), isNull(), eq(FLAG_STANDALONE), isNull(), isNull(), - eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), isNull()); + eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE), isNull()); } @Test @@ -1792,7 +1919,7 @@ public class AlarmManagerServiceTest { verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L), eq(alarmPi), isNull(), isNull(), eq(FLAG_ALLOW_WHILE_IDLE_COMPAT | FLAG_STANDALONE), isNull(), isNull(), - eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture()); + eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture()); final BroadcastOptions idleOptions = new BroadcastOptions(bundleCaptor.getValue()); final int type = idleOptions.getTemporaryAppAllowlistType(); @@ -1812,7 +1939,7 @@ public class AlarmManagerServiceTest { final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), anyLong(), eq(0L), eq(alarmPi), isNull(), isNull(), eq(FLAG_ALLOW_WHILE_IDLE_COMPAT), isNull(), - isNull(), eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture()); + isNull(), eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture()); final BroadcastOptions idleOptions = new BroadcastOptions(bundleCaptor.getValue()); final int type = idleOptions.getTemporaryAppAllowlistType(); @@ -1832,7 +1959,7 @@ public class AlarmManagerServiceTest { verify(mService).setImpl(eq(RTC_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L), eq(alarmPi), isNull(), isNull(), eq(FLAG_STANDALONE | FLAG_WAKE_FROM_IDLE), - isNull(), eq(alarmClock), eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), isNull()); + isNull(), eq(alarmClock), eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE), isNull()); } @Test @@ -1842,8 +1969,9 @@ public class AlarmManagerServiceTest { anyString(), any(UserHandle.class))); doReturn(PermissionChecker.PERMISSION_GRANTED).when( - () -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, - Manifest.permission.SCHEDULE_EXACT_ALARM)); + () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), + eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), + eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); final PendingIntent alarmPi = getNewMockPendingIntent(); final AlarmManager.AlarmClockInfo alarmClock = mock(AlarmManager.AlarmClockInfo.class); @@ -1851,14 +1979,15 @@ public class AlarmManagerServiceTest { alarmPi, null, null, null, alarmClock); // Correct permission checks are invoked. - verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, - Manifest.permission.SCHEDULE_EXACT_ALARM)); + verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), + eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), + eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt()); final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); verify(mService).setImpl(eq(RTC_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L), eq(alarmPi), isNull(), isNull(), eq(FLAG_STANDALONE | FLAG_WAKE_FROM_IDLE), - isNull(), eq(alarmClock), eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), + isNull(), eq(alarmClock), eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture()); final BroadcastOptions idleOptions = new BroadcastOptions(bundleCaptor.getValue()); @@ -1874,8 +2003,9 @@ public class AlarmManagerServiceTest { anyString(), any(UserHandle.class))); doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when( - () -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, - Manifest.permission.SCHEDULE_EXACT_ALARM)); + () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), + eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), + eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true); final PendingIntent alarmPi = getNewMockPendingIntent(); @@ -1887,9 +2017,9 @@ public class AlarmManagerServiceTest { } catch (SecurityException se) { // Expected. } - - verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, - Manifest.permission.SCHEDULE_EXACT_ALARM)); + verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), + eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), + eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt()); } @@ -1904,15 +2034,16 @@ public class AlarmManagerServiceTest { mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0, 0, alarmPi, null, null, null, null); - verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, - Manifest.permission.SCHEDULE_EXACT_ALARM)); + verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), + eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), + eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt()); final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L), eq(alarmPi), isNull(), isNull(), eq(FLAG_STANDALONE), isNull(), isNull(), - eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture()); + eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture()); final BroadcastOptions idleOptions = new BroadcastOptions(bundleCaptor.getValue()); final int type = idleOptions.getTemporaryAppAllowlistType(); @@ -1926,24 +2057,24 @@ public class AlarmManagerServiceTest { anyString(), any(UserHandle.class))); // If permission is denied, only then allowlist will be checked. doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when( - () -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, - Manifest.permission.SCHEDULE_EXACT_ALARM)); + () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), + eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), + eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true); final PendingIntent alarmPi = getNewMockPendingIntent(); mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0, 0, alarmPi, null, null, null, null); - verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, - Manifest.permission.SCHEDULE_EXACT_ALARM)); - verify(mDeviceIdleInternal).isAppOnWhitelist(UserHandle.getAppId(Process.myUid())); + verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), + eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), + eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); + verify(mDeviceIdleInternal).isAppOnWhitelist(UserHandle.getAppId(TEST_CALLING_UID)); - final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L), eq(alarmPi), isNull(), isNull(), eq(FLAG_STANDALONE), isNull(), isNull(), - eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture()); - System.out.println("what got captured: " + bundleCaptor.getValue()); + eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE), isNull()); } @Test @@ -1957,15 +2088,16 @@ public class AlarmManagerServiceTest { mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0, FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null); - verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, - Manifest.permission.SCHEDULE_EXACT_ALARM)); + verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), + eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), + eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt()); final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L), eq(alarmPi), isNull(), isNull(), eq(FLAG_ALLOW_WHILE_IDLE | FLAG_STANDALONE), isNull(), isNull(), - eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture()); + eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture()); final BroadcastOptions idleOptions = new BroadcastOptions(bundleCaptor.getValue()); final int type = idleOptions.getTemporaryAppAllowlistType(); @@ -1979,23 +2111,25 @@ public class AlarmManagerServiceTest { anyString(), any(UserHandle.class))); // If permission is denied, only then allowlist will be checked. doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when( - () -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, - Manifest.permission.SCHEDULE_EXACT_ALARM)); + () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), + eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), + eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true); 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); - verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, - Manifest.permission.SCHEDULE_EXACT_ALARM)); - verify(mDeviceIdleInternal).isAppOnWhitelist(UserHandle.getAppId(Process.myUid())); + verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), + eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), + eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); + verify(mDeviceIdleInternal).isAppOnWhitelist(UserHandle.getAppId(TEST_CALLING_UID)); final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L), eq(alarmPi), isNull(), isNull(), eq(FLAG_ALLOW_WHILE_IDLE_COMPAT | FLAG_STANDALONE), isNull(), isNull(), - eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture()); + eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture()); final BroadcastOptions idleOptions = new BroadcastOptions(bundleCaptor.getValue()); final int type = idleOptions.getTemporaryAppAllowlistType(); @@ -2011,8 +2145,9 @@ public class AlarmManagerServiceTest { anyString(), any(UserHandle.class))); doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when( - () -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, - Manifest.permission.SCHEDULE_EXACT_ALARM)); + () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), + eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), + eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(false); final PendingIntent alarmPi = getNewMockPendingIntent(); @@ -2030,8 +2165,9 @@ public class AlarmManagerServiceTest { } catch (SecurityException se) { // Expected. } - verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, - Manifest.permission.SCHEDULE_EXACT_ALARM), times(2)); + verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), + eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), + eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)), times(2)); verify(mDeviceIdleInternal, times(2)).isAppOnWhitelist(anyInt()); } @@ -2047,14 +2183,15 @@ public class AlarmManagerServiceTest { mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 4321, WINDOW_HEURISTIC, 0, FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null); - verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, - Manifest.permission.SCHEDULE_EXACT_ALARM), never()); + verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), + eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), + eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)), never()); verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt()); final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(4321L), anyLong(), eq(0L), eq(alarmPi), isNull(), isNull(), eq(FLAG_ALLOW_WHILE_IDLE_COMPAT), isNull(), - isNull(), eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture()); + isNull(), eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture()); final BroadcastOptions idleOptions = new BroadcastOptions(bundleCaptor.getValue()); final int type = idleOptions.getTemporaryAppAllowlistType(); @@ -2068,10 +2205,11 @@ public class AlarmManagerServiceTest { anyString(), any(UserHandle.class))); doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when( - () -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, - Manifest.permission.SCHEDULE_EXACT_ALARM)); + () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), + eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), + eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true); - when(mAppStateTracker.isUidPowerSaveUserExempt(Process.myUid())).thenReturn(true); + when(mAppStateTracker.isUidPowerSaveUserExempt(TEST_CALLING_UID)).thenReturn(true); final PendingIntent alarmPi = getNewMockPendingIntent(); mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0, @@ -2081,7 +2219,7 @@ public class AlarmManagerServiceTest { verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L), eq(alarmPi), isNull(), isNull(), eq(FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED | FLAG_STANDALONE), isNull(), isNull(), - eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), isNull()); + eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE), isNull()); } @Test @@ -2101,9 +2239,79 @@ public class AlarmManagerServiceTest { } @Test + public void denyListPackagesAdded() { + mService.mConstants.EXACT_ALARM_DENY_LIST = new ArraySet<>(new String[]{"p1", "p2", "p3"}); + setDeviceConfigString(KEY_EXACT_ALARM_DENY_LIST, "p2,p4,p5"); + assertAndHandleMessageSync(EXACT_ALARM_DENY_LIST_CHANGED); + + ArraySet<String> added = new ArraySet<>(new String[]{"p4", "p5"}); + verify(mService).handlePackagesAddedToExactAlarmsDenyListLocked(eq(added)); + } + + @Test + public void denyListPackagesRemoved() { + clearInvocations(mService.mHandler); + mService.mConstants.EXACT_ALARM_DENY_LIST = new ArraySet<>(new String[]{"p1", "p2", "p3"}); + setDeviceConfigString(KEY_EXACT_ALARM_DENY_LIST, "p2"); + verifyNoMoreInteractions(mService.mHandler); + } + + @Test + public void removeExactAlarmsOnPackageAddedToDenyList() { + doReturn(true).when( + () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), + anyString(), any(UserHandle.class))); + + // basic exact alarm + setTestAlarm(ELAPSED_REALTIME, 1, 0, getNewMockPendingIntent(), 0, 0, TEST_CALLING_UID, + null); + // exact and allow-while-idle alarm + setTestAlarm(ELAPSED_REALTIME, 2, 0, getNewMockPendingIntent(), 0, FLAG_ALLOW_WHILE_IDLE, + TEST_CALLING_UID, null); + // alarm clock + setWakeFromIdle(RTC_WAKEUP, 3, getNewMockPendingIntent()); + + final PendingIntent inexact = getNewMockPendingIntent(); + setTestAlarm(ELAPSED_REALTIME, 4, 10, inexact, 0, 0, TEST_CALLING_UID, null); + + final PendingIntent inexactAwi = getNewMockPendingIntent(); + setTestAlarm(ELAPSED_REALTIME, 5, 10, inexactAwi, 0, FLAG_ALLOW_WHILE_IDLE, + TEST_CALLING_UID, null); + + final String differentPackage = "different.package"; + final PendingIntent exactButDifferentPackage = getNewMockPendingIntent( + TEST_CALLING_UID, differentPackage); + setTestAlarm(ELAPSED_REALTIME, 6, 0, exactButDifferentPackage, 0, 0, + TEST_CALLING_UID, differentPackage, null); + assertEquals(6, mService.mAlarmStore.size()); + + when(mAppOpsManager.checkOpNoThrow(eq(OP_SCHEDULE_EXACT_ALARM), anyInt(), + anyString())).thenReturn(MODE_DEFAULT); + setDeviceConfigString(KEY_EXACT_ALARM_DENY_LIST, TEST_CALLING_PACKAGE); + assertAndHandleMessageSync(EXACT_ALARM_DENY_LIST_CHANGED); + verify(mService).handlePackagesAddedToExactAlarmsDenyListLocked( + argThat(set -> (set.size() == 1 && set.contains(TEST_CALLING_PACKAGE)))); + + final ArrayList<Alarm> remaining = mService.mAlarmStore.asList(); + assertEquals(3, remaining.size()); + assertTrue("Basic inexact alarm removed", + remaining.removeIf(a -> a.matches(inexact, null))); + assertTrue("Inexact allow-while-idle alarm removed", + remaining.removeIf(a -> a.matches(inexactAwi, null))); + assertTrue("Alarm from different package removed", + remaining.removeIf(a -> a.matches(exactButDifferentPackage, null))); + + // Mock should return false by default. + verify(mDeviceIdleInternal, atLeastOnce()).isAppOnWhitelist( + UserHandle.getAppId(TEST_CALLING_UID)); + } + + @Test public void opScheduleExactAlarmRevoked() throws Exception { - when(mIAppOpsService.checkOperation(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, - TEST_CALLING_PACKAGE)).thenReturn(AppOpsManager.MODE_ERRORED); + doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when( + () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), + eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), + eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); mIAppOpsCallback.opChanged(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, TEST_CALLING_PACKAGE); assertAndHandleMessageSync(REMOVE_EXACT_ALARMS); verify(mService).removeExactAlarmsOnPermissionRevokedLocked(TEST_CALLING_UID, @@ -2149,7 +2357,8 @@ public class AlarmManagerServiceTest { remaining.removeIf(a -> a.matches(exactButDifferentUid, null))); // Mock should return false by default. - verify(mDeviceIdleInternal).isAppOnWhitelist(UserHandle.getAppId(TEST_CALLING_UID)); + verify(mDeviceIdleInternal, atLeastOnce()).isAppOnWhitelist( + UserHandle.getAppId(TEST_CALLING_UID)); } @Test diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index 8991e9fedadf..f9c8a23bdfae 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -81,6 +81,7 @@ import android.os.UserHandle; import android.platform.test.annotations.Presubmit; import android.provider.DeviceConfig; import android.util.ArraySet; +import android.util.Pair; import android.view.Display; import androidx.test.InstrumentationRegistry; @@ -114,8 +115,10 @@ import java.util.concurrent.TimeUnit; @SmallTest public class AppStandbyControllerTests { - private static final String PACKAGE_1 = "com.example.foo"; + private static final String PACKAGE_1 = "com.example.foo.1"; private static final int UID_1 = 10000; + private static final String PACKAGE_2 = "com.example.foo.2"; + private static final int UID_2 = 20000; private static final String PACKAGE_EXEMPTED_1 = "com.android.exempted"; private static final int UID_EXEMPTED_1 = 10001; private static final String PACKAGE_SYSTEM_HEADFULL = "com.example.system.headfull"; @@ -196,6 +199,7 @@ public class AppStandbyControllerTests { int[] mRunningUsers = new int[] {USER_ID}; List<UserHandle> mCrossProfileTargets = Collections.emptyList(); boolean mDeviceIdleMode = false; + Set<Pair<String, Integer>> mClockApps = new ArraySet<>(); DeviceConfig.Properties.Builder mSettingsBuilder = new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_APP_STANDBY) .setLong("screen_threshold_active", 0) @@ -254,6 +258,11 @@ public class AppStandbyControllerTests { } @Override + boolean hasScheduleExactAlarm(String packageName, int uid) { + return mClockApps.contains(Pair.create(packageName, uid)); + } + + @Override void updatePowerWhitelistCache() { } @@ -349,6 +358,12 @@ public class AppStandbyControllerTests { pi.packageName = PACKAGE_1; packages.add(pi); + PackageInfo pInfo = new PackageInfo(); + pInfo.applicationInfo = new ApplicationInfo(); + pInfo.applicationInfo.uid = UID_2; + pInfo.packageName = PACKAGE_2; + packages.add(pInfo); + PackageInfo pie = new PackageInfo(); pie.applicationInfo = new ApplicationInfo(); pie.applicationInfo.uid = UID_EXEMPTED_1; @@ -1662,6 +1677,29 @@ public class AppStandbyControllerTests { } @Test + public void testClockAppElevated() throws Exception { + mInjector.mClockApps.add(Pair.create(PACKAGE_1, UID_1)); + + reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1); + assertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1); + + reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_2); + assertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_2); + + mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD; + + // Make sure a clock app does not get lowered below WORKING_SET. + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, + REASON_MAIN_TIMEOUT); + assertBucket(STANDBY_BUCKET_WORKING_SET, PACKAGE_1); + + // A non clock app should be able to fall lower than WORKING_SET. + mController.setAppStandbyBucket(PACKAGE_2, USER_ID, STANDBY_BUCKET_RARE, + REASON_MAIN_TIMEOUT); + assertBucket(STANDBY_BUCKET_RARE, PACKAGE_2); + } + + @Test public void testChangingSettings_ElapsedThreshold_Invalid() { mInjector.mSettingsBuilder .setLong("elapsed_threshold_active", -1) |