From 72692a1eb7631c50dd56669740068776a0ca902f Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Mon, 29 Aug 2022 14:59:35 -0600 Subject: [PATCH 1/3] [2/?] Tests for generic BroadcastQueue. Add test that verifies broadcast dispatch to cold-start apps. Fix a few bugs with "processName" handling. Bug: 243656033 Test: atest CtsContentTestCases:BroadcastReceiverTest Test: atest FrameworksMockingServicesTests:BroadcastQueueTest Change-Id: I827171a5bb5dfb3e1406d66e2372ecf4aea17ad8 --- .../android/server/am/BroadcastQueueTest.java | 106 +++++++++++++++--- 1 file changed, 88 insertions(+), 18 deletions(-) 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 130b02d3989ea..01759099bc987 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java @@ -71,12 +71,14 @@ import java.io.File; import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; /** * Common tests for {@link BroadcastQueue} implementations. */ @MediumTest @RunWith(Parameterized.class) +@SuppressWarnings("GuardedBy") public class BroadcastQueueTest { private static final String TAG = "BroadcastQueueTest"; @@ -92,10 +94,13 @@ public class BroadcastQueueTest { private Context mContext; private HandlerThread mHandlerThread; + private AtomicInteger mNextPid; @Mock private AppOpsService mAppOpsService; @Mock + private ProcessList mProcessList; + @Mock private PackageManagerInternal mPackageManagerInt; private ActivityManagerService mAms; @@ -118,6 +123,7 @@ public class BroadcastQueueTest { mHandlerThread = new HandlerThread(TAG); mHandlerThread.start(); + mNextPid = new AtomicInteger(100); LocalServices.removeServiceForTest(PackageManagerInternal.class); LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt); @@ -131,6 +137,18 @@ public class BroadcastQueueTest { realAms.mAtmInternal = spy(realAms.mActivityTaskManager.getAtmInternal()); realAms.mPackageManagerInt = mPackageManagerInt; mAms = spy(realAms); + doAnswer((invocation) -> { + Log.v(TAG, "Intercepting startProcessLocked() for " + + Arrays.toString(invocation.getArguments())); + final String processName = invocation.getArgument(0); + final ApplicationInfo ai = invocation.getArgument(1); + final ProcessRecord res = makeActiveProcessRecord(ai, processName); + mHandlerThread.getThreadHandler().post(() -> { + mQueue.onApplicationAttachedLocked(res); + }); + return res; + }).when(mAms).startProcessLocked(any(), any(), anyBoolean(), anyInt(), + any(), anyInt(), anyBoolean(), anyBoolean()); final BroadcastConstants constants = new BroadcastConstants( Settings.Global.BROADCAST_FG_CONSTANTS); @@ -165,11 +183,23 @@ public class BroadcastQueueTest { public Handler getUiHandler(ActivityManagerService service) { return mHandlerThread.getThreadHandler(); } + + @Override + public ProcessList getProcessList(ActivityManagerService service) { + return mProcessList; + } } private ProcessRecord makeActiveProcessRecord(String packageName) throws Exception { - final ProcessRecord r = new ProcessRecord(mAms, makeApplicationInfo(packageName), null, - getUidForPackage(packageName)); + final ApplicationInfo ai = makeApplicationInfo(packageName); + return makeActiveProcessRecord(ai, ai.processName); + } + + private ProcessRecord makeActiveProcessRecord(ApplicationInfo ai, String processName) + throws Exception { + final ProcessRecord r = new ProcessRecord(mAms, ai, processName, ai.uid); + r.setPid(mNextPid.getAndIncrement()); + final IApplicationThread thread = mock(IApplicationThread.class); final IBinder threadBinder = new Binder(); doReturn(threadBinder).when(thread).asBinder(); @@ -178,10 +208,12 @@ public class BroadcastQueueTest { doReturn(r).when(mAms).getProcessRecordLocked(eq(r.info.processName), eq(r.info.uid)); doAnswer((invocation) -> { - Log.v(TAG, "Delivering finishReceiverLocked() for " + Log.v(TAG, "Intercepting finishReceiverLocked() for " + Arrays.toString(invocation.getArguments())); - mQueue.finishReceiverLocked(threadBinder, Activity.RESULT_OK, - null, null, false, false); + mHandlerThread.getThreadHandler().post(() -> { + mQueue.finishReceiverLocked(threadBinder, Activity.RESULT_OK, + null, null, false, false); + }); return null; }).when(thread).scheduleReceiver(any(), any(), any(), anyInt(), any(), any(), anyBoolean(), anyInt(), anyInt()); @@ -192,6 +224,7 @@ public class BroadcastQueueTest { private ApplicationInfo makeApplicationInfo(String packageName) { final ApplicationInfo ai = new ApplicationInfo(); ai.packageName = packageName; + ai.processName = packageName; ai.uid = getUidForPackage(packageName); return ai; } @@ -200,6 +233,7 @@ public class BroadcastQueueTest { final ResolveInfo ri = new ResolveInfo(); ri.activityInfo = new ActivityInfo(); ri.activityInfo.packageName = packageName; + ri.activityInfo.processName = packageName; ri.activityInfo.name = name; ri.activityInfo.applicationInfo = makeApplicationInfo(packageName); return ri; @@ -236,6 +270,12 @@ public class BroadcastQueueTest { assertTrue(mQueue.isIdle()); } + private void verifyScheduleReceiver(ProcessRecord app, Intent intent) throws Exception { + verify(app.getThread()).scheduleReceiver( + argThat(filterEqualsIgnoringComponent(intent)), any(), any(), anyInt(), any(), + any(), eq(false), eq(UserHandle.USER_SYSTEM), anyInt()); + } + private static final String PACKAGE_RED = "com.example.red"; private static final String PACKAGE_GREEN = "com.example.green"; private static final String PACKAGE_BLUE = "com.example.blue"; @@ -253,6 +293,10 @@ public class BroadcastQueueTest { } } + /** + * Verify dispatch of simple broadcast to single manifest receiver in + * already-running warm app. + */ @Test public void testSimple_Manifest_Warm() throws Exception { final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED); @@ -263,11 +307,13 @@ public class BroadcastQueueTest { List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN)))); waitForIdle(); - verify(receiverApp.getThread()).scheduleReceiver( - argThat(filterEqualsIgnoringComponent(intent)), any(), any(), anyInt(), any(), - any(), eq(false), eq(UserHandle.USER_SYSTEM), anyInt()); + verifyScheduleReceiver(receiverApp, intent); } + /** + * Verify dispatch of multiple broadcasts to multiple manifest receivers in + * already-running warm apps. + */ @Test public void testSimple_Manifest_Warm_Multiple() throws Exception { final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED); @@ -285,19 +331,43 @@ public class BroadcastQueueTest { List.of(makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE)))); waitForIdle(); - verify(receiverGreenApp.getThread()).scheduleReceiver( - argThat(filterEqualsIgnoringComponent(timezone)), any(), any(), anyInt(), any(), - any(), eq(false), eq(UserHandle.USER_SYSTEM), anyInt()); - verify(receiverBlueApp.getThread()).scheduleReceiver( - argThat(filterEqualsIgnoringComponent(timezone)), any(), any(), anyInt(), any(), - any(), eq(false), eq(UserHandle.USER_SYSTEM), anyInt()); - verify(receiverBlueApp.getThread()).scheduleReceiver( - argThat(filterEqualsIgnoringComponent(airplane)), any(), any(), anyInt(), any(), - any(), eq(false), eq(UserHandle.USER_SYSTEM), anyInt()); + verifyScheduleReceiver(receiverGreenApp, timezone); + verifyScheduleReceiver(receiverBlueApp, timezone); + verifyScheduleReceiver(receiverBlueApp, airplane); + } + + /** + * Verify dispatch of multiple broadcast to multiple manifest receivers in + * apps that require cold starts. + */ + @Test + public void testSimple_Manifest_ColdThenWarm() throws Exception { + final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED); + + // We purposefully dispatch into green twice; the first time cold and + // the second time it should already be running + + final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED); + mQueue.enqueueBroadcastLocked(makeBroadcastRecord(timezone, callerApp, + List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN), + makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE)))); + + final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); + mQueue.enqueueBroadcastLocked(makeBroadcastRecord(airplane, callerApp, + List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN)))); + + waitForIdle(); + final ProcessRecord receiverGreenApp = mAms.getProcessRecordLocked(PACKAGE_GREEN, + getUidForPackage(PACKAGE_GREEN)); + final ProcessRecord receiverBlueApp = mAms.getProcessRecordLocked(PACKAGE_BLUE, + getUidForPackage(PACKAGE_BLUE)); + assertTrue(receiverBlueApp.getPid() > receiverGreenApp.getPid()); + verifyScheduleReceiver(receiverGreenApp, timezone); + verifyScheduleReceiver(receiverGreenApp, airplane); + verifyScheduleReceiver(receiverBlueApp, timezone); } // TODO: verify registered receiver in warm app - // TODO: verify manifest receiver in cold app // TODO: verify mixing multiple manifest and registered receivers of same broadcast // TODO: verify delivery of 3 distinct broadcasts From 857143cf6efa26bc30fd6001332ff35d43b982b3 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Tue, 30 Aug 2022 18:53:27 -0600 Subject: [PATCH 2/3] [3/?] Tests for generic BroadcastQueue. Add test that verifies broadcast dispatch to registered runtime receivers, both for single and multiple app use-cases. Bug: 243656033 Test: atest CtsContentTestCases:BroadcastReceiverTest Test: atest FrameworksMockingServicesTests:BroadcastQueueTest Change-Id: Icf51e3a293efa3f7b9a5999ee37f8d80527bfb91 --- .../android/server/am/BroadcastQueueTest.java | 89 ++++++++++++++++++- 1 file changed, 86 insertions(+), 3 deletions(-) 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 01759099bc987..b736dbf154b22 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java @@ -35,7 +35,9 @@ import android.app.BroadcastOptions; import android.app.IApplicationThread; import android.content.ComponentName; import android.content.Context; +import android.content.IIntentReceiver; import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManagerInternal; @@ -48,6 +50,7 @@ import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; import android.util.Log; +import android.util.SparseArray; import androidx.test.filters.MediumTest; import androidx.test.platform.app.InstrumentationRegistry; @@ -106,6 +109,11 @@ public class BroadcastQueueTest { private ActivityManagerService mAms; private BroadcastQueue mQueue; + /** + * Map from PID to registered registered runtime receivers. + */ + private SparseArray mRegisteredReceivers = new SparseArray<>(); + @Parameters(name = "impl={0}") public static Collection data() { return Arrays.asList(new Object[][] { {Impl.DEFAULT} }); @@ -204,11 +212,17 @@ public class BroadcastQueueTest { final IBinder threadBinder = new Binder(); doReturn(threadBinder).when(thread).asBinder(); r.makeActive(thread, mAms.mProcessStats); - doReturn(r).when(mAms).getProcessRecordLocked(eq(r.info.processName), eq(r.info.uid)); + final IIntentReceiver receiver = mock(IIntentReceiver.class); + final IBinder receiverBinder = new Binder(); + doReturn(receiverBinder).when(receiver).asBinder(); + final ReceiverList receiverList = new ReceiverList(mAms, r, r.getPid(), r.info.uid, + UserHandle.getUserId(r.info.uid), receiver); + mRegisteredReceivers.put(r.getPid(), receiverList); + doAnswer((invocation) -> { - Log.v(TAG, "Intercepting finishReceiverLocked() for " + Log.v(TAG, "Intercepting scheduleReceiver() for " + Arrays.toString(invocation.getArguments())); mHandlerThread.getThreadHandler().post(() -> { mQueue.finishReceiverLocked(threadBinder, Activity.RESULT_OK, @@ -218,6 +232,17 @@ public class BroadcastQueueTest { }).when(thread).scheduleReceiver(any(), any(), any(), anyInt(), any(), any(), anyBoolean(), anyInt(), anyInt()); + doAnswer((invocation) -> { + Log.v(TAG, "Intercepting scheduleRegisteredReceiver() for " + + Arrays.toString(invocation.getArguments())); + mHandlerThread.getThreadHandler().post(() -> { + mQueue.finishReceiverLocked(receiverBinder, Activity.RESULT_OK, null, null, + false, false); + }); + return null; + }).when(thread).scheduleRegisteredReceiver(any(), any(), anyInt(), any(), any(), + anyBoolean(), anyBoolean(), anyInt(), anyInt()); + return r; } @@ -239,6 +264,16 @@ public class BroadcastQueueTest { return ri; } + private BroadcastFilter makeRegisteredReceiver(ProcessRecord app) { + final ReceiverList receiverList = mRegisteredReceivers.get(app.getPid()); + final IntentFilter filter = new IntentFilter(); + final BroadcastFilter res = new BroadcastFilter(filter, receiverList, + receiverList.app.info.packageName, null, null, null, receiverList.uid, + receiverList.userId, false, false, true); + receiverList.add(res); + return res; + } + private BroadcastRecord makeBroadcastRecord(Intent intent, ProcessRecord callerApp, List receivers) { return makeBroadcastRecord(intent, callerApp, BroadcastOptions.makeBasic(), receivers); @@ -276,6 +311,13 @@ public class BroadcastQueueTest { any(), eq(false), eq(UserHandle.USER_SYSTEM), anyInt()); } + private void verifyScheduleRegisteredReceiver(ProcessRecord app, Intent intent) + throws Exception { + verify(app.getThread()).scheduleRegisteredReceiver(any(), + argThat(filterEqualsIgnoringComponent(intent)), anyInt(), any(), any(), + anyBoolean(), anyBoolean(), eq(UserHandle.USER_SYSTEM), anyInt()); + } + private static final String PACKAGE_RED = "com.example.red"; private static final String PACKAGE_GREEN = "com.example.green"; private static final String PACKAGE_BLUE = "com.example.blue"; @@ -367,7 +409,48 @@ public class BroadcastQueueTest { verifyScheduleReceiver(receiverBlueApp, timezone); } - // TODO: verify registered receiver in warm app + /** + * Verify dispatch of simple broadcast to single registered receiver in + * already-running warm app. + */ + @Test + public void testSimple_Registered() throws Exception { + final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED); + final ProcessRecord receiverApp = makeActiveProcessRecord(PACKAGE_GREEN); + + final Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED); + mQueue.enqueueBroadcastLocked(makeBroadcastRecord(intent, callerApp, + List.of(makeRegisteredReceiver(receiverApp)))); + + waitForIdle(); + verifyScheduleRegisteredReceiver(receiverApp, intent); + } + + /** + * Verify dispatch of multiple broadcasts to multiple registered receivers + * in already-running warm apps. + */ + @Test + public void testSimple_Registered_Multiple() throws Exception { + final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED); + + final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN); + final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE); + + final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED); + mQueue.enqueueBroadcastLocked(makeBroadcastRecord(timezone, callerApp, + List.of(makeRegisteredReceiver(receiverGreenApp), + makeRegisteredReceiver(receiverBlueApp)))); + + final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); + mQueue.enqueueBroadcastLocked(makeBroadcastRecord(airplane, callerApp, + List.of(makeRegisteredReceiver(receiverBlueApp)))); + + waitForIdle(); + verifyScheduleRegisteredReceiver(receiverGreenApp, timezone); + verifyScheduleRegisteredReceiver(receiverBlueApp, timezone); + verifyScheduleRegisteredReceiver(receiverBlueApp, airplane); + } // TODO: verify mixing multiple manifest and registered receivers of same broadcast // TODO: verify delivery of 3 distinct broadcasts From 7cf2f0491775d80ebea03da0b9898d6dde288e08 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Tue, 30 Aug 2022 19:02:32 -0600 Subject: [PATCH 3/3] [4/?] Tests for generic BroadcastQueue. Add test that verifies broadcast dispatch to a mix of both manifest and registered runtime receivers, including one manifest receiver in the middle of dispatch which requires a cold start. Bug: 243656033 Test: atest CtsContentTestCases:BroadcastReceiverTest Test: atest FrameworksMockingServicesTests:BroadcastQueueTest Change-Id: I22bcb2840c7aee305ea714bb50bf105974cae261 --- .../android/server/am/BroadcastQueueTest.java | 36 +++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) 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 b736dbf154b22..c00c707b2c668 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java @@ -321,16 +321,19 @@ public class BroadcastQueueTest { private static final String PACKAGE_RED = "com.example.red"; private static final String PACKAGE_GREEN = "com.example.green"; private static final String PACKAGE_BLUE = "com.example.blue"; + private static final String PACKAGE_YELLOW = "com.example.yellow"; private static final String CLASS_RED = "com.example.red.Red"; private static final String CLASS_GREEN = "com.example.green.Green"; private static final String CLASS_BLUE = "com.example.blue.Blue"; + private static final String CLASS_YELLOW = "com.example.yellow.Yellow"; private static int getUidForPackage(String packageName) { switch (packageName) { case PACKAGE_RED: return android.os.Process.FIRST_APPLICATION_UID + 1; case PACKAGE_GREEN: return android.os.Process.FIRST_APPLICATION_UID + 2; case PACKAGE_BLUE: return android.os.Process.FIRST_APPLICATION_UID + 3; + case PACKAGE_YELLOW: return android.os.Process.FIRST_APPLICATION_UID + 4; default: throw new IllegalArgumentException(); } } @@ -452,6 +455,35 @@ public class BroadcastQueueTest { verifyScheduleRegisteredReceiver(receiverBlueApp, airplane); } - // TODO: verify mixing multiple manifest and registered receivers of same broadcast - // TODO: verify delivery of 3 distinct broadcasts + /** + * Verify dispatch of multiple broadcasts mixed to both manifest and + * registered receivers, to both warm and cold apps. + */ + @Test + public void testComplex() throws Exception { + final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED); + + final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN); + final ProcessRecord receiverYellowApp = makeActiveProcessRecord(PACKAGE_YELLOW); + + final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED); + mQueue.enqueueBroadcastLocked(makeBroadcastRecord(timezone, callerApp, + List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN), + makeRegisteredReceiver(receiverGreenApp), + makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE), + makeRegisteredReceiver(receiverYellowApp)))); + + final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); + mQueue.enqueueBroadcastLocked(makeBroadcastRecord(airplane, callerApp, + List.of(makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW)))); + + waitForIdle(); + final ProcessRecord receiverBlueApp = mAms.getProcessRecordLocked(PACKAGE_BLUE, + getUidForPackage(PACKAGE_BLUE)); + verifyScheduleReceiver(receiverGreenApp, timezone); + verifyScheduleRegisteredReceiver(receiverGreenApp, timezone); + verifyScheduleReceiver(receiverBlueApp, timezone); + verifyScheduleRegisteredReceiver(receiverYellowApp, timezone); + verifyScheduleReceiver(receiverYellowApp, airplane); + } }