From 101dfec453f8d551119120c3fc668adf5e2e1c8f Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Thu, 3 Nov 2022 11:55:14 -0600 Subject: [PATCH] BroadcastQueue: tests for broadcasts that thaw. Several broadcast flavors should cause us to consider thawing an otherwise cached process, causing it to become immediately runnable. This doesn't prevent other more nuanced policy from being added in the future, such as letting a sender configure a yet-to-be-defined BroadcastOption to delay thawing a manifest-targeted broadcast. Bug: 253906105 Test: atest FrameworksMockingServicesTests:BroadcastRecordTest Test: atest FrameworksMockingServicesTests:BroadcastQueueTest Test: atest FrameworksMockingServicesTests:BroadcastQueueModernImplTest Change-Id: I16cc7eb7fc28ba0f68b6fce92607827b71ec569a --- .../server/am/BroadcastProcessQueue.java | 6 + .../am/BroadcastQueueModernImplTest.java | 113 +++++++++++++++++- 2 files changed, 116 insertions(+), 3 deletions(-) diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java index c994f13d4f736..47ca427be9ff4 100644 --- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java +++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java @@ -643,6 +643,7 @@ class BroadcastProcessQueue { static final int REASON_CONTAINS_INTERACTIVE = 14; static final int REASON_CONTAINS_RESULT_TO = 15; static final int REASON_CONTAINS_INSTRUMENTED = 16; + static final int REASON_CONTAINS_MANIFEST = 17; @IntDef(flag = false, prefix = { "REASON_" }, value = { REASON_EMPTY, @@ -658,6 +659,7 @@ class BroadcastProcessQueue { REASON_CONTAINS_INTERACTIVE, REASON_CONTAINS_RESULT_TO, REASON_CONTAINS_INSTRUMENTED, + REASON_CONTAINS_MANIFEST, }) @Retention(RetentionPolicy.SOURCE) public @interface Reason {} @@ -677,6 +679,7 @@ class BroadcastProcessQueue { case REASON_CONTAINS_INTERACTIVE: return "CONTAINS_INTERACTIVE"; case REASON_CONTAINS_RESULT_TO: return "CONTAINS_RESULT_TO"; case REASON_CONTAINS_INSTRUMENTED: return "CONTAINS_INSTRUMENTED"; + case REASON_CONTAINS_MANIFEST: return "CONTAINS_MANIFEST"; default: return Integer.toString(reason); } } @@ -725,6 +728,9 @@ class BroadcastProcessQueue { } else if (mCountResultTo > 0) { mRunnableAt = runnableAt; mRunnableAtReason = REASON_CONTAINS_RESULT_TO; + } else if (mCountManifest > 0) { + mRunnableAt = runnableAt; + mRunnableAtReason = REASON_CONTAINS_MANIFEST; } else if (mProcessCached) { mRunnableAt = runnableAt + constants.DELAY_CACHED_MILLIS; mRunnableAtReason = REASON_CACHED; 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 4d92b7f480a4e..0f7c0d73ad087 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java @@ -16,6 +16,13 @@ package com.android.server.am; +import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_ALARM; +import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_FOREGROUND; +import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_INTERACTIVE; +import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_MANIFEST; +import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_ORDERED; +import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_PRIORITIZED; +import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_RESULT_TO; import static com.android.server.am.BroadcastProcessQueue.insertIntoRunnableList; import static com.android.server.am.BroadcastProcessQueue.removeFromRunnableList; import static com.android.server.am.BroadcastQueueTest.CLASS_GREEN; @@ -36,13 +43,16 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import android.annotation.NonNull; import android.app.Activity; import android.app.AppOpsManager; import android.app.BroadcastOptions; +import android.content.IIntentReceiver; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.ResolveInfo; import android.media.AudioManager; import android.os.Bundle; import android.os.BundleMerger; @@ -140,6 +150,18 @@ public class BroadcastQueueModernImplTest { } } + private static Intent makeMockIntent() { + return mock(Intent.class); + } + + private static ResolveInfo makeMockManifestReceiver() { + return mock(ResolveInfo.class); + } + + private static BroadcastFilter makeMockRegisteredReceiver() { + return mock(BroadcastFilter.class); + } + private BroadcastRecord makeBroadcastRecord(Intent intent) { return makeBroadcastRecord(intent, BroadcastOptions.makeBasic(), List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN)), false); @@ -150,6 +172,10 @@ public class BroadcastQueueModernImplTest { List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN)), true); } + private BroadcastRecord makeBroadcastRecord(Intent intent, List receivers) { + return makeBroadcastRecord(intent, BroadcastOptions.makeBasic(), receivers, false); + } + private BroadcastRecord makeBroadcastRecord(Intent intent, BroadcastOptions options) { return makeBroadcastRecord(intent, options, List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN)), false); @@ -157,8 +183,13 @@ public class BroadcastQueueModernImplTest { private BroadcastRecord makeBroadcastRecord(Intent intent, BroadcastOptions options, List receivers, boolean ordered) { + return makeBroadcastRecord(intent, options, receivers, null, ordered); + } + + private BroadcastRecord makeBroadcastRecord(Intent intent, BroadcastOptions options, + List receivers, IIntentReceiver resultTo, boolean ordered) { return new BroadcastRecord(mImpl, intent, mProcess, PACKAGE_RED, null, 21, 42, false, null, - null, null, null, AppOpsManager.OP_NONE, options, receivers, null, null, + null, null, null, AppOpsManager.OP_NONE, options, receivers, null, resultTo, Activity.RESULT_OK, null, null, ordered, false, false, UserHandle.USER_SYSTEM, false, null, false, null); } @@ -287,7 +318,8 @@ public class BroadcastQueueModernImplTest { PACKAGE_GREEN, getUidForPackage(PACKAGE_GREEN)); final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); - final BroadcastRecord airplaneRecord = makeBroadcastRecord(airplane); + final BroadcastRecord airplaneRecord = makeBroadcastRecord(airplane, + List.of(makeMockRegisteredReceiver())); queue.enqueueOrReplaceBroadcast(airplaneRecord, 0); queue.setProcessCached(false); @@ -367,7 +399,8 @@ public class BroadcastQueueModernImplTest { PACKAGE_GREEN, getUidForPackage(PACKAGE_GREEN)); final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); - final BroadcastRecord airplaneRecord = makeBroadcastRecord(airplane); + final BroadcastRecord airplaneRecord = makeBroadcastRecord(airplane, + List.of(makeMockRegisteredReceiver())); queue.enqueueOrReplaceBroadcast(airplaneRecord, 0); mConstants.MAX_PENDING_BROADCASTS = 128; @@ -381,6 +414,80 @@ public class BroadcastQueueModernImplTest { assertEquals(BroadcastProcessQueue.REASON_MAX_PENDING, queue.getRunnableAtReason()); } + /** + * Verify that a cached process that would normally be delayed becomes + * immediately runnable when the given broadcast is enqueued. + */ + private void doRunnableAt_Cached(BroadcastRecord testRecord, int testRunnableAtReason) { + final BroadcastProcessQueue queue = new BroadcastProcessQueue(mConstants, + PACKAGE_GREEN, getUidForPackage(PACKAGE_GREEN)); + queue.setProcessCached(true); + + final BroadcastRecord lazyRecord = makeBroadcastRecord( + new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED), + List.of(makeMockRegisteredReceiver())); + + queue.enqueueOrReplaceBroadcast(lazyRecord, 0); + assertThat(queue.getRunnableAt()).isGreaterThan(lazyRecord.enqueueTime); + assertThat(queue.getRunnableAtReason()).isNotEqualTo(testRunnableAtReason); + + queue.enqueueOrReplaceBroadcast(testRecord, 0); + assertThat(queue.getRunnableAt()).isAtMost(testRecord.enqueueTime); + assertThat(queue.getRunnableAtReason()).isEqualTo(testRunnableAtReason); + } + + @Test + public void testRunnableAt_Cached_Manifest() { + doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), null, + List.of(makeMockManifestReceiver()), null, false), REASON_CONTAINS_MANIFEST); + } + + @Test + public void testRunnableAt_Cached_Ordered() { + doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), null, + List.of(makeMockRegisteredReceiver()), null, true), REASON_CONTAINS_ORDERED); + } + + @Test + public void testRunnableAt_Cached_ResultTo() { + final IIntentReceiver resultTo = mock(IIntentReceiver.class); + doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), null, + List.of(makeMockRegisteredReceiver()), resultTo, false), REASON_CONTAINS_RESULT_TO); + } + + @Test + public void testRunnableAt_Cached_Foreground() { + final Intent foregroundIntent = new Intent(); + foregroundIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + doRunnableAt_Cached(makeBroadcastRecord(foregroundIntent, null, + List.of(makeMockRegisteredReceiver()), null, false), REASON_CONTAINS_FOREGROUND); + } + + @Test + public void testRunnableAt_Cached_Interactive() { + final BroadcastOptions options = BroadcastOptions.makeBasic(); + options.setInteractiveBroadcast(true); + doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), options, + List.of(makeMockRegisteredReceiver()), null, false), REASON_CONTAINS_INTERACTIVE); + } + + @Test + public void testRunnableAt_Cached_Alarm() { + final BroadcastOptions options = BroadcastOptions.makeBasic(); + options.setAlarmBroadcast(true); + doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), options, + List.of(makeMockRegisteredReceiver()), null, false), REASON_CONTAINS_ALARM); + } + + @Test + public void testRunnableAt_Cached_Prioritized() { + final List receivers = List.of( + withPriority(makeManifestReceiver(PACKAGE_RED, PACKAGE_RED), 10), + withPriority(makeManifestReceiver(PACKAGE_GREEN, PACKAGE_GREEN), -10)); + doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), null, + receivers, null, false), REASON_CONTAINS_PRIORITIZED); + } + /** * Confirm that we always prefer running pending items marked as "urgent", * then "normal", then "offload", dispatching by the relative ordering