Loading services/core/java/com/android/server/am/BroadcastQueueModernImpl.java +44 −9 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.LOG_WRITER_INFO; import static com.android.server.am.BroadcastProcessQueue.insertIntoRunnableList; import static com.android.server.am.BroadcastProcessQueue.reasonToString; import static com.android.server.am.BroadcastProcessQueue.removeFromRunnableList; import static com.android.server.am.BroadcastRecord.DELIVERY_DEFERRED; import static com.android.server.am.BroadcastRecord.deliveryStateToString; import static com.android.server.am.BroadcastRecord.getReceiverClassName; import static com.android.server.am.BroadcastRecord.getReceiverPackageName; Loading Loading @@ -68,6 +69,7 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.IndentingPrintWriter; import android.util.MathUtils; Loading Loading @@ -212,6 +214,13 @@ class BroadcastQueueModernImpl extends BroadcastQueue { private final AtomicReference<ArraySet<BroadcastRecord>> mReplacedBroadcastsCache = new AtomicReference<>(); /** * Container for holding the set of broadcast records that satisfied a certain criteria. */ @GuardedBy("mService") private final AtomicReference<ArrayMap<BroadcastRecord, Boolean>> mRecordsLookupCache = new AtomicReference<>(); /** * Map from UID to its last known "foreground" state. A UID is considered to be in * "foreground" state when it's procState is {@link ActivityManager#PROCESS_STATE_TOP}. Loading Loading @@ -742,13 +751,16 @@ class BroadcastQueueModernImpl extends BroadcastQueue { broadcastConsumer = mBroadcastConsumerSkipAndCanceled; break; case BroadcastOptions.DELIVERY_GROUP_POLICY_MERGED: // TODO: Allow applying MERGED policy for broadcasts with more than one receiver. if (r.receivers.size() > 1) { return; } final BundleMerger extrasMerger = r.options.getDeliveryGroupExtrasMerger(); if (extrasMerger == null) { // Extras merger is required to be able to merge the extras. So, if it's not // supplied, then ignore the delivery group policy. return; } // TODO: Don't merge with the same BroadcastRecord more than once. broadcastConsumer = (record, recordIndex) -> { r.intent.mergeExtras(record.intent, extrasMerger); mBroadcastConsumerSkipAndCanceled.accept(record, recordIndex); Loading @@ -758,6 +770,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue { logw("Unknown delivery group policy: " + policy); return; } final ArrayMap<BroadcastRecord, Boolean> recordsLookupCache = getRecordsLookupCache(); forEachMatchingBroadcast(QUEUE_PREDICATE_ANY, (testRecord, testIndex) -> { // If the receiver is already in a terminal state, then ignore it. if (isDeliveryStateTerminal(testRecord.getDeliveryState(testIndex))) { Loading @@ -769,22 +782,44 @@ class BroadcastQueueModernImpl extends BroadcastQueue { || !r.matchesDeliveryGroup(testRecord)) { return false; } // TODO: If a process is in a deferred state, we can always apply the policy as long // as it is one of the receivers for the new broadcast. // For ordered broadcast, check if the receivers for the new broadcast is a superset // of those for the previous one as skipping and removing only one of them could result // in an inconsistent state. if (testRecord.ordered || testRecord.resultTo != null) { // TODO: Cache this result in some way so that we don't have to perform the // same check for all the broadcast receivers. return r.containsAllReceivers(testRecord.receivers); } else if (testRecord.prioritized) { return r.containsAllReceivers(testRecord.receivers); if (testRecord.ordered || testRecord.prioritized) { return containsAllReceivers(r, testRecord, recordsLookupCache); } else if (testRecord.resultTo != null) { return testRecord.getDeliveryState(testIndex) == DELIVERY_DEFERRED ? r.containsReceiver(testRecord.receivers.get(testIndex)) : containsAllReceivers(r, testRecord, recordsLookupCache); } else { return r.containsReceiver(testRecord.receivers.get(testIndex)); } }, broadcastConsumer, true); recordsLookupCache.clear(); mRecordsLookupCache.compareAndSet(null, recordsLookupCache); } @NonNull private ArrayMap<BroadcastRecord, Boolean> getRecordsLookupCache() { ArrayMap<BroadcastRecord, Boolean> recordsLookupCache = mRecordsLookupCache.getAndSet(null); if (recordsLookupCache == null) { recordsLookupCache = new ArrayMap<>(); } return recordsLookupCache; } private boolean containsAllReceivers(@NonNull BroadcastRecord record, @NonNull BroadcastRecord testRecord, @NonNull ArrayMap<BroadcastRecord, Boolean> recordsLookupCache) { final int idx = recordsLookupCache.indexOfKey(testRecord); if (idx > 0) { return recordsLookupCache.valueAt(idx); } final boolean containsAll = record.containsAllReceivers(testRecord.receivers); recordsLookupCache.put(testRecord, containsAll); return containsAll; } /** Loading services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java +41 −0 Original line number Diff line number Diff line Loading @@ -1094,6 +1094,17 @@ public final class BroadcastQueueModernImplTest { 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), resultTo, false); screenOffRecord.setDeliveryState(2, BroadcastRecord.DELIVERY_DEFERRED, "testDeliveryGroupPolicy_resultTo_diffReceivers"); mImpl.enqueueBroadcastLocked(screenOffRecord); mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOn, screenOnOffOptions, List.of(greenReceiver, blueReceiver), resultTo, false)); verifyPendingRecords(greenQueue, List.of(screenOff, screenOn)); verifyPendingRecords(redQueue, List.of(screenOff)); verifyPendingRecords(blueQueue, List.of(screenOn)); } @Test Loading Loading @@ -1278,6 +1289,36 @@ public final class BroadcastQueueModernImplTest { dropboxEntryBroadcast2.first, expectedMergedBroadcast.first)); } @Test public void testDeliveryGroupPolicy_merged_multipleReceivers() { final long now = SystemClock.elapsedRealtime(); final Pair<Intent, BroadcastOptions> dropboxEntryBroadcast1 = createDropboxBroadcast( "TAG_A", now, 2); final Pair<Intent, BroadcastOptions> dropboxEntryBroadcast2 = createDropboxBroadcast( "TAG_A", now + 1000, 4); mImpl.enqueueBroadcastLocked(makeBroadcastRecord(dropboxEntryBroadcast1.first, dropboxEntryBroadcast1.second, List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN), makeManifestReceiver(PACKAGE_RED, CLASS_RED)), false)); mImpl.enqueueBroadcastLocked(makeBroadcastRecord(dropboxEntryBroadcast2.first, dropboxEntryBroadcast2.second, List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN), makeManifestReceiver(PACKAGE_RED, CLASS_RED)), false)); final BroadcastProcessQueue greenQueue = mImpl.getProcessQueue(PACKAGE_GREEN, getUidForPackage(PACKAGE_GREEN)); final BroadcastProcessQueue redQueue = mImpl.getProcessQueue(PACKAGE_RED, getUidForPackage(PACKAGE_RED)); verifyPendingRecords(greenQueue, List.of(dropboxEntryBroadcast1.first, dropboxEntryBroadcast2.first)); verifyPendingRecords(redQueue, List.of(dropboxEntryBroadcast1.first, dropboxEntryBroadcast2.first)); } @Test public void testDeliveryGroupPolicy_sameAction_differentMatchingCriteria() { final Intent closeSystemDialogs1 = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); Loading Loading
services/core/java/com/android/server/am/BroadcastQueueModernImpl.java +44 −9 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.LOG_WRITER_INFO; import static com.android.server.am.BroadcastProcessQueue.insertIntoRunnableList; import static com.android.server.am.BroadcastProcessQueue.reasonToString; import static com.android.server.am.BroadcastProcessQueue.removeFromRunnableList; import static com.android.server.am.BroadcastRecord.DELIVERY_DEFERRED; import static com.android.server.am.BroadcastRecord.deliveryStateToString; import static com.android.server.am.BroadcastRecord.getReceiverClassName; import static com.android.server.am.BroadcastRecord.getReceiverPackageName; Loading Loading @@ -68,6 +69,7 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.IndentingPrintWriter; import android.util.MathUtils; Loading Loading @@ -212,6 +214,13 @@ class BroadcastQueueModernImpl extends BroadcastQueue { private final AtomicReference<ArraySet<BroadcastRecord>> mReplacedBroadcastsCache = new AtomicReference<>(); /** * Container for holding the set of broadcast records that satisfied a certain criteria. */ @GuardedBy("mService") private final AtomicReference<ArrayMap<BroadcastRecord, Boolean>> mRecordsLookupCache = new AtomicReference<>(); /** * Map from UID to its last known "foreground" state. A UID is considered to be in * "foreground" state when it's procState is {@link ActivityManager#PROCESS_STATE_TOP}. Loading Loading @@ -742,13 +751,16 @@ class BroadcastQueueModernImpl extends BroadcastQueue { broadcastConsumer = mBroadcastConsumerSkipAndCanceled; break; case BroadcastOptions.DELIVERY_GROUP_POLICY_MERGED: // TODO: Allow applying MERGED policy for broadcasts with more than one receiver. if (r.receivers.size() > 1) { return; } final BundleMerger extrasMerger = r.options.getDeliveryGroupExtrasMerger(); if (extrasMerger == null) { // Extras merger is required to be able to merge the extras. So, if it's not // supplied, then ignore the delivery group policy. return; } // TODO: Don't merge with the same BroadcastRecord more than once. broadcastConsumer = (record, recordIndex) -> { r.intent.mergeExtras(record.intent, extrasMerger); mBroadcastConsumerSkipAndCanceled.accept(record, recordIndex); Loading @@ -758,6 +770,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue { logw("Unknown delivery group policy: " + policy); return; } final ArrayMap<BroadcastRecord, Boolean> recordsLookupCache = getRecordsLookupCache(); forEachMatchingBroadcast(QUEUE_PREDICATE_ANY, (testRecord, testIndex) -> { // If the receiver is already in a terminal state, then ignore it. if (isDeliveryStateTerminal(testRecord.getDeliveryState(testIndex))) { Loading @@ -769,22 +782,44 @@ class BroadcastQueueModernImpl extends BroadcastQueue { || !r.matchesDeliveryGroup(testRecord)) { return false; } // TODO: If a process is in a deferred state, we can always apply the policy as long // as it is one of the receivers for the new broadcast. // For ordered broadcast, check if the receivers for the new broadcast is a superset // of those for the previous one as skipping and removing only one of them could result // in an inconsistent state. if (testRecord.ordered || testRecord.resultTo != null) { // TODO: Cache this result in some way so that we don't have to perform the // same check for all the broadcast receivers. return r.containsAllReceivers(testRecord.receivers); } else if (testRecord.prioritized) { return r.containsAllReceivers(testRecord.receivers); if (testRecord.ordered || testRecord.prioritized) { return containsAllReceivers(r, testRecord, recordsLookupCache); } else if (testRecord.resultTo != null) { return testRecord.getDeliveryState(testIndex) == DELIVERY_DEFERRED ? r.containsReceiver(testRecord.receivers.get(testIndex)) : containsAllReceivers(r, testRecord, recordsLookupCache); } else { return r.containsReceiver(testRecord.receivers.get(testIndex)); } }, broadcastConsumer, true); recordsLookupCache.clear(); mRecordsLookupCache.compareAndSet(null, recordsLookupCache); } @NonNull private ArrayMap<BroadcastRecord, Boolean> getRecordsLookupCache() { ArrayMap<BroadcastRecord, Boolean> recordsLookupCache = mRecordsLookupCache.getAndSet(null); if (recordsLookupCache == null) { recordsLookupCache = new ArrayMap<>(); } return recordsLookupCache; } private boolean containsAllReceivers(@NonNull BroadcastRecord record, @NonNull BroadcastRecord testRecord, @NonNull ArrayMap<BroadcastRecord, Boolean> recordsLookupCache) { final int idx = recordsLookupCache.indexOfKey(testRecord); if (idx > 0) { return recordsLookupCache.valueAt(idx); } final boolean containsAll = record.containsAllReceivers(testRecord.receivers); recordsLookupCache.put(testRecord, containsAll); return containsAll; } /** Loading
services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java +41 −0 Original line number Diff line number Diff line Loading @@ -1094,6 +1094,17 @@ public final class BroadcastQueueModernImplTest { 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), resultTo, false); screenOffRecord.setDeliveryState(2, BroadcastRecord.DELIVERY_DEFERRED, "testDeliveryGroupPolicy_resultTo_diffReceivers"); mImpl.enqueueBroadcastLocked(screenOffRecord); mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOn, screenOnOffOptions, List.of(greenReceiver, blueReceiver), resultTo, false)); verifyPendingRecords(greenQueue, List.of(screenOff, screenOn)); verifyPendingRecords(redQueue, List.of(screenOff)); verifyPendingRecords(blueQueue, List.of(screenOn)); } @Test Loading Loading @@ -1278,6 +1289,36 @@ public final class BroadcastQueueModernImplTest { dropboxEntryBroadcast2.first, expectedMergedBroadcast.first)); } @Test public void testDeliveryGroupPolicy_merged_multipleReceivers() { final long now = SystemClock.elapsedRealtime(); final Pair<Intent, BroadcastOptions> dropboxEntryBroadcast1 = createDropboxBroadcast( "TAG_A", now, 2); final Pair<Intent, BroadcastOptions> dropboxEntryBroadcast2 = createDropboxBroadcast( "TAG_A", now + 1000, 4); mImpl.enqueueBroadcastLocked(makeBroadcastRecord(dropboxEntryBroadcast1.first, dropboxEntryBroadcast1.second, List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN), makeManifestReceiver(PACKAGE_RED, CLASS_RED)), false)); mImpl.enqueueBroadcastLocked(makeBroadcastRecord(dropboxEntryBroadcast2.first, dropboxEntryBroadcast2.second, List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN), makeManifestReceiver(PACKAGE_RED, CLASS_RED)), false)); final BroadcastProcessQueue greenQueue = mImpl.getProcessQueue(PACKAGE_GREEN, getUidForPackage(PACKAGE_GREEN)); final BroadcastProcessQueue redQueue = mImpl.getProcessQueue(PACKAGE_RED, getUidForPackage(PACKAGE_RED)); verifyPendingRecords(greenQueue, List.of(dropboxEntryBroadcast1.first, dropboxEntryBroadcast2.first)); verifyPendingRecords(redQueue, List.of(dropboxEntryBroadcast1.first, dropboxEntryBroadcast2.first)); } @Test public void testDeliveryGroupPolicy_sameAction_differentMatchingCriteria() { final Intent closeSystemDialogs1 = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); Loading