Merge "Handle exact alarm permission state changes" into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
cd140900ed
@ -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