Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d4a3ef02 authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

[1/?] Tests for generic BroadcastQueue.

Add test that verifies dispatching to multiple warm processes are
delivered and that the queue is flushed.  To keep the broadcast queue
flowing, the default mock ProcessRecord now immediately dispatches
finishReceiverLocked() back into the broadcast queue.

Bug: 243656033
Test: atest CtsContentTestCases:BroadcastReceiverTest
Test: atest FrameworksMockingServicesTests:BroadcastQueueTest
Change-Id: I6049b285f44cd891ce67faec76dfb1e1a92002cc
parent 5f87f996
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -659,7 +659,7 @@ public class ActivityManagerService extends IActivityManager.Stub
    final BroadcastQueue mFgOffloadBroadcastQueue;
    // Convenient for easy iteration over the queues. Foreground is first
    // so that dispatch of foreground broadcasts gets precedence.
    final BroadcastQueue[] mBroadcastQueues = new BroadcastQueue[4];
    final BroadcastQueue[] mBroadcastQueues;
    @GuardedBy("this")
    BroadcastStats mLastBroadcastStats;
@@ -2351,6 +2351,7 @@ public class ActivityManagerService extends IActivityManager.Stub
        mPendingStartActivityUids = new PendingStartActivityUids();
        mUseFifoUiScheduling = false;
        mEnableOffloadQueue = false;
        mBroadcastQueues = new BroadcastQueue[0];
        mFgBroadcastQueue = mBgBroadcastQueue = mBgOffloadBroadcastQueue =
                mFgOffloadBroadcastQueue = null;
        mComponentAliasResolver = new ComponentAliasResolver(this);
@@ -2409,6 +2410,7 @@ public class ActivityManagerService extends IActivityManager.Stub
        mEnableOffloadQueue = SystemProperties.getBoolean(
                "persist.device_config.activity_manager_native_boot.offload_queue_enabled", true);
        mBroadcastQueues = new BroadcastQueue[4];
        mFgBroadcastQueue = new BroadcastQueueImpl(this, mHandler,
                "foreground", foreConstants, false);
        mBgBroadcastQueue = new BroadcastQueueImpl(this, mHandler,
+10 −3
Original line number Diff line number Diff line
@@ -526,11 +526,18 @@ public class BroadcastQueueImpl extends BroadcastQueue {

    public BroadcastRecord getMatchingOrderedReceiver(IBinder receiver) {
        BroadcastRecord br = mDispatcher.getActiveBroadcastLocked();
        if (br != null && br.receiver == receiver) {
            return br;
        if (br == null) {
            Slog.w(TAG_BROADCAST, "getMatchingOrderedReceiver [" + mQueueName
                    + "] no active broadcast");
            return null;
        }
        if (br.receiver != receiver) {
            Slog.w(TAG_BROADCAST, "getMatchingOrderedReceiver [" + mQueueName
                    + "] active broadcast " + br.receiver + " doesn't match " + receiver);
            return null;
        }
        return br;
    }

    // > 0 only, no worry about "eventual" recycling
    private int nextSplitTokenLocked() {
+62 −22
Original line number Diff line number Diff line
@@ -16,17 +16,18 @@

package com.android.server.am;

import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.app.Activity;
import android.app.AppOpsManager;
@@ -39,11 +40,14 @@ import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.MessageQueue;
import android.os.IBinder;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;

import androidx.test.filters.MediumTest;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -67,8 +71,6 @@ import java.io.File;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * Common tests for {@link BroadcastQueue} implementations.
@@ -165,12 +167,25 @@ public class BroadcastQueueTest {
        }
    }

    private ProcessRecord makeActiveProcessRecord(String packageName) {
    private ProcessRecord makeActiveProcessRecord(String packageName) throws Exception {
        final ProcessRecord r = new ProcessRecord(mAms, makeApplicationInfo(packageName), null,
                getUidForPackage(packageName));
        final IApplicationThread thread = mock(IApplicationThread.class);
        final IBinder threadBinder = new Binder();
        doReturn(threadBinder).when(thread).asBinder();
        r.makeActive(thread, mAms.mProcessStats);
        when(mAms.getProcessRecordLocked(r.info.processName, r.info.uid)).thenReturn(r);

        doReturn(r).when(mAms).getProcessRecordLocked(eq(r.info.processName), eq(r.info.uid));

        doAnswer((invocation) -> {
            Log.v(TAG, "Delivering finishReceiverLocked() for "
                    + Arrays.toString(invocation.getArguments()));
            mQueue.finishReceiverLocked(threadBinder, Activity.RESULT_OK,
                    null, null, false, false);
            return null;
        }).when(thread).scheduleReceiver(any(), any(), any(), anyInt(), any(), any(), anyBoolean(),
                anyInt(), anyInt());

        return r;
    }

@@ -190,6 +205,11 @@ public class BroadcastQueueTest {
        return ri;
    }

    private BroadcastRecord makeBroadcastRecord(Intent intent, ProcessRecord callerApp,
            List receivers) {
        return makeBroadcastRecord(intent, callerApp, BroadcastOptions.makeBasic(), receivers);
    }

    private BroadcastRecord makeBroadcastRecord(Intent intent, ProcessRecord callerApp,
            BroadcastOptions options, List receivers) {
        return new BroadcastRecord(mQueue, intent, callerApp, callerApp.info.packageName, null,
@@ -208,18 +228,12 @@ public class BroadcastQueueTest {
        };
    }

    private void waitForHandlerIdle() throws Exception {
        final CountDownLatch latch = new CountDownLatch(1);
        final MessageQueue queue = mHandlerThread.getLooper().getQueue();
        final MessageQueue.IdleHandler idler = () -> {
            latch.countDown();
            return false;
        };
        queue.addIdleHandler(idler);
        if (queue.isIdle()) {
            latch.countDown();
    private void waitForIdle() throws Exception {
        for (int i = 0; i < 100; i++) {
            if (mQueue.isIdle()) break;
            SystemClock.sleep(100);
        }
        latch.await(5, TimeUnit.SECONDS);
        assertTrue(mQueue.isIdle());
    }

    private static final String PACKAGE_RED = "com.example.red";
@@ -245,17 +259,43 @@ public class BroadcastQueueTest {
        final ProcessRecord receiverApp = makeActiveProcessRecord(PACKAGE_GREEN);

        final Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
        final BroadcastRecord r = makeBroadcastRecord(intent, callerApp,
                BroadcastOptions.makeBasic(),
                List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN)));
        mQueue.enqueueBroadcastLocked(r);
        mQueue.enqueueBroadcastLocked(makeBroadcastRecord(intent, callerApp,
                List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN))));

        waitForHandlerIdle();
        waitForIdle();
        verify(receiverApp.getThread()).scheduleReceiver(
                argThat(filterEqualsIgnoringComponent(intent)), any(), any(), anyInt(), any(),
                any(), eq(false), eq(UserHandle.USER_SYSTEM), anyInt());
    }

    @Test
    public void testSimple_Manifest_Warm_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(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_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());
    }

    // TODO: verify registered receiver in warm app
    // TODO: verify manifest receiver in cold app