Loading apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +53 −10 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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()); Loading Loading @@ -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; } /** Loading Loading @@ -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(); Loading Loading @@ -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()); Loading Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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(); Loading @@ -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) { Loading apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +31 −47 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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); } } 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 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); } 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++; } if (DEBUG) { Slog.d(TAG, "getIdleUids took " + (mInjector.elapsedRealtime() - elapsedRealtime)); } Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); return res; return idleUids; } @Override Loading services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java +148 −112 File changed.Preview size limit exceeded, changes collapsed. Show changes services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +46 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -194,6 +201,8 @@ public class AppStandbyControllerTests { } static class MyInjector extends AppStandbyController.Injector { @Mock private PackageManagerInternal mPackageManagerInternal; long mElapsedRealtime; boolean mIsAppIdleEnabled = true; boolean mIsCharging; Loading Loading @@ -222,6 +231,7 @@ public class AppStandbyControllerTests { MyInjector(Context context, Looper looper) { super(context, looper); MockitoAnnotations.initMocks(this); } @Override Loading Loading @@ -268,6 +278,11 @@ public class AppStandbyControllerTests { return mClockApps.contains(Pair.create(packageName, uid)); } @Override PackageManagerInternal getPackageManagerInternal() { return mPackageManagerInternal; } @Override void updatePowerWhitelistCache() { } Loading Loading @@ -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; Loading Loading
apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +53 −10 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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()); Loading Loading @@ -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; } /** Loading Loading @@ -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(); Loading Loading @@ -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()); Loading Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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(); Loading @@ -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) { Loading
apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +31 −47 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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); } } 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 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); } 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++; } if (DEBUG) { Slog.d(TAG, "getIdleUids took " + (mInjector.elapsedRealtime() - elapsedRealtime)); } Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); return res; return idleUids; } @Override Loading
services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java +148 −112 File changed.Preview size limit exceeded, changes collapsed. Show changes
services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +46 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -194,6 +201,8 @@ public class AppStandbyControllerTests { } static class MyInjector extends AppStandbyController.Injector { @Mock private PackageManagerInternal mPackageManagerInternal; long mElapsedRealtime; boolean mIsAppIdleEnabled = true; boolean mIsCharging; Loading Loading @@ -222,6 +231,7 @@ public class AppStandbyControllerTests { MyInjector(Context context, Looper looper) { super(context, looper); MockitoAnnotations.initMocks(this); } @Override Loading Loading @@ -268,6 +278,11 @@ public class AppStandbyControllerTests { return mClockApps.contains(Pair.create(packageName, uid)); } @Override PackageManagerInternal getPackageManagerInternal() { return mPackageManagerInternal; } @Override void updatePowerWhitelistCache() { } Loading Loading @@ -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; Loading