Fix bug around deferred prioritized broadcasts.
When deciding if a particular "tranche" of prioritized broadcasts has been finished, we should consider both the terminal and deferred states of members of that tranche. Previous attempts to do this used a pure counting-based mechanism, which can risk opening up future tranches too early when many receivers have been deferred. This fix pivots to a new "beyond" concept that tracks the highest receiver that a broadcast has "moved beyond". This can be either a terminal state, or deferred states within the currently active tranche. Once we've moved "beyond" particular receiver, we treat it as a high-water mark, even when previously deferred broadcasts that we've moved beyond pivot back to a pending state. Bug: 272147987 Test: atest FrameworksMockingServicesTests:BroadcastQueueTest Test: atest FrameworksMockingServicesTests:BroadcastQueueModernImplTest Test: atest FrameworksMockingServicesTests:BroadcastRecordTest Change-Id: Iac4c5c7e92a7e009acbe1c248f6f56106152f032
This commit is contained in:
@@ -18,7 +18,6 @@ package com.android.server.am;
|
||||
|
||||
import static com.android.internal.util.Preconditions.checkState;
|
||||
import static com.android.server.am.BroadcastRecord.deliveryStateToString;
|
||||
import static com.android.server.am.BroadcastRecord.isDeliveryStateTerminal;
|
||||
import static com.android.server.am.BroadcastRecord.isReceiverEquals;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
@@ -709,7 +708,7 @@ class BroadcastProcessQueue {
|
||||
|| consecutiveHighPriorityCount >= maxHighPriorityDispatchLimit);
|
||||
final boolean isLPQueueEligible = shouldConsiderLPQueue
|
||||
&& nextLPRecord.enqueueTime <= nextHPRecord.enqueueTime
|
||||
&& !blockedOnOrderedDispatch(nextLPRecord, nextLPRecordIndex);
|
||||
&& !nextLPRecord.isBlocked(nextLPRecordIndex);
|
||||
return isLPQueueEligible ? lowPriorityQueue : highPriorityQueue;
|
||||
}
|
||||
|
||||
@@ -912,39 +911,20 @@ class BroadcastProcessQueue {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean blockedOnOrderedDispatch(BroadcastRecord r, int index) {
|
||||
final int blockedUntilTerminalCount = r.blockedUntilTerminalCount[index];
|
||||
|
||||
int existingDeferredCount = 0;
|
||||
if (r.deferUntilActive) {
|
||||
for (int i = 0; i < index; i++) {
|
||||
if (r.deferredUntilActive[i]) existingDeferredCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// We might be blocked waiting for other receivers to finish,
|
||||
// typically for an ordered broadcast or priority traunches
|
||||
if ((r.terminalCount + existingDeferredCount) < blockedUntilTerminalCount
|
||||
&& !isDeliveryStateTerminal(r.getDeliveryState(index))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update {@link #getRunnableAt()} if it's currently invalidated.
|
||||
*/
|
||||
private void updateRunnableAt() {
|
||||
final SomeArgs next = peekNextBroadcast();
|
||||
if (!mRunnableAtInvalidated) return;
|
||||
mRunnableAtInvalidated = false;
|
||||
|
||||
final SomeArgs next = peekNextBroadcast();
|
||||
if (next != null) {
|
||||
final BroadcastRecord r = (BroadcastRecord) next.arg1;
|
||||
final int index = next.argi1;
|
||||
final long runnableAt = r.enqueueTime;
|
||||
|
||||
// If we're specifically queued behind other ordered dispatch activity,
|
||||
// we aren't runnable yet
|
||||
if (blockedOnOrderedDispatch(r, index)) {
|
||||
if (r.isBlocked(index)) {
|
||||
mRunnableAt = Long.MAX_VALUE;
|
||||
mRunnableAtReason = REASON_BLOCKED;
|
||||
return;
|
||||
@@ -1262,12 +1242,12 @@ class BroadcastProcessQueue {
|
||||
pw.print(info.activityInfo.name);
|
||||
}
|
||||
pw.println();
|
||||
final int blockedUntilTerminalCount = record.blockedUntilTerminalCount[recordIndex];
|
||||
if (blockedUntilTerminalCount != -1) {
|
||||
final int blockedUntilBeyondCount = record.blockedUntilBeyondCount[recordIndex];
|
||||
if (blockedUntilBeyondCount != -1) {
|
||||
pw.print(" blocked until ");
|
||||
pw.print(blockedUntilTerminalCount);
|
||||
pw.print(blockedUntilBeyondCount);
|
||||
pw.print(", currently at ");
|
||||
pw.print(record.terminalCount);
|
||||
pw.print(record.beyondCount);
|
||||
pw.print(" of ");
|
||||
pw.println(record.receivers.size());
|
||||
}
|
||||
|
||||
@@ -1008,6 +1008,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
|
||||
}
|
||||
|
||||
final BroadcastRecord r = queue.getActive();
|
||||
final int index = queue.getActiveIndex();
|
||||
if (r.ordered) {
|
||||
r.resultCode = resultCode;
|
||||
r.resultData = resultData;
|
||||
@@ -1015,18 +1016,24 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
|
||||
if (!r.isNoAbort()) {
|
||||
r.resultAbort = resultAbort;
|
||||
}
|
||||
}
|
||||
|
||||
// When the caller aborted an ordered broadcast, we mark all
|
||||
// remaining receivers as skipped
|
||||
if (r.resultAbort) {
|
||||
for (int i = r.terminalCount + 1; i < r.receivers.size(); i++) {
|
||||
setDeliveryState(null, null, r, i, r.receivers.get(i),
|
||||
BroadcastRecord.DELIVERY_SKIPPED, "resultAbort");
|
||||
}
|
||||
// To ensure that "beyond" high-water marks are updated in a monotonic
|
||||
// way, we finish this receiver before possibly skipping any remaining
|
||||
// aborted receivers
|
||||
final boolean res = finishReceiverActiveLocked(queue,
|
||||
BroadcastRecord.DELIVERY_DELIVERED, "remote app");
|
||||
|
||||
// When the caller aborted an ordered broadcast, we mark all
|
||||
// remaining receivers as skipped
|
||||
if (r.resultAbort) {
|
||||
for (int i = index + 1; i < r.receivers.size(); i++) {
|
||||
setDeliveryState(null, null, r, i, r.receivers.get(i),
|
||||
BroadcastRecord.DELIVERY_SKIPPED, "resultAbort");
|
||||
}
|
||||
}
|
||||
|
||||
return finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_DELIVERED, "remote app");
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1108,21 +1115,10 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
|
||||
@NonNull Object receiver, @DeliveryState int newDeliveryState,
|
||||
@NonNull String reason) {
|
||||
final int cookie = traceBegin("setDeliveryState");
|
||||
final int oldDeliveryState = getDeliveryState(r, index);
|
||||
boolean checkFinished = false;
|
||||
|
||||
// Only apply state when we haven't already reached a terminal state;
|
||||
// this is how we ignore racing timeout messages
|
||||
if (!isDeliveryStateTerminal(oldDeliveryState)) {
|
||||
r.setDeliveryState(index, newDeliveryState, reason);
|
||||
if (oldDeliveryState == BroadcastRecord.DELIVERY_DEFERRED) {
|
||||
r.deferredCount--;
|
||||
} else if (newDeliveryState == BroadcastRecord.DELIVERY_DEFERRED) {
|
||||
// If we're deferring a broadcast, maybe that's enough to unblock the final callback
|
||||
r.deferredCount++;
|
||||
checkFinished = true;
|
||||
}
|
||||
}
|
||||
// Remember the old state and apply the new state
|
||||
final int oldDeliveryState = getDeliveryState(r, index);
|
||||
final boolean beyondCountChanged = r.setDeliveryState(index, newDeliveryState, reason);
|
||||
|
||||
// Emit any relevant tracing results when we're changing the delivery
|
||||
// state as part of running from a queue
|
||||
@@ -1147,15 +1143,13 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
|
||||
+ deliveryStateToString(newDeliveryState) + " because " + reason);
|
||||
}
|
||||
|
||||
r.terminalCount++;
|
||||
notifyFinishReceiver(queue, app, r, index, receiver);
|
||||
checkFinished = true;
|
||||
}
|
||||
// When entire ordered broadcast finished, deliver final result
|
||||
if (checkFinished) {
|
||||
final boolean recordFinished =
|
||||
((r.terminalCount + r.deferredCount) == r.receivers.size());
|
||||
if (recordFinished) {
|
||||
|
||||
// When we've reached a new high-water mark, we might be in a position
|
||||
// to unblock other receivers or the final resultTo
|
||||
if (beyondCountChanged) {
|
||||
if (r.beyondCount == r.receivers.size()) {
|
||||
scheduleResultTo(r);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROA
|
||||
import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_NONE;
|
||||
import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_TARGET_T_ONLY;
|
||||
|
||||
import android.annotation.CheckResult;
|
||||
import android.annotation.CurrentTimeMillisLong;
|
||||
import android.annotation.ElapsedRealtimeLong;
|
||||
import android.annotation.IntDef;
|
||||
@@ -101,8 +102,7 @@ final class BroadcastRecord extends Binder {
|
||||
final @NonNull List<Object> receivers; // contains BroadcastFilter and ResolveInfo
|
||||
final @DeliveryState int[] delivery; // delivery state of each receiver
|
||||
final @NonNull String[] deliveryReasons; // reasons for delivery state of each receiver
|
||||
final boolean[] deferredUntilActive; // whether each receiver is infinitely deferred
|
||||
final int[] blockedUntilTerminalCount; // blocked until count of each receiver
|
||||
final int[] blockedUntilBeyondCount; // blocked until count of each receiver
|
||||
@Nullable ProcessRecord resultToApp; // who receives final result if non-null
|
||||
@Nullable IIntentReceiver resultTo; // who receives final result if non-null
|
||||
boolean deferred;
|
||||
@@ -134,6 +134,7 @@ final class BroadcastRecord extends Binder {
|
||||
int manifestSkipCount; // number of manifest receivers skipped.
|
||||
int terminalCount; // number of receivers in terminal state.
|
||||
int deferredCount; // number of receivers in deferred state.
|
||||
int beyondCount; // high-water number of receivers we've moved beyond.
|
||||
@Nullable BroadcastQueue queue; // the outbound queue handling this broadcast
|
||||
|
||||
// Determines the privileges the app's process has in regard to background starts.
|
||||
@@ -219,6 +220,23 @@ final class BroadcastRecord extends Binder {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if the given delivery state is "beyond", which means that we've
|
||||
* moved beyond this receiver, and future receivers are now unblocked.
|
||||
*/
|
||||
static boolean isDeliveryStateBeyond(@DeliveryState int deliveryState) {
|
||||
switch (deliveryState) {
|
||||
case DELIVERY_DELIVERED:
|
||||
case DELIVERY_SKIPPED:
|
||||
case DELIVERY_TIMEOUT:
|
||||
case DELIVERY_FAILURE:
|
||||
case DELIVERY_DEFERRED:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ProcessRecord curApp; // hosting application of current receiver.
|
||||
ComponentName curComponent; // the receiver class that is currently running.
|
||||
ActivityInfo curReceiver; // the manifest receiver that is currently running.
|
||||
@@ -356,7 +374,7 @@ final class BroadcastRecord extends Binder {
|
||||
TimeUtils.formatDuration(terminalTime[i] - scheduledTime[i], pw);
|
||||
pw.print(' ');
|
||||
}
|
||||
pw.print("("); pw.print(blockedUntilTerminalCount[i]); pw.print(") ");
|
||||
pw.print("("); pw.print(blockedUntilBeyondCount[i]); pw.print(") ");
|
||||
pw.print("#"); pw.print(i); pw.print(": ");
|
||||
if (o instanceof BroadcastFilter) {
|
||||
pw.println(o);
|
||||
@@ -411,8 +429,7 @@ final class BroadcastRecord extends Binder {
|
||||
urgent = calculateUrgent(_intent, _options);
|
||||
deferUntilActive = calculateDeferUntilActive(_callingUid,
|
||||
_options, _resultTo, _serialized, urgent);
|
||||
deferredUntilActive = new boolean[deferUntilActive ? delivery.length : 0];
|
||||
blockedUntilTerminalCount = calculateBlockedUntilTerminalCount(receivers, _serialized);
|
||||
blockedUntilBeyondCount = calculateBlockedUntilBeyondCount(receivers, _serialized);
|
||||
scheduledTime = new long[delivery.length];
|
||||
terminalTime = new long[delivery.length];
|
||||
resultToApp = _resultToApp;
|
||||
@@ -423,7 +440,7 @@ final class BroadcastRecord extends Binder {
|
||||
ordered = _serialized;
|
||||
sticky = _sticky;
|
||||
initialSticky = _initialSticky;
|
||||
prioritized = isPrioritized(blockedUntilTerminalCount, _serialized);
|
||||
prioritized = isPrioritized(blockedUntilBeyondCount, _serialized);
|
||||
userId = _userId;
|
||||
nextReceiver = 0;
|
||||
state = IDLE;
|
||||
@@ -467,8 +484,7 @@ final class BroadcastRecord extends Binder {
|
||||
delivery = from.delivery;
|
||||
deliveryReasons = from.deliveryReasons;
|
||||
deferUntilActive = from.deferUntilActive;
|
||||
deferredUntilActive = from.deferredUntilActive;
|
||||
blockedUntilTerminalCount = from.blockedUntilTerminalCount;
|
||||
blockedUntilBeyondCount = from.blockedUntilBeyondCount;
|
||||
scheduledTime = from.scheduledTime;
|
||||
terminalTime = from.terminalTime;
|
||||
resultToApp = from.resultToApp;
|
||||
@@ -627,32 +643,72 @@ final class BroadcastRecord extends Binder {
|
||||
/**
|
||||
* Update the delivery state of the given {@link #receivers} index.
|
||||
* Automatically updates any time measurements related to state changes.
|
||||
*
|
||||
* @return if {@link #beyondCount} changed due to this state transition,
|
||||
* indicating that other events may be unblocked.
|
||||
*/
|
||||
void setDeliveryState(int index, @DeliveryState int deliveryState,
|
||||
@CheckResult
|
||||
boolean setDeliveryState(int index, @DeliveryState int newDeliveryState,
|
||||
@NonNull String reason) {
|
||||
delivery[index] = deliveryState;
|
||||
deliveryReasons[index] = reason;
|
||||
if (deferUntilActive) deferredUntilActive[index] = false;
|
||||
switch (deliveryState) {
|
||||
final int oldDeliveryState = delivery[index];
|
||||
if (isDeliveryStateTerminal(oldDeliveryState)
|
||||
|| newDeliveryState == oldDeliveryState) {
|
||||
// We've already arrived in terminal or requested state, so leave
|
||||
// any statistics and reasons intact from the first transition
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (oldDeliveryState) {
|
||||
case DELIVERY_DEFERRED:
|
||||
deferredCount--;
|
||||
break;
|
||||
}
|
||||
switch (newDeliveryState) {
|
||||
case DELIVERY_SCHEDULED:
|
||||
scheduledTime[index] = SystemClock.uptimeMillis();
|
||||
break;
|
||||
case DELIVERY_DEFERRED:
|
||||
deferredCount++;
|
||||
break;
|
||||
case DELIVERY_DELIVERED:
|
||||
case DELIVERY_SKIPPED:
|
||||
case DELIVERY_TIMEOUT:
|
||||
case DELIVERY_FAILURE:
|
||||
terminalTime[index] = SystemClock.uptimeMillis();
|
||||
break;
|
||||
case DELIVERY_SCHEDULED:
|
||||
scheduledTime[index] = SystemClock.uptimeMillis();
|
||||
break;
|
||||
case DELIVERY_DEFERRED:
|
||||
if (deferUntilActive) deferredUntilActive[index] = true;
|
||||
terminalCount++;
|
||||
break;
|
||||
}
|
||||
|
||||
delivery[index] = newDeliveryState;
|
||||
deliveryReasons[index] = reason;
|
||||
|
||||
// If this state change might bring us to a new high-water mark, bring
|
||||
// ourselves as high as we possibly can
|
||||
final int oldBeyondCount = beyondCount;
|
||||
if (index >= beyondCount) {
|
||||
for (int i = beyondCount; i < delivery.length; i++) {
|
||||
if (isDeliveryStateBeyond(getDeliveryState(i))) {
|
||||
beyondCount = i + 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (beyondCount != oldBeyondCount);
|
||||
}
|
||||
|
||||
@DeliveryState int getDeliveryState(int index) {
|
||||
return delivery[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return if the given {@link #receivers} index should be considered
|
||||
* blocked based on the current status of the overall broadcast.
|
||||
*/
|
||||
boolean isBlocked(int index) {
|
||||
return (beyondCount < blockedUntilBeyondCount[index]);
|
||||
}
|
||||
|
||||
boolean wasDeliveryAttempted(int index) {
|
||||
final int deliveryState = getDeliveryState(index);
|
||||
switch (deliveryState) {
|
||||
@@ -757,36 +813,36 @@ final class BroadcastRecord extends Binder {
|
||||
* has prioritized tranches of receivers.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static boolean isPrioritized(@NonNull int[] blockedUntilTerminalCount,
|
||||
static boolean isPrioritized(@NonNull int[] blockedUntilBeyondCount,
|
||||
boolean ordered) {
|
||||
return !ordered && (blockedUntilTerminalCount.length > 0)
|
||||
&& (blockedUntilTerminalCount[0] != -1);
|
||||
return !ordered && (blockedUntilBeyondCount.length > 0)
|
||||
&& (blockedUntilBeyondCount[0] != -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the {@link #terminalCount} that each receiver should be
|
||||
* Calculate the {@link #beyondCount} that each receiver should be
|
||||
* considered blocked until.
|
||||
* <p>
|
||||
* For example, in an ordered broadcast, receiver {@code N} is blocked until
|
||||
* receiver {@code N-1} reaches a terminal state. Similarly, in a
|
||||
* prioritized broadcast, receiver {@code N} is blocked until all receivers
|
||||
* of a higher priority reach a terminal state.
|
||||
* receiver {@code N-1} reaches a terminal or deferred state. Similarly, in
|
||||
* a prioritized broadcast, receiver {@code N} is blocked until all
|
||||
* receivers of a higher priority reach a terminal or deferred state.
|
||||
* <p>
|
||||
* When there are no terminal count constraints, the blocked value for each
|
||||
* When there are no beyond count constraints, the blocked value for each
|
||||
* receiver is {@code -1}.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static @NonNull int[] calculateBlockedUntilTerminalCount(
|
||||
static @NonNull int[] calculateBlockedUntilBeyondCount(
|
||||
@NonNull List<Object> receivers, boolean ordered) {
|
||||
final int N = receivers.size();
|
||||
final int[] blockedUntilTerminalCount = new int[N];
|
||||
final int[] blockedUntilBeyondCount = new int[N];
|
||||
int lastPriority = 0;
|
||||
int lastPriorityIndex = 0;
|
||||
for (int i = 0; i < N; i++) {
|
||||
if (ordered) {
|
||||
// When sending an ordered broadcast, we need to block this
|
||||
// receiver until all previous receivers have terminated
|
||||
blockedUntilTerminalCount[i] = i;
|
||||
blockedUntilBeyondCount[i] = i;
|
||||
} else {
|
||||
// When sending a prioritized broadcast, we only need to wait
|
||||
// for the previous tranche of receivers to be terminated
|
||||
@@ -794,18 +850,18 @@ final class BroadcastRecord extends Binder {
|
||||
if ((i == 0) || (thisPriority != lastPriority)) {
|
||||
lastPriority = thisPriority;
|
||||
lastPriorityIndex = i;
|
||||
blockedUntilTerminalCount[i] = i;
|
||||
blockedUntilBeyondCount[i] = i;
|
||||
} else {
|
||||
blockedUntilTerminalCount[i] = lastPriorityIndex;
|
||||
blockedUntilBeyondCount[i] = lastPriorityIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
// If the entire list is in the same priority tranche, mark as -1 to
|
||||
// indicate that none of them need to wait
|
||||
if (N > 0 && blockedUntilTerminalCount[N - 1] == 0) {
|
||||
Arrays.fill(blockedUntilTerminalCount, -1);
|
||||
if (N > 0 && blockedUntilBeyondCount[N - 1] == 0) {
|
||||
Arrays.fill(blockedUntilBeyondCount, -1);
|
||||
}
|
||||
return blockedUntilTerminalCount;
|
||||
return blockedUntilBeyondCount;
|
||||
}
|
||||
|
||||
static int getReceiverUid(@NonNull Object receiver) {
|
||||
|
||||
@@ -463,7 +463,8 @@ public final class BroadcastQueueModernImplTest {
|
||||
assertEquals(BroadcastProcessQueue.REASON_BLOCKED, queue.getRunnableAtReason());
|
||||
|
||||
// Bumping past barrier makes us now runnable
|
||||
airplaneRecord.terminalCount++;
|
||||
airplaneRecord.setDeliveryState(0, BroadcastRecord.DELIVERY_DELIVERED,
|
||||
"testRunnableAt_Ordered");
|
||||
queue.invalidateRunnableAt();
|
||||
assertTrue(queue.isRunnable());
|
||||
assertNotEquals(BroadcastProcessQueue.REASON_BLOCKED, queue.getRunnableAtReason());
|
||||
|
||||
@@ -464,7 +464,7 @@ public class BroadcastQueueTest {
|
||||
|
||||
doAnswer((invocation) -> {
|
||||
Log.v(TAG, "Intercepting scheduleReceiver() for "
|
||||
+ Arrays.toString(invocation.getArguments()));
|
||||
+ Arrays.toString(invocation.getArguments()) + " package " + ai.packageName);
|
||||
assertHealth();
|
||||
final Intent intent = invocation.getArgument(0);
|
||||
final Bundle extras = invocation.getArgument(5);
|
||||
@@ -486,7 +486,7 @@ public class BroadcastQueueTest {
|
||||
|
||||
doAnswer((invocation) -> {
|
||||
Log.v(TAG, "Intercepting scheduleRegisteredReceiver() for "
|
||||
+ Arrays.toString(invocation.getArguments()));
|
||||
+ Arrays.toString(invocation.getArguments()) + " package " + ai.packageName);
|
||||
assertHealth();
|
||||
final Intent intent = invocation.getArgument(1);
|
||||
final Bundle extras = invocation.getArgument(4);
|
||||
|
||||
@@ -24,7 +24,12 @@ import static android.content.Intent.ACTION_TIME_CHANGED;
|
||||
import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_ALL;
|
||||
import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_BACKGROUND_RESTRICTED_ONLY;
|
||||
import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_NONE;
|
||||
import static com.android.server.am.BroadcastRecord.calculateBlockedUntilTerminalCount;
|
||||
import static com.android.server.am.BroadcastRecord.DELIVERY_DEFERRED;
|
||||
import static com.android.server.am.BroadcastRecord.DELIVERY_DELIVERED;
|
||||
import static com.android.server.am.BroadcastRecord.DELIVERY_PENDING;
|
||||
import static com.android.server.am.BroadcastRecord.DELIVERY_SKIPPED;
|
||||
import static com.android.server.am.BroadcastRecord.DELIVERY_TIMEOUT;
|
||||
import static com.android.server.am.BroadcastRecord.calculateBlockedUntilBeyondCount;
|
||||
import static com.android.server.am.BroadcastRecord.calculateDeferUntilActive;
|
||||
import static com.android.server.am.BroadcastRecord.calculateUrgent;
|
||||
import static com.android.server.am.BroadcastRecord.isReceiverEquals;
|
||||
@@ -58,7 +63,6 @@ import androidx.test.filters.SmallTest;
|
||||
import com.android.server.am.BroadcastDispatcher.DeferredBootCompletedBroadcastPerUser;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
@@ -79,6 +83,7 @@ import java.util.function.BiFunction;
|
||||
@SmallTest
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class BroadcastRecordTest {
|
||||
private static final String TAG = "BroadcastRecordTest";
|
||||
|
||||
private static final int USER0 = UserHandle.USER_SYSTEM;
|
||||
private static final int USER1 = USER0 + 1;
|
||||
@@ -120,13 +125,13 @@ public class BroadcastRecordTest {
|
||||
assertFalse(isPrioritized(List.of(createResolveInfo(PACKAGE1, getAppId(1), 10))));
|
||||
|
||||
assertArrayEquals(new int[] {-1},
|
||||
calculateBlockedUntilTerminalCount(List.of(
|
||||
calculateBlockedUntilBeyondCount(List.of(
|
||||
createResolveInfo(PACKAGE1, getAppId(1), 0)), false));
|
||||
assertArrayEquals(new int[] {-1},
|
||||
calculateBlockedUntilTerminalCount(List.of(
|
||||
calculateBlockedUntilBeyondCount(List.of(
|
||||
createResolveInfo(PACKAGE1, getAppId(1), -10)), false));
|
||||
assertArrayEquals(new int[] {-1},
|
||||
calculateBlockedUntilTerminalCount(List.of(
|
||||
calculateBlockedUntilBeyondCount(List.of(
|
||||
createResolveInfo(PACKAGE1, getAppId(1), 10)), false));
|
||||
}
|
||||
|
||||
@@ -142,12 +147,12 @@ public class BroadcastRecordTest {
|
||||
createResolveInfo(PACKAGE3, getAppId(3), 10))));
|
||||
|
||||
assertArrayEquals(new int[] {-1,-1,-1},
|
||||
calculateBlockedUntilTerminalCount(List.of(
|
||||
calculateBlockedUntilBeyondCount(List.of(
|
||||
createResolveInfo(PACKAGE1, getAppId(1), 0),
|
||||
createResolveInfo(PACKAGE2, getAppId(2), 0),
|
||||
createResolveInfo(PACKAGE3, getAppId(3), 0)), false));
|
||||
assertArrayEquals(new int[] {-1,-1,-1},
|
||||
calculateBlockedUntilTerminalCount(List.of(
|
||||
calculateBlockedUntilBeyondCount(List.of(
|
||||
createResolveInfo(PACKAGE1, getAppId(1), 10),
|
||||
createResolveInfo(PACKAGE2, getAppId(2), 10),
|
||||
createResolveInfo(PACKAGE3, getAppId(3), 10)), false));
|
||||
@@ -156,26 +161,176 @@ public class BroadcastRecordTest {
|
||||
@Test
|
||||
public void testIsPrioritized_Yes() {
|
||||
assertTrue(isPrioritized(List.of(
|
||||
createResolveInfo(PACKAGE1, getAppId(1), -10),
|
||||
createResolveInfo(PACKAGE1, getAppId(1), 10),
|
||||
createResolveInfo(PACKAGE2, getAppId(2), 0),
|
||||
createResolveInfo(PACKAGE3, getAppId(3), 10))));
|
||||
createResolveInfo(PACKAGE3, getAppId(3), -10))));
|
||||
assertTrue(isPrioritized(List.of(
|
||||
createResolveInfo(PACKAGE1, getAppId(1), 0),
|
||||
createResolveInfo(PACKAGE1, getAppId(1), 10),
|
||||
createResolveInfo(PACKAGE2, getAppId(2), 0),
|
||||
createResolveInfo(PACKAGE3, getAppId(3), 10))));
|
||||
createResolveInfo(PACKAGE3, getAppId(3), 0))));
|
||||
|
||||
assertArrayEquals(new int[] {0,1,2},
|
||||
calculateBlockedUntilTerminalCount(List.of(
|
||||
createResolveInfo(PACKAGE1, getAppId(1), -10),
|
||||
calculateBlockedUntilBeyondCount(List.of(
|
||||
createResolveInfo(PACKAGE1, getAppId(1), 10),
|
||||
createResolveInfo(PACKAGE2, getAppId(2), 0),
|
||||
createResolveInfo(PACKAGE3, getAppId(3), 10)), false));
|
||||
createResolveInfo(PACKAGE3, getAppId(3), -10)), false));
|
||||
assertArrayEquals(new int[] {0,0,2,3,3},
|
||||
calculateBlockedUntilTerminalCount(List.of(
|
||||
createResolveInfo(PACKAGE1, getAppId(1), 0),
|
||||
createResolveInfo(PACKAGE2, getAppId(2), 0),
|
||||
calculateBlockedUntilBeyondCount(List.of(
|
||||
createResolveInfo(PACKAGE1, getAppId(1), 20),
|
||||
createResolveInfo(PACKAGE2, getAppId(2), 20),
|
||||
createResolveInfo(PACKAGE3, getAppId(3), 10),
|
||||
createResolveInfo(PACKAGE3, getAppId(3), 20),
|
||||
createResolveInfo(PACKAGE3, getAppId(3), 20)), false));
|
||||
createResolveInfo(PACKAGE3, getAppId(3), 0),
|
||||
createResolveInfo(PACKAGE3, getAppId(3), 0)), false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetDeliveryState_Single() {
|
||||
final BroadcastRecord r = createBroadcastRecord(
|
||||
new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED), List.of(
|
||||
createResolveInfoWithPriority(0)));
|
||||
assertEquals(DELIVERY_PENDING, r.getDeliveryState(0));
|
||||
assertBlocked(r, false);
|
||||
assertTerminalDeferredBeyond(r, 0, 0, 0);
|
||||
|
||||
r.setDeliveryState(0, DELIVERY_DEFERRED, TAG);
|
||||
assertEquals(DELIVERY_DEFERRED, r.getDeliveryState(0));
|
||||
assertBlocked(r, false);
|
||||
assertTerminalDeferredBeyond(r, 0, 1, 1);
|
||||
|
||||
// Identical state change has no effect
|
||||
r.setDeliveryState(0, DELIVERY_DEFERRED, TAG);
|
||||
assertEquals(DELIVERY_DEFERRED, r.getDeliveryState(0));
|
||||
assertBlocked(r, false);
|
||||
assertTerminalDeferredBeyond(r, 0, 1, 1);
|
||||
|
||||
// Moving to terminal state updates counters
|
||||
r.setDeliveryState(0, DELIVERY_DELIVERED, TAG);
|
||||
assertEquals(DELIVERY_DELIVERED, r.getDeliveryState(0));
|
||||
assertBlocked(r, false);
|
||||
assertTerminalDeferredBeyond(r, 1, 0, 1);
|
||||
|
||||
// Trying to change terminal state has no effect
|
||||
r.setDeliveryState(0, DELIVERY_TIMEOUT, TAG);
|
||||
assertEquals(DELIVERY_DELIVERED, r.getDeliveryState(0));
|
||||
assertBlocked(r, false);
|
||||
assertTerminalDeferredBeyond(r, 1, 0, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetDeliveryState_Unordered() {
|
||||
final BroadcastRecord r = createBroadcastRecord(
|
||||
new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED), List.of(
|
||||
createResolveInfoWithPriority(0),
|
||||
createResolveInfoWithPriority(0),
|
||||
createResolveInfoWithPriority(0)));
|
||||
assertBlocked(r, false, false, false);
|
||||
assertTerminalDeferredBeyond(r, 0, 0, 0);
|
||||
|
||||
// Even though we finish a middle item in the tranche, we're not
|
||||
// "beyond" it because there is still unfinished work before it
|
||||
r.setDeliveryState(1, DELIVERY_DELIVERED, TAG);
|
||||
assertBlocked(r, false, false, false);
|
||||
assertTerminalDeferredBeyond(r, 1, 0, 0);
|
||||
|
||||
r.setDeliveryState(0, DELIVERY_DELIVERED, TAG);
|
||||
assertBlocked(r, false, false, false);
|
||||
assertTerminalDeferredBeyond(r, 2, 0, 2);
|
||||
|
||||
r.setDeliveryState(2, DELIVERY_DELIVERED, TAG);
|
||||
assertBlocked(r, false, false, false);
|
||||
assertTerminalDeferredBeyond(r, 3, 0, 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetDeliveryState_Ordered() {
|
||||
final BroadcastRecord r = createOrderedBroadcastRecord(
|
||||
new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED), List.of(
|
||||
createResolveInfoWithPriority(0),
|
||||
createResolveInfoWithPriority(0),
|
||||
createResolveInfoWithPriority(0)));
|
||||
assertBlocked(r, false, true, true);
|
||||
assertTerminalDeferredBeyond(r, 0, 0, 0);
|
||||
|
||||
r.setDeliveryState(0, DELIVERY_DELIVERED, TAG);
|
||||
assertBlocked(r, false, false, true);
|
||||
assertTerminalDeferredBeyond(r, 1, 0, 1);
|
||||
|
||||
r.setDeliveryState(1, DELIVERY_DELIVERED, TAG);
|
||||
assertBlocked(r, false, false, false);
|
||||
assertTerminalDeferredBeyond(r, 2, 0, 2);
|
||||
|
||||
r.setDeliveryState(2, DELIVERY_DELIVERED, TAG);
|
||||
assertBlocked(r, false, false, false);
|
||||
assertTerminalDeferredBeyond(r, 3, 0, 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetDeliveryState_DeferUntilActive() {
|
||||
final BroadcastRecord r = createBroadcastRecord(
|
||||
new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED), List.of(
|
||||
createResolveInfoWithPriority(10),
|
||||
createResolveInfoWithPriority(10),
|
||||
createResolveInfoWithPriority(10),
|
||||
createResolveInfoWithPriority(0),
|
||||
createResolveInfoWithPriority(0),
|
||||
createResolveInfoWithPriority(0),
|
||||
createResolveInfoWithPriority(-10),
|
||||
createResolveInfoWithPriority(-10),
|
||||
createResolveInfoWithPriority(-10)));
|
||||
assertBlocked(r, false, false, false, true, true, true, true, true, true);
|
||||
assertTerminalDeferredBeyond(r, 0, 0, 0);
|
||||
|
||||
r.setDeliveryState(0, DELIVERY_PENDING, TAG);
|
||||
r.setDeliveryState(1, DELIVERY_DEFERRED, TAG);
|
||||
r.setDeliveryState(2, DELIVERY_PENDING, TAG);
|
||||
r.setDeliveryState(3, DELIVERY_DEFERRED, TAG);
|
||||
r.setDeliveryState(4, DELIVERY_DEFERRED, TAG);
|
||||
r.setDeliveryState(5, DELIVERY_DEFERRED, TAG);
|
||||
r.setDeliveryState(6, DELIVERY_DEFERRED, TAG);
|
||||
r.setDeliveryState(7, DELIVERY_PENDING, TAG);
|
||||
r.setDeliveryState(8, DELIVERY_DEFERRED, TAG);
|
||||
|
||||
// Verify deferred counts ratchet up, but we're not "beyond" the first
|
||||
// still-pending receiver
|
||||
assertBlocked(r, false, false, false, true, true, true, true, true, true);
|
||||
assertTerminalDeferredBeyond(r, 0, 6, 0);
|
||||
|
||||
// We're still not "beyond" the first still-pending receiver, even when
|
||||
// we finish a receiver later in the first tranche
|
||||
r.setDeliveryState(2, DELIVERY_DELIVERED, TAG);
|
||||
assertBlocked(r, false, false, false, true, true, true, true, true, true);
|
||||
assertTerminalDeferredBeyond(r, 1, 6, 0);
|
||||
|
||||
// Completing that last item in first tranche means we now unblock the
|
||||
// second tranche, and since it's entirely deferred, the third traunche
|
||||
// is unblocked too
|
||||
r.setDeliveryState(0, DELIVERY_DELIVERED, TAG);
|
||||
assertBlocked(r, false, false, false, false, false, false, false, false, false);
|
||||
assertTerminalDeferredBeyond(r, 2, 6, 7);
|
||||
|
||||
// Moving a deferred item in an earlier tranche back to being pending
|
||||
// doesn't change the fact that we've already moved beyond it
|
||||
r.setDeliveryState(1, DELIVERY_PENDING, TAG);
|
||||
assertBlocked(r, false, false, false, false, false, false, false, false, false);
|
||||
assertTerminalDeferredBeyond(r, 2, 5, 7);
|
||||
r.setDeliveryState(1, DELIVERY_DELIVERED, TAG);
|
||||
assertBlocked(r, false, false, false, false, false, false, false, false, false);
|
||||
assertTerminalDeferredBeyond(r, 3, 5, 7);
|
||||
|
||||
// Completing middle pending item is enough to fast-forward to end
|
||||
r.setDeliveryState(7, DELIVERY_DELIVERED, TAG);
|
||||
assertBlocked(r, false, false, false, false, false, false, false, false, false);
|
||||
assertTerminalDeferredBeyond(r, 4, 5, 9);
|
||||
|
||||
// Moving everyone else directly into a finished state updates all the
|
||||
// terminal counters
|
||||
r.setDeliveryState(3, DELIVERY_SKIPPED, TAG);
|
||||
r.setDeliveryState(4, DELIVERY_SKIPPED, TAG);
|
||||
r.setDeliveryState(5, DELIVERY_SKIPPED, TAG);
|
||||
r.setDeliveryState(6, DELIVERY_SKIPPED, TAG);
|
||||
r.setDeliveryState(8, DELIVERY_SKIPPED, TAG);
|
||||
assertBlocked(r, false, false, false, false, false, false, false, false, false);
|
||||
assertTerminalDeferredBeyond(r, 9, 0, 9);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -688,6 +843,10 @@ public class BroadcastRecordTest {
|
||||
: errorMsg.insert(0, "Contains unexpected receiver: ").toString();
|
||||
}
|
||||
|
||||
private static ResolveInfo createResolveInfoWithPriority(int priority) {
|
||||
return createResolveInfo(PACKAGE1, getAppId(1), priority);
|
||||
}
|
||||
|
||||
private static ResolveInfo createResolveInfo(String packageName, int uid) {
|
||||
return createResolveInfo(packageName, uid, 0);
|
||||
}
|
||||
@@ -738,21 +897,40 @@ public class BroadcastRecordTest {
|
||||
return excludedList;
|
||||
}
|
||||
|
||||
private BroadcastRecord createBroadcastRecord(Intent intent,
|
||||
List<ResolveInfo> receivers) {
|
||||
return createBroadcastRecord(receivers, USER0, intent, null /* filterExtrasForReceiver */,
|
||||
null /* options */, false);
|
||||
}
|
||||
|
||||
private BroadcastRecord createOrderedBroadcastRecord(Intent intent,
|
||||
List<ResolveInfo> receivers) {
|
||||
return createBroadcastRecord(receivers, USER0, intent, null /* filterExtrasForReceiver */,
|
||||
null /* options */, true);
|
||||
}
|
||||
|
||||
private BroadcastRecord createBroadcastRecord(List<ResolveInfo> receivers, int userId,
|
||||
Intent intent) {
|
||||
return createBroadcastRecord(receivers, userId, intent, null /* filterExtrasForReceiver */,
|
||||
null /* options */);
|
||||
null /* options */, false);
|
||||
}
|
||||
|
||||
private BroadcastRecord createBroadcastRecord(List<ResolveInfo> receivers, int userId,
|
||||
Intent intent, BroadcastOptions options) {
|
||||
return createBroadcastRecord(receivers, userId, intent, null /* filterExtrasForReceiver */,
|
||||
options);
|
||||
options, false);
|
||||
}
|
||||
|
||||
private BroadcastRecord createBroadcastRecord(List<ResolveInfo> receivers, int userId,
|
||||
Intent intent, BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver,
|
||||
BroadcastOptions options) {
|
||||
return createBroadcastRecord(receivers, userId, intent, filterExtrasForReceiver,
|
||||
options, false);
|
||||
}
|
||||
|
||||
private BroadcastRecord createBroadcastRecord(List<ResolveInfo> receivers, int userId,
|
||||
Intent intent, BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver,
|
||||
BroadcastOptions options, boolean ordered) {
|
||||
return new BroadcastRecord(
|
||||
mQueue /* queue */,
|
||||
intent,
|
||||
@@ -774,7 +952,7 @@ public class BroadcastRecordTest {
|
||||
0 /* resultCode */,
|
||||
null /* resultData */,
|
||||
null /* resultExtras */,
|
||||
false /* serialized */,
|
||||
ordered /* serialized */,
|
||||
false /* sticky */,
|
||||
false /* initialSticky */,
|
||||
userId,
|
||||
@@ -789,6 +967,20 @@ public class BroadcastRecordTest {
|
||||
|
||||
private static boolean isPrioritized(List<Object> receivers) {
|
||||
return BroadcastRecord.isPrioritized(
|
||||
calculateBlockedUntilTerminalCount(receivers, false), false);
|
||||
calculateBlockedUntilBeyondCount(receivers, false), false);
|
||||
}
|
||||
|
||||
private static void assertBlocked(BroadcastRecord r, boolean... blocked) {
|
||||
assertEquals(r.receivers.size(), blocked.length);
|
||||
for (int i = 0; i < blocked.length; i++) {
|
||||
assertEquals("blocked " + i, blocked[i], r.isBlocked(i));
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertTerminalDeferredBeyond(BroadcastRecord r,
|
||||
int expectedTerminalCount, int expectedDeferredCount, int expectedBeyondCount) {
|
||||
assertEquals("terminal", expectedTerminalCount, r.terminalCount);
|
||||
assertEquals("deferred", expectedDeferredCount, r.deferredCount);
|
||||
assertEquals("beyond", expectedBeyondCount, r.beyondCount);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user