diff options
| author | 2024-11-09 00:05:25 +0000 | |
|---|---|---|
| committer | 2024-11-09 00:05:25 +0000 | |
| commit | dad192db71a0f9093d2b785f6626e7742c5bb860 (patch) | |
| tree | ae88df05a333a649ab937e5540129366190a3129 | |
| parent | cc218995d5f66ea2e07479e3abe52d2d82d5a384 (diff) | |
| parent | 102e1422e3567f6bc98153a753dea4a29937a5c7 (diff) | |
Merge "Limit the scope of receiver priorities to within a process." into main
8 files changed, 964 insertions, 55 deletions
diff --git a/services/core/java/com/android/server/am/BroadcastController.java b/services/core/java/com/android/server/am/BroadcastController.java index b0f880710eb6..8a128582c507 100644 --- a/services/core/java/com/android/server/am/BroadcastController.java +++ b/services/core/java/com/android/server/am/BroadcastController.java @@ -593,7 +593,7 @@ class BroadcastController { originalStickyCallingUid, BackgroundStartPrivileges.NONE, false /* only PRE_BOOT_COMPLETED should be exempt, no stickies */, null /* filterExtrasForReceiver */, - broadcast.originalCallingAppProcessState); + broadcast.originalCallingAppProcessState, mService.mPlatformCompat); queue.enqueueBroadcastLocked(r); } } @@ -1631,7 +1631,7 @@ class BroadcastController { receivers, resultToApp, resultTo, resultCode, resultData, resultExtras, ordered, sticky, false, userId, backgroundStartPrivileges, timeoutExempt, filterExtrasForReceiver, - callerAppProcessState); + callerAppProcessState, mService.mPlatformCompat); broadcastSentEventRecord.setBroadcastRecord(r); if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r); diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java index f908c67d7ec9..116aeeaa35c1 100644 --- a/services/core/java/com/android/server/am/BroadcastRecord.java +++ b/services/core/java/com/android/server/am/BroadcastRecord.java @@ -42,6 +42,9 @@ import android.app.AppOpsManager; import android.app.BackgroundStartPrivileges; import android.app.BroadcastOptions; import android.app.BroadcastOptions.DeliveryGroupPolicy; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledSince; +import android.compat.annotation.Overridable; import android.content.ComponentName; import android.content.IIntentReceiver; import android.content.Intent; @@ -55,10 +58,12 @@ import android.os.UserHandle; import android.util.ArrayMap; import android.util.IntArray; import android.util.PrintWriterPrinter; +import android.util.SparseBooleanArray; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.compat.PlatformCompat; import dalvik.annotation.optimization.NeverCompile; @@ -77,6 +82,16 @@ import java.util.function.BiFunction; * An active intent broadcast. */ final class BroadcastRecord extends Binder { + /** + * Limit the scope of the priority values to the process level. This means that priority values + * will only influence the order of broadcast delivery within the same process. + */ + @ChangeId + @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.BASE) + @Overridable + @VisibleForTesting + static final long CHANGE_LIMIT_PRIORITY_SCOPE = 371307720L; + final @NonNull Intent intent; // the original intent that generated us final @Nullable ComponentName targetComp; // original component name set on the intent final @Nullable ProcessRecord callerApp; // process that sent this @@ -417,13 +432,13 @@ final class BroadcastRecord extends Binder { @NonNull BackgroundStartPrivileges backgroundStartPrivileges, boolean timeoutExempt, @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver, - int callerAppProcessState) { + int callerAppProcessState, PlatformCompat platformCompat) { this(queue, intent, callerApp, callerPackage, callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType, requiredPermissions, excludedPermissions, excludedPackages, appOp, options, receivers, resultToApp, resultTo, resultCode, resultData, resultExtras, serialized, sticky, initialSticky, userId, -1, backgroundStartPrivileges, timeoutExempt, - filterExtrasForReceiver, callerAppProcessState); + filterExtrasForReceiver, callerAppProcessState, platformCompat); } BroadcastRecord(BroadcastQueue _queue, @@ -439,7 +454,7 @@ final class BroadcastRecord extends Binder { @NonNull BackgroundStartPrivileges backgroundStartPrivileges, boolean timeoutExempt, @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver, - int callerAppProcessState) { + int callerAppProcessState, PlatformCompat platformCompat) { if (_intent == null) { throw new NullPointerException("Can't construct with a null intent"); } @@ -466,7 +481,8 @@ final class BroadcastRecord extends Binder { urgent = calculateUrgent(_intent, _options); deferUntilActive = calculateDeferUntilActive(_callingUid, _options, _resultTo, _serialized, urgent); - blockedUntilBeyondCount = calculateBlockedUntilBeyondCount(receivers, _serialized); + blockedUntilBeyondCount = calculateBlockedUntilBeyondCount( + receivers, _serialized, platformCompat); scheduledTime = new long[delivery.length]; terminalTime = new long[delivery.length]; resultToApp = _resultToApp; @@ -730,7 +746,8 @@ final class BroadcastRecord extends Binder { } /** - * Determine if the result of {@link #calculateBlockedUntilBeyondCount(List, boolean)} + * Determine if the result of + * {@link #calculateBlockedUntilBeyondCount(List, boolean, PlatformCompat)} * has prioritized tranches of receivers. */ @VisibleForTesting @@ -754,37 +771,121 @@ final class BroadcastRecord extends Binder { */ @VisibleForTesting static @NonNull int[] calculateBlockedUntilBeyondCount( - @NonNull List<Object> receivers, boolean ordered) { + @NonNull List<Object> receivers, boolean ordered, PlatformCompat platformCompat) { final int N = receivers.size(); 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 + if (ordered) { + // When sending an ordered broadcast, we need to block this + // receiver until all previous receivers have terminated + for (int i = 0; i < N; i++) { blockedUntilBeyondCount[i] = i; + } + } else { + if (Flags.limitPriorityScope()) { + final boolean[] changeEnabled = calculateChangeStateForReceivers( + receivers, CHANGE_LIMIT_PRIORITY_SCOPE, platformCompat); + + // Priority of the previous tranche + int lastTranchePriority = 0; + // Priority of the current tranche + int currentTranchePriority = 0; + // Index of the last receiver in the previous tranche + int lastTranchePriorityIndex = -1; + // Index of the last receiver with change disabled in the previous tranche + int lastTrancheChangeDisabledIndex = -1; + // Index of the last receiver with change disabled in the current tranche + int currentTrancheChangeDisabledIndex = -1; + + for (int i = 0; i < N; i++) { + final int thisPriority = getReceiverPriority(receivers.get(i)); + if (i == 0) { + currentTranchePriority = thisPriority; + if (!changeEnabled[i]) { + currentTrancheChangeDisabledIndex = i; + } + continue; + } + + // Check if a new priority tranche has started + if (thisPriority != currentTranchePriority) { + // Update tranche boundaries and reset the disabled index. + if (currentTrancheChangeDisabledIndex != -1) { + lastTrancheChangeDisabledIndex = currentTrancheChangeDisabledIndex; + } + lastTranchePriority = currentTranchePriority; + lastTranchePriorityIndex = i - 1; + currentTranchePriority = thisPriority; + currentTrancheChangeDisabledIndex = -1; + } + if (!changeEnabled[i]) { + currentTrancheChangeDisabledIndex = i; + + // Since the change is disabled, block the current receiver until the + // last receiver in the previous tranche. + blockedUntilBeyondCount[i] = lastTranchePriorityIndex + 1; + } else if (thisPriority != lastTranchePriority) { + // If the changeId was disabled for an earlier receiver and the current + // receiver has a different priority, block the current receiver + // until that earlier receiver. + if (lastTrancheChangeDisabledIndex != -1) { + blockedUntilBeyondCount[i] = lastTrancheChangeDisabledIndex + 1; + } + } + } + // If the entire list is in the same priority tranche or no receivers had + // changeId disabled, mark as -1 to indicate that none of them need to wait + if (N > 0 && (lastTranchePriorityIndex == -1 + || (lastTrancheChangeDisabledIndex == -1 + && currentTrancheChangeDisabledIndex == -1))) { + Arrays.fill(blockedUntilBeyondCount, -1); + } } else { // When sending a prioritized broadcast, we only need to wait // for the previous tranche of receivers to be terminated - final int thisPriority = getReceiverPriority(receivers.get(i)); - if ((i == 0) || (thisPriority != lastPriority)) { - lastPriority = thisPriority; - lastPriorityIndex = i; - blockedUntilBeyondCount[i] = i; - } else { - blockedUntilBeyondCount[i] = lastPriorityIndex; + int lastPriority = 0; + int lastPriorityIndex = 0; + for (int i = 0; i < N; i++) { + final int thisPriority = getReceiverPriority(receivers.get(i)); + if ((i == 0) || (thisPriority != lastPriority)) { + lastPriority = thisPriority; + lastPriorityIndex = i; + blockedUntilBeyondCount[i] = i; + } else { + 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 && blockedUntilBeyondCount[N - 1] == 0) { + Arrays.fill(blockedUntilBeyondCount, -1); } } } - // 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 && blockedUntilBeyondCount[N - 1] == 0) { - Arrays.fill(blockedUntilBeyondCount, -1); - } return blockedUntilBeyondCount; } + @VisibleForTesting + static @NonNull boolean[] calculateChangeStateForReceivers(@NonNull List<Object> receivers, + long changeId, PlatformCompat platformCompat) { + final SparseBooleanArray changeStateForUids = new SparseBooleanArray(); + final int count = receivers.size(); + final boolean[] changeStateForReceivers = new boolean[count]; + for (int i = 0; i < count; ++i) { + final int receiverUid = getReceiverUid(receivers.get(i)); + final boolean isChangeEnabled; + final int idx = changeStateForUids.indexOfKey(receiverUid); + if (idx >= 0) { + isChangeEnabled = changeStateForUids.valueAt(idx); + } else { + isChangeEnabled = platformCompat.isChangeEnabledByUidInternalNoLogging( + changeId, receiverUid); + changeStateForUids.put(receiverUid, isChangeEnabled); + } + changeStateForReceivers[i] = isChangeEnabled; + } + return changeStateForReceivers; + } + static int getReceiverUid(@NonNull Object receiver) { if (receiver instanceof BroadcastFilter) { return ((BroadcastFilter) receiver).owningUid; diff --git a/services/core/java/com/android/server/am/broadcasts_flags.aconfig b/services/core/java/com/android/server/am/broadcasts_flags.aconfig index b1185d552941..7f169db7dcec 100644 --- a/services/core/java/com/android/server/am/broadcasts_flags.aconfig +++ b/services/core/java/com/android/server/am/broadcasts_flags.aconfig @@ -7,4 +7,12 @@ flag { description: "Restrict priority values defined by non-system apps" is_fixed_read_only: true bug: "369487976" +} + +flag { + name: "limit_priority_scope" + namespace: "backstage_power" + description: "Limit the scope of receiver priorities to within a process" + is_fixed_read_only: true + bug: "369487976" }
\ No newline at end of file diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp index be698b2673ad..979384c6b2db 100644 --- a/services/tests/mockingservicestests/Android.bp +++ b/services/tests/mockingservicestests/Android.bp @@ -134,6 +134,7 @@ android_ravenwood_test { "androidx.annotation_annotation", "androidx.test.rules", "services.core", + "servicestests-utils-mockito-extended", ], srcs: [ "src/com/android/server/am/BroadcastRecordTest.java", diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java index d602660597ff..a1a8b0ec7d2f 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java @@ -41,6 +41,7 @@ import android.os.TestLooperManager; import android.os.UserHandle; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; +import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import android.util.SparseArray; @@ -97,6 +98,9 @@ public abstract class BaseBroadcastQueueTest { .spyStatic(ProcessList.class) .build(); + + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @Rule public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); @@ -112,6 +116,8 @@ public abstract class BaseBroadcastQueueTest { AlarmManagerInternal mAlarmManagerInt; @Mock ProcessList mProcessList; + @Mock + PlatformCompat mPlatformCompat; @Mock AppStartInfoTracker mAppStartInfoTracker; @@ -178,6 +184,11 @@ public abstract class BaseBroadcastQueueTest { doReturn(false).when(mSkipPolicy).disallowBackgroundStart(any()); doReturn(mAppStartInfoTracker).when(mProcessList).getAppStartInfoTracker(); + + doReturn(true).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging( + eq(BroadcastFilter.CHANGE_RESTRICT_PRIORITY_VALUES), anyInt()); + doReturn(true).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging( + eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), anyInt()); } public void tearDown() throws Exception { 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 100b54897573..1481085c5f71 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java @@ -18,6 +18,7 @@ package com.android.server.am; import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED; import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD; @@ -49,7 +50,6 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; @@ -65,7 +65,6 @@ import android.content.IIntentReceiver; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; -import android.content.pm.ResolveInfo; import android.media.AudioManager; import android.os.Bundle; import android.os.BundleMerger; @@ -73,6 +72,8 @@ import android.os.DropBoxManager; import android.os.Process; import android.os.SystemClock; import android.os.UserHandle; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; import android.util.IndentingPrintWriter; import android.util.Pair; @@ -182,10 +183,6 @@ public final class BroadcastQueueModernImplTest extends BaseBroadcastQueueTest { return mock(Intent.class); } - private static ResolveInfo makeMockManifestReceiver() { - return mock(ResolveInfo.class); - } - private static BroadcastFilter makeMockRegisteredReceiver() { return mock(BroadcastFilter.class); } @@ -214,7 +211,8 @@ public final class BroadcastQueueModernImplTest extends BaseBroadcastQueueTest { return new BroadcastRecord(mImpl, intent, mProcess, PACKAGE_RED, null, 21, TEST_UID, false, null, null, null, null, AppOpsManager.OP_NONE, options, receivers, null, resultTo, Activity.RESULT_OK, null, null, ordered, false, false, UserHandle.USER_SYSTEM, - BackgroundStartPrivileges.NONE, false, null, PROCESS_STATE_UNKNOWN); + BackgroundStartPrivileges.NONE, false, null, PROCESS_STATE_UNKNOWN, + mPlatformCompat); } private void enqueueOrReplaceBroadcast(BroadcastProcessQueue queue, @@ -646,7 +644,8 @@ public final class BroadcastQueueModernImplTest extends BaseBroadcastQueueTest { @Test public void testRunnableAt_Cached_Manifest() { doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), null, - List.of(makeMockManifestReceiver()), null, false), REASON_CONTAINS_MANIFEST); + List.of(makeManifestReceiver(PACKAGE_RED, CLASS_RED)), null, false), + REASON_CONTAINS_MANIFEST); } @Test @@ -679,6 +678,19 @@ public final class BroadcastQueueModernImplTest extends BaseBroadcastQueueTest { List.of(makeMockRegisteredReceiver()), null, false), REASON_CONTAINS_ALARM); } + @DisableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) + @Test + public void testRunnableAt_Cached_Prioritized_NonDeferrable_flagDisabled() { + final List receivers = List.of( + withPriority(makeManifestReceiver(PACKAGE_RED, PACKAGE_RED), 10), + withPriority(makeManifestReceiver(PACKAGE_GREEN, PACKAGE_GREEN), -10)); + final BroadcastOptions options = BroadcastOptions.makeBasic() + .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_NONE); + doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), options, + receivers, null, false), REASON_CONTAINS_PRIORITIZED); + } + + @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) @Test public void testRunnableAt_Cached_Prioritized_NonDeferrable() { final List receivers = List.of( @@ -687,6 +699,32 @@ public final class BroadcastQueueModernImplTest extends BaseBroadcastQueueTest { final BroadcastOptions options = BroadcastOptions.makeBasic() .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_NONE); doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), options, + receivers, null, false), REASON_CONTAINS_MANIFEST); + } + + @Test + public void testRunnableAt_Cached_Ordered_NonDeferrable() { + final List receivers = List.of( + withPriority(makeManifestReceiver(PACKAGE_RED, PACKAGE_RED), 10), + withPriority(makeManifestReceiver(PACKAGE_GREEN, PACKAGE_GREEN), -10)); + final BroadcastOptions options = BroadcastOptions.makeBasic() + .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_NONE); + doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), options, + receivers, mock(IIntentReceiver.class), true), REASON_CONTAINS_ORDERED); + } + + @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) + @Test + public void testRunnableAt_Cached_Prioritized_NonDeferrable_changeIdDisabled() { + doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging( + eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), + eq(getUidForPackage(PACKAGE_GREEN))); + final List receivers = List.of( + withPriority(makeManifestReceiver(PACKAGE_RED, PACKAGE_RED), 10), + withPriority(makeManifestReceiver(PACKAGE_GREEN, PACKAGE_GREEN), -10)); + final BroadcastOptions options = BroadcastOptions.makeBasic() + .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_NONE); + doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), options, receivers, null, false), REASON_CONTAINS_PRIORITIZED); } @@ -1136,6 +1174,63 @@ public final class BroadcastQueueModernImplTest extends BaseBroadcastQueueTest { verifyPendingRecords(blueQueue, List.of(screenOn)); } + @DisableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) + @SuppressWarnings("GuardedBy") + @Test + public void testDeliveryGroupPolicy_prioritized_diffReceivers_flagDisabled() { + final Intent screenOn = new Intent(Intent.ACTION_SCREEN_ON); + final Intent screenOff = new Intent(Intent.ACTION_SCREEN_OFF); + final BroadcastOptions screenOnOffOptions = BroadcastOptions.makeBasic() + .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT) + .setDeliveryGroupMatchingKey("screenOnOff", Intent.ACTION_SCREEN_ON); + + final Object greenReceiver = withPriority( + makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN), 10); + final Object redReceiver = withPriority( + makeManifestReceiver(PACKAGE_RED, CLASS_RED), 5); + final Object blueReceiver = withPriority( + makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE), 0); + + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOn, screenOnOffOptions, + List.of(greenReceiver, blueReceiver), false)); + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOff, screenOnOffOptions, + List.of(greenReceiver, redReceiver, blueReceiver), false)); + final BroadcastProcessQueue greenQueue = mImpl.getProcessQueue(PACKAGE_GREEN, + getUidForPackage(PACKAGE_GREEN)); + final BroadcastProcessQueue redQueue = mImpl.getProcessQueue(PACKAGE_RED, + getUidForPackage(PACKAGE_RED)); + final BroadcastProcessQueue blueQueue = mImpl.getProcessQueue(PACKAGE_BLUE, + getUidForPackage(PACKAGE_BLUE)); + verifyPendingRecords(greenQueue, List.of(screenOff)); + verifyPendingRecords(redQueue, List.of(screenOff)); + verifyPendingRecords(blueQueue, List.of(screenOff)); + + assertTrue(greenQueue.isEmpty()); + assertTrue(redQueue.isEmpty()); + assertTrue(blueQueue.isEmpty()); + + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOff, screenOnOffOptions, + List.of(greenReceiver, redReceiver, blueReceiver), false)); + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOn, screenOnOffOptions, + List.of(greenReceiver, blueReceiver), false)); + verifyPendingRecords(greenQueue, List.of(screenOff, screenOn)); + verifyPendingRecords(redQueue, List.of(screenOff)); + verifyPendingRecords(blueQueue, List.of(screenOff, screenOn)); + + final BroadcastRecord screenOffRecord = makeBroadcastRecord(screenOff, screenOnOffOptions, + List.of(greenReceiver, redReceiver, blueReceiver), false); + screenOffRecord.setDeliveryState(2, BroadcastRecord.DELIVERY_DEFERRED, + "testDeliveryGroupPolicy_prioritized_diffReceivers_flagDisabled"); + mImpl.enqueueBroadcastLocked(screenOffRecord); + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOn, screenOnOffOptions, + List.of(greenReceiver, blueReceiver), false)); + verifyPendingRecords(greenQueue, List.of(screenOff, screenOn)); + verifyPendingRecords(redQueue, List.of(screenOff)); + verifyPendingRecords(blueQueue, List.of(screenOn)); + } + + @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) + @SuppressWarnings("GuardedBy") @Test public void testDeliveryGroupPolicy_prioritized_diffReceivers() { final Intent screenOn = new Intent(Intent.ACTION_SCREEN_ON); @@ -1173,6 +1268,65 @@ public final class BroadcastQueueModernImplTest extends BaseBroadcastQueueTest { List.of(greenReceiver, redReceiver, blueReceiver), false)); mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOn, screenOnOffOptions, List.of(greenReceiver, blueReceiver), false)); + verifyPendingRecords(greenQueue, List.of(screenOn)); + verifyPendingRecords(redQueue, List.of(screenOff)); + verifyPendingRecords(blueQueue, List.of(screenOn)); + + final BroadcastRecord screenOffRecord = makeBroadcastRecord(screenOff, screenOnOffOptions, + List.of(greenReceiver, redReceiver, blueReceiver), false); + screenOffRecord.setDeliveryState(2, BroadcastRecord.DELIVERY_DEFERRED, + "testDeliveryGroupPolicy_prioritized_diffReceivers"); + mImpl.enqueueBroadcastLocked(screenOffRecord); + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOn, screenOnOffOptions, + List.of(greenReceiver, blueReceiver), false)); + verifyPendingRecords(greenQueue, List.of(screenOn)); + verifyPendingRecords(redQueue, List.of(screenOff)); + verifyPendingRecords(blueQueue, List.of(screenOn)); + } + + @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) + @SuppressWarnings("GuardedBy") + @Test + public void testDeliveryGroupPolicy_prioritized_diffReceivers_changeIdDisabled() { + doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging( + eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), + eq(getUidForPackage(PACKAGE_GREEN))); + + final Intent screenOn = new Intent(Intent.ACTION_SCREEN_ON); + final Intent screenOff = new Intent(Intent.ACTION_SCREEN_OFF); + final BroadcastOptions screenOnOffOptions = BroadcastOptions.makeBasic() + .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT) + .setDeliveryGroupMatchingKey("screenOnOff", Intent.ACTION_SCREEN_ON); + + final Object greenReceiver = withPriority( + makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN), 10); + final Object redReceiver = withPriority( + makeManifestReceiver(PACKAGE_RED, CLASS_RED), 5); + final Object blueReceiver = withPriority( + makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE), 0); + + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOn, screenOnOffOptions, + List.of(greenReceiver, blueReceiver), false)); + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOff, screenOnOffOptions, + List.of(greenReceiver, redReceiver, blueReceiver), false)); + final BroadcastProcessQueue greenQueue = mImpl.getProcessQueue(PACKAGE_GREEN, + getUidForPackage(PACKAGE_GREEN)); + final BroadcastProcessQueue redQueue = mImpl.getProcessQueue(PACKAGE_RED, + getUidForPackage(PACKAGE_RED)); + final BroadcastProcessQueue blueQueue = mImpl.getProcessQueue(PACKAGE_BLUE, + getUidForPackage(PACKAGE_BLUE)); + verifyPendingRecords(greenQueue, List.of(screenOff)); + verifyPendingRecords(redQueue, List.of(screenOff)); + verifyPendingRecords(blueQueue, List.of(screenOff)); + + assertTrue(greenQueue.isEmpty()); + assertTrue(redQueue.isEmpty()); + assertTrue(blueQueue.isEmpty()); + + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOff, screenOnOffOptions, + List.of(greenReceiver, redReceiver, blueReceiver), false)); + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOn, screenOnOffOptions, + List.of(greenReceiver, blueReceiver), false)); verifyPendingRecords(greenQueue, List.of(screenOff, screenOn)); verifyPendingRecords(redQueue, List.of(screenOff)); verifyPendingRecords(blueQueue, List.of(screenOff, screenOn)); @@ -1569,8 +1723,9 @@ public final class BroadcastQueueModernImplTest extends BaseBroadcastQueueTest { verifyPendingRecords(redQueue, List.of(userPresent, timeTick)); } + @DisableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) @Test - public void testDeliveryDeferredForCached() throws Exception { + public void testDeliveryDeferredForCached_flagDisabled() throws Exception { final ProcessRecord greenProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_GREEN)); final ProcessRecord redProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_RED)); @@ -1664,8 +1819,217 @@ public final class BroadcastQueueModernImplTest extends BaseBroadcastQueueTest { }, false /* andRemove */); } + @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) + @SuppressWarnings("GuardedBy") + @Test + public void testDeliveryDeferredForCached_changeIdDisabled() throws Exception { + doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging( + eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), + eq(getUidForPackage(PACKAGE_GREEN))); + + final ProcessRecord greenProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_GREEN)); + final ProcessRecord redProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_RED)); + + final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK); + final BroadcastRecord timeTickRecord = makeBroadcastRecord(timeTick, + List.of(makeRegisteredReceiver(greenProcess, 0))); + + final Intent batteryChanged = new Intent(Intent.ACTION_BATTERY_CHANGED); + final BroadcastOptions optionsBatteryChanged = + BroadcastOptions.makeWithDeferUntilActive(true); + final BroadcastRecord batteryChangedRecord = makeBroadcastRecord(batteryChanged, + optionsBatteryChanged, + List.of(makeRegisteredReceiver(greenProcess, 10), + makeRegisteredReceiver(redProcess, 0)), + false /* ordered */); + + mImpl.enqueueBroadcastLocked(timeTickRecord); + mImpl.enqueueBroadcastLocked(batteryChangedRecord); + + final BroadcastProcessQueue greenQueue = mImpl.getProcessQueue(PACKAGE_GREEN, + getUidForPackage(PACKAGE_GREEN)); + final BroadcastProcessQueue redQueue = mImpl.getProcessQueue(PACKAGE_RED, + getUidForPackage(PACKAGE_RED)); + assertEquals(BroadcastProcessQueue.REASON_NORMAL, greenQueue.getRunnableAtReason()); + assertFalse(greenQueue.shouldBeDeferred()); + assertEquals(BroadcastProcessQueue.REASON_BLOCKED, redQueue.getRunnableAtReason()); + assertFalse(redQueue.shouldBeDeferred()); + + // Simulate process state change + greenQueue.setProcessAndUidState(greenProcess, false /* uidForeground */, + true /* processFreezable */); + greenQueue.updateDeferredStates(mImpl.mBroadcastConsumerDeferApply, + mImpl.mBroadcastConsumerDeferClear); + + assertEquals(BroadcastProcessQueue.REASON_CACHED, greenQueue.getRunnableAtReason()); + assertTrue(greenQueue.shouldBeDeferred()); + // Once the broadcasts to green process are deferred, broadcasts to red process + // shouldn't be blocked anymore. + assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason()); + assertFalse(redQueue.shouldBeDeferred()); + + // All broadcasts to green process should be deferred. + greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> { + assertEquals("Unexpected state for " + r, + BroadcastRecord.DELIVERY_DEFERRED, r.getDeliveryState(i)); + }, false /* andRemove */); + redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> { + assertEquals("Unexpected state for " + r, + BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i)); + }, false /* andRemove */); + + final Intent packageChanged = new Intent(Intent.ACTION_PACKAGE_CHANGED); + final BroadcastRecord packageChangedRecord = makeBroadcastRecord(packageChanged, + List.of(makeRegisteredReceiver(greenProcess, 0))); + mImpl.enqueueBroadcastLocked(packageChangedRecord); + + assertEquals(BroadcastProcessQueue.REASON_CACHED, greenQueue.getRunnableAtReason()); + assertTrue(greenQueue.shouldBeDeferred()); + assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason()); + assertFalse(redQueue.shouldBeDeferred()); + + // All broadcasts to the green process, including the newly enqueued one, should be + // deferred. + greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> { + assertEquals("Unexpected state for " + r, + BroadcastRecord.DELIVERY_DEFERRED, r.getDeliveryState(i)); + }, false /* andRemove */); + redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> { + assertEquals("Unexpected state for " + r, + BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i)); + }, false /* andRemove */); + + // Simulate process state change + greenQueue.setProcessAndUidState(greenProcess, false /* uidForeground */, + false /* processFreezable */); + greenQueue.updateDeferredStates(mImpl.mBroadcastConsumerDeferApply, + mImpl.mBroadcastConsumerDeferClear); + + assertEquals(BroadcastProcessQueue.REASON_NORMAL, greenQueue.getRunnableAtReason()); + assertFalse(greenQueue.shouldBeDeferred()); + assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason()); + assertFalse(redQueue.shouldBeDeferred()); + + greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> { + assertEquals("Unexpected state for " + r, + BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i)); + }, false /* andRemove */); + redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> { + assertEquals("Unexpected state for " + r, + BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i)); + }, false /* andRemove */); + } + + @DisableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) + @SuppressWarnings("GuardedBy") + @Test + public void testDeliveryDeferredForCached_withInfiniteDeferred_flagDisabled() throws Exception { + final ProcessRecord greenProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_GREEN)); + final ProcessRecord redProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_RED)); + + final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK); + final BroadcastOptions optionsTimeTick = BroadcastOptions.makeWithDeferUntilActive(true); + final BroadcastRecord timeTickRecord = makeBroadcastRecord(timeTick, optionsTimeTick, + List.of(makeRegisteredReceiver(greenProcess, 0)), false /* ordered */); + + final Intent batteryChanged = new Intent(Intent.ACTION_BATTERY_CHANGED); + final BroadcastOptions optionsBatteryChanged = + BroadcastOptions.makeWithDeferUntilActive(true); + final BroadcastRecord batteryChangedRecord = makeBroadcastRecord(batteryChanged, + optionsBatteryChanged, + List.of(makeRegisteredReceiver(greenProcess, 10), + makeRegisteredReceiver(redProcess, 0)), + false /* ordered */); + + mImpl.enqueueBroadcastLocked(timeTickRecord); + mImpl.enqueueBroadcastLocked(batteryChangedRecord); + + final BroadcastProcessQueue greenQueue = mImpl.getProcessQueue(PACKAGE_GREEN, + getUidForPackage(PACKAGE_GREEN)); + final BroadcastProcessQueue redQueue = mImpl.getProcessQueue(PACKAGE_RED, + getUidForPackage(PACKAGE_RED)); + assertEquals(BroadcastProcessQueue.REASON_NORMAL, greenQueue.getRunnableAtReason()); + assertFalse(greenQueue.shouldBeDeferred()); + assertEquals(BroadcastProcessQueue.REASON_BLOCKED, redQueue.getRunnableAtReason()); + assertFalse(redQueue.shouldBeDeferred()); + + // Simulate process state change + greenQueue.setProcessAndUidState(greenProcess, false /* uidForeground */, + true /* processFreezable */); + greenQueue.updateDeferredStates(mImpl.mBroadcastConsumerDeferApply, + mImpl.mBroadcastConsumerDeferClear); + + assertEquals(BroadcastProcessQueue.REASON_CACHED_INFINITE_DEFER, + greenQueue.getRunnableAtReason()); + assertTrue(greenQueue.shouldBeDeferred()); + // Once the broadcasts to green process are deferred, broadcasts to red process + // shouldn't be blocked anymore. + assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason()); + assertFalse(redQueue.shouldBeDeferred()); + + // All broadcasts to green process should be deferred. + greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> { + assertEquals("Unexpected state for " + r, + BroadcastRecord.DELIVERY_DEFERRED, r.getDeliveryState(i)); + }, false /* andRemove */); + redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> { + assertEquals("Unexpected state for " + r, + BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i)); + }, false /* andRemove */); + + final Intent packageChanged = new Intent(Intent.ACTION_PACKAGE_CHANGED); + final BroadcastOptions optionsPackageChanged = + BroadcastOptions.makeWithDeferUntilActive(true); + final BroadcastRecord packageChangedRecord = makeBroadcastRecord(packageChanged, + optionsPackageChanged, + List.of(makeRegisteredReceiver(greenProcess, 0)), false /* ordered */); + mImpl.enqueueBroadcastLocked(packageChangedRecord); + + assertEquals(BroadcastProcessQueue.REASON_CACHED_INFINITE_DEFER, + greenQueue.getRunnableAtReason()); + assertTrue(greenQueue.shouldBeDeferred()); + assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason()); + assertFalse(redQueue.shouldBeDeferred()); + + // All broadcasts to the green process, including the newly enqueued one, should be + // deferred. + greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> { + assertEquals("Unexpected state for " + r, + BroadcastRecord.DELIVERY_DEFERRED, r.getDeliveryState(i)); + }, false /* andRemove */); + redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> { + assertEquals("Unexpected state for " + r, + BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i)); + }, false /* andRemove */); + + // Simulate process state change + greenQueue.setProcessAndUidState(greenProcess, false /* uidForeground */, + false /* processFreezable */); + greenQueue.updateDeferredStates(mImpl.mBroadcastConsumerDeferApply, + mImpl.mBroadcastConsumerDeferClear); + + assertEquals(BroadcastProcessQueue.REASON_NORMAL, greenQueue.getRunnableAtReason()); + assertFalse(greenQueue.shouldBeDeferred()); + assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason()); + assertFalse(redQueue.shouldBeDeferred()); + + greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> { + assertEquals("Unexpected state for " + r, + BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i)); + }, false /* andRemove */); + redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> { + assertEquals("Unexpected state for " + r, + BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i)); + }, false /* andRemove */); + } + + @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) @Test - public void testDeliveryDeferredForCached_withInfiniteDeferred() throws Exception { + public void testDeliveryDeferredForCached_withInfiniteDeferred_changeIdDisabled() + throws Exception { + doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging( + eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), + eq(getUidForPackage(PACKAGE_GREEN))); final ProcessRecord greenProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_GREEN)); final ProcessRecord redProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_RED)); 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 3aaf2e5c61a6..eab5ce3cee2f 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java @@ -21,6 +21,7 @@ import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_FINISH_RECEIVER import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_START_RECEIVER; import static android.os.UserHandle.USER_SYSTEM; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.server.am.ActivityManagerDebugConfig.LOG_WRITER_INFO; import static com.android.server.am.BroadcastProcessQueue.reasonToString; import static com.android.server.am.BroadcastRecord.deliveryStateToString; @@ -45,7 +46,6 @@ import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -77,6 +77,8 @@ import android.os.IBinder; import android.os.PowerExemptionManager; import android.os.SystemClock; import android.os.UserHandle; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.RequiresFlagsEnabled; import android.util.ArrayMap; import android.util.Log; @@ -446,7 +448,8 @@ public class BroadcastQueueTest extends BaseBroadcastQueueTest { callerApp.getPid(), callerApp.info.uid, false, null, null, null, null, AppOpsManager.OP_NONE, options, receivers, callerApp, resultTo, Activity.RESULT_OK, null, resultExtras, ordered, false, false, userId, - BackgroundStartPrivileges.NONE, false, null, PROCESS_STATE_UNKNOWN); + BackgroundStartPrivileges.NONE, false, null, PROCESS_STATE_UNKNOWN, + mPlatformCompat); } private void assertHealth() { @@ -1495,7 +1498,7 @@ public class BroadcastQueueTest extends BaseBroadcastQueueTest { null, null, null, null, AppOpsManager.OP_NONE, BroadcastOptions.makeBasic(), List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN)), null, null, Activity.RESULT_OK, null, null, false, false, false, UserHandle.USER_SYSTEM, - backgroundStartPrivileges, false, null, PROCESS_STATE_UNKNOWN); + backgroundStartPrivileges, false, null, PROCESS_STATE_UNKNOWN, mPlatformCompat); enqueueBroadcast(r); waitForIdle(); @@ -1550,8 +1553,9 @@ public class BroadcastQueueTest extends BaseBroadcastQueueTest { /** * Verify that when dispatching we respect tranches of priority. */ + @DisableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) @Test - public void testPriority() throws Exception { + public void testPriority_flagDisabled() throws Exception { final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED); final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE); final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN); @@ -1594,6 +1598,104 @@ public class BroadcastQueueTest extends BaseBroadcastQueueTest { } /** + * Verify that when dispatching we respect tranches of priority. + */ + @Test + public void testOrdered_withPriorities() throws Exception { + final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED); + final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE); + final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN); + final ProcessRecord receiverYellowApp = makeActiveProcessRecord(PACKAGE_YELLOW); + + // Enqueue a normal broadcast that will go to several processes, and + // then enqueue a foreground broadcast that risks reordering + final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED); + final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); + airplane.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + final IIntentReceiver orderedResultTo = mock(IIntentReceiver.class); + enqueueBroadcast(makeOrderedBroadcastRecord(timezone, callerApp, + List.of(makeRegisteredReceiver(receiverBlueApp, 10), + makeRegisteredReceiver(receiverGreenApp, 10), + makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE), + makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW), + makeRegisteredReceiver(receiverYellowApp, -10)), + orderedResultTo, null)); + enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, + List.of(makeRegisteredReceiver(receiverBlueApp)))); + waitForIdle(); + + // Ignore the final foreground broadcast + mScheduledBroadcasts.remove(makeScheduledBroadcast(receiverBlueApp, airplane)); + assertEquals(6, mScheduledBroadcasts.size()); + + // We're only concerned about enforcing ordering between tranches; + // within a tranche we're okay with reordering + assertEquals( + Set.of(makeScheduledBroadcast(receiverBlueApp, timezone), + makeScheduledBroadcast(receiverGreenApp, timezone)), + Set.of(mScheduledBroadcasts.remove(0), + mScheduledBroadcasts.remove(0))); + assertEquals( + Set.of(makeScheduledBroadcast(receiverBlueApp, timezone), + makeScheduledBroadcast(receiverYellowApp, timezone)), + Set.of(mScheduledBroadcasts.remove(0), + mScheduledBroadcasts.remove(0))); + assertEquals( + Set.of(makeScheduledBroadcast(receiverYellowApp, timezone)), + Set.of(mScheduledBroadcasts.remove(0))); + } + + /** + * Verify that when dispatching we respect tranches of priority. + */ + @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) + @Test + public void testPriority_changeIdDisabled() throws Exception { + final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED); + final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE); + final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN); + final ProcessRecord receiverYellowApp = makeActiveProcessRecord(PACKAGE_YELLOW); + + doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging( + eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(receiverBlueApp.uid)); + + // Enqueue a normal broadcast that will go to several processes, and + // then enqueue a foreground broadcast that risks reordering + final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED); + final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); + airplane.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + enqueueBroadcast(makeBroadcastRecord(timezone, callerApp, + List.of(makeRegisteredReceiver(receiverBlueApp, 10), + makeRegisteredReceiver(receiverGreenApp, 10), + makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE), + makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW), + makeRegisteredReceiver(receiverYellowApp, -10)))); + enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, + List.of(makeRegisteredReceiver(receiverBlueApp)))); + waitForIdle(); + + // Ignore the final foreground broadcast + mScheduledBroadcasts.remove(makeScheduledBroadcast(receiverBlueApp, airplane)); + assertEquals(5, mScheduledBroadcasts.size()); + + // We're only concerned about enforcing ordering between tranches; + // within a tranche we're okay with reordering + assertEquals( + Set.of(makeScheduledBroadcast(receiverBlueApp, timezone), + makeScheduledBroadcast(receiverGreenApp, timezone)), + Set.of(mScheduledBroadcasts.remove(0), + mScheduledBroadcasts.remove(0))); + assertEquals( + Set.of(makeScheduledBroadcast(receiverBlueApp, timezone), + makeScheduledBroadcast(receiverYellowApp, timezone)), + Set.of(mScheduledBroadcasts.remove(0), + mScheduledBroadcasts.remove(0))); + assertEquals( + Set.of(makeScheduledBroadcast(receiverYellowApp, timezone)), + Set.of(mScheduledBroadcasts.remove(0))); + } + + /** * Verify prioritized receivers work as expected with deferrable broadcast - broadcast to * app in cached state should be deferred and the rest should be delivered as per the priority * order. @@ -2305,8 +2407,35 @@ public class BroadcastQueueTest extends BaseBroadcastQueueTest { .isLessThan(getReceiverScheduledTime(timeTickRecord, receiverBlue)); } + @DisableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) + @Test + public void testPrioritizedBroadcastDelivery_uidForeground_flagDisabled() throws Exception { + 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); + waitForIdle(); + + 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)); + } + @Test - public void testPrioritizedBroadcastDelivery_uidForeground() throws Exception { + public void testOrderedBroadcastDelivery_uidForeground() throws Exception { final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED); final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE); final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN); @@ -2319,6 +2448,37 @@ public class BroadcastQueueTest extends BaseBroadcastQueueTest { final BroadcastFilter receiverBlue = makeRegisteredReceiver(receiverBlueApp, 10); final BroadcastFilter receiverGreen = makeRegisteredReceiver(receiverGreenApp, 5); + final IIntentReceiver resultTo = mock(IIntentReceiver.class); + final BroadcastRecord prioritizedRecord = makeOrderedBroadcastRecord(timeTick, callerApp, + List.of(receiverBlue, receiverGreen), resultTo, null); + + 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)); + } + + @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) + @Test + public void testPrioritizedBroadcastDelivery_uidForeground_changeIdDisabled() throws Exception { + final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED); + final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE); + final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN); + + doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging( + eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(receiverBlueApp.uid)); + + mUidObserver.onUidStateChanged(receiverGreenApp.info.uid, + ActivityManager.PROCESS_STATE_TOP, 0, ActivityManager.PROCESS_CAPABILITY_NONE); + waitForIdle(); + + 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)); diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java index 8cd0da721364..4a370a3cc431 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java @@ -18,6 +18,8 @@ package com.android.server.am; import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.server.am.BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE; 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; @@ -33,6 +35,8 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; import android.app.BackgroundStartPrivileges; import android.app.BroadcastOptions; @@ -46,11 +50,17 @@ import android.os.Bundle; import android.os.PersistableBundle; import android.os.Process; import android.os.UserHandle; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import android.telephony.SubscriptionManager; import androidx.test.filters.SmallTest; +import com.android.server.compat.PlatformCompat; + import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -73,6 +83,9 @@ import java.util.function.BiFunction; public class BroadcastRecordTest { private static final String TAG = "BroadcastRecordTest"; + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + private static final int USER0 = UserHandle.USER_SYSTEM; private static final String PACKAGE1 = "pkg1"; private static final String PACKAGE2 = "pkg2"; @@ -89,10 +102,14 @@ public class BroadcastRecordTest { @Mock BroadcastQueue mQueue; @Mock ProcessRecord mProcess; + @Mock PlatformCompat mPlatformCompat; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + + doReturn(true).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging( + eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), anyInt()); } @Test @@ -108,13 +125,13 @@ public class BroadcastRecordTest { assertArrayEquals(new int[] {-1}, calculateBlockedUntilBeyondCount(List.of( - createResolveInfo(PACKAGE1, getAppId(1), 0)), false)); + createResolveInfo(PACKAGE1, getAppId(1), 0)), false, mPlatformCompat)); assertArrayEquals(new int[] {-1}, calculateBlockedUntilBeyondCount(List.of( - createResolveInfo(PACKAGE1, getAppId(1), -10)), false)); + createResolveInfo(PACKAGE1, getAppId(1), -10)), false, mPlatformCompat)); assertArrayEquals(new int[] {-1}, calculateBlockedUntilBeyondCount(List.of( - createResolveInfo(PACKAGE1, getAppId(1), 10)), false)); + createResolveInfo(PACKAGE1, getAppId(1), 10)), false, mPlatformCompat)); } @Test @@ -128,18 +145,19 @@ public class BroadcastRecordTest { createResolveInfo(PACKAGE2, getAppId(2), 10), createResolveInfo(PACKAGE3, getAppId(3), 10)))); - assertArrayEquals(new int[] {-1,-1,-1}, + assertArrayEquals(new int[] {-1, -1, -1}, 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}, + createResolveInfo(PACKAGE3, getAppId(3), 0)), false, mPlatformCompat)); + assertArrayEquals(new int[] {-1, -1, -1}, calculateBlockedUntilBeyondCount(List.of( createResolveInfo(PACKAGE1, getAppId(1), 10), createResolveInfo(PACKAGE2, getAppId(2), 10), - createResolveInfo(PACKAGE3, getAppId(3), 10)), false)); + createResolveInfo(PACKAGE3, getAppId(3), 10)), false, mPlatformCompat)); } + @DisableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) @Test public void testIsPrioritized_Yes() { assertTrue(isPrioritized(List.of( @@ -151,18 +169,203 @@ public class BroadcastRecordTest { createResolveInfo(PACKAGE2, getAppId(2), 0), createResolveInfo(PACKAGE3, getAppId(3), 0)))); - assertArrayEquals(new int[] {0,1,2}, + assertArrayEquals(new int[] {0, 1, 2}, + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), -10)), false, mPlatformCompat)); + assertArrayEquals(new int[] {0, 0, 2, 3, 3}, + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 20), + createResolveInfo(PACKAGE2, getAppId(2), 20), + createResolveInfo(PACKAGE3, getAppId(3), 10), + createResolveInfo(PACKAGE3, getAppId(3), 0), + createResolveInfo(PACKAGE3, getAppId(3), 0)), false, mPlatformCompat)); + } + + @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) + @Test + public void testIsPrioritized_withDifferentPriorities() { + assertFalse(isPrioritized(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), -10)))); + assertFalse(isPrioritized(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), 0)))); + + assertArrayEquals(new int[] {-1, -1, -1}, + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), -10)), false, mPlatformCompat)); + assertArrayEquals(new int[] {-1, -1, -1}, + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 10), + createResolveInfo(PACKAGE3, getAppId(3), -10)), false, mPlatformCompat)); + assertArrayEquals(new int[] {-1, -1, -1}, + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), 0)), false, mPlatformCompat)); + assertArrayEquals(new int[] {-1, -1, -1, -1, -1}, + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 20), + createResolveInfo(PACKAGE2, getAppId(2), 20), + createResolveInfo(PACKAGE3, getAppId(3), 10), + createResolveInfo(PACKAGE3, getAppId(3), 0), + createResolveInfo(PACKAGE3, getAppId(3), 0)), false, mPlatformCompat)); + } + + @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) + @Test + public void testIsPrioritized_withDifferentPriorities_withFirstUidChangeIdDisabled() { + doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging( + eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(1))); + + assertTrue(isPrioritized(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), -10)))); + assertTrue(isPrioritized(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), 0)))); + + assertArrayEquals(new int[] {0, 1, 1}, + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), -10)), false, mPlatformCompat)); + assertArrayEquals(new int[] {0, 0, 1}, + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 10), + createResolveInfo(PACKAGE3, getAppId(3), -10)), false, mPlatformCompat)); + assertArrayEquals(new int[] {0, 0, 1, 1, 1}, + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 20), + createResolveInfo(PACKAGE2, getAppId(2), 20), + createResolveInfo(PACKAGE3, getAppId(3), 10), + createResolveInfo(PACKAGE3, getAppId(3), 0), + createResolveInfo(PACKAGE3, getAppId(3), 0)), false, mPlatformCompat)); + } + + @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) + @Test + public void testIsPrioritized_withDifferentPriorities_withLastUidChangeIdDisabled() { + doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging( + eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(3))); + + assertTrue(isPrioritized(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), -10)))); + assertTrue(isPrioritized(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), 0)))); + + assertArrayEquals(new int[] {0, 0, 2}, + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), -10)), false, mPlatformCompat)); + assertArrayEquals(new int[] {0, 1}, + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE3, getAppId(3), 0)), false, mPlatformCompat)); + assertArrayEquals(new int[] {0, 0, 1}, + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), 0)), false, mPlatformCompat)); + assertArrayEquals(new int[] {0, 0, 2, 3, 3}, + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 20), + createResolveInfo(PACKAGE2, getAppId(2), 20), + createResolveInfo(PACKAGE3, getAppId(3), 10), + createResolveInfo(PACKAGE3, getAppId(3), 0), + createResolveInfo(PACKAGE3, getAppId(3), 0)), false, mPlatformCompat)); + } + + @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) + @Test + public void testIsPrioritized_withDifferentPriorities_withUidChangeIdDisabled() { + doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging( + eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(2))); + + assertTrue(isPrioritized(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), -10)))); + assertTrue(isPrioritized(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), 0)))); + + assertArrayEquals(new int[] {0, 1, 2}, + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), -10)), false, mPlatformCompat)); + assertArrayEquals(new int[] {0, 1, 0}, + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), 0)), false, mPlatformCompat)); + assertArrayEquals(new int[] {0, 0, 2, 2, 2}, + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 20), + createResolveInfo(PACKAGE2, getAppId(2), 20), + createResolveInfo(PACKAGE3, getAppId(3), 10), + createResolveInfo(PACKAGE3, getAppId(3), 0), + createResolveInfo(PACKAGE3, getAppId(4), 0)), false, mPlatformCompat)); + } + + @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) + @Test + public void testIsPrioritized_withDifferentPriorities_withMultipleUidChangeIdDisabled() { + doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging( + eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(1))); + doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging( + eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(2))); + + assertTrue(isPrioritized(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), -10)))); + assertTrue(isPrioritized(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), 0)))); + + assertArrayEquals(new int[] {0, 1, 2}, calculateBlockedUntilBeyondCount(List.of( createResolveInfo(PACKAGE1, getAppId(1), 10), createResolveInfo(PACKAGE2, getAppId(2), 0), - createResolveInfo(PACKAGE3, getAppId(3), -10)), false)); - assertArrayEquals(new int[] {0,0,2,3,3}, + createResolveInfo(PACKAGE3, getAppId(3), -10)), false, mPlatformCompat)); + assertArrayEquals(new int[] {0, 1, 1}, + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), 0)), false, mPlatformCompat)); + assertArrayEquals(new int[] {0, 0, 2, 2, 2}, calculateBlockedUntilBeyondCount(List.of( createResolveInfo(PACKAGE1, getAppId(1), 20), createResolveInfo(PACKAGE2, getAppId(2), 20), createResolveInfo(PACKAGE3, getAppId(3), 10), createResolveInfo(PACKAGE3, getAppId(3), 0), - createResolveInfo(PACKAGE3, getAppId(3), 0)), false)); + createResolveInfo(PACKAGE3, getAppId(4), 0)), false, mPlatformCompat)); + assertArrayEquals(new int[] {0, 0, 1, 1, 3}, + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 20), + createResolveInfo(PACKAGE2, getAppId(3), 20), + createResolveInfo(PACKAGE3, getAppId(3), 10), + createResolveInfo(PACKAGE3, getAppId(3), 0), + createResolveInfo(PACKAGE3, getAppId(2), 0)), false, mPlatformCompat)); } @Test @@ -602,6 +805,66 @@ public class BroadcastRecordTest { assertTrue(record3.matchesDeliveryGroup(record1)); } + @Test + public void testCalculateChangeStateForReceivers() { + assertArrayEquals(new boolean[] {true, true, true}, calculateChangeState( + List.of(createResolveInfo(PACKAGE1, getAppId(1)), + createResolveInfo(PACKAGE2, getAppId(2)), + createResolveInfo(PACKAGE3, getAppId(3))))); + assertArrayEquals(new boolean[] {true, true, true, true}, calculateChangeState( + List.of(createResolveInfo(PACKAGE1, getAppId(1)), + createResolveInfo(PACKAGE2, getAppId(2)), + createResolveInfo(PACKAGE2, getAppId(2)), + createResolveInfo(PACKAGE3, getAppId(3))))); + + doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging( + eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(1))); + assertArrayEquals(new boolean[] {false, true, true}, calculateChangeState( + List.of(createResolveInfo(PACKAGE1, getAppId(1)), + createResolveInfo(PACKAGE2, getAppId(2)), + createResolveInfo(PACKAGE3, getAppId(3))))); + assertArrayEquals(new boolean[] {false, true, false, true}, calculateChangeState( + List.of(createResolveInfo(PACKAGE1, getAppId(1)), + createResolveInfo(PACKAGE2, getAppId(2)), + createResolveInfo(PACKAGE2, getAppId(1)), + createResolveInfo(PACKAGE3, getAppId(3))))); + + doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging( + eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(2))); + assertArrayEquals(new boolean[] {false, false, true}, calculateChangeState( + List.of(createResolveInfo(PACKAGE1, getAppId(1)), + createResolveInfo(PACKAGE2, getAppId(2)), + createResolveInfo(PACKAGE3, getAppId(3))))); + assertArrayEquals(new boolean[] {false, true, false, false, false, true}, + calculateChangeState( + List.of(createResolveInfo(PACKAGE1, getAppId(1)), + createResolveInfo(PACKAGE3, getAppId(3)), + createResolveInfo(PACKAGE2, getAppId(2)), + createResolveInfo(PACKAGE2, getAppId(1)), + createResolveInfo(PACKAGE2, getAppId(2)), + createResolveInfo(PACKAGE3, getAppId(3))))); + + doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging( + eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(3))); + assertArrayEquals(new boolean[] {false, false, false}, calculateChangeState( + List.of(createResolveInfo(PACKAGE1, getAppId(1)), + createResolveInfo(PACKAGE2, getAppId(2)), + createResolveInfo(PACKAGE3, getAppId(3))))); + assertArrayEquals(new boolean[] {false, false, false, false, false, false}, + calculateChangeState( + List.of(createResolveInfo(PACKAGE1, getAppId(1)), + createResolveInfo(PACKAGE3, getAppId(3)), + createResolveInfo(PACKAGE2, getAppId(2)), + createResolveInfo(PACKAGE2, getAppId(1)), + createResolveInfo(PACKAGE2, getAppId(2)), + createResolveInfo(PACKAGE3, getAppId(3))))); + } + + private boolean[] calculateChangeState(List<Object> receivers) { + return BroadcastRecord.calculateChangeStateForReceivers(receivers, + CHANGE_LIMIT_PRIORITY_SCOPE, mPlatformCompat); + } + private static void cleanupDisabledPackageReceivers(BroadcastRecord record, String packageName, int userId) { record.cleanupDisabledPackageReceiversLocked(packageName, null /* filterByClasses */, @@ -753,16 +1016,17 @@ public class BroadcastRecordTest { BackgroundStartPrivileges.NONE, false /* timeoutExempt */, filterExtrasForReceiver, - PROCESS_STATE_UNKNOWN); + PROCESS_STATE_UNKNOWN, + mPlatformCompat); } private static int getAppId(int i) { return Process.FIRST_APPLICATION_UID + i; } - private static boolean isPrioritized(List<Object> receivers) { + private boolean isPrioritized(List<Object> receivers) { return BroadcastRecord.isPrioritized( - calculateBlockedUntilBeyondCount(receivers, false), false); + calculateBlockedUntilBeyondCount(receivers, false, mPlatformCompat), false); } private static void assertBlocked(BroadcastRecord r, boolean... blocked) { |