Merge "Optimizing AppStandby.getIdleUidsForUser" into sc-dev

This commit is contained in:
TreeHugger Robot
2021-04-23 02:00:08 +00:00
committed by Android (Google) Code Review
4 changed files with 281 additions and 172 deletions

View File

@@ -59,7 +59,7 @@ import android.content.BroadcastReceiver;
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.JobSchedulerBackgroundThread;
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 @@ public class AlarmManagerService extends SystemService {
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 @@ public class AlarmManagerService extends SystemService {
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 @@ public class AlarmManagerService extends SystemService {
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 @@ public class AlarmManagerService extends SystemService {
boolean hasScheduleExactAlarmInternal(String packageName, int uid) {
final long start = mStatLogger.getTime();
// No locking needed as EXACT_ALARM_DENY_LIST is immutable.
final boolean isOnDenyList = mConstants.EXACT_ALARM_DENY_LIST.contains(packageName);
if (isOnDenyList && mAppOps.checkOpNoThrow(AppOpsManager.OP_SCHEDULE_EXACT_ALARM, uid,
packageName) != AppOpsManager.MODE_ALLOWED) {
return false;
final boolean 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;
}
/**
@@ -2489,6 +2518,9 @@ public class AlarmManagerService extends SystemService {
pw.print("Num time change events: ");
pw.println(mNumTimeChanged);
pw.println();
pw.println("App ids requesting SCHEDULE_EXACT_ALARM: " + mExactAlarmCandidates);
pw.println();
pw.println("Next alarm clock information: ");
pw.increaseIndent();
@@ -3924,6 +3956,7 @@ public class AlarmManagerService extends SystemService {
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 @@ public class AlarmManagerService extends SystemService {
handlePackagesAddedToExactAlarmsDenyListLocked((ArraySet<String>) msg.obj);
}
break;
case REFRESH_EXACT_ALARM_CANDIDATES:
refreshExactAlarmCandidates();
break;
default:
// nope, just ignore it
break;
@@ -4135,6 +4171,7 @@ public class AlarmManagerService extends SystemService {
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 @@ public class AlarmManagerService extends SystemService {
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 @@ public class AlarmManagerService extends SystemService {
}
}
break;
case Intent.ACTION_PACKAGE_ADDED:
mHandler.sendEmptyMessage(AlarmHandler.REFRESH_EXACT_ALARM_CANDIDATES);
return;
}
if (pkgList != null && (pkgList.length > 0)) {
for (String pkg : pkgList) {

View File

@@ -58,7 +58,6 @@ import android.annotation.NonNull;
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.CrossProfileAppsInternal;
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.ArraySet;
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.LocalServices;
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 @@ public class AppStandbyController
@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

View File

@@ -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.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.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.never;
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.compat.CompatChanges;
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.dx.mockito.inline.extended.MockedVoidMethod;
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.quality.Strictness;
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 @@ public class AlarmManagerServiceTest {
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 @@ public class AlarmManagerServiceTest {
@Mock
private DeviceIdleInternal mDeviceIdleInternal;
@Mock
private PermissionManagerServiceInternal mPermissionManagerInternal;
@Mock
private UsageStatsManagerInternal mUsageStatsManagerInternal;
@Mock
private AppStandbyInternal mAppStandbyInternal;
@@ -351,7 +359,6 @@ public class AlarmManagerServiceTest {
.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 @@ public class AlarmManagerServiceTest {
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 @@ public class AlarmManagerServiceTest {
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 @@ public class AlarmManagerServiceTest {
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 @@ public class AlarmManagerServiceTest {
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 @@ public class AlarmManagerServiceTest {
}
@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 @@ public class AlarmManagerServiceTest {
mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_HEURISTIC, 0,
FLAG_ALLOW_WHILE_IDLE, getNewMockPendingIntent(), null, null, null, null);
verify(() -> PermissionChecker.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 @@ public class AlarmManagerServiceTest {
() -> 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 @@ public class AlarmManagerServiceTest {
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 @@ public class AlarmManagerServiceTest {
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 @@ public class AlarmManagerServiceTest {
() -> 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 @@ public class AlarmManagerServiceTest {
} 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 @@ public class AlarmManagerServiceTest {
() -> 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 @@ public class AlarmManagerServiceTest {
() -> 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 @@ public class AlarmManagerServiceTest {
() -> 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 @@ public class AlarmManagerServiceTest {
() -> 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 @@ public class AlarmManagerServiceTest {
() -> 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 @@ public class AlarmManagerServiceTest {
} 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 @@ public class AlarmManagerServiceTest {
mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 4321, WINDOW_HEURISTIC, 0,
FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null);
verify(() -> PermissionChecker.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 @@ public class AlarmManagerServiceTest {
() -> 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 @@ public class AlarmManagerServiceTest {
@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 @@ public class AlarmManagerServiceTest {
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) {

View File

@@ -59,11 +59,14 @@ import static org.junit.Assert.assertTrue;
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.ActivityInfo;
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.InstrumentationRegistry;
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.Before;
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 @@ public class AppStandbyControllerTests {
}
static class MyInjector extends AppStandbyController.Injector {
@Mock
private PackageManagerInternal mPackageManagerInternal;
long mElapsedRealtime;
boolean mIsAppIdleEnabled = true;
boolean mIsCharging;
@@ -222,6 +231,7 @@ public class AppStandbyControllerTests {
MyInjector(Context context, Looper looper) {
super(context, looper);
MockitoAnnotations.initMocks(this);
}
@Override
@@ -268,6 +278,11 @@ public class AppStandbyControllerTests {
return mClockApps.contains(Pair.create(packageName, uid));
}
@Override
PackageManagerInternal getPackageManagerInternal() {
return mPackageManagerInternal;
}
@Override
void updatePowerWhitelistCache() {
}
@@ -491,6 +506,37 @@ public class AppStandbyControllerTests {
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;