Merge "Optimizing AppStandby.getIdleUidsForUser" into sc-dev
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 03d9a96..e63a7c4 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -59,7 +59,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.PermissionChecker;
+import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.net.Uri;
import android.os.BatteryManager;
@@ -123,6 +123,7 @@
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.SystemServiceManager;
+import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.usage.AppStandbyInternal;
import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
@@ -198,9 +199,13 @@
private UsageStatsManagerInternal mUsageStatsManagerInternal;
private ActivityManagerInternal mActivityManagerInternal;
private PackageManagerInternal mPackageManagerInternal;
+ private PermissionManagerServiceInternal mLocalPermissionManager;
final Object mLock = new Object();
+ /** Immutable set of app ids that have requested SCHEDULE_EXACT_ALARM permission.*/
+ @VisibleForTesting
+ volatile Set<Integer> mExactAlarmCandidates = Collections.emptySet();
// 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;
@@ -1540,6 +1545,21 @@
publishBinderService(Context.ALARM_SERVICE, mService);
}
+ void refreshExactAlarmCandidates() {
+ final String[] candidates = mLocalPermissionManager.getAppOpPermissionPackages(
+ Manifest.permission.SCHEDULE_EXACT_ALARM);
+ final Set<Integer> appIds = 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));
+ }
+ }
+ // No need to lock. Assignment is always atomic.
+ mExactAlarmCandidates = Collections.unmodifiableSet(appIds);
+ }
+
@Override
public void onBootPhase(int phase) {
if (phase == PHASE_SYSTEM_SERVICES_READY) {
@@ -1569,6 +1589,11 @@
LocalServices.getService(DeviceIdleInternal.class);
mUsageStatsManagerInternal =
LocalServices.getService(UsageStatsManagerInternal.class);
+
+ mLocalPermissionManager = LocalServices.getService(
+ PermissionManagerServiceInternal.class);
+ refreshExactAlarmCandidates();
+
AppStandbyInternal appStandbyInternal =
LocalServices.getService(AppStandbyInternal.class);
appStandbyInternal.addListener(new AppStandbyTracker());
@@ -2097,17 +2122,21 @@
boolean hasScheduleExactAlarmInternal(String packageName, int uid) {
final long start = mStatLogger.getTime();
- // No locking needed as EXACT_ALARM_DENY_LIST is immutable.
- final boolean isOnDenyList = mConstants.EXACT_ALARM_DENY_LIST.contains(packageName);
- if (isOnDenyList && mAppOps.checkOpNoThrow(AppOpsManager.OP_SCHEDULE_EXACT_ALARM, uid,
- packageName) != AppOpsManager.MODE_ALLOWED) {
- return false;
+ final boolean hasPermission;
+ // No locking needed as all internal containers being queried are immutable.
+ if (!mExactAlarmCandidates.contains(UserHandle.getAppId(uid))) {
+ hasPermission = false;
+ } else {
+ final int mode = mAppOps.checkOpNoThrow(AppOpsManager.OP_SCHEDULE_EXACT_ALARM, uid,
+ packageName);
+ if (mode == AppOpsManager.MODE_DEFAULT) {
+ hasPermission = !mConstants.EXACT_ALARM_DENY_LIST.contains(packageName);
+ } else {
+ hasPermission = (mode == AppOpsManager.MODE_ALLOWED);
+ }
}
- final boolean has = PermissionChecker.checkPermissionForPreflight(getContext(),
- Manifest.permission.SCHEDULE_EXACT_ALARM, -1, uid, packageName)
- == PermissionChecker.PERMISSION_GRANTED;
mStatLogger.logDurationStat(Stats.HAS_SCHEDULE_EXACT_ALARM, start);
- return has;
+ return hasPermission;
}
/**
@@ -2490,6 +2519,9 @@
pw.println(mNumTimeChanged);
pw.println();
+ pw.println("App ids requesting SCHEDULE_EXACT_ALARM: " + mExactAlarmCandidates);
+
+ pw.println();
pw.println("Next alarm clock information: ");
pw.increaseIndent();
final TreeSet<Integer> users = new TreeSet<>();
@@ -3924,6 +3956,7 @@
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;
AlarmHandler() {
super(Looper.myLooper());
@@ -4015,6 +4048,9 @@
handlePackagesAddedToExactAlarmsDenyListLocked((ArraySet<String>) msg.obj);
}
break;
+ case REFRESH_EXACT_ALARM_CANDIDATES:
+ refreshExactAlarmCandidates();
+ break;
default:
// nope, just ignore it
break;
@@ -4135,6 +4171,7 @@
public UninstallReceiver() {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addAction(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
filter.addDataScheme(IntentFilter.SCHEME_PACKAGE);
@@ -4179,8 +4216,11 @@
case Intent.ACTION_PACKAGE_REMOVED:
if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
// This package is being updated; don't kill its alarms.
+ // We will refresh the exact alarm candidates on subsequent receipt of
+ // PACKAGE_ADDED.
return;
}
+ mHandler.sendEmptyMessage(AlarmHandler.REFRESH_EXACT_ALARM_CANDIDATES);
// Intentional fall-through.
case Intent.ACTION_PACKAGE_RESTARTED:
final Uri data = intent.getData();
@@ -4191,6 +4231,9 @@
}
}
break;
+ case Intent.ACTION_PACKAGE_ADDED:
+ mHandler.sendEmptyMessage(AlarmHandler.REFRESH_EXACT_ALARM_CANDIDATES);
+ return;
}
if (pkgList != null && (pkgList.length > 0)) {
for (String pkg : pkgList) {
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 97ee0e1..ebf4ed0 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -58,7 +58,6 @@
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
-import android.app.AppGlobals;
import android.app.usage.AppStandbyInfo;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManager.StandbyBuckets;
@@ -75,7 +74,6 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.ParceledListSlice;
import android.database.ContentObserver;
import android.hardware.display.DisplayManager;
import android.net.NetworkScoreManager;
@@ -101,7 +99,7 @@
import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.SparseIntArray;
+import android.util.SparseBooleanArray;
import android.util.TimeUtils;
import android.view.Display;
import android.widget.Toast;
@@ -118,6 +116,8 @@
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.usage.AppIdleHistory.AppUsageHistory;
+import libcore.util.EmptyArray;
+
import java.io.File;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -1249,71 +1249,55 @@
@Override
public int[] getIdleUidsForUser(int userId) {
if (!mAppIdleEnabled) {
- return new int[0];
+ return EmptyArray.INT;
}
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "getIdleUidsForUser");
final long elapsedRealtime = mInjector.elapsedRealtime();
- List<ApplicationInfo> apps;
- try {
- ParceledListSlice<ApplicationInfo> slice = AppGlobals.getPackageManager()
- .getInstalledApplications(/* flags= */ 0, userId);
- if (slice == null) {
- return new int[0];
- }
- apps = slice.getList();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ final PackageManagerInternal pmi = mInjector.getPackageManagerInternal();
+ final List<ApplicationInfo> apps = pmi.getInstalledApplications(0, userId, Process.myUid());
+ if (apps == null) {
+ return EmptyArray.INT;
}
- // State of each uid. Key is the uid. Value lower 16 bits is the number of apps
- // associated with that uid, upper 16 bits is the number of those apps that is idle.
- SparseIntArray uidStates = new SparseIntArray();
-
- // Now resolve all app state. Iterating over all apps, keeping track of how many
- // we find for each uid and how many of those are idle.
+ // State of each uid: Key is the uid, value is whether all the apps in that uid are idle.
+ final SparseBooleanArray uidIdleStates = new SparseBooleanArray();
+ int notIdleCount = 0;
for (int i = apps.size() - 1; i >= 0; i--) {
- ApplicationInfo ai = apps.get(i);
+ final ApplicationInfo ai = apps.get(i);
+ final int index = uidIdleStates.indexOfKey(ai.uid);
- // Check whether this app is idle.
- boolean idle = isAppIdleFiltered(ai.packageName, UserHandle.getAppId(ai.uid),
- userId, elapsedRealtime);
+ final boolean currentIdle = (index < 0) ? true : uidIdleStates.valueAt(index);
- int index = uidStates.indexOfKey(ai.uid);
+ final boolean newIdle = currentIdle && isAppIdleFiltered(ai.packageName,
+ UserHandle.getAppId(ai.uid), userId, elapsedRealtime);
+
+ if (currentIdle && !newIdle) {
+ // This transition from true to false can happen at most once per uid in this loop.
+ notIdleCount++;
+ }
if (index < 0) {
- uidStates.put(ai.uid, 1 + (idle ? 1<<16 : 0));
+ uidIdleStates.put(ai.uid, newIdle);
} else {
- int value = uidStates.valueAt(index);
- uidStates.setValueAt(index, value + 1 + (idle ? 1<<16 : 0));
+ uidIdleStates.setValueAt(index, newIdle);
}
}
+ int numIdleUids = uidIdleStates.size() - notIdleCount;
+ final int[] idleUids = new int[numIdleUids];
+ for (int i = uidIdleStates.size() - 1; i >= 0; i--) {
+ if (uidIdleStates.valueAt(i)) {
+ idleUids[--numIdleUids] = uidIdleStates.keyAt(i);
+ }
+ }
if (DEBUG) {
Slog.d(TAG, "getIdleUids took " + (mInjector.elapsedRealtime() - elapsedRealtime));
}
- int numIdle = 0;
- for (int i = uidStates.size() - 1; i >= 0; i--) {
- int value = uidStates.valueAt(i);
- if ((value&0x7fff) == (value>>16)) {
- numIdle++;
- }
- }
-
- int[] res = new int[numIdle];
- numIdle = 0;
- for (int i = uidStates.size() - 1; i >= 0; i--) {
- int value = uidStates.valueAt(i);
- if ((value&0x7fff) == (value>>16)) {
- res[numIdle] = uidStates.keyAt(i);
- numIdle++;
- }
- }
-
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- return res;
+ return idleUids;
}
@Override
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index d55bbd1..683fbd1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -15,6 +15,7 @@
*/
package com.android.server.alarm;
+import static android.Manifest.permission.SCHEDULE_EXACT_ALARM;
import static android.app.AlarmManager.ELAPSED_REALTIME;
import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE;
@@ -54,6 +55,7 @@
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.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;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA;
@@ -99,7 +101,6 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import android.Manifest;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AlarmManager;
@@ -114,7 +115,6 @@
import android.app.usage.UsageStatsManagerInternal;
import android.content.Context;
import android.content.Intent;
-import android.content.PermissionChecker;
import android.content.pm.PackageManagerInternal;
import android.os.BatteryManager;
import android.os.Bundle;
@@ -139,14 +139,18 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
+import com.android.internal.util.ArrayUtils;
import com.android.server.AlarmManagerInternal;
import com.android.server.AppStateTracker;
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.permission.PermissionManagerServiceInternal;
import com.android.server.usage.AppStandbyInternal;
+import libcore.util.EmptyArray;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -160,6 +164,7 @@
import org.mockito.stubbing.Answer;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashSet;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
@@ -176,6 +181,7 @@
private long mAllowWhileIdleWindow;
private AlarmManagerService mService;
private AppStandbyInternal.AppIdleStateChangeListener mAppStandbyListener;
+ private AlarmManagerService.UninstallReceiver mPackageChangesReceiver;
private AlarmManagerService.ChargingReceiver mChargingReceiver;
private IAppOpsCallback mIAppOpsCallback;
private IAlarmManager mBinder;
@@ -190,6 +196,8 @@
@Mock
private DeviceIdleInternal mDeviceIdleInternal;
@Mock
+ private PermissionManagerServiceInternal mPermissionManagerInternal;
+ @Mock
private UsageStatsManagerInternal mUsageStatsManagerInternal;
@Mock
private AppStandbyInternal mAppStandbyInternal;
@@ -351,7 +359,6 @@
.spyStatic(DeviceConfig.class)
.mockStatic(LocalServices.class)
.spyStatic(Looper.class)
- .mockStatic(PermissionChecker.class)
.mockStatic(Settings.Global.class)
.mockStatic(ServiceManager.class)
.spyStatic(UserHandle.class)
@@ -361,6 +368,8 @@
doReturn(mIActivityManager).when(ActivityManager::getService);
doReturn(mDeviceIdleInternal).when(
() -> LocalServices.getService(DeviceIdleInternal.class));
+ doReturn(mPermissionManagerInternal).when(
+ () -> LocalServices.getService(PermissionManagerServiceInternal.class));
doReturn(mActivityManagerInternal).when(
() -> LocalServices.getService(ActivityManagerInternal.class));
doReturn(mPackageManagerInternal).when(
@@ -399,8 +408,10 @@
when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager);
- when(mPackageManagerInternal.getPackageUid(eq(TEST_CALLING_PACKAGE), anyInt(),
- eq(TEST_CALLING_USER))).thenReturn(TEST_CALLING_UID);
+ registerAppIds(new String[]{TEST_CALLING_PACKAGE},
+ new Integer[]{UserHandle.getAppId(TEST_CALLING_UID)});
+ when(mPermissionManagerInternal.getAppOpPermissionPackages(
+ SCHEDULE_EXACT_ALARM)).thenReturn(EmptyArray.STRING);
mInjector = new Injector(mMockContext);
mService = new AlarmManagerService(mMockContext, mInjector);
@@ -424,13 +435,22 @@
verify(mAppStandbyInternal).addListener(captor.capture());
mAppStandbyListener = captor.getValue();
- ArgumentCaptor<AlarmManagerService.ChargingReceiver> chargingReceiverCaptor =
+ final ArgumentCaptor<AlarmManagerService.ChargingReceiver> chargingReceiverCaptor =
ArgumentCaptor.forClass(AlarmManagerService.ChargingReceiver.class);
verify(mMockContext).registerReceiver(chargingReceiverCaptor.capture(),
argThat((filter) -> filter.hasAction(BatteryManager.ACTION_CHARGING)
&& filter.hasAction(BatteryManager.ACTION_DISCHARGING)));
mChargingReceiver = chargingReceiverCaptor.getValue();
+ final ArgumentCaptor<AlarmManagerService.UninstallReceiver> packageReceiverCaptor =
+ ArgumentCaptor.forClass(AlarmManagerService.UninstallReceiver.class);
+ verify(mMockContext).registerReceiver(packageReceiverCaptor.capture(),
+ argThat((filter) -> filter.hasAction(Intent.ACTION_PACKAGE_ADDED)
+ && filter.hasAction(Intent.ACTION_PACKAGE_REMOVED)));
+ mPackageChangesReceiver = packageReceiverCaptor.getValue();
+
+ assertEquals(mService.mExactAlarmCandidates, Collections.emptySet());
+
ArgumentCaptor<IBinder> binderCaptor = ArgumentCaptor.forClass(IBinder.class);
verify(() -> ServiceManager.addService(eq(Context.ALARM_SERVICE), binderCaptor.capture(),
anyBoolean(), anyInt()));
@@ -927,7 +947,8 @@
private void assertAndHandleMessageSync(int what) {
final ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
- verify(mService.mHandler, atLeastOnce()).sendMessage(messageCaptor.capture());
+ verify(mService.mHandler, atLeastOnce()).sendMessageAtTime(messageCaptor.capture(),
+ anyLong());
final Message lastMessage = messageCaptor.getValue();
assertEquals("Unexpected message send to handler", lastMessage.what,
what);
@@ -1795,70 +1816,45 @@
}
@Test
- public void hasScheduleExactAlarmBinderCallEmptyDenyList() throws RemoteException {
- doReturn(PermissionChecker.PERMISSION_GRANTED).when(
- () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext),
- eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(),
- eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)));
+ public void hasScheduleExactAlarmBinderCallNotDenyListed() throws RemoteException {
+ mockExactAlarmPermissionGrant(true, false, MODE_DEFAULT);
assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
- doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when(
- () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext),
- eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(),
- eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)));
+ mockExactAlarmPermissionGrant(true, false, MODE_ALLOWED);
+ assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
+
+ mockExactAlarmPermissionGrant(true, false, MODE_ERRORED);
+ assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
+
+ mockExactAlarmPermissionGrant(true, false, MODE_IGNORED);
assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
}
@Test
- public void hasScheduleExactAlarmBinderCallWithDenyList() throws RemoteException {
- setDeviceConfigString(KEY_EXACT_ALARM_DENY_LIST, TEST_CALLING_PACKAGE);
-
- when(mAppOpsManager.checkOpNoThrow(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID,
- TEST_CALLING_PACKAGE)).thenReturn(MODE_ERRORED);
-
+ public void hasScheduleExactAlarmBinderCallDenyListed() throws RemoteException {
+ mockExactAlarmPermissionGrant(true, true, MODE_ERRORED);
assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
- verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext),
- eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(),
- eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)), never());
- when(mAppOpsManager.checkOpNoThrow(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID,
- TEST_CALLING_PACKAGE)).thenReturn(MODE_DEFAULT);
-
+ mockExactAlarmPermissionGrant(true, true, MODE_DEFAULT);
assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
- verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext),
- eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(),
- eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)), never());
- when(mAppOpsManager.checkOpNoThrow(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID,
- TEST_CALLING_PACKAGE)).thenReturn(MODE_IGNORED);
-
+ mockExactAlarmPermissionGrant(true, true, MODE_IGNORED);
assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
- verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext),
- eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(),
- eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)), never());
- when(mAppOpsManager.checkOpNoThrow(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID,
- TEST_CALLING_PACKAGE)).thenReturn(MODE_ALLOWED);
-
- doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when(
- () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext),
- eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(),
- eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)));
-
- assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
- verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext),
- eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(),
- eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)));
-
- doReturn(PermissionChecker.PERMISSION_GRANTED).when(
- () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext),
- eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(),
- eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)));
-
+ mockExactAlarmPermissionGrant(true, true, MODE_ALLOWED);
assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
- verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext),
- eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(),
- eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)), times(2));
+ }
+
+ @Test
+ public void hasScheduleExactAlarmBinderCallNotDeclared() throws RemoteException {
+ mockExactAlarmPermissionGrant(false, false, MODE_DEFAULT);
+ assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
+
+ mockExactAlarmPermissionGrant(false, false, MODE_ALLOWED);
+ assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
+
+ mockExactAlarmPermissionGrant(false, true, MODE_ALLOWED);
+ assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
}
@Test
@@ -1884,9 +1880,8 @@
mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_HEURISTIC, 0,
FLAG_ALLOW_WHILE_IDLE, getNewMockPendingIntent(), null, null, null, null);
- verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext),
- eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(),
- eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)), never());
+ verify(mService, never()).hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE,
+ TEST_CALLING_UID);
verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt());
}
@@ -1969,10 +1964,7 @@
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
anyString(), any(UserHandle.class)));
- doReturn(PermissionChecker.PERMISSION_GRANTED).when(
- () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext),
- eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(),
- eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)));
+ mockExactAlarmPermissionGrant(true, false, MODE_ALLOWED);
final PendingIntent alarmPi = getNewMockPendingIntent();
final AlarmManager.AlarmClockInfo alarmClock = mock(AlarmManager.AlarmClockInfo.class);
@@ -1980,9 +1972,7 @@
alarmPi, null, null, null, alarmClock);
// Correct permission checks are invoked.
- verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext),
- eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(),
- eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)));
+ verify(mService).hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID);
verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt());
final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
@@ -1996,6 +1986,22 @@
assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
}
+ private void mockExactAlarmPermissionGrant(boolean declared, boolean denyList, int mode) {
+ String[] requesters = declared ? new String[]{TEST_CALLING_PACKAGE} : EmptyArray.STRING;
+ when(mPermissionManagerInternal.getAppOpPermissionPackages(SCHEDULE_EXACT_ALARM))
+ .thenReturn(requesters);
+ mService.refreshExactAlarmCandidates();
+
+ if (denyList) {
+ setDeviceConfigString(KEY_EXACT_ALARM_DENY_LIST, TEST_CALLING_PACKAGE);
+ } else {
+ setDeviceConfigString(KEY_EXACT_ALARM_DENY_LIST, "");
+ }
+
+ when(mAppOpsManager.checkOpNoThrow(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID,
+ TEST_CALLING_PACKAGE)).thenReturn(mode);
+ }
+
@Test
public void alarmClockBinderCallWithoutPermission() throws RemoteException {
setDeviceConfigBoolean(KEY_CRASH_NON_CLOCK_APPS, true);
@@ -2003,10 +2009,7 @@
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
anyString(), any(UserHandle.class)));
- doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when(
- () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext),
- eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(),
- eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)));
+ mockExactAlarmPermissionGrant(true, false, MODE_ERRORED);
when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true);
final PendingIntent alarmPi = getNewMockPendingIntent();
@@ -2018,9 +2021,6 @@
} catch (SecurityException se) {
// Expected.
}
- verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext),
- eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(),
- eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)));
verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt());
}
@@ -2030,14 +2030,12 @@
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
anyString(), any(UserHandle.class)));
- // Permission check is granted by default by the mock.
+ mockExactAlarmPermissionGrant(true, false, MODE_ALLOWED);
final PendingIntent alarmPi = getNewMockPendingIntent();
mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
0, alarmPi, null, null, null, null);
- verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext),
- eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(),
- eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)));
+ verify(mService).hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID);
verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt());
final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
@@ -2057,19 +2055,13 @@
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
anyString(), any(UserHandle.class)));
// If permission is denied, only then allowlist will be checked.
- doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when(
- () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext),
- eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(),
- eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)));
+ mockExactAlarmPermissionGrant(true, false, MODE_ERRORED);
when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true);
final PendingIntent alarmPi = getNewMockPendingIntent();
mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
0, alarmPi, null, null, null, null);
- verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext),
- eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(),
- eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)));
verify(mDeviceIdleInternal).isAppOnWhitelist(UserHandle.getAppId(TEST_CALLING_UID));
verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L),
@@ -2084,14 +2076,11 @@
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
anyString(), any(UserHandle.class)));
- // Permission check is granted by default by the mock.
+ mockExactAlarmPermissionGrant(true, false, MODE_ALLOWED);
final PendingIntent alarmPi = getNewMockPendingIntent();
mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null);
- verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext),
- eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(),
- eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)));
verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt());
final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
@@ -2111,19 +2100,13 @@
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
anyString(), any(UserHandle.class)));
// If permission is denied, only then allowlist will be checked.
- doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when(
- () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext),
- eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(),
- eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)));
+ mockExactAlarmPermissionGrant(true, false, MODE_ERRORED);
when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true);
final PendingIntent alarmPi = getNewMockPendingIntent();
mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null);
- verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext),
- eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(),
- eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)));
verify(mDeviceIdleInternal).isAppOnWhitelist(UserHandle.getAppId(TEST_CALLING_UID));
final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
@@ -2145,10 +2128,7 @@
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
anyString(), any(UserHandle.class)));
- doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when(
- () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext),
- eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(),
- eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)));
+ mockExactAlarmPermissionGrant(true, false, MODE_ERRORED);
when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(false);
final PendingIntent alarmPi = getNewMockPendingIntent();
@@ -2166,9 +2146,6 @@
} catch (SecurityException se) {
// Expected.
}
- verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext),
- eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(),
- eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)), times(2));
verify(mDeviceIdleInternal, times(2)).isAppOnWhitelist(anyInt());
}
@@ -2184,9 +2161,7 @@
mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 4321, WINDOW_HEURISTIC, 0,
FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null);
- verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext),
- eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(),
- eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)), never());
+ verify(mService, never()).hasScheduleExactAlarmInternal(anyString(), anyInt());
verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt());
final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
@@ -2205,10 +2180,7 @@
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
anyString(), any(UserHandle.class)));
- doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when(
- () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext),
- eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(),
- eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)));
+ mockExactAlarmPermissionGrant(true, false, MODE_ERRORED);
when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true);
when(mAppStateTracker.isUidPowerSaveUserExempt(TEST_CALLING_UID)).thenReturn(true);
@@ -2355,10 +2327,7 @@
@Test
public void opScheduleExactAlarmRevoked() throws Exception {
- doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when(
- () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext),
- eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(),
- eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)));
+ 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,
@@ -2456,6 +2425,73 @@
assertEquals(LazyAlarmStore.TAG, mService.mAlarmStore.getName());
}
+ private void registerAppIds(String[] packages, Integer[] ids) {
+ assertEquals(packages.length, ids.length);
+
+ when(mPackageManagerInternal.getPackageUid(anyString(), anyInt(), anyInt())).thenAnswer(
+ invocation -> {
+ final String pkg = invocation.getArgument(0);
+ final int index = ArrayUtils.indexOf(packages, pkg);
+ if (index < 0) {
+ return index;
+ }
+ final int userId = invocation.getArgument(2);
+ return UserHandle.getUid(userId, ids[index]);
+ });
+ }
+
+ @Test
+ public void refreshExactAlarmCandidatesOnPackageAdded() {
+ final String[] exactAlarmRequesters = new String[]{"p11", "p2", "p9"};
+ final Integer[] appIds = new Integer[]{11, 2, 9};
+ registerAppIds(exactAlarmRequesters, appIds);
+
+ when(mPermissionManagerInternal.getAppOpPermissionPackages(
+ SCHEDULE_EXACT_ALARM)).thenReturn(exactAlarmRequesters);
+
+ final Intent packageAdded = new Intent(Intent.ACTION_PACKAGE_ADDED)
+ .setPackage(TEST_CALLING_PACKAGE);
+ mPackageChangesReceiver.onReceive(mMockContext, packageAdded);
+
+ assertAndHandleMessageSync(REFRESH_EXACT_ALARM_CANDIDATES);
+ assertEquals(new ArraySet<>(appIds), mService.mExactAlarmCandidates);
+ }
+
+ @Test
+ public void refreshExactAlarmCandidatesOnPackageReplaced() {
+ final String[] exactAlarmRequesters = new String[]{"p15", "p21", "p3"};
+ final Integer[] appIds = new Integer[]{15, 21, 3};
+ registerAppIds(exactAlarmRequesters, appIds);
+
+ when(mPermissionManagerInternal.getAppOpPermissionPackages(
+ SCHEDULE_EXACT_ALARM)).thenReturn(exactAlarmRequesters);
+
+ final Intent packageAdded = new Intent(Intent.ACTION_PACKAGE_ADDED)
+ .setPackage(TEST_CALLING_PACKAGE)
+ .putExtra(Intent.EXTRA_REPLACING, true);
+ mPackageChangesReceiver.onReceive(mMockContext, packageAdded);
+
+ assertAndHandleMessageSync(REFRESH_EXACT_ALARM_CANDIDATES);
+ assertEquals(new ArraySet<>(appIds), mService.mExactAlarmCandidates);
+ }
+
+ @Test
+ public void refreshExactAlarmCandidatesOnPackageRemoved() {
+ final String[] exactAlarmRequesters = new String[]{"p99", "p1", "p19"};
+ final Integer[] appIds = new Integer[]{99, 1, 19};
+ registerAppIds(exactAlarmRequesters, appIds);
+
+ when(mPermissionManagerInternal.getAppOpPermissionPackages(
+ SCHEDULE_EXACT_ALARM)).thenReturn(exactAlarmRequesters);
+
+ final Intent packageRemoved = new Intent(Intent.ACTION_PACKAGE_REMOVED)
+ .setPackage(TEST_CALLING_PACKAGE);
+ mPackageChangesReceiver.onReceive(mMockContext, packageRemoved);
+
+ assertAndHandleMessageSync(REFRESH_EXACT_ALARM_CANDIDATES);
+ assertEquals(new ArraySet<>(appIds), mService.mExactAlarmCandidates);
+ }
+
@After
public void tearDown() {
if (mMockingSession != null) {
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 916a278..a246917 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -59,11 +59,14 @@
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import static org.mockito.AdditionalMatchers.not;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
import android.annotation.NonNull;
import android.app.ActivityManager;
@@ -77,6 +80,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.hardware.display.DisplayManager;
import android.os.Handler;
import android.os.Looper;
@@ -92,6 +96,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.util.ArrayUtils;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
@@ -101,6 +106,8 @@
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import java.io.File;
import java.util.ArrayList;
@@ -194,6 +201,8 @@
}
static class MyInjector extends AppStandbyController.Injector {
+ @Mock
+ private PackageManagerInternal mPackageManagerInternal;
long mElapsedRealtime;
boolean mIsAppIdleEnabled = true;
boolean mIsCharging;
@@ -222,6 +231,7 @@
MyInjector(Context context, Looper looper) {
super(context, looper);
+ MockitoAnnotations.initMocks(this);
}
@Override
@@ -269,6 +279,11 @@
}
@Override
+ PackageManagerInternal getPackageManagerInternal() {
+ return mPackageManagerInternal;
+ }
+
+ @Override
void updatePowerWhitelistCache() {
}
@@ -491,6 +506,37 @@
mInjector.mElapsedRealtime, false));
}
+ @Test
+ public void testGetIdleUidsForUser() {
+ final AppStandbyController controllerUnderTest = spy(mController);
+
+ final int userIdForTest = 325;
+ final int[] uids = new int[]{129, 23, 129, 129, 44, 23, 41, 751};
+ final boolean[] idle = new boolean[]{true, true, false, true, false, true, false, true};
+ // Based on uids[] and idle[], the only two uids that have all true's in idle[].
+ final int[] expectedIdleUids = new int[]{23, 751};
+
+ final List<ApplicationInfo> installedApps = new ArrayList<>();
+ for (int i = 0; i < uids.length; i++) {
+ final ApplicationInfo ai = mock(ApplicationInfo.class);
+ ai.uid = uids[i];
+ ai.packageName = "example.package.name." + i;
+ installedApps.add(ai);
+ when(controllerUnderTest.isAppIdleFiltered(eq(ai.packageName),
+ eq(UserHandle.getAppId(ai.uid)), eq(userIdForTest), anyLong()))
+ .thenReturn(idle[i]);
+ }
+ when(mInjector.mPackageManagerInternal.getInstalledApplications(anyInt(), eq(userIdForTest),
+ anyInt())).thenReturn(installedApps);
+ final int[] returnedIdleUids = controllerUnderTest.getIdleUidsForUser(userIdForTest);
+
+ assertEquals(expectedIdleUids.length, returnedIdleUids.length);
+ for (final int uid : expectedIdleUids) {
+ assertTrue("Idle uid: " + uid + " not found in result: " + Arrays.toString(
+ returnedIdleUids), ArrayUtils.contains(returnedIdleUids, uid));
+ }
+ }
+
private static class TestParoleListener extends AppIdleStateChangeListener {
private boolean mIsParoleOn = false;
private CountDownLatch mLatch;