Handle exact alarm permission state changes
The permission SCHEDULE_EXACT_ALARM state changes at the following boundaries: 1. App-op: This gets toggled by the user via Settings. 2. Deny-list: Packages can be added to the deny list via DeviceConfig APIs. 3. Package changes: A package's manifest may changes when it gets updated. In both cases 1 and 2, if alarm manager detects a permission change from revoked to granted, it sends the ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED broadcast to the app. If it detects a permission change from granted to revoked, it kills all the processes within the hosting uid. In all three cases, when the permssion changes from granted to revoked, all the exact alarms scheduled by the relevant package are removed. Package updates are treated differently as they require processes to restart anyway. The broadcast is not needed in this case as there are no alarms expected to have been lost by a previous revocation, and apps can always use ACTION_MY_PACKAGE_REPLACED for post-update setup as usual. All this only applies to packages that have the change REQUIRE_EXACT_ALARM_PERMISSION enabled. Also changed canScheduleExactAlarms to return false if the change is not enabled for the calling package. Test: atest FrameworksMockingServicesTests:AlarmManagerServiceTest atest CtsAlarmManagerTestCases Bug: 179541791 Bug: 187206399 Change-Id: Icd68275701f2804be65b3a10a7dd81bbf6e2a0bb
This commit is contained in:
@ -136,6 +136,8 @@ import com.android.server.JobSchedulerBackgroundThread;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.SystemService;
|
||||
import com.android.server.SystemServiceManager;
|
||||
import com.android.server.pm.parsing.pkg.AndroidPackage;
|
||||
import com.android.server.pm.permission.PermissionManagerService;
|
||||
import com.android.server.pm.permission.PermissionManagerServiceInternal;
|
||||
import com.android.server.usage.AppStandbyInternal;
|
||||
import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
|
||||
@ -215,9 +217,18 @@ public class AlarmManagerService extends SystemService {
|
||||
|
||||
final Object mLock = new Object();
|
||||
|
||||
/** Immutable set of app ids that have requested SCHEDULE_EXACT_ALARM permission.*/
|
||||
/** Immutable set of app ids requesting {@link Manifest.permission#SCHEDULE_EXACT_ALARM} */
|
||||
@VisibleForTesting
|
||||
volatile Set<Integer> mExactAlarmCandidates = Collections.emptySet();
|
||||
|
||||
/**
|
||||
* A map from uid to the last op-mode we have seen for
|
||||
* {@link AppOpsManager#OP_SCHEDULE_EXACT_ALARM}
|
||||
*/
|
||||
@VisibleForTesting
|
||||
@GuardedBy("mLock")
|
||||
SparseIntArray mLastOpScheduleExactAlarm = new SparseIntArray();
|
||||
|
||||
// List of alarms per uid deferred due to user applied background restrictions on the source app
|
||||
SparseArray<ArrayList<Alarm>> mPendingBackgroundAlarms = new SparseArray<>();
|
||||
private long mNextWakeup;
|
||||
@ -522,6 +533,9 @@ public class AlarmManagerService extends SystemService {
|
||||
static final String KEY_MIN_DEVICE_IDLE_FUZZ = "min_device_idle_fuzz";
|
||||
@VisibleForTesting
|
||||
static final String KEY_MAX_DEVICE_IDLE_FUZZ = "max_device_idle_fuzz";
|
||||
@VisibleForTesting
|
||||
static final String KEY_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED =
|
||||
"kill_on_schedule_exact_alarm_revoked";
|
||||
|
||||
private static final long DEFAULT_MIN_FUTURITY = 5 * 1000;
|
||||
private static final long DEFAULT_MIN_INTERVAL = 60 * 1000;
|
||||
@ -564,6 +578,8 @@ public class AlarmManagerService extends SystemService {
|
||||
private static final long DEFAULT_MIN_DEVICE_IDLE_FUZZ = 2 * 60_000;
|
||||
private static final long DEFAULT_MAX_DEVICE_IDLE_FUZZ = 15 * 60_000;
|
||||
|
||||
private static final boolean DEFAULT_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED = true;
|
||||
|
||||
// Minimum futurity of a new alarm
|
||||
public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY;
|
||||
|
||||
@ -644,6 +660,13 @@ public class AlarmManagerService extends SystemService {
|
||||
*/
|
||||
public long MAX_DEVICE_IDLE_FUZZ = DEFAULT_MAX_DEVICE_IDLE_FUZZ;
|
||||
|
||||
/**
|
||||
* Whether or not to kill app when the permission
|
||||
* {@link Manifest.permission#SCHEDULE_EXACT_ALARM} is revoked.
|
||||
*/
|
||||
public boolean KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED =
|
||||
DEFAULT_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED;
|
||||
|
||||
private long mLastAllowWhileIdleWhitelistDuration = -1;
|
||||
private int mVersion = 0;
|
||||
|
||||
@ -816,6 +839,11 @@ public class AlarmManagerService extends SystemService {
|
||||
deviceIdleFuzzBoundariesUpdated = true;
|
||||
}
|
||||
break;
|
||||
case KEY_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED:
|
||||
KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED = properties.getBoolean(
|
||||
KEY_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED,
|
||||
DEFAULT_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED);
|
||||
break;
|
||||
default:
|
||||
if (name.startsWith(KEY_PREFIX_STANDBY_QUOTA) && !standbyQuotaUpdated) {
|
||||
// The quotas need to be updated in order, so we can't just rely
|
||||
@ -830,17 +858,24 @@ public class AlarmManagerService extends SystemService {
|
||||
}
|
||||
|
||||
private void updateExactAlarmDenyList(String[] newDenyList) {
|
||||
final Set<String> newSet = Collections.unmodifiableSet(new ArraySet<>(newDenyList));
|
||||
final Set<String> removed = new ArraySet<>(EXACT_ALARM_DENY_LIST);
|
||||
final Set<String> added = new ArraySet<>(newDenyList);
|
||||
|
||||
added.removeAll(EXACT_ALARM_DENY_LIST);
|
||||
removed.removeAll(newSet);
|
||||
if (added.size() > 0) {
|
||||
mHandler.obtainMessage(AlarmHandler.EXACT_ALARM_DENY_LIST_PACKAGES_ADDED, added)
|
||||
.sendToTarget();
|
||||
}
|
||||
if (removed.size() > 0) {
|
||||
mHandler.obtainMessage(AlarmHandler.EXACT_ALARM_DENY_LIST_PACKAGES_REMOVED, removed)
|
||||
.sendToTarget();
|
||||
}
|
||||
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();
|
||||
}
|
||||
EXACT_ALARM_DENY_LIST = newSet;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1007,6 +1042,20 @@ public class AlarmManagerService extends SystemService {
|
||||
pw.print(KEY_EXACT_ALARM_DENY_LIST, EXACT_ALARM_DENY_LIST);
|
||||
pw.println();
|
||||
|
||||
pw.print(KEY_MIN_DEVICE_IDLE_FUZZ);
|
||||
pw.print("=");
|
||||
TimeUtils.formatDuration(MIN_DEVICE_IDLE_FUZZ, pw);
|
||||
pw.println();
|
||||
|
||||
pw.print(KEY_MAX_DEVICE_IDLE_FUZZ);
|
||||
pw.print("=");
|
||||
TimeUtils.formatDuration(MAX_DEVICE_IDLE_FUZZ, pw);
|
||||
pw.println();
|
||||
|
||||
pw.print(KEY_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED,
|
||||
KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED);
|
||||
pw.println();
|
||||
|
||||
pw.decreaseIndent();
|
||||
}
|
||||
|
||||
@ -1667,16 +1716,57 @@ public class AlarmManagerService extends SystemService {
|
||||
void refreshExactAlarmCandidates() {
|
||||
final String[] candidates = mLocalPermissionManager.getAppOpPermissionPackages(
|
||||
Manifest.permission.SCHEDULE_EXACT_ALARM);
|
||||
final Set<Integer> appIds = new ArraySet<>(candidates.length);
|
||||
final Set<Integer> newAppIds = new ArraySet<>(candidates.length);
|
||||
for (final String candidate : candidates) {
|
||||
final int uid = mPackageManagerInternal.getPackageUid(candidate,
|
||||
PackageManager.MATCH_ANY_USER, USER_SYSTEM);
|
||||
if (uid > 0) {
|
||||
appIds.add(UserHandle.getAppId(uid));
|
||||
newAppIds.add(UserHandle.getAppId(uid));
|
||||
}
|
||||
}
|
||||
final ArraySet<Integer> removed = new ArraySet<>(mExactAlarmCandidates);
|
||||
removed.removeAll(newAppIds);
|
||||
// This code is only called on package_added and boot. The set {removed} is only expected to
|
||||
// be non-empty when a package was updated and it removed the permission from its manifest.
|
||||
for (int i = 0; i < removed.size(); i++) {
|
||||
final int removedAppId = removed.valueAt(i);
|
||||
synchronized (mLock) {
|
||||
Slog.i(TAG, "App id " + removedAppId + " lost SCHEDULE_EXACT_ALARM on update");
|
||||
|
||||
final Predicate<Alarm> whichAlarms = a -> {
|
||||
if (UserHandle.getAppId(a.uid) != removedAppId || a.windowLength != 0) {
|
||||
return false;
|
||||
}
|
||||
if (!isExactAlarmChangeEnabled(a.packageName, UserHandle.getUserId(a.uid))) {
|
||||
return false;
|
||||
}
|
||||
return a.alarmClock != null || !isExemptFromExactAlarmPermission(a.uid);
|
||||
};
|
||||
removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_EXACT_PERMISSION_REVOKED);
|
||||
}
|
||||
}
|
||||
// No need to lock. Assignment is always atomic.
|
||||
mExactAlarmCandidates = Collections.unmodifiableSet(appIds);
|
||||
mExactAlarmCandidates = Collections.unmodifiableSet(newAppIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUserStarting(TargetUser user) {
|
||||
super.onUserStarting(user);
|
||||
final int userId = user.getUserIdentifier();
|
||||
mHandler.post(() -> {
|
||||
for (final int appId : mExactAlarmCandidates) {
|
||||
final int uid = UserHandle.getUid(userId, appId);
|
||||
final AndroidPackage androidPackage = mPackageManagerInternal.getPackage(uid);
|
||||
// It will be null if it is not installed on the starting user.
|
||||
if (androidPackage != null) {
|
||||
final int mode = mAppOps.checkOpNoThrow(AppOpsManager.OP_SCHEDULE_EXACT_ALARM,
|
||||
uid, androidPackage.getPackageName());
|
||||
synchronized (mLock) {
|
||||
mLastOpScheduleExactAlarm.put(uid, mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1706,17 +1796,44 @@ public class AlarmManagerService extends SystemService {
|
||||
@Override
|
||||
public void opChanged(int op, int uid, String packageName)
|
||||
throws RemoteException {
|
||||
if (op != AppOpsManager.OP_SCHEDULE_EXACT_ALARM) {
|
||||
final int userId = UserHandle.getUserId(uid);
|
||||
if (op != AppOpsManager.OP_SCHEDULE_EXACT_ALARM
|
||||
|| !isExactAlarmChangeEnabled(packageName, userId)) {
|
||||
return;
|
||||
}
|
||||
if (!hasScheduleExactAlarmInternal(packageName, uid)) {
|
||||
|
||||
final boolean requested = mExactAlarmCandidates.contains(
|
||||
UserHandle.getAppId(uid));
|
||||
final boolean denyListed =
|
||||
mConstants.EXACT_ALARM_DENY_LIST.contains(packageName);
|
||||
|
||||
final int newMode = mAppOps.checkOpNoThrow(
|
||||
AppOpsManager.OP_SCHEDULE_EXACT_ALARM, uid, packageName);
|
||||
|
||||
final int oldMode;
|
||||
synchronized (mLock) {
|
||||
final int index = mLastOpScheduleExactAlarm.indexOfKey(uid);
|
||||
if (index < 0) {
|
||||
oldMode = AppOpsManager.opToDefaultMode(
|
||||
AppOpsManager.OP_SCHEDULE_EXACT_ALARM);
|
||||
mLastOpScheduleExactAlarm.put(uid, newMode);
|
||||
} else {
|
||||
oldMode = mLastOpScheduleExactAlarm.valueAt(index);
|
||||
mLastOpScheduleExactAlarm.setValueAt(index, newMode);
|
||||
}
|
||||
}
|
||||
|
||||
final boolean hadPermission = getScheduleExactAlarmState(requested,
|
||||
denyListed, oldMode);
|
||||
final boolean hasPermission = getScheduleExactAlarmState(requested,
|
||||
denyListed, newMode);
|
||||
|
||||
if (hadPermission && !hasPermission) {
|
||||
mHandler.obtainMessage(AlarmHandler.REMOVE_EXACT_ALARMS,
|
||||
uid, 0, packageName).sendToTarget();
|
||||
} else {
|
||||
// TODO(b/187206399) Make sure this won't be sent, if the app
|
||||
// already had the appop previously.
|
||||
} else if (!hadPermission && hasPermission) {
|
||||
sendScheduleExactAlarmPermissionStateChangedBroadcast(
|
||||
packageName, UserHandle.getUserId(uid));
|
||||
packageName, userId);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -2256,12 +2373,28 @@ public class AlarmManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean getScheduleExactAlarmState(boolean requested, boolean denyListed,
|
||||
int appOpMode) {
|
||||
if (!requested) {
|
||||
return false;
|
||||
}
|
||||
if (appOpMode == AppOpsManager.MODE_DEFAULT) {
|
||||
return !denyListed;
|
||||
}
|
||||
return appOpMode == AppOpsManager.MODE_ALLOWED;
|
||||
}
|
||||
|
||||
boolean hasScheduleExactAlarmInternal(String packageName, int uid) {
|
||||
// Not using getScheduleExactAlarmState as this can avoid some calls to AppOpsService.
|
||||
// Not using #mLastOpScheduleExactAlarm as it may contain stale values.
|
||||
// No locking needed as all internal containers being queried are immutable.
|
||||
|
||||
final long start = mStatLogger.getTime();
|
||||
final boolean hasPermission;
|
||||
// No locking needed as all internal containers being queried are immutable.
|
||||
if (!mExactAlarmCandidates.contains(UserHandle.getAppId(uid))) {
|
||||
hasPermission = false;
|
||||
} else if (!isExactAlarmChangeEnabled(packageName, UserHandle.getUserId(uid))) {
|
||||
hasPermission = false;
|
||||
} else {
|
||||
final int mode = mAppOps.checkOpNoThrow(AppOpsManager.OP_SCHEDULE_EXACT_ALARM, uid,
|
||||
packageName);
|
||||
@ -2368,8 +2501,7 @@ public class AlarmManagerService extends SystemService {
|
||||
} else if (exact || allowWhileIdle) {
|
||||
final boolean needsPermission;
|
||||
boolean lowerQuota;
|
||||
if (CompatChanges.isChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION,
|
||||
callingPackage, UserHandle.of(callingUserId))) {
|
||||
if (isExactAlarmChangeEnabled(callingPackage, callingUserId)) {
|
||||
needsPermission = exact;
|
||||
lowerQuota = !exact;
|
||||
idleOptions = exact ? mOptsWithFgs.toBundle() : mOptsWithoutFgs.toBundle();
|
||||
@ -2524,6 +2656,11 @@ public class AlarmManagerService extends SystemService {
|
||||
}
|
||||
};
|
||||
|
||||
private static boolean isExactAlarmChangeEnabled(String packageName, int userId) {
|
||||
return CompatChanges.isChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION,
|
||||
packageName, UserHandle.of(userId));
|
||||
}
|
||||
|
||||
void dumpImpl(IndentingPrintWriter pw) {
|
||||
synchronized (mLock) {
|
||||
pw.println("Current Alarm Manager state:");
|
||||
@ -2672,6 +2809,17 @@ public class AlarmManagerService extends SystemService {
|
||||
pw.println();
|
||||
pw.println("App ids requesting SCHEDULE_EXACT_ALARM: " + mExactAlarmCandidates);
|
||||
|
||||
pw.println();
|
||||
pw.print("Last OP_SCHEDULE_EXACT_ALARM: [");
|
||||
for (int i = 0; i < mLastOpScheduleExactAlarm.size(); i++) {
|
||||
if (i > 0) {
|
||||
pw.print(", ");
|
||||
}
|
||||
UserHandle.formatUid(pw, mLastOpScheduleExactAlarm.keyAt(i));
|
||||
pw.print(":" + AppOpsManager.modeToName(mLastOpScheduleExactAlarm.valueAt(i)));
|
||||
}
|
||||
pw.println("]");
|
||||
|
||||
pw.println();
|
||||
pw.println("Next alarm clock information: ");
|
||||
pw.increaseIndent();
|
||||
@ -3362,30 +3510,58 @@ 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.
|
||||
* Called when the {@link Constants#EXACT_ALARM_DENY_LIST}, changes with the packages that
|
||||
* either got added or deleted.
|
||||
* These packages may lose or gain the SCHEDULE_EXACT_ALARM 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.
|
||||
* Note that these packages don't need to be installed on the device, but if they are and they
|
||||
* do undergo a permission change, we will handle them appropriately.
|
||||
*
|
||||
* This should not be called with the lock held as it calls out to other services.
|
||||
* 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;
|
||||
void handleChangesToExactAlarmDenyList(ArraySet<String> changedPackages, boolean added) {
|
||||
Slog.w(TAG, "Packages " + changedPackages + (added ? " added to" : " removed from")
|
||||
+ " the exact alarm deny list.");
|
||||
|
||||
final int[] startedUserIds = mActivityManagerInternal.getStartedUserIds();
|
||||
|
||||
for (int i = 0; i < changedPackages.size(); i++) {
|
||||
final String changedPackage = changedPackages.valueAt(i);
|
||||
for (final int userId : startedUserIds) {
|
||||
final int uid = mPackageManagerInternal.getPackageUid(changedPackage, 0, userId);
|
||||
if (uid <= 0) {
|
||||
continue;
|
||||
}
|
||||
if (!isExactAlarmChangeEnabled(changedPackage, userId)) {
|
||||
continue;
|
||||
}
|
||||
final int appOpMode;
|
||||
synchronized (mLock) {
|
||||
appOpMode = mLastOpScheduleExactAlarm.get(uid,
|
||||
AppOpsManager.opToDefaultMode(AppOpsManager.OP_SCHEDULE_EXACT_ALARM));
|
||||
}
|
||||
final boolean requested = mExactAlarmCandidates.contains(UserHandle.getAppId(uid));
|
||||
|
||||
// added: true => package was added to the deny list
|
||||
// added: false => package was removed from the deny list
|
||||
final boolean hadPermission = getScheduleExactAlarmState(requested, !added,
|
||||
appOpMode);
|
||||
final boolean hasPermission = getScheduleExactAlarmState(requested, added,
|
||||
appOpMode);
|
||||
|
||||
if (hadPermission == hasPermission) {
|
||||
continue;
|
||||
}
|
||||
if (added) {
|
||||
synchronized (mLock) {
|
||||
removeExactAlarmsOnPermissionRevokedLocked(uid, changedPackage);
|
||||
}
|
||||
} else {
|
||||
sendScheduleExactAlarmPermissionStateChangedBroadcast(changedPackage, userId);
|
||||
}
|
||||
}
|
||||
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, REMOVE_REASON_EXACT_PERMISSION_REVOKED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -3396,9 +3572,7 @@ public class AlarmManagerService extends SystemService {
|
||||
*/
|
||||
void removeExactAlarmsOnPermissionRevokedLocked(int uid, String packageName) {
|
||||
Slog.w(TAG, "Package " + packageName + ", uid " + uid + " lost SCHEDULE_EXACT_ALARM!");
|
||||
if (!CompatChanges.isChangeEnabled(
|
||||
AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION,
|
||||
packageName, UserHandle.getUserHandleForUid(uid))) {
|
||||
if (!isExactAlarmChangeEnabled(packageName, UserHandle.getUserId(uid))) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -3409,6 +3583,11 @@ public class AlarmManagerService extends SystemService {
|
||||
return false;
|
||||
};
|
||||
removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_EXACT_PERMISSION_REVOKED);
|
||||
|
||||
if (mConstants.KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED) {
|
||||
PermissionManagerService.killUid(UserHandle.getAppId(uid), UserHandle.getUserId(uid),
|
||||
"schedule_exact_alarm revoked");
|
||||
}
|
||||
}
|
||||
|
||||
private void removeAlarmsInternalLocked(Predicate<Alarm> whichAlarms, int reason) {
|
||||
@ -3535,6 +3714,11 @@ public class AlarmManagerService extends SystemService {
|
||||
mRemovalHistory.removeAt(i);
|
||||
}
|
||||
}
|
||||
for (int i = mLastOpScheduleExactAlarm.size() - 1; i >= 0; i--) {
|
||||
if (UserHandle.getUserId(mLastOpScheduleExactAlarm.keyAt(i)) == userHandle) {
|
||||
mLastOpScheduleExactAlarm.removeAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void interactiveStateChangedLocked(boolean interactive) {
|
||||
@ -4091,8 +4275,9 @@ 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;
|
||||
public static final int REFRESH_EXACT_ALARM_CANDIDATES = 10;
|
||||
public static final int EXACT_ALARM_DENY_LIST_PACKAGES_ADDED = 9;
|
||||
public static final int EXACT_ALARM_DENY_LIST_PACKAGES_REMOVED = 10;
|
||||
public static final int REFRESH_EXACT_ALARM_CANDIDATES = 11;
|
||||
|
||||
AlarmHandler() {
|
||||
super(Looper.myLooper());
|
||||
@ -4179,10 +4364,11 @@ public class AlarmManagerService extends SystemService {
|
||||
removeExactAlarmsOnPermissionRevokedLocked(uid, packageName);
|
||||
}
|
||||
break;
|
||||
case EXACT_ALARM_DENY_LIST_CHANGED:
|
||||
synchronized (mLock) {
|
||||
handlePackagesAddedToExactAlarmsDenyListLocked((ArraySet<String>) msg.obj);
|
||||
}
|
||||
case EXACT_ALARM_DENY_LIST_PACKAGES_ADDED:
|
||||
handleChangesToExactAlarmDenyList((ArraySet<String>) msg.obj, true);
|
||||
break;
|
||||
case EXACT_ALARM_DENY_LIST_PACKAGES_REMOVED:
|
||||
handleChangesToExactAlarmDenyList((ArraySet<String>) msg.obj, false);
|
||||
break;
|
||||
case REFRESH_EXACT_ALARM_CANDIDATES:
|
||||
refreshExactAlarmCandidates();
|
||||
@ -4349,6 +4535,7 @@ public class AlarmManagerService extends SystemService {
|
||||
case Intent.ACTION_UID_REMOVED:
|
||||
mLastPriorityAlarmDispatch.delete(uid);
|
||||
mRemovalHistory.delete(uid);
|
||||
mLastOpScheduleExactAlarm.delete(uid);
|
||||
return;
|
||||
case Intent.ACTION_PACKAGE_REMOVED:
|
||||
if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
|
||||
|
@ -254,6 +254,9 @@ public abstract class ActivityManagerInternal {
|
||||
/** Returns the current user id. */
|
||||
public abstract int getCurrentUserId();
|
||||
|
||||
/** Returns the currently started user ids. */
|
||||
public abstract int[] getStartedUserIds();
|
||||
|
||||
/** Returns true if the user is running. */
|
||||
public abstract boolean isUserRunning(@UserIdInt int userId, int flags);
|
||||
|
||||
|
@ -15348,6 +15348,11 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
return mPendingIntentController.getPendingIntentFlags(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getStartedUserIds() {
|
||||
return mUserController.getStartedUserArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPendingIntentAllowBgActivityStarts(IIntentSender target,
|
||||
IBinder allowlistToken, int flags) {
|
||||
|
@ -58,7 +58,8 @@ import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_PERMISSION;
|
||||
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.EXACT_ALARM_DENY_LIST_PACKAGES_ADDED;
|
||||
import static com.android.server.alarm.AlarmManagerService.AlarmHandler.EXACT_ALARM_DENY_LIST_PACKAGES_REMOVED;
|
||||
import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REFRESH_EXACT_ALARM_CANDIDATES;
|
||||
import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REMOVE_EXACT_ALARMS;
|
||||
import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REMOVE_FOR_CANCELED;
|
||||
@ -103,10 +104,8 @@ 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.app.ActivityManager;
|
||||
import android.app.ActivityManagerInternal;
|
||||
@ -155,6 +154,8 @@ import com.android.server.AppStateTrackerImpl;
|
||||
import com.android.server.DeviceIdleInternal;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.SystemService;
|
||||
import com.android.server.pm.parsing.pkg.AndroidPackage;
|
||||
import com.android.server.pm.permission.PermissionManagerService;
|
||||
import com.android.server.pm.permission.PermissionManagerServiceInternal;
|
||||
import com.android.server.usage.AppStandbyInternal;
|
||||
|
||||
@ -175,6 +176,7 @@ import org.mockito.stubbing.Answer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@ -370,8 +372,9 @@ public class AlarmManagerServiceTest {
|
||||
.mockStatic(LocalServices.class)
|
||||
.spyStatic(Looper.class)
|
||||
.mockStatic(MetricsHelper.class)
|
||||
.mockStatic(Settings.Global.class)
|
||||
.mockStatic(PermissionManagerService.class)
|
||||
.mockStatic(ServiceManager.class)
|
||||
.mockStatic(Settings.Global.class)
|
||||
.mockStatic(SystemProperties.class)
|
||||
.spyStatic(UserHandle.class)
|
||||
.strictness(Strictness.WARN)
|
||||
@ -394,7 +397,7 @@ public class AlarmManagerServiceTest {
|
||||
doCallRealMethod().when((MockedVoidMethod) () ->
|
||||
LocalServices.addService(eq(AlarmManagerInternal.class), any()));
|
||||
doCallRealMethod().when(() -> LocalServices.getService(AlarmManagerInternal.class));
|
||||
doReturn(false).when(() -> UserHandle.isCore(TEST_CALLING_UID));
|
||||
doReturn(false).when(() -> UserHandle.isCore(anyInt()));
|
||||
when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE),
|
||||
eq(TEST_CALLING_USER), anyLong())).thenReturn(STANDBY_BUCKET_ACTIVE);
|
||||
doReturn(Looper.getMainLooper()).when(Looper::myLooper);
|
||||
@ -983,8 +986,7 @@ public class AlarmManagerServiceTest {
|
||||
verify(mService.mHandler, atLeastOnce()).sendMessageAtTime(messageCaptor.capture(),
|
||||
anyLong());
|
||||
final Message lastMessage = messageCaptor.getValue();
|
||||
assertEquals("Unexpected message send to handler", lastMessage.what,
|
||||
what);
|
||||
assertEquals("Unexpected message send to handler", what, lastMessage.what);
|
||||
mService.mHandler.handleMessage(lastMessage);
|
||||
}
|
||||
|
||||
@ -1876,6 +1878,8 @@ public class AlarmManagerServiceTest {
|
||||
|
||||
@Test
|
||||
public void hasScheduleExactAlarmBinderCallNotDenyListed() throws RemoteException {
|
||||
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
|
||||
|
||||
mockExactAlarmPermissionGrant(true, false, MODE_DEFAULT);
|
||||
assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
|
||||
|
||||
@ -1891,6 +1895,8 @@ public class AlarmManagerServiceTest {
|
||||
|
||||
@Test
|
||||
public void hasScheduleExactAlarmBinderCallDenyListed() throws RemoteException {
|
||||
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
|
||||
|
||||
mockExactAlarmPermissionGrant(true, true, MODE_ERRORED);
|
||||
assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
|
||||
|
||||
@ -1904,8 +1910,26 @@ public class AlarmManagerServiceTest {
|
||||
assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasScheduleExactAlarmBinderCallChangeDisabled() throws RemoteException {
|
||||
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false);
|
||||
|
||||
mockExactAlarmPermissionGrant(true, false, MODE_DEFAULT);
|
||||
assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
|
||||
|
||||
mockExactAlarmPermissionGrant(true, true, MODE_ALLOWED);
|
||||
assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
|
||||
}
|
||||
|
||||
private void mockChangeEnabled(long changeId, boolean enabled) {
|
||||
doReturn(enabled).when(() -> CompatChanges.isChangeEnabled(eq(changeId), anyString(),
|
||||
any(UserHandle.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasScheduleExactAlarmBinderCallNotDeclared() throws RemoteException {
|
||||
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
|
||||
|
||||
mockExactAlarmPermissionGrant(false, false, MODE_DEFAULT);
|
||||
assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
|
||||
|
||||
@ -1918,9 +1942,7 @@ public class AlarmManagerServiceTest {
|
||||
|
||||
@Test
|
||||
public void noPermissionCheckWhenChangeDisabled() throws RemoteException {
|
||||
doReturn(false).when(
|
||||
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
|
||||
anyString(), any(UserHandle.class)));
|
||||
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false);
|
||||
|
||||
// alarm clock
|
||||
mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0, 0,
|
||||
@ -1946,9 +1968,7 @@ public class AlarmManagerServiceTest {
|
||||
|
||||
@Test
|
||||
public void exactBinderCallWhenChangeDisabled() throws Exception {
|
||||
doReturn(false).when(
|
||||
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
|
||||
anyString(), any(UserHandle.class)));
|
||||
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false);
|
||||
|
||||
final PendingIntent alarmPi = getNewMockPendingIntent();
|
||||
mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
|
||||
@ -1963,9 +1983,7 @@ public class AlarmManagerServiceTest {
|
||||
|
||||
@Test
|
||||
public void exactAllowWhileIdleBinderCallWhenChangeDisabled() throws Exception {
|
||||
doReturn(false).when(
|
||||
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
|
||||
anyString(), any(UserHandle.class)));
|
||||
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false);
|
||||
|
||||
final PendingIntent alarmPi = getNewMockPendingIntent();
|
||||
mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
|
||||
@ -1985,9 +2003,7 @@ public class AlarmManagerServiceTest {
|
||||
|
||||
@Test
|
||||
public void inexactAllowWhileIdleBinderCallWhenChangeDisabled() throws Exception {
|
||||
doReturn(false).when(
|
||||
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
|
||||
anyString(), any(UserHandle.class)));
|
||||
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false);
|
||||
|
||||
final PendingIntent alarmPi = getNewMockPendingIntent();
|
||||
mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_HEURISTIC, 0,
|
||||
@ -2006,9 +2022,7 @@ public class AlarmManagerServiceTest {
|
||||
|
||||
@Test
|
||||
public void alarmClockBinderCallWhenChangeDisabled() throws Exception {
|
||||
doReturn(false).when(
|
||||
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
|
||||
anyString(), any(UserHandle.class)));
|
||||
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false);
|
||||
|
||||
final PendingIntent alarmPi = getNewMockPendingIntent();
|
||||
final AlarmManager.AlarmClockInfo alarmClock = mock(AlarmManager.AlarmClockInfo.class);
|
||||
@ -2023,9 +2037,7 @@ public class AlarmManagerServiceTest {
|
||||
|
||||
@Test
|
||||
public void alarmClockBinderCall() throws RemoteException {
|
||||
doReturn(true).when(
|
||||
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
|
||||
anyString(), any(UserHandle.class)));
|
||||
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
|
||||
|
||||
mockExactAlarmPermissionGrant(true, false, MODE_ALLOWED);
|
||||
|
||||
@ -2060,7 +2072,6 @@ public class AlarmManagerServiceTest {
|
||||
} else {
|
||||
setDeviceConfigString(KEY_EXACT_ALARM_DENY_LIST, "");
|
||||
}
|
||||
|
||||
when(mAppOpsManager.checkOpNoThrow(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID,
|
||||
TEST_CALLING_PACKAGE)).thenReturn(mode);
|
||||
}
|
||||
@ -2068,9 +2079,7 @@ public class AlarmManagerServiceTest {
|
||||
@Test
|
||||
public void alarmClockBinderCallWithoutPermission() throws RemoteException {
|
||||
setDeviceConfigBoolean(KEY_CRASH_NON_CLOCK_APPS, true);
|
||||
doReturn(true).when(
|
||||
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
|
||||
anyString(), any(UserHandle.class)));
|
||||
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
|
||||
|
||||
mockExactAlarmPermissionGrant(true, false, MODE_ERRORED);
|
||||
when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true);
|
||||
@ -2089,9 +2098,7 @@ public class AlarmManagerServiceTest {
|
||||
|
||||
@Test
|
||||
public void exactBinderCallWithPermission() throws RemoteException {
|
||||
doReturn(true).when(
|
||||
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
|
||||
anyString(), any(UserHandle.class)));
|
||||
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
|
||||
|
||||
mockExactAlarmPermissionGrant(true, false, MODE_ALLOWED);
|
||||
final PendingIntent alarmPi = getNewMockPendingIntent();
|
||||
@ -2115,9 +2122,7 @@ public class AlarmManagerServiceTest {
|
||||
|
||||
@Test
|
||||
public void exactBinderCallWithAllowlist() throws RemoteException {
|
||||
doReturn(true).when(
|
||||
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
|
||||
anyString(), any(UserHandle.class)));
|
||||
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
|
||||
// If permission is denied, only then allowlist will be checked.
|
||||
mockExactAlarmPermissionGrant(true, false, MODE_ERRORED);
|
||||
when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true);
|
||||
@ -2137,9 +2142,7 @@ public class AlarmManagerServiceTest {
|
||||
|
||||
@Test
|
||||
public void exactAllowWhileIdleBinderCallWithPermission() throws RemoteException {
|
||||
doReturn(true).when(
|
||||
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
|
||||
anyString(), any(UserHandle.class)));
|
||||
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
|
||||
|
||||
mockExactAlarmPermissionGrant(true, false, MODE_ALLOWED);
|
||||
final PendingIntent alarmPi = getNewMockPendingIntent();
|
||||
@ -2162,9 +2165,7 @@ public class AlarmManagerServiceTest {
|
||||
|
||||
@Test
|
||||
public void exactAllowWhileIdleBinderCallWithAllowlist() throws RemoteException {
|
||||
doReturn(true).when(
|
||||
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
|
||||
anyString(), any(UserHandle.class)));
|
||||
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
|
||||
// If permission is denied, only then allowlist will be checked.
|
||||
mockExactAlarmPermissionGrant(true, false, MODE_ERRORED);
|
||||
when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true);
|
||||
@ -2191,9 +2192,7 @@ public class AlarmManagerServiceTest {
|
||||
@Test
|
||||
public void exactBinderCallsWithoutPermissionWithoutAllowlist() throws RemoteException {
|
||||
setDeviceConfigBoolean(KEY_CRASH_NON_CLOCK_APPS, true);
|
||||
doReturn(true).when(
|
||||
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
|
||||
anyString(), any(UserHandle.class)));
|
||||
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
|
||||
|
||||
mockExactAlarmPermissionGrant(true, false, MODE_ERRORED);
|
||||
when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(false);
|
||||
@ -2220,9 +2219,7 @@ public class AlarmManagerServiceTest {
|
||||
public void inexactAllowWhileIdleBinderCall() throws RemoteException {
|
||||
// Both permission and power exemption status don't matter for these alarms.
|
||||
// We only want to test that the flags and idleOptions are correct.
|
||||
doReturn(true).when(
|
||||
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
|
||||
anyString(), any(UserHandle.class)));
|
||||
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
|
||||
|
||||
final PendingIntent alarmPi = getNewMockPendingIntent();
|
||||
mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 4321, WINDOW_HEURISTIC, 0,
|
||||
@ -2244,9 +2241,7 @@ public class AlarmManagerServiceTest {
|
||||
|
||||
@Test
|
||||
public void binderCallWithUserAllowlist() throws RemoteException {
|
||||
doReturn(true).when(
|
||||
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
|
||||
anyString(), any(UserHandle.class)));
|
||||
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
|
||||
|
||||
mockExactAlarmPermissionGrant(true, false, MODE_ERRORED);
|
||||
when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true);
|
||||
@ -2266,10 +2261,7 @@ public class AlarmManagerServiceTest {
|
||||
|
||||
@Test
|
||||
public void minWindowChangeEnabled() {
|
||||
doReturn(true).when(
|
||||
() -> CompatChanges.isChangeEnabled(
|
||||
eq(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS),
|
||||
anyString(), any(UserHandle.class)));
|
||||
mockChangeEnabled(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS, true);
|
||||
final int minWindow = 73;
|
||||
setDeviceConfigLong(KEY_MIN_WINDOW, minWindow);
|
||||
|
||||
@ -2315,10 +2307,7 @@ public class AlarmManagerServiceTest {
|
||||
|
||||
@Test
|
||||
public void minWindowChangeDisabled() {
|
||||
doReturn(false).when(
|
||||
() -> CompatChanges.isChangeEnabled(
|
||||
eq(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS),
|
||||
anyString(), any(UserHandle.class)));
|
||||
mockChangeEnabled(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS, false);
|
||||
final long minWindow = 73;
|
||||
setDeviceConfigLong(KEY_MIN_WINDOW, minWindow);
|
||||
|
||||
@ -2335,10 +2324,7 @@ public class AlarmManagerServiceTest {
|
||||
|
||||
@Test
|
||||
public void minWindowPriorityAlarm() {
|
||||
doReturn(true).when(
|
||||
() -> CompatChanges.isChangeEnabled(
|
||||
eq(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS),
|
||||
anyString(), any(UserHandle.class)));
|
||||
mockChangeEnabled(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS, true);
|
||||
final long minWindow = 73;
|
||||
setDeviceConfigLong(KEY_MIN_WINDOW, minWindow);
|
||||
|
||||
@ -2356,76 +2342,135 @@ public class AlarmManagerServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void denyListPackagesAdded() {
|
||||
public void denyListChanged() {
|
||||
mService.mConstants.EXACT_ALARM_DENY_LIST = new ArraySet<>(new String[]{"p1", "p2", "p3"});
|
||||
when(mActivityManagerInternal.getStartedUserIds()).thenReturn(EmptyArray.INT);
|
||||
|
||||
setDeviceConfigString(KEY_EXACT_ALARM_DENY_LIST, "p2,p4,p5");
|
||||
assertAndHandleMessageSync(EXACT_ALARM_DENY_LIST_CHANGED);
|
||||
|
||||
final ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
|
||||
verify(mService.mHandler, times(2)).sendMessageAtTime(messageCaptor.capture(),
|
||||
anyLong());
|
||||
|
||||
final List<Message> messages = messageCaptor.getAllValues();
|
||||
for (final Message msg : messages) {
|
||||
assertTrue("Unwanted message sent to handler: " + msg.what,
|
||||
msg.what == EXACT_ALARM_DENY_LIST_PACKAGES_ADDED
|
||||
|| msg.what == EXACT_ALARM_DENY_LIST_PACKAGES_REMOVED);
|
||||
mService.mHandler.handleMessage(msg);
|
||||
}
|
||||
|
||||
ArraySet<String> added = new ArraySet<>(new String[]{"p4", "p5"});
|
||||
verify(mService).handlePackagesAddedToExactAlarmsDenyListLocked(eq(added));
|
||||
verify(mService).handleChangesToExactAlarmDenyList(eq(added), eq(true));
|
||||
|
||||
ArraySet<String> removed = new ArraySet<>(new String[]{"p1", "p3"});
|
||||
verify(mService).handleChangesToExactAlarmDenyList(eq(removed), eq(false));
|
||||
}
|
||||
|
||||
@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);
|
||||
public void permissionGrantedDueToDenyList() {
|
||||
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
|
||||
|
||||
final String[] packages = {"example.package.1", "example.package.2"};
|
||||
|
||||
final int appId1 = 232;
|
||||
final int appId2 = 431;
|
||||
|
||||
final int userId1 = 42;
|
||||
final int userId2 = 53;
|
||||
|
||||
registerAppIds(packages, new Integer[]{appId1, appId2});
|
||||
|
||||
when(mActivityManagerInternal.getStartedUserIds()).thenReturn(new int[]{userId1, userId2});
|
||||
|
||||
when(mPermissionManagerInternal.getAppOpPermissionPackages(
|
||||
SCHEDULE_EXACT_ALARM)).thenReturn(packages);
|
||||
mService.refreshExactAlarmCandidates();
|
||||
|
||||
final long allowListDuration = 53442;
|
||||
when(mActivityManagerInternal.getBootTimeTempAllowListDuration()).thenReturn(
|
||||
allowListDuration);
|
||||
|
||||
mService.mLastOpScheduleExactAlarm.put(UserHandle.getUid(userId1, appId1), MODE_ALLOWED);
|
||||
mService.mLastOpScheduleExactAlarm.put(UserHandle.getUid(userId2, appId1), MODE_DEFAULT);
|
||||
mService.mLastOpScheduleExactAlarm.put(UserHandle.getUid(userId1, appId2), MODE_IGNORED);
|
||||
mService.mLastOpScheduleExactAlarm.put(UserHandle.getUid(userId2, appId2), MODE_ERRORED);
|
||||
|
||||
mService.handleChangesToExactAlarmDenyList(new ArraySet<>(packages), false);
|
||||
|
||||
// No permission revoked.
|
||||
verify(mService, never()).removeExactAlarmsOnPermissionRevokedLocked(anyInt(), anyString());
|
||||
|
||||
// Permission got granted only for (appId1, userId2).
|
||||
final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
|
||||
final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
|
||||
final ArgumentCaptor<UserHandle> userCaptor = ArgumentCaptor.forClass(UserHandle.class);
|
||||
|
||||
verify(mMockContext).sendBroadcastAsUser(intentCaptor.capture(), userCaptor.capture(),
|
||||
isNull(), bundleCaptor.capture());
|
||||
|
||||
assertEquals(userId2, userCaptor.getValue().getIdentifier());
|
||||
|
||||
// Validate the intent.
|
||||
assertEquals(AlarmManager.ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED,
|
||||
intentCaptor.getValue().getAction());
|
||||
assertEquals(packages[0], intentCaptor.getValue().getPackage());
|
||||
|
||||
// Validate the options.
|
||||
final BroadcastOptions bOptions = new BroadcastOptions(bundleCaptor.getValue());
|
||||
assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
|
||||
bOptions.getTemporaryAppAllowlistType());
|
||||
assertEquals(allowListDuration, bOptions.getTemporaryAppAllowlistDuration());
|
||||
assertEquals(PowerExemptionManager.REASON_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED,
|
||||
bOptions.getTemporaryAppAllowlistReasonCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeExactAlarmsOnPackageAddedToDenyList() {
|
||||
doReturn(true).when(
|
||||
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
|
||||
anyString(), any(UserHandle.class)));
|
||||
public void permissionRevokedDueToDenyList() {
|
||||
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
|
||||
|
||||
// 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 String[] packages = {"example.package.1", "example.package.2"};
|
||||
|
||||
final PendingIntent inexact = getNewMockPendingIntent();
|
||||
setTestAlarm(ELAPSED_REALTIME, 4, 10, inexact, 0, 0, TEST_CALLING_UID, null);
|
||||
final int appId1 = 232;
|
||||
final int appId2 = 431;
|
||||
|
||||
final PendingIntent inexactAwi = getNewMockPendingIntent();
|
||||
setTestAlarm(ELAPSED_REALTIME, 5, 10, inexactAwi, 0, FLAG_ALLOW_WHILE_IDLE,
|
||||
TEST_CALLING_UID, null);
|
||||
final int userId1 = 42;
|
||||
final int userId2 = 53;
|
||||
|
||||
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());
|
||||
registerAppIds(packages, new Integer[]{appId1, appId2});
|
||||
|
||||
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))));
|
||||
when(mActivityManagerInternal.getStartedUserIds()).thenReturn(new int[]{userId1, userId2});
|
||||
|
||||
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)));
|
||||
when(mPermissionManagerInternal.getAppOpPermissionPackages(
|
||||
SCHEDULE_EXACT_ALARM)).thenReturn(packages);
|
||||
mService.refreshExactAlarmCandidates();
|
||||
|
||||
// Mock should return false by default.
|
||||
verify(mDeviceIdleInternal, atLeastOnce()).isAppOnWhitelist(
|
||||
UserHandle.getAppId(TEST_CALLING_UID));
|
||||
mService.mLastOpScheduleExactAlarm.put(UserHandle.getUid(userId1, appId1), MODE_ALLOWED);
|
||||
mService.mLastOpScheduleExactAlarm.put(UserHandle.getUid(userId2, appId1), MODE_DEFAULT);
|
||||
mService.mLastOpScheduleExactAlarm.put(UserHandle.getUid(userId1, appId2), MODE_IGNORED);
|
||||
mService.mLastOpScheduleExactAlarm.put(UserHandle.getUid(userId2, appId2), MODE_ERRORED);
|
||||
|
||||
mService.handleChangesToExactAlarmDenyList(new ArraySet<>(packages), true);
|
||||
|
||||
// Permission got revoked only for (appId1, userId2)
|
||||
verify(mService, never()).removeExactAlarmsOnPermissionRevokedLocked(
|
||||
eq(UserHandle.getUid(userId1, appId1)), eq(packages[0]));
|
||||
verify(mService, never()).removeExactAlarmsOnPermissionRevokedLocked(
|
||||
eq(UserHandle.getUid(userId1, appId2)), eq(packages[1]));
|
||||
verify(mService, never()).removeExactAlarmsOnPermissionRevokedLocked(
|
||||
eq(UserHandle.getUid(userId2, appId2)), eq(packages[1]));
|
||||
|
||||
verify(mService).removeExactAlarmsOnPermissionRevokedLocked(
|
||||
eq(UserHandle.getUid(userId2, appId1)), eq(packages[0]));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void opScheduleExactAlarmRevoked() throws Exception {
|
||||
public void opChangedPermissionRevoked() throws Exception {
|
||||
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
|
||||
|
||||
mService.mLastOpScheduleExactAlarm.put(TEST_CALLING_UID, MODE_ALLOWED);
|
||||
mockExactAlarmPermissionGrant(true, false, MODE_ERRORED);
|
||||
|
||||
mIAppOpsCallback.opChanged(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, TEST_CALLING_PACKAGE);
|
||||
assertAndHandleMessageSync(REMOVE_EXACT_ALARMS);
|
||||
verify(mService).removeExactAlarmsOnPermissionRevokedLocked(TEST_CALLING_UID,
|
||||
@ -2433,10 +2478,39 @@ public class AlarmManagerServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void opScheduleExactAlarmGranted() throws Exception {
|
||||
public void opChangedChangeDisabled() throws Exception {
|
||||
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false);
|
||||
|
||||
mService.mLastOpScheduleExactAlarm.put(TEST_CALLING_UID, MODE_ALLOWED);
|
||||
mockExactAlarmPermissionGrant(true, false, MODE_ERRORED);
|
||||
|
||||
mIAppOpsCallback.opChanged(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, TEST_CALLING_PACKAGE);
|
||||
|
||||
verify(mService.mHandler, never()).sendMessageAtTime(
|
||||
argThat(m -> m.what == REMOVE_EXACT_ALARMS), anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void opChangedNoPermissionChangeDueToDenyList() throws Exception {
|
||||
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false);
|
||||
|
||||
mService.mLastOpScheduleExactAlarm.put(TEST_CALLING_UID, MODE_ERRORED);
|
||||
mockExactAlarmPermissionGrant(true, true, MODE_DEFAULT);
|
||||
|
||||
mIAppOpsCallback.opChanged(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, TEST_CALLING_PACKAGE);
|
||||
|
||||
verify(mService.mHandler, never()).sendMessageAtTime(
|
||||
argThat(m -> m.what == REMOVE_EXACT_ALARMS), anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void opChangedPermissionGranted() throws Exception {
|
||||
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
|
||||
|
||||
final long durationMs = 20000L;
|
||||
when(mActivityManagerInternal.getBootTimeTempAllowListDuration()).thenReturn(durationMs);
|
||||
|
||||
mService.mLastOpScheduleExactAlarm.put(TEST_CALLING_UID, MODE_ERRORED);
|
||||
mockExactAlarmPermissionGrant(true, false, MODE_ALLOWED);
|
||||
mIAppOpsCallback.opChanged(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, TEST_CALLING_PACKAGE);
|
||||
|
||||
@ -2462,9 +2536,7 @@ public class AlarmManagerServiceTest {
|
||||
|
||||
@Test
|
||||
public void removeExactAlarmsOnPermissionRevoked() {
|
||||
doReturn(true).when(
|
||||
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
|
||||
anyString(), any(UserHandle.class)));
|
||||
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
|
||||
|
||||
// basic exact alarm
|
||||
setTestAlarm(ELAPSED_REALTIME, 0, 0, getNewMockPendingIntent(), 0, 0, TEST_CALLING_UID,
|
||||
@ -2501,6 +2573,9 @@ public class AlarmManagerServiceTest {
|
||||
// Mock should return false by default.
|
||||
verify(mDeviceIdleInternal, atLeastOnce()).isAppOnWhitelist(
|
||||
UserHandle.getAppId(TEST_CALLING_UID));
|
||||
|
||||
verify(() -> PermissionManagerService.killUid(eq(TEST_CALLING_UID), eq(TEST_CALLING_USER),
|
||||
anyString()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -2566,6 +2641,86 @@ public class AlarmManagerServiceTest {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onLastOpScheduleExactAlarmOnUserStart() {
|
||||
final int userId = 54;
|
||||
SystemService.TargetUser mockTargetUser = mock(SystemService.TargetUser.class);
|
||||
when(mockTargetUser.getUserIdentifier()).thenReturn(userId);
|
||||
|
||||
final Integer[] appIds = new Integer[]{43, 254, 7731};
|
||||
final int unknownAppId = 2347;
|
||||
final String[] packageNames = new String[]{"p43", "p254", "p7731"};
|
||||
final int[] appOpModes = new int[]{MODE_ALLOWED, MODE_IGNORED, MODE_ERRORED};
|
||||
mService.mExactAlarmCandidates = new ArraySet<>(appIds);
|
||||
mService.mExactAlarmCandidates.add(unknownAppId);
|
||||
|
||||
for (int i = 0; i < appIds.length; i++) {
|
||||
final int uid = UserHandle.getUid(userId, appIds[i]);
|
||||
final AndroidPackage pkg = mock(AndroidPackage.class);
|
||||
when(pkg.getPackageName()).thenReturn(packageNames[i]);
|
||||
|
||||
when(mPackageManagerInternal.getPackage(uid)).thenReturn(pkg);
|
||||
when(mAppOpsManager.checkOpNoThrow(OP_SCHEDULE_EXACT_ALARM, uid,
|
||||
packageNames[i])).thenReturn(appOpModes[i]);
|
||||
}
|
||||
|
||||
final ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
|
||||
doReturn(true).when(mService.mHandler).post(runnableCaptor.capture());
|
||||
|
||||
mService.onUserStarting(mockTargetUser);
|
||||
runnableCaptor.getValue().run();
|
||||
|
||||
assertEquals(appIds.length, mService.mLastOpScheduleExactAlarm.size());
|
||||
for (int i = 0; i < appIds.length; i++) {
|
||||
final int uid = UserHandle.getUid(userId, appIds[i]);
|
||||
assertEquals(appOpModes[i], mService.mLastOpScheduleExactAlarm.get(uid, -1));
|
||||
}
|
||||
assertTrue(mService.mLastOpScheduleExactAlarm.indexOfKey(
|
||||
UserHandle.getUid(userId, unknownAppId)) < 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refreshExactAlarmCandidatesRemovesExactAlarmsIfNeeded() {
|
||||
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
|
||||
|
||||
mService.mExactAlarmCandidates = new ArraySet<>(new Integer[]{1, 2, 5});
|
||||
final String[] updatedRequesters = new String[]{"p11", "p2", "p9"};
|
||||
final Integer[] appIds = new Integer[]{11, 2, 9};
|
||||
registerAppIds(updatedRequesters, appIds);
|
||||
|
||||
when(mPermissionManagerInternal.getAppOpPermissionPackages(
|
||||
SCHEDULE_EXACT_ALARM)).thenReturn(updatedRequesters);
|
||||
|
||||
final PendingIntent exactAppId1 = getNewMockPendingIntent();
|
||||
setTestAlarm(ELAPSED_REALTIME, 0, 0, exactAppId1, 0, 0,
|
||||
UserHandle.getUid(TEST_CALLING_USER, 1), null);
|
||||
|
||||
final PendingIntent exactAppId2 = getNewMockPendingIntent();
|
||||
setTestAlarm(ELAPSED_REALTIME, 0, 0, exactAppId2, 0, 0,
|
||||
UserHandle.getUid(TEST_CALLING_USER, 2), null);
|
||||
|
||||
final PendingIntent exactAppId5 = getNewMockPendingIntent();
|
||||
setTestAlarm(ELAPSED_REALTIME, 0, 0, exactAppId5, 0, 0,
|
||||
UserHandle.getUid(TEST_CALLING_USER, 5), null);
|
||||
|
||||
final PendingIntent inexactAppId5 = getNewMockPendingIntent();
|
||||
setTestAlarm(ELAPSED_REALTIME, 0, 23, inexactAppId5, 0, 0,
|
||||
UserHandle.getUid(TEST_CALLING_USER, 5), null);
|
||||
|
||||
assertEquals(4, mService.mAlarmStore.size());
|
||||
|
||||
mService.refreshExactAlarmCandidates();
|
||||
// App ids 1 and 5 lost the permission, so there alarms should be removed.
|
||||
|
||||
final ArrayList<Alarm> remaining = mService.mAlarmStore.asList();
|
||||
assertEquals(2, remaining.size());
|
||||
|
||||
assertTrue("Inexact alarm removed",
|
||||
remaining.removeIf(a -> a.matches(inexactAppId5, null)));
|
||||
assertTrue("Alarm from app id 2 removed",
|
||||
remaining.removeIf(a -> a.matches(exactAppId2, null)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refreshExactAlarmCandidatesOnPackageAdded() {
|
||||
final String[] exactAlarmRequesters = new String[]{"p11", "p2", "p9"};
|
||||
|
Reference in New Issue
Block a user