Loading services/core/java/com/android/server/am/BroadcastProcessQueue.java +30 −8 Original line number Diff line number Diff line Loading @@ -200,6 +200,7 @@ class BroadcastProcessQueue { */ private boolean mLastDeferredStates; private boolean mUidForeground; private boolean mUidCached; private boolean mProcessInstrumented; private boolean mProcessPersistent; Loading Loading @@ -409,7 +410,8 @@ class BroadcastProcessQueue { * {@link BroadcastQueueModernImpl#updateRunnableList} */ @CheckResult public boolean setProcessAndUidCached(@Nullable ProcessRecord app, boolean uidCached) { public boolean setProcessAndUidState(@Nullable ProcessRecord app, boolean uidForeground, boolean uidCached) { this.app = app; // Since we may have just changed our PID, invalidate cached strings Loading @@ -419,16 +421,34 @@ class BroadcastProcessQueue { boolean didSomething = false; if (app != null) { didSomething |= setUidCached(uidCached); didSomething |= setUidForeground(uidForeground); didSomething |= setProcessInstrumented(app.getActiveInstrumentation() != null); didSomething |= setProcessPersistent(app.isPersistent()); } else { didSomething |= setUidCached(uidCached); didSomething |= setUidForeground(false); didSomething |= setProcessInstrumented(false); didSomething |= setProcessPersistent(false); } return didSomething; } /** * Update if the UID this process is belongs to is in "foreground" state, which signals * broadcast dispatch should prioritize delivering broadcasts to this process to minimize any * delays in UI updates. */ @CheckResult private boolean setUidForeground(boolean uidForeground) { if (mUidForeground != uidForeground) { mUidForeground = uidForeground; invalidateRunnableAt(); return true; } else { return false; } } /** * Update if this process is in the "cached" state, typically signaling that * broadcast dispatch should be paused or delayed. Loading Loading @@ -994,7 +1014,7 @@ class BroadcastProcessQueue { static final int REASON_CONTAINS_RESULT_TO = 15; static final int REASON_CONTAINS_INSTRUMENTED = 16; static final int REASON_CONTAINS_MANIFEST = 17; static final int REASON_FOREGROUND_ACTIVITIES = 18; static final int REASON_FOREGROUND = 18; @IntDef(flag = false, prefix = { "REASON_" }, value = { REASON_EMPTY, Loading @@ -1014,7 +1034,7 @@ class BroadcastProcessQueue { REASON_CONTAINS_RESULT_TO, REASON_CONTAINS_INSTRUMENTED, REASON_CONTAINS_MANIFEST, REASON_FOREGROUND_ACTIVITIES, REASON_FOREGROUND, }) @Retention(RetentionPolicy.SOURCE) public @interface Reason {} Loading @@ -1038,7 +1058,7 @@ class BroadcastProcessQueue { case REASON_CONTAINS_RESULT_TO: return "CONTAINS_RESULT_TO"; case REASON_CONTAINS_INSTRUMENTED: return "CONTAINS_INSTRUMENTED"; case REASON_CONTAINS_MANIFEST: return "CONTAINS_MANIFEST"; case REASON_FOREGROUND_ACTIVITIES: return "FOREGROUND_ACTIVITIES"; case REASON_FOREGROUND: return "FOREGROUND"; default: return Integer.toString(reason); } } Loading Loading @@ -1077,11 +1097,9 @@ class BroadcastProcessQueue { } else if (mProcessInstrumented) { mRunnableAt = runnableAt + constants.DELAY_URGENT_MILLIS; mRunnableAtReason = REASON_INSTRUMENTED; } else if (app != null && app.hasForegroundActivities()) { // TODO: Listen for uid state changes to check when an uid goes in and out of // the TOP state. } else if (mUidForeground) { mRunnableAt = runnableAt + constants.DELAY_URGENT_MILLIS; mRunnableAtReason = REASON_FOREGROUND_ACTIVITIES; mRunnableAtReason = REASON_FOREGROUND; } else if (mCountOrdered > 0) { mRunnableAt = runnableAt; mRunnableAtReason = REASON_CONTAINS_ORDERED; Loading Loading @@ -1351,7 +1369,11 @@ class BroadcastProcessQueue { @NeverCompile private void dumpProcessState(@NonNull IndentingPrintWriter pw) { final StringBuilder sb = new StringBuilder(); if (mUidForeground) { sb.append("FG"); } if (mUidCached) { if (sb.length() > 0) sb.append("|"); sb.append("CACHED"); } if (mProcessInstrumented) { Loading services/core/java/com/android/server/am/BroadcastQueueModernImpl.java +67 −13 Original line number Diff line number Diff line Loading @@ -211,6 +211,17 @@ class BroadcastQueueModernImpl extends BroadcastQueue { private final AtomicReference<ArraySet<BroadcastRecord>> mReplacedBroadcastsCache = new AtomicReference<>(); /** * Map from UID to its last known "foreground" state. A UID is considered to be in * "foreground" state when it's procState is {@link ActivityManager#PROCESS_STATE_TOP}. * <p> * We manually maintain this data structure since the lifecycle of * {@link ProcessRecord} and {@link BroadcastProcessQueue} can be * mismatched. */ @GuardedBy("mService") private final SparseBooleanArray mUidForeground = new SparseBooleanArray(); /** * Map from UID to its last known "cached" state. * <p> Loading Loading @@ -1284,11 +1295,24 @@ class BroadcastQueueModernImpl extends BroadcastQueue { return UserHandle.getUserId(q.uid) == userId; }; broadcastPredicate = BROADCAST_PREDICATE_ANY; cleanupUserStateLocked(mUidCached, userId); cleanupUserStateLocked(mUidForeground, userId); } return forEachMatchingBroadcast(queuePredicate, broadcastPredicate, mBroadcastConsumerSkip, true); } @GuardedBy("mService") private void cleanupUserStateLocked(@NonNull SparseBooleanArray uidState, int userId) { for (int i = uidState.size() - 1; i >= 0; --i) { final int uid = uidState.keyAt(i); if (UserHandle.getUserId(uid) == userId) { uidState.removeAt(i); } } } private static final Predicate<BroadcastProcessQueue> QUEUE_PREDICATE_ANY = (q) -> true; private static final BroadcastPredicate BROADCAST_PREDICATE_ANY = Loading Loading @@ -1403,6 +1427,19 @@ class BroadcastQueueModernImpl extends BroadcastQueue { mBgConstants.startObserving(mHandler, resolver); mService.registerUidObserver(new UidObserver() { @Override public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) { synchronized (mService) { if (procState == ActivityManager.PROCESS_STATE_TOP) { mUidForeground.put(uid, true); } else { mUidForeground.delete(uid); } refreshProcessQueuesLocked(uid); } } @Override public void onUidCachedChanged(int uid, boolean cached) { synchronized (mService) { Loading @@ -1411,18 +1448,11 @@ class BroadcastQueueModernImpl extends BroadcastQueue { } else { mUidCached.delete(uid); } BroadcastProcessQueue leaf = mProcessQueues.get(uid); while (leaf != null) { // Update internal state by refreshing values previously // read from any known running process setQueueProcess(leaf, leaf.app); leaf = leaf.processNameNext; } enqueueUpdateRunningList(); refreshProcessQueuesLocked(uid); } } }, ActivityManager.UID_OBSERVER_CACHED, 0, "android"); }, ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_CACHED, ActivityManager.PROCESS_STATE_TOP, "android"); // Kick off periodic health checks mLocalHandler.sendEmptyMessage(MSG_CHECK_HEALTH); Loading Loading @@ -1611,8 +1641,9 @@ class BroadcastQueueModernImpl extends BroadcastQueue { // warm via this operation, we're going to immediately promote it to // be running, and any side effect of this operation will then apply // after it's finished and is returned to the runnable list. queue.setProcessAndUidCached( queue.setProcessAndUidState( mService.getProcessRecordLocked(queue.processName, queue.uid), mUidForeground.get(queue.uid, false), mUidCached.get(queue.uid, false)); } } Loading @@ -1624,11 +1655,28 @@ class BroadcastQueueModernImpl extends BroadcastQueue { */ private void setQueueProcess(@NonNull BroadcastProcessQueue queue, @Nullable ProcessRecord app) { if (queue.setProcessAndUidCached(app, mUidCached.get(queue.uid, false))) { if (queue.setProcessAndUidState(app, mUidForeground.get(queue.uid, false), mUidCached.get(queue.uid, false))) { updateRunnableList(queue); } } /** * Refresh the process queues with the latest process state so that runnableAt * can be updated. */ @GuardedBy("mService") private void refreshProcessQueuesLocked(int uid) { BroadcastProcessQueue leaf = mProcessQueues.get(uid); while (leaf != null) { // Update internal state by refreshing values previously // read from any known running process setQueueProcess(leaf, leaf.app); leaf = leaf.processNameNext; } enqueueUpdateRunningList(); } /** * Inform other parts of OS that the given broadcast queue has started * running, typically for internal bookkeeping. Loading Loading @@ -1950,7 +1998,13 @@ class BroadcastQueueModernImpl extends BroadcastQueue { ipw.println("Cached UIDs:"); ipw.increaseIndent(); ipw.println(mUidCached.toString()); ipw.println(mUidCached); ipw.decreaseIndent(); ipw.println(); ipw.println("Foreground UIDs:"); ipw.increaseIndent(); ipw.println(mUidForeground); ipw.decreaseIndent(); ipw.println(); Loading services/core/java/com/android/server/am/ProcessRecord.java +0 −5 Original line number Diff line number Diff line Loading @@ -1072,11 +1072,6 @@ class ProcessRecord implements WindowProcessListener { return mState.isCached(); } @GuardedBy(anyOf = {"mService", "mProcLock"}) public boolean hasForegroundActivities() { return mState.hasForegroundActivities(); } boolean hasActivities() { return mWindowProcessController.hasActivities(); } Loading services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java +29 −7 Original line number Diff line number Diff line Loading @@ -393,9 +393,9 @@ public final class BroadcastQueueModernImplTest { List.of(makeMockRegisteredReceiver()), false); enqueueOrReplaceBroadcast(queue, airplaneRecord, 0); queue.setProcessAndUidCached(null, false); queue.setProcessAndUidState(null, false, false); final long notCachedRunnableAt = queue.getRunnableAt(); queue.setProcessAndUidCached(null, true); queue.setProcessAndUidState(null, false, true); final long cachedRunnableAt = queue.getRunnableAt(); assertThat(cachedRunnableAt).isGreaterThan(notCachedRunnableAt); assertFalse(queue.isRunnable()); Loading @@ -420,9 +420,9 @@ public final class BroadcastQueueModernImplTest { List.of(makeMockRegisteredReceiver()), false); enqueueOrReplaceBroadcast(queue, airplaneRecord, 0); queue.setProcessAndUidCached(null, false); queue.setProcessAndUidState(null, false, false); final long notCachedRunnableAt = queue.getRunnableAt(); queue.setProcessAndUidCached(null, true); queue.setProcessAndUidState(null, false, true); final long cachedRunnableAt = queue.getRunnableAt(); assertThat(cachedRunnableAt).isGreaterThan(notCachedRunnableAt); assertTrue(queue.isRunnable()); Loading Loading @@ -452,13 +452,13 @@ public final class BroadcastQueueModernImplTest { // verify that: // (a) the queue is immediately runnable by existence of a fg-priority broadcast // (b) the next one up is the fg-priority broadcast despite its later enqueue time queue.setProcessAndUidCached(null, false); queue.setProcessAndUidState(null, false, false); assertTrue(queue.isRunnable()); assertThat(queue.getRunnableAt()).isAtMost(airplaneRecord.enqueueClockTime); assertEquals(ProcessList.SCHED_GROUP_UNDEFINED, queue.getPreferredSchedulingGroupLocked()); assertEquals(queue.peekNextBroadcastRecord(), airplaneRecord); queue.setProcessAndUidCached(null, true); queue.setProcessAndUidState(null, false, true); assertTrue(queue.isRunnable()); assertThat(queue.getRunnableAt()).isAtMost(airplaneRecord.enqueueClockTime); assertEquals(ProcessList.SCHED_GROUP_UNDEFINED, queue.getPreferredSchedulingGroupLocked()); Loading Loading @@ -515,6 +515,28 @@ public final class BroadcastQueueModernImplTest { assertEquals(BroadcastProcessQueue.REASON_MAX_PENDING, queue.getRunnableAtReason()); } @Test public void testRunnableAt_uidForeground() { final BroadcastProcessQueue queue = new BroadcastProcessQueue(mConstants, PACKAGE_GREEN, getUidForPackage(PACKAGE_GREEN)); final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK); final BroadcastRecord timeTickRecord = makeBroadcastRecord(timeTick, List.of(makeMockRegisteredReceiver())); enqueueOrReplaceBroadcast(queue, timeTickRecord, 0); assertThat(queue.getRunnableAt()).isGreaterThan(timeTickRecord.enqueueTime); assertEquals(BroadcastProcessQueue.REASON_NORMAL, queue.getRunnableAtReason()); queue.setProcessAndUidState(mProcess, true, false); assertThat(queue.getRunnableAt()).isLessThan(timeTickRecord.enqueueTime); assertEquals(BroadcastProcessQueue.REASON_FOREGROUND, queue.getRunnableAtReason()); queue.setProcessAndUidState(mProcess, false, false); assertThat(queue.getRunnableAt()).isGreaterThan(timeTickRecord.enqueueTime); assertEquals(BroadcastProcessQueue.REASON_NORMAL, queue.getRunnableAtReason()); } /** * Verify that a cached process that would normally be delayed becomes * immediately runnable when the given broadcast is enqueued. Loading @@ -522,7 +544,7 @@ public final class BroadcastQueueModernImplTest { private void doRunnableAt_Cached(BroadcastRecord testRecord, int testRunnableAtReason) { final BroadcastProcessQueue queue = new BroadcastProcessQueue(mConstants, PACKAGE_GREEN, getUidForPackage(PACKAGE_GREEN)); queue.setProcessAndUidCached(null, true); queue.setProcessAndUidState(null, false, true); final BroadcastRecord lazyRecord = makeBroadcastRecord( new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED), Loading services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java +74 −0 Original line number Diff line number Diff line Loading @@ -25,12 +25,15 @@ import static com.android.server.am.BroadcastProcessQueue.reasonToString; import static com.android.server.am.BroadcastRecord.deliveryStateToString; import static com.android.server.am.BroadcastRecord.isReceiverEquals; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; Loading Loading @@ -2132,4 +2135,75 @@ public class BroadcastQueueTest { waitForIdle(); verifyScheduleRegisteredReceiver(times(1), receiverGreenApp, airplane); } @Test public void testBroadcastDelivery_uidForeground() throws Exception { // Legacy stack doesn't support prioritization to foreground app. Assume.assumeTrue(mImpl == Impl.MODERN); final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED); final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE); final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN); mUidObserver.onUidStateChanged(receiverGreenApp.info.uid, ActivityManager.PROCESS_STATE_TOP, 0, ActivityManager.PROCESS_CAPABILITY_NONE); final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK); final BroadcastFilter receiverBlue = makeRegisteredReceiver(receiverBlueApp); final BroadcastFilter receiverGreen = makeRegisteredReceiver(receiverGreenApp); final BroadcastRecord airplaneRecord = makeBroadcastRecord(airplane, callerApp, List.of(receiverBlue)); final BroadcastRecord timeTickRecord = makeBroadcastRecord(timeTick, callerApp, List.of(receiverBlue, receiverGreen)); enqueueBroadcast(airplaneRecord); enqueueBroadcast(timeTickRecord); waitForIdle(); // Verify that broadcasts to receiverGreenApp gets scheduled first. assertThat(getReceiverScheduledTime(timeTickRecord, receiverGreen)) .isLessThan(getReceiverScheduledTime(airplaneRecord, receiverBlue)); assertThat(getReceiverScheduledTime(timeTickRecord, receiverGreen)) .isLessThan(getReceiverScheduledTime(timeTickRecord, receiverBlue)); } @Test public void testPrioritizedBroadcastDelivery_uidForeground() throws Exception { // Legacy stack doesn't support prioritization to foreground app. Assume.assumeTrue(mImpl == Impl.MODERN); final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED); final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE); final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN); mUidObserver.onUidStateChanged(receiverGreenApp.info.uid, ActivityManager.PROCESS_STATE_TOP, 0, ActivityManager.PROCESS_CAPABILITY_NONE); final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK); final BroadcastFilter receiverBlue = makeRegisteredReceiver(receiverBlueApp, 10); final BroadcastFilter receiverGreen = makeRegisteredReceiver(receiverGreenApp, 5); final BroadcastRecord prioritizedRecord = makeBroadcastRecord(timeTick, callerApp, List.of(receiverBlue, receiverGreen)); enqueueBroadcast(prioritizedRecord); waitForIdle(); // Verify that uid foreground-ness does not impact that delivery of prioritized broadcast. // That is, broadcast to receiverBlueApp gets scheduled before the one to receiverGreenApp. assertThat(getReceiverScheduledTime(prioritizedRecord, receiverGreen)) .isGreaterThan(getReceiverScheduledTime(prioritizedRecord, receiverBlue)); } private long getReceiverScheduledTime(@NonNull BroadcastRecord r, @NonNull Object receiver) { for (int i = 0; i < r.receivers.size(); ++i) { if (isReceiverEquals(receiver, r.receivers.get(i))) { return r.scheduledTime[i]; } } fail(receiver + "not found in " + r); return -1; } } Loading
services/core/java/com/android/server/am/BroadcastProcessQueue.java +30 −8 Original line number Diff line number Diff line Loading @@ -200,6 +200,7 @@ class BroadcastProcessQueue { */ private boolean mLastDeferredStates; private boolean mUidForeground; private boolean mUidCached; private boolean mProcessInstrumented; private boolean mProcessPersistent; Loading Loading @@ -409,7 +410,8 @@ class BroadcastProcessQueue { * {@link BroadcastQueueModernImpl#updateRunnableList} */ @CheckResult public boolean setProcessAndUidCached(@Nullable ProcessRecord app, boolean uidCached) { public boolean setProcessAndUidState(@Nullable ProcessRecord app, boolean uidForeground, boolean uidCached) { this.app = app; // Since we may have just changed our PID, invalidate cached strings Loading @@ -419,16 +421,34 @@ class BroadcastProcessQueue { boolean didSomething = false; if (app != null) { didSomething |= setUidCached(uidCached); didSomething |= setUidForeground(uidForeground); didSomething |= setProcessInstrumented(app.getActiveInstrumentation() != null); didSomething |= setProcessPersistent(app.isPersistent()); } else { didSomething |= setUidCached(uidCached); didSomething |= setUidForeground(false); didSomething |= setProcessInstrumented(false); didSomething |= setProcessPersistent(false); } return didSomething; } /** * Update if the UID this process is belongs to is in "foreground" state, which signals * broadcast dispatch should prioritize delivering broadcasts to this process to minimize any * delays in UI updates. */ @CheckResult private boolean setUidForeground(boolean uidForeground) { if (mUidForeground != uidForeground) { mUidForeground = uidForeground; invalidateRunnableAt(); return true; } else { return false; } } /** * Update if this process is in the "cached" state, typically signaling that * broadcast dispatch should be paused or delayed. Loading Loading @@ -994,7 +1014,7 @@ class BroadcastProcessQueue { static final int REASON_CONTAINS_RESULT_TO = 15; static final int REASON_CONTAINS_INSTRUMENTED = 16; static final int REASON_CONTAINS_MANIFEST = 17; static final int REASON_FOREGROUND_ACTIVITIES = 18; static final int REASON_FOREGROUND = 18; @IntDef(flag = false, prefix = { "REASON_" }, value = { REASON_EMPTY, Loading @@ -1014,7 +1034,7 @@ class BroadcastProcessQueue { REASON_CONTAINS_RESULT_TO, REASON_CONTAINS_INSTRUMENTED, REASON_CONTAINS_MANIFEST, REASON_FOREGROUND_ACTIVITIES, REASON_FOREGROUND, }) @Retention(RetentionPolicy.SOURCE) public @interface Reason {} Loading @@ -1038,7 +1058,7 @@ class BroadcastProcessQueue { case REASON_CONTAINS_RESULT_TO: return "CONTAINS_RESULT_TO"; case REASON_CONTAINS_INSTRUMENTED: return "CONTAINS_INSTRUMENTED"; case REASON_CONTAINS_MANIFEST: return "CONTAINS_MANIFEST"; case REASON_FOREGROUND_ACTIVITIES: return "FOREGROUND_ACTIVITIES"; case REASON_FOREGROUND: return "FOREGROUND"; default: return Integer.toString(reason); } } Loading Loading @@ -1077,11 +1097,9 @@ class BroadcastProcessQueue { } else if (mProcessInstrumented) { mRunnableAt = runnableAt + constants.DELAY_URGENT_MILLIS; mRunnableAtReason = REASON_INSTRUMENTED; } else if (app != null && app.hasForegroundActivities()) { // TODO: Listen for uid state changes to check when an uid goes in and out of // the TOP state. } else if (mUidForeground) { mRunnableAt = runnableAt + constants.DELAY_URGENT_MILLIS; mRunnableAtReason = REASON_FOREGROUND_ACTIVITIES; mRunnableAtReason = REASON_FOREGROUND; } else if (mCountOrdered > 0) { mRunnableAt = runnableAt; mRunnableAtReason = REASON_CONTAINS_ORDERED; Loading Loading @@ -1351,7 +1369,11 @@ class BroadcastProcessQueue { @NeverCompile private void dumpProcessState(@NonNull IndentingPrintWriter pw) { final StringBuilder sb = new StringBuilder(); if (mUidForeground) { sb.append("FG"); } if (mUidCached) { if (sb.length() > 0) sb.append("|"); sb.append("CACHED"); } if (mProcessInstrumented) { Loading
services/core/java/com/android/server/am/BroadcastQueueModernImpl.java +67 −13 Original line number Diff line number Diff line Loading @@ -211,6 +211,17 @@ class BroadcastQueueModernImpl extends BroadcastQueue { private final AtomicReference<ArraySet<BroadcastRecord>> mReplacedBroadcastsCache = new AtomicReference<>(); /** * Map from UID to its last known "foreground" state. A UID is considered to be in * "foreground" state when it's procState is {@link ActivityManager#PROCESS_STATE_TOP}. * <p> * We manually maintain this data structure since the lifecycle of * {@link ProcessRecord} and {@link BroadcastProcessQueue} can be * mismatched. */ @GuardedBy("mService") private final SparseBooleanArray mUidForeground = new SparseBooleanArray(); /** * Map from UID to its last known "cached" state. * <p> Loading Loading @@ -1284,11 +1295,24 @@ class BroadcastQueueModernImpl extends BroadcastQueue { return UserHandle.getUserId(q.uid) == userId; }; broadcastPredicate = BROADCAST_PREDICATE_ANY; cleanupUserStateLocked(mUidCached, userId); cleanupUserStateLocked(mUidForeground, userId); } return forEachMatchingBroadcast(queuePredicate, broadcastPredicate, mBroadcastConsumerSkip, true); } @GuardedBy("mService") private void cleanupUserStateLocked(@NonNull SparseBooleanArray uidState, int userId) { for (int i = uidState.size() - 1; i >= 0; --i) { final int uid = uidState.keyAt(i); if (UserHandle.getUserId(uid) == userId) { uidState.removeAt(i); } } } private static final Predicate<BroadcastProcessQueue> QUEUE_PREDICATE_ANY = (q) -> true; private static final BroadcastPredicate BROADCAST_PREDICATE_ANY = Loading Loading @@ -1403,6 +1427,19 @@ class BroadcastQueueModernImpl extends BroadcastQueue { mBgConstants.startObserving(mHandler, resolver); mService.registerUidObserver(new UidObserver() { @Override public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) { synchronized (mService) { if (procState == ActivityManager.PROCESS_STATE_TOP) { mUidForeground.put(uid, true); } else { mUidForeground.delete(uid); } refreshProcessQueuesLocked(uid); } } @Override public void onUidCachedChanged(int uid, boolean cached) { synchronized (mService) { Loading @@ -1411,18 +1448,11 @@ class BroadcastQueueModernImpl extends BroadcastQueue { } else { mUidCached.delete(uid); } BroadcastProcessQueue leaf = mProcessQueues.get(uid); while (leaf != null) { // Update internal state by refreshing values previously // read from any known running process setQueueProcess(leaf, leaf.app); leaf = leaf.processNameNext; } enqueueUpdateRunningList(); refreshProcessQueuesLocked(uid); } } }, ActivityManager.UID_OBSERVER_CACHED, 0, "android"); }, ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_CACHED, ActivityManager.PROCESS_STATE_TOP, "android"); // Kick off periodic health checks mLocalHandler.sendEmptyMessage(MSG_CHECK_HEALTH); Loading Loading @@ -1611,8 +1641,9 @@ class BroadcastQueueModernImpl extends BroadcastQueue { // warm via this operation, we're going to immediately promote it to // be running, and any side effect of this operation will then apply // after it's finished and is returned to the runnable list. queue.setProcessAndUidCached( queue.setProcessAndUidState( mService.getProcessRecordLocked(queue.processName, queue.uid), mUidForeground.get(queue.uid, false), mUidCached.get(queue.uid, false)); } } Loading @@ -1624,11 +1655,28 @@ class BroadcastQueueModernImpl extends BroadcastQueue { */ private void setQueueProcess(@NonNull BroadcastProcessQueue queue, @Nullable ProcessRecord app) { if (queue.setProcessAndUidCached(app, mUidCached.get(queue.uid, false))) { if (queue.setProcessAndUidState(app, mUidForeground.get(queue.uid, false), mUidCached.get(queue.uid, false))) { updateRunnableList(queue); } } /** * Refresh the process queues with the latest process state so that runnableAt * can be updated. */ @GuardedBy("mService") private void refreshProcessQueuesLocked(int uid) { BroadcastProcessQueue leaf = mProcessQueues.get(uid); while (leaf != null) { // Update internal state by refreshing values previously // read from any known running process setQueueProcess(leaf, leaf.app); leaf = leaf.processNameNext; } enqueueUpdateRunningList(); } /** * Inform other parts of OS that the given broadcast queue has started * running, typically for internal bookkeeping. Loading Loading @@ -1950,7 +1998,13 @@ class BroadcastQueueModernImpl extends BroadcastQueue { ipw.println("Cached UIDs:"); ipw.increaseIndent(); ipw.println(mUidCached.toString()); ipw.println(mUidCached); ipw.decreaseIndent(); ipw.println(); ipw.println("Foreground UIDs:"); ipw.increaseIndent(); ipw.println(mUidForeground); ipw.decreaseIndent(); ipw.println(); Loading
services/core/java/com/android/server/am/ProcessRecord.java +0 −5 Original line number Diff line number Diff line Loading @@ -1072,11 +1072,6 @@ class ProcessRecord implements WindowProcessListener { return mState.isCached(); } @GuardedBy(anyOf = {"mService", "mProcLock"}) public boolean hasForegroundActivities() { return mState.hasForegroundActivities(); } boolean hasActivities() { return mWindowProcessController.hasActivities(); } Loading
services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java +29 −7 Original line number Diff line number Diff line Loading @@ -393,9 +393,9 @@ public final class BroadcastQueueModernImplTest { List.of(makeMockRegisteredReceiver()), false); enqueueOrReplaceBroadcast(queue, airplaneRecord, 0); queue.setProcessAndUidCached(null, false); queue.setProcessAndUidState(null, false, false); final long notCachedRunnableAt = queue.getRunnableAt(); queue.setProcessAndUidCached(null, true); queue.setProcessAndUidState(null, false, true); final long cachedRunnableAt = queue.getRunnableAt(); assertThat(cachedRunnableAt).isGreaterThan(notCachedRunnableAt); assertFalse(queue.isRunnable()); Loading @@ -420,9 +420,9 @@ public final class BroadcastQueueModernImplTest { List.of(makeMockRegisteredReceiver()), false); enqueueOrReplaceBroadcast(queue, airplaneRecord, 0); queue.setProcessAndUidCached(null, false); queue.setProcessAndUidState(null, false, false); final long notCachedRunnableAt = queue.getRunnableAt(); queue.setProcessAndUidCached(null, true); queue.setProcessAndUidState(null, false, true); final long cachedRunnableAt = queue.getRunnableAt(); assertThat(cachedRunnableAt).isGreaterThan(notCachedRunnableAt); assertTrue(queue.isRunnable()); Loading Loading @@ -452,13 +452,13 @@ public final class BroadcastQueueModernImplTest { // verify that: // (a) the queue is immediately runnable by existence of a fg-priority broadcast // (b) the next one up is the fg-priority broadcast despite its later enqueue time queue.setProcessAndUidCached(null, false); queue.setProcessAndUidState(null, false, false); assertTrue(queue.isRunnable()); assertThat(queue.getRunnableAt()).isAtMost(airplaneRecord.enqueueClockTime); assertEquals(ProcessList.SCHED_GROUP_UNDEFINED, queue.getPreferredSchedulingGroupLocked()); assertEquals(queue.peekNextBroadcastRecord(), airplaneRecord); queue.setProcessAndUidCached(null, true); queue.setProcessAndUidState(null, false, true); assertTrue(queue.isRunnable()); assertThat(queue.getRunnableAt()).isAtMost(airplaneRecord.enqueueClockTime); assertEquals(ProcessList.SCHED_GROUP_UNDEFINED, queue.getPreferredSchedulingGroupLocked()); Loading Loading @@ -515,6 +515,28 @@ public final class BroadcastQueueModernImplTest { assertEquals(BroadcastProcessQueue.REASON_MAX_PENDING, queue.getRunnableAtReason()); } @Test public void testRunnableAt_uidForeground() { final BroadcastProcessQueue queue = new BroadcastProcessQueue(mConstants, PACKAGE_GREEN, getUidForPackage(PACKAGE_GREEN)); final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK); final BroadcastRecord timeTickRecord = makeBroadcastRecord(timeTick, List.of(makeMockRegisteredReceiver())); enqueueOrReplaceBroadcast(queue, timeTickRecord, 0); assertThat(queue.getRunnableAt()).isGreaterThan(timeTickRecord.enqueueTime); assertEquals(BroadcastProcessQueue.REASON_NORMAL, queue.getRunnableAtReason()); queue.setProcessAndUidState(mProcess, true, false); assertThat(queue.getRunnableAt()).isLessThan(timeTickRecord.enqueueTime); assertEquals(BroadcastProcessQueue.REASON_FOREGROUND, queue.getRunnableAtReason()); queue.setProcessAndUidState(mProcess, false, false); assertThat(queue.getRunnableAt()).isGreaterThan(timeTickRecord.enqueueTime); assertEquals(BroadcastProcessQueue.REASON_NORMAL, queue.getRunnableAtReason()); } /** * Verify that a cached process that would normally be delayed becomes * immediately runnable when the given broadcast is enqueued. Loading @@ -522,7 +544,7 @@ public final class BroadcastQueueModernImplTest { private void doRunnableAt_Cached(BroadcastRecord testRecord, int testRunnableAtReason) { final BroadcastProcessQueue queue = new BroadcastProcessQueue(mConstants, PACKAGE_GREEN, getUidForPackage(PACKAGE_GREEN)); queue.setProcessAndUidCached(null, true); queue.setProcessAndUidState(null, false, true); final BroadcastRecord lazyRecord = makeBroadcastRecord( new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED), Loading
services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java +74 −0 Original line number Diff line number Diff line Loading @@ -25,12 +25,15 @@ import static com.android.server.am.BroadcastProcessQueue.reasonToString; import static com.android.server.am.BroadcastRecord.deliveryStateToString; import static com.android.server.am.BroadcastRecord.isReceiverEquals; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; Loading Loading @@ -2132,4 +2135,75 @@ public class BroadcastQueueTest { waitForIdle(); verifyScheduleRegisteredReceiver(times(1), receiverGreenApp, airplane); } @Test public void testBroadcastDelivery_uidForeground() throws Exception { // Legacy stack doesn't support prioritization to foreground app. Assume.assumeTrue(mImpl == Impl.MODERN); final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED); final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE); final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN); mUidObserver.onUidStateChanged(receiverGreenApp.info.uid, ActivityManager.PROCESS_STATE_TOP, 0, ActivityManager.PROCESS_CAPABILITY_NONE); final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK); final BroadcastFilter receiverBlue = makeRegisteredReceiver(receiverBlueApp); final BroadcastFilter receiverGreen = makeRegisteredReceiver(receiverGreenApp); final BroadcastRecord airplaneRecord = makeBroadcastRecord(airplane, callerApp, List.of(receiverBlue)); final BroadcastRecord timeTickRecord = makeBroadcastRecord(timeTick, callerApp, List.of(receiverBlue, receiverGreen)); enqueueBroadcast(airplaneRecord); enqueueBroadcast(timeTickRecord); waitForIdle(); // Verify that broadcasts to receiverGreenApp gets scheduled first. assertThat(getReceiverScheduledTime(timeTickRecord, receiverGreen)) .isLessThan(getReceiverScheduledTime(airplaneRecord, receiverBlue)); assertThat(getReceiverScheduledTime(timeTickRecord, receiverGreen)) .isLessThan(getReceiverScheduledTime(timeTickRecord, receiverBlue)); } @Test public void testPrioritizedBroadcastDelivery_uidForeground() throws Exception { // Legacy stack doesn't support prioritization to foreground app. Assume.assumeTrue(mImpl == Impl.MODERN); final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED); final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE); final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN); mUidObserver.onUidStateChanged(receiverGreenApp.info.uid, ActivityManager.PROCESS_STATE_TOP, 0, ActivityManager.PROCESS_CAPABILITY_NONE); final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK); final BroadcastFilter receiverBlue = makeRegisteredReceiver(receiverBlueApp, 10); final BroadcastFilter receiverGreen = makeRegisteredReceiver(receiverGreenApp, 5); final BroadcastRecord prioritizedRecord = makeBroadcastRecord(timeTick, callerApp, List.of(receiverBlue, receiverGreen)); enqueueBroadcast(prioritizedRecord); waitForIdle(); // Verify that uid foreground-ness does not impact that delivery of prioritized broadcast. // That is, broadcast to receiverBlueApp gets scheduled before the one to receiverGreenApp. assertThat(getReceiverScheduledTime(prioritizedRecord, receiverGreen)) .isGreaterThan(getReceiverScheduledTime(prioritizedRecord, receiverBlue)); } private long getReceiverScheduledTime(@NonNull BroadcastRecord r, @NonNull Object receiver) { for (int i = 0; i < r.receivers.size(); ++i) { if (isReceiverEquals(receiver, r.receivers.get(i))) { return r.scheduledTime[i]; } } fail(receiver + "not found in " + r); return -1; } }