summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java7
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java14
-rw-r--r--services/core/java/com/android/server/am/BroadcastDispatcher.java32
-rw-r--r--services/core/java/com/android/server/am/BroadcastProcessQueue.java80
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java22
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueueImpl.java21
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueueModernImpl.java37
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java24
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java30
9 files changed, 244 insertions, 23 deletions
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5d3bb31bc31f..a31015ff7aa2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -18964,6 +18964,13 @@ public class ActivityManagerService extends IActivityManager.Stub
pw.flush();
}
+ void waitForBroadcastDispatch(@NonNull PrintWriter pw, @NonNull Intent intent) {
+ enforceCallingPermission(permission.DUMP, "waitForBroadcastDispatch");
+ for (BroadcastQueue queue : mBroadcastQueues) {
+ queue.waitForDispatched(intent, pw);
+ }
+ }
+
void setIgnoreDeliveryGroupPolicy(@NonNull String broadcastAction) {
Objects.requireNonNull(broadcastAction);
enforceCallingPermission(permission.DUMP, "waitForBroadcastBarrier()");
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 17a0d62c27b3..8759e3f207c4 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -368,6 +368,8 @@ final class ActivityManagerShellCommand extends ShellCommand {
return runWaitForBroadcastBarrier(pw);
case "wait-for-application-barrier":
return runWaitForApplicationBarrier(pw);
+ case "wait-for-broadcast-dispatch":
+ return runWaitForBroadcastDispatch(pw);
case "set-ignore-delivery-group-policy":
return runSetIgnoreDeliveryGroupPolicy(pw);
case "clear-ignore-delivery-group-policy":
@@ -3472,6 +3474,18 @@ final class ActivityManagerShellCommand extends ShellCommand {
return 0;
}
+ int runWaitForBroadcastDispatch(PrintWriter pw) throws RemoteException {
+ pw = new PrintWriter(new TeeWriter(LOG_WRITER_INFO, pw));
+ final Intent intent;
+ try {
+ intent = makeIntent(UserHandle.USER_CURRENT);
+ } catch (URISyntaxException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ mInternal.waitForBroadcastDispatch(pw, intent);
+ return 0;
+ }
+
int runSetIgnoreDeliveryGroupPolicy(PrintWriter pw) throws RemoteException {
final String broadcastAction = getNextArgRequired();
mInternal.setIgnoreDeliveryGroupPolicy(broadcastAction);
diff --git a/services/core/java/com/android/server/am/BroadcastDispatcher.java b/services/core/java/com/android/server/am/BroadcastDispatcher.java
index 2adcf2f48343..8aa3921d3f2f 100644
--- a/services/core/java/com/android/server/am/BroadcastDispatcher.java
+++ b/services/core/java/com/android/server/am/BroadcastDispatcher.java
@@ -582,6 +582,38 @@ public class BroadcastDispatcher {
}
}
+ private static boolean isDispatchedInDeferrals(@NonNull ArrayList<Deferrals> list,
+ @NonNull Intent intent) {
+ for (int i = 0; i < list.size(); i++) {
+ if (!isDispatched(list.get(i).broadcasts, intent)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static boolean isDispatched(@NonNull ArrayList<BroadcastRecord> list,
+ @NonNull Intent intent) {
+ for (int i = 0; i < list.size(); i++) {
+ if (intent.filterEquals(list.get(i).intent)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public boolean isDispatched(@NonNull Intent intent) {
+ synchronized (mLock) {
+ if ((mCurrentBroadcast != null) && intent.filterEquals(mCurrentBroadcast.intent)) {
+ return false;
+ }
+ return isDispatched(mOrderedBroadcasts, intent)
+ && isDispatched(mAlarmQueue, intent)
+ && isDispatchedInDeferrals(mDeferredBroadcasts, intent)
+ && isDispatchedInDeferrals(mAlarmDeferrals, intent);
+ }
+ }
+
private static int pendingInDeferralsList(ArrayList<Deferrals> list) {
int pending = 0;
final int numEntries = list.size();
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index 5c68e6759083..c7ef8fab2da6 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -182,7 +182,7 @@ class BroadcastProcessQueue {
private int mCountInstrumented;
private int mCountManifest;
- private boolean mPrioritizeEarliest;
+ private int mCountPrioritizeEarliestRequests;
private @UptimeMillisLong long mRunnableAt = Long.MAX_VALUE;
private @Reason int mRunnableAtReason = REASON_EMPTY;
@@ -748,7 +748,7 @@ class BroadcastProcessQueue {
final BroadcastRecord nextLPRecord = (BroadcastRecord) nextLPArgs.arg1;
final int nextLPRecordIndex = nextLPArgs.argi1;
final BroadcastRecord nextHPRecord = (BroadcastRecord) highPriorityQueue.peekFirst().arg1;
- final boolean shouldConsiderLPQueue = (mPrioritizeEarliest
+ final boolean shouldConsiderLPQueue = (mCountPrioritizeEarliestRequests > 0
|| consecutiveHighPriorityCount >= maxHighPriorityDispatchLimit);
final boolean isLPQueueEligible = shouldConsiderLPQueue
&& nextLPRecord.enqueueTime <= nextHPRecord.enqueueTime
@@ -761,10 +761,9 @@ class BroadcastProcessQueue {
}
/**
- * When {@code prioritizeEarliest} is set to {@code true}, then earliest enqueued
- * broadcasts would be prioritized for dispatching, even if there are urgent broadcasts
- * waiting. This is typically used in case there are callers waiting for "barrier" to be
- * reached.
+ * Add a request to prioritize dispatching of broadcasts that have been enqueued the earliest,
+ * even if there are urgent broadcasts waiting to be dispatched. This is typically used in
+ * case there are callers waiting for "barrier" to be reached.
*
* @return if this operation may have changed internal state, indicating
* that the caller is responsible for invoking
@@ -772,12 +771,38 @@ class BroadcastProcessQueue {
*/
@CheckResult
@VisibleForTesting
- boolean setPrioritizeEarliest(boolean prioritizeEarliest) {
- if (mPrioritizeEarliest != prioritizeEarliest) {
- mPrioritizeEarliest = prioritizeEarliest;
+ boolean addPrioritizeEarliestRequest() {
+ if (mCountPrioritizeEarliestRequests == 0) {
+ mCountPrioritizeEarliestRequests++;
invalidateRunnableAt();
return true;
} else {
+ mCountPrioritizeEarliestRequests++;
+ return false;
+ }
+ }
+
+ /**
+ * Remove a request to prioritize dispatching of broadcasts that have been enqueued the
+ * earliest, even if there are urgent broadcasts waiting to be dispatched. This is typically
+ * used in case there are callers waiting for "barrier" to be reached.
+ *
+ * <p> Once there are no more remaining requests, the dispatching order reverts back to normal.
+ *
+ * @return if this operation may have changed internal state, indicating
+ * that the caller is responsible for invoking
+ * {@link BroadcastQueueModernImpl#updateRunnableList}
+ */
+ @CheckResult
+ boolean removePrioritizeEarliestRequest() {
+ mCountPrioritizeEarliestRequests--;
+ if (mCountPrioritizeEarliestRequests == 0) {
+ invalidateRunnableAt();
+ return true;
+ } else if (mCountPrioritizeEarliestRequests < 0) {
+ mCountPrioritizeEarliestRequests = 0;
+ return false;
+ } else {
return false;
}
}
@@ -837,7 +862,7 @@ class BroadcastProcessQueue {
}
/**
- * Quickly determine if this queue has broadcasts enqueued before the given
+ * Quickly determine if this queue has non-deferred broadcasts enqueued before the given
* barrier timestamp that are still waiting to be delivered.
*/
public boolean isBeyondBarrierLocked(@UptimeMillisLong long barrierTime) {
@@ -859,6 +884,41 @@ class BroadcastProcessQueue {
|| isDeferredUntilActive();
}
+ /**
+ * Quickly determine if this queue has non-deferred broadcasts waiting to be dispatched,
+ * that match {@code intent}, as defined by {@link Intent#filterEquals(Intent)}.
+ */
+ public boolean isDispatched(@NonNull Intent intent) {
+ final boolean activeDispatched = (mActive == null)
+ || (!intent.filterEquals(mActive.intent));
+ final boolean dispatched = isDispatchedInQueue(mPending, intent);
+ final boolean urgentDispatched = isDispatchedInQueue(mPendingUrgent, intent);
+ final boolean offloadDispatched = isDispatchedInQueue(mPendingOffload, intent);
+
+ return (activeDispatched && dispatched && urgentDispatched && offloadDispatched)
+ || isDeferredUntilActive();
+ }
+
+ /**
+ * Quickly determine if the {@code queue} has non-deferred broadcasts waiting to be dispatched,
+ * that match {@code intent}, as defined by {@link Intent#filterEquals(Intent)}.
+ */
+ private boolean isDispatchedInQueue(@NonNull ArrayDeque<SomeArgs> queue,
+ @NonNull Intent intent) {
+ final Iterator<SomeArgs> it = queue.iterator();
+ while (it.hasNext()) {
+ final SomeArgs args = it.next();
+ if (args == null) {
+ return true;
+ }
+ final BroadcastRecord record = (BroadcastRecord) args.arg1;
+ if (intent.filterEquals(record.intent)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
public boolean isRunnable() {
if (mRunnableAtInvalidated) updateRunnableAt();
return mRunnableAt != Long.MAX_VALUE;
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 6d1344d79b6c..8e76e5b5cf48 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -192,7 +192,7 @@ public abstract class BroadcastQueue {
public abstract boolean isIdleLocked();
/**
- * Quickly determine if this queue has broadcasts enqueued before the given
+ * Quickly determine if this queue has non-deferred broadcasts enqueued before the given
* barrier timestamp that are still waiting to be delivered.
*
* @see #waitForIdle
@@ -202,6 +202,15 @@ public abstract class BroadcastQueue {
public abstract boolean isBeyondBarrierLocked(@UptimeMillisLong long barrierTime);
/**
+ * Quickly determine if this queue has non-deferred broadcasts waiting to be dispatched,
+ * that match {@code intent}, as defined by {@link Intent#filterEquals(Intent)}.
+ *
+ * @see #waitForDispatched(Intent, PrintWriter)
+ */
+ @GuardedBy("mService")
+ public abstract boolean isDispatchedLocked(@NonNull Intent intent);
+
+ /**
* Wait until this queue becomes completely idle.
* <p>
* Any broadcasts waiting to be delivered at some point in the future will
@@ -214,7 +223,7 @@ public abstract class BroadcastQueue {
public abstract void waitForIdle(@NonNull PrintWriter pw);
/**
- * Wait until any currently waiting broadcasts have been dispatched.
+ * Wait until any currently waiting non-deferred broadcasts have been dispatched.
* <p>
* Any broadcasts waiting to be delivered at some point in the future will
* be dispatched as quickly as possible.
@@ -225,6 +234,15 @@ public abstract class BroadcastQueue {
public abstract void waitForBarrier(@NonNull PrintWriter pw);
/**
+ * Wait until all non-deferred broadcasts matching {@code intent}, as defined by
+ * {@link Intent#filterEquals(Intent)}, have been dispatched.
+ * <p>
+ * Any broadcasts waiting to be delivered at some point in the future will
+ * be dispatched as quickly as possible.
+ */
+ public abstract void waitForDispatched(@NonNull Intent intent, @NonNull PrintWriter pw);
+
+ /**
* Delays delivering broadcasts to the specified package.
*
* <p> Note that this is only valid for modern queue.
diff --git a/services/core/java/com/android/server/am/BroadcastQueueImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
index 4a69f90d9fc0..7f3ceb578891 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
@@ -1793,6 +1793,23 @@ public class BroadcastQueueImpl extends BroadcastQueue {
return mDispatcher.isBeyondBarrier(barrierTime);
}
+ public boolean isDispatchedLocked(Intent intent) {
+ if (isIdleLocked()) return true;
+
+ for (int i = 0; i < mParallelBroadcasts.size(); i++) {
+ if (intent.filterEquals(mParallelBroadcasts.get(i).intent)) {
+ return false;
+ }
+ }
+
+ final BroadcastRecord pending = getPendingBroadcastLocked();
+ if ((pending != null) && intent.filterEquals(pending.intent)) {
+ return false;
+ }
+
+ return mDispatcher.isDispatched(intent);
+ }
+
public void waitForIdle(PrintWriter pw) {
waitFor(() -> isIdleLocked(), pw, "idle");
}
@@ -1802,6 +1819,10 @@ public class BroadcastQueueImpl extends BroadcastQueue {
waitFor(() -> isBeyondBarrierLocked(barrierTime), pw, "barrier");
}
+ public void waitForDispatched(Intent intent, PrintWriter pw) {
+ waitFor(() -> isDispatchedLocked(intent), pw, "dispatch");
+ }
+
private void waitFor(BooleanSupplier condition, PrintWriter pw, String conditionName) {
long lastPrint = 0;
while (true) {
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index 96e152320282..539f4e844413 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -1376,6 +1376,16 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
}
@Override
+ public boolean isDispatchedLocked(@NonNull Intent intent) {
+ return isDispatchedLocked(intent, LOG_WRITER_INFO);
+ }
+
+ public boolean isDispatchedLocked(@NonNull Intent intent, @NonNull PrintWriter pw) {
+ return testAllProcessQueues(q -> q.isDispatched(intent),
+ "dispatch of " + intent, pw);
+ }
+
+ @Override
public void waitForIdle(@NonNull PrintWriter pw) {
waitFor(() -> isIdleLocked(pw));
}
@@ -1383,28 +1393,35 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
@Override
public void waitForBarrier(@NonNull PrintWriter pw) {
final long now = SystemClock.uptimeMillis();
- waitFor(() -> isBeyondBarrierLocked(now, pw));
+ synchronized (mService) {
+ forEachMatchingQueue(QUEUE_PREDICATE_ANY,
+ q -> q.addPrioritizeEarliestRequest());
+ }
+ try {
+ waitFor(() -> isBeyondBarrierLocked(now, pw));
+ } finally {
+ synchronized (mService) {
+ forEachMatchingQueue(QUEUE_PREDICATE_ANY,
+ q -> q.removePrioritizeEarliestRequest());
+ }
+ }
+ }
+
+ @Override
+ public void waitForDispatched(@NonNull Intent intent, @NonNull PrintWriter pw) {
+ waitFor(() -> isDispatchedLocked(intent, pw));
}
private void waitFor(@NonNull BooleanSupplier condition) {
final CountDownLatch latch = new CountDownLatch(1);
synchronized (mService) {
mWaitingFor.add(Pair.create(condition, latch));
- forEachMatchingQueue(QUEUE_PREDICATE_ANY,
- (q) -> q.setPrioritizeEarliest(true));
}
enqueueUpdateRunningList();
try {
latch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
- } finally {
- synchronized (mService) {
- if (mWaitingFor.isEmpty()) {
- forEachMatchingQueue(QUEUE_PREDICATE_ANY,
- (q) -> q.setPrioritizeEarliest(false));
- }
- }
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
index acfea85d60a2..5d3b91368dcb 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
@@ -769,7 +769,7 @@ public final class BroadcastQueueModernImplTest {
BroadcastProcessQueue queue = new BroadcastProcessQueue(mConstants,
PACKAGE_GREEN, getUidForPackage(PACKAGE_GREEN));
- queue.setPrioritizeEarliest(true);
+ queue.addPrioritizeEarliestRequest();
long timeCounter = 100;
enqueueOrReplaceBroadcast(queue,
@@ -814,6 +814,28 @@ public final class BroadcastQueueModernImplTest {
queue.makeActiveNextPending();
assertEquals(AppWidgetManager.ACTION_APPWIDGET_UPDATE,
queue.getActive().intent.getAction());
+
+
+ queue.removePrioritizeEarliestRequest();
+
+ enqueueOrReplaceBroadcast(queue,
+ makeBroadcastRecord(new Intent(Intent.ACTION_BOOT_COMPLETED)
+ .addFlags(Intent.FLAG_RECEIVER_OFFLOAD)), 0, timeCounter++);
+ enqueueOrReplaceBroadcast(queue,
+ makeBroadcastRecord(new Intent(Intent.ACTION_TIMEZONE_CHANGED)),
+ 0, timeCounter++);
+ enqueueOrReplaceBroadcast(queue,
+ makeBroadcastRecord(new Intent(Intent.ACTION_LOCALE_CHANGED)
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)), 0, timeCounter++);
+
+ // Once the request to prioritize earliest is removed, we should expect broadcasts
+ // to be dispatched in the order of foreground, normal and then offload.
+ queue.makeActiveNextPending();
+ assertEquals(Intent.ACTION_LOCALE_CHANGED, queue.getActive().intent.getAction());
+ queue.makeActiveNextPending();
+ assertEquals(Intent.ACTION_TIMEZONE_CHANGED, queue.getActive().intent.getAction());
+ queue.makeActiveNextPending();
+ assertEquals(Intent.ACTION_BOOT_COMPLETED, queue.getActive().intent.getAction());
}
/**
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index 7be1d7cde27f..3a8d2c92eaff 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -1890,6 +1890,36 @@ public class BroadcastQueueTest {
assertTrue(mQueue.isBeyondBarrierLocked(afterSecond));
}
+ @Test
+ public void testWaitForBroadcastDispatch() throws Exception {
+ final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+ final ProcessRecord receiverApp = makeActiveProcessRecord(PACKAGE_GREEN);
+
+ final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK);
+ assertTrue(mQueue.isDispatchedLocked(timeTick));
+
+ final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
+ enqueueBroadcast(makeBroadcastRecord(timezone, callerApp,
+ List.of(makeRegisteredReceiver(receiverApp))));
+
+ assertTrue(mQueue.isDispatchedLocked(timeTick));
+ assertFalse(mQueue.isDispatchedLocked(timezone));
+
+ enqueueBroadcast(makeBroadcastRecord(timeTick, callerApp,
+ List.of(makeRegisteredReceiver(receiverApp))));
+
+ assertFalse(mQueue.isDispatchedLocked(timeTick));
+ assertFalse(mQueue.isDispatchedLocked(timezone));
+
+ mLooper.release();
+
+ mQueue.waitForDispatched(timeTick, LOG_WRITER_INFO);
+ assertTrue(mQueue.isDispatchedLocked(timeTick));
+
+ mQueue.waitForDispatched(timezone, LOG_WRITER_INFO);
+ assertTrue(mQueue.isDispatchedLocked(timezone));
+ }
+
/**
* Verify that we OOM adjust for manifest receivers.
*/