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..c00c707b2c668 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; @@ -71,12 +74,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,15 +97,23 @@ 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; 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} }); @@ -118,6 +131,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 +145,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,33 +191,65 @@ 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(); 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, "Delivering finishReceiverLocked() for " + Log.v(TAG, "Intercepting scheduleReceiver() 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()); + 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; } private ApplicationInfo makeApplicationInfo(String packageName) { final ApplicationInfo ai = new ApplicationInfo(); ai.packageName = packageName; + ai.processName = packageName; ai.uid = getUidForPackage(packageName); return ai; } @@ -200,11 +258,22 @@ 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; } + 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); @@ -236,23 +305,43 @@ 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 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"; + 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(); } } + /** + * 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 +352,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,20 +376,114 @@ 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); } - // TODO: verify registered receiver in warm app - // TODO: verify manifest receiver in cold app + /** + * 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); - // TODO: verify mixing multiple manifest and registered receivers of same broadcast - // TODO: verify delivery of 3 distinct broadcasts + // 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); + } + + /** + * 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); + } + + /** + * 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); + } }