Merge "More changes for the exact alarm permission" into sc-dev

This commit is contained in:
Suprabh Shukla
2021-04-09 22:27:50 +00:00
committed by Android (Google) Code Review
9 changed files with 549 additions and 286 deletions

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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();
}
@@ -2028,6 +2076,11 @@ public class AlarmManagerService extends SystemService {
mHandler.obtainMessage(AlarmHandler.REMOVE_FOR_CANCELED, pi).sendToTarget();
}
@Override
public boolean hasScheduleExactAlarm(String packageName, int uid) {
return hasScheduleExactAlarmInternal(packageName, uid);
}
@Override
public void registerInFlightListener(InFlightListener callback) {
synchronized (mLock) {
@@ -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
@@ -3071,28 +3152,64 @@ 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;

View File

@@ -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.

View File

@@ -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();
}

View File

@@ -1277,18 +1277,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 &amp; 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>

View File

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

View File

@@ -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;
@@ -269,6 +283,11 @@ public class AlarmManagerServiceTest {
// Do nothing.
}
@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.
@@ -601,6 +637,47 @@ public class AlarmManagerServiceTest {
assertEquals(55, mService.mConstants.PRIORITY_ALARM_DELAY);
}
@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);
@@ -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));
}
doReturn(PermissionChecker.PERMISSION_SOFT_DENIED).when(
() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
Manifest.permission.SCHEDULE_EXACT_ALARM));
assertFalse(mBinder.canScheduleExactAlarms());
@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);
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
@@ -2100,10 +2238,80 @@ 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

View File

@@ -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)
@@ -253,6 +257,11 @@ public class AppStandbyControllerTests {
return PACKAGE_WELLBEING.equals(packageName);
}
@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;
@@ -1661,6 +1676,29 @@ public class AppStandbyControllerTests {
assertBucket(STANDBY_BUCKET_RARE, PACKAGE_1);
}
@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