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

Commit 8aac249f authored by Sudheer Shanka's avatar Sudheer Shanka
Browse files

Check if the receiver can be skipped before starting a new process.

Bug: 263141882
Test: atest services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
Test: atest services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
Change-Id: Iddedc57aa65cf3dde3b46cc8259f06a6c28dac3a
parent 5cb9f358
Loading
Loading
Loading
Loading
+31 −14
Original line number Diff line number Diff line
@@ -731,6 +731,11 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
            return;
        }

        if (maybeSkipReceiver(queue, null, r, index)) {
            mRunningColdStart = null;
            return;
        }

        final ApplicationInfo info = ((ResolveInfo) receiver).activityInfo.applicationInfo;
        final ComponentName component = ((ResolveInfo) receiver).activityInfo.getComponentName();

@@ -790,41 +795,53 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
     * skipped (and therefore no more work is required).
     */
    private boolean maybeSkipReceiver(@NonNull BroadcastProcessQueue queue,
            @NonNull BroadcastReceiverBatch batch, @NonNull BroadcastRecord r, int index) {
            @Nullable BroadcastReceiverBatch batch, @NonNull BroadcastRecord r, int index) {
        final String reason = shouldSkipReceiver(queue, r, index);
        if (reason != null) {
            if (batch == null) {
                enqueueFinishReceiver(queue, r, index, BroadcastRecord.DELIVERY_SKIPPED, reason);
            } else {
                batch.finish(r, index, BroadcastRecord.DELIVERY_SKIPPED, reason);
            }
            return true;
        }
        return false;
    }

    /**
     * Consults {@link BroadcastSkipPolicy} and the receiver process state to decide whether or
     * not the broadcast to a receiver can be skipped.
     */
    private String shouldSkipReceiver(@NonNull BroadcastProcessQueue queue,
            @NonNull BroadcastRecord r, int index) {
        final int oldDeliveryState = getDeliveryState(r, index);
        final ProcessRecord app = queue.app;
        final Object receiver = r.receivers.get(index);

        // If someone already finished this broadcast, finish immediately
        if (isDeliveryStateTerminal(oldDeliveryState)) {
            batch.finish(r, index, oldDeliveryState, "already terminal state");
            return true;
            return "already terminal state";
        }

        // Consider additional cases where we'd want to finish immediately
        if (app.isInFullBackup()) {
            batch.finish(r, index, BroadcastRecord.DELIVERY_SKIPPED, "isInFullBackup");
            return true;
        if (app != null && app.isInFullBackup()) {
            return "isInFullBackup";
        }
        if (mSkipPolicy.shouldSkip(r, receiver)) {
            batch.finish(r, index, BroadcastRecord.DELIVERY_SKIPPED, "mSkipPolicy");
            return true;
            return "mSkipPolicy";
        }
        final Intent receiverIntent = r.getReceiverIntent(receiver);
        if (receiverIntent == null) {
            batch.finish(r, index, BroadcastRecord.DELIVERY_SKIPPED, "getReceiverIntent");
            return true;
            return "getReceiverIntent";
        }

        // Ignore registered receivers from a previous PID
        if ((receiver instanceof BroadcastFilter)
                && ((BroadcastFilter) receiver).receiverList.pid != app.getPid()) {
            batch.finish(r, index, BroadcastRecord.DELIVERY_SKIPPED,
                    "BroadcastFilter for mismatched PID");
            return true;
            return "BroadcastFilter for mismatched PID";
        }
        // The receiver was not handled in this method.
        return false;
        return null;
    }

    /**
+81 −12
Original line number Diff line number Diff line
@@ -75,6 +75,8 @@ import android.os.PowerExemptionManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
@@ -156,6 +158,7 @@ public class BroadcastQueueTest {
    private ActivityManagerService mAms;
    private BroadcastQueue mQueue;
    BroadcastConstants mConstants;
    private TestBroadcastSkipPolicy mSkipPolicy;

    /**
     * Desired behavior of the next
@@ -282,16 +285,8 @@ public class BroadcastQueueTest {
        mConstants = new BroadcastConstants(Settings.Global.BROADCAST_FG_CONSTANTS);
        mConstants.TIMEOUT = 100;
        mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT = 0;
        final BroadcastSkipPolicy emptySkipPolicy = new BroadcastSkipPolicy(mAms) {
            public boolean shouldSkip(BroadcastRecord r, Object o) {
                // Ignored
                return false;
            }
            public String shouldSkipMessage(BroadcastRecord r, Object o) {
                // Ignored
                return null;
            }
        };
        mSkipPolicy = new TestBroadcastSkipPolicy(mAms);

        final BroadcastHistory emptyHistory = new BroadcastHistory(mConstants) {
            public void addBroadcastToHistoryLocked(BroadcastRecord original) {
                // Ignored
@@ -300,13 +295,13 @@ public class BroadcastQueueTest {

        if (mImpl == Impl.DEFAULT) {
            var q = new BroadcastQueueImpl(mAms, mHandlerThread.getThreadHandler(), TAG,
                    mConstants, emptySkipPolicy, emptyHistory, false,
                    mConstants, mSkipPolicy, emptyHistory, false,
                    ProcessList.SCHED_GROUP_DEFAULT);
            q.mReceiverBatch.mDeepReceiverCopy = true;
            mQueue = q;
        } else if (mImpl == Impl.MODERN) {
            var q = new BroadcastQueueModernImpl(mAms, mHandlerThread.getThreadHandler(),
                    mConstants, mConstants, emptySkipPolicy, emptyHistory);
                    mConstants, mConstants, mSkipPolicy, emptyHistory);
            q.mReceiverBatch.mDeepReceiverCopy = true;
            mQueue = q;
        } else {
@@ -327,6 +322,43 @@ public class BroadcastQueueTest {
        }
    }

    private static class TestBroadcastSkipPolicy extends BroadcastSkipPolicy {
        private final ArrayMap<String, ArraySet> mReceiversToSkip = new ArrayMap<>();

        TestBroadcastSkipPolicy(ActivityManagerService service) {
            super(service);
        }

        public String shouldSkipMessage(BroadcastRecord r, Object o) {
            if (shouldSkipReceiver(r.intent.getAction(), o)) {
                return "test skipped receiver";
            }
            return null;
        }

        private boolean shouldSkipReceiver(String action, Object o) {
            final ArraySet<Object> receiversToSkip = mReceiversToSkip.get(action);
            if (receiversToSkip == null) {
                return false;
            }
            for (int i = 0; i < receiversToSkip.size(); ++i) {
                if (BroadcastRecord.isReceiverEquals(o, receiversToSkip.valueAt(i))) {
                    return true;
                }
            }
            return false;
        }

        public void setSkipReceiver(String action, Object o) {
            ArraySet<Object> receiversToSkip = mReceiversToSkip.get(action);
            if (receiversToSkip == null) {
                receiversToSkip = new ArraySet<>();
                mReceiversToSkip.put(action, receiversToSkip);
            }
            receiversToSkip.add(o);
        }
    }

    private class TestInjector extends Injector {
        TestInjector(Context context) {
            super(context);
@@ -835,6 +867,7 @@ public class BroadcastQueueTest {
    static final String PACKAGE_GREEN = "com.example.green";
    static final String PACKAGE_BLUE = "com.example.blue";
    static final String PACKAGE_YELLOW = "com.example.yellow";
    static final String PACKAGE_ORANGE = "com.example.orange";

    static final String PROCESS_SYSTEM = "system";

@@ -842,6 +875,7 @@ public class BroadcastQueueTest {
    static final String CLASS_GREEN = "com.example.green.Green";
    static final String CLASS_BLUE = "com.example.blue.Blue";
    static final String CLASS_YELLOW = "com.example.yellow.Yellow";
    static final String CLASS_ORANGE = "com.example.orange.Orange";

    static int getUidForPackage(@NonNull String packageName) {
        switch (packageName) {
@@ -851,6 +885,7 @@ public class BroadcastQueueTest {
            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;
            case PACKAGE_ORANGE: return android.os.Process.FIRST_APPLICATION_UID + 5;
            default: throw new IllegalArgumentException();
        }
    }
@@ -1873,4 +1908,38 @@ public class BroadcastQueueTest {
        verify(mAms).addBroadcastStatLocked(eq(Intent.ACTION_TIMEZONE_CHANGED), eq(PACKAGE_RED),
                eq(1), eq(0), anyLong());
    }

    /**
     * Verify that we skip broadcasts if {@link BroadcastSkipPolicy} decides it should be skipped.
     */
    @Test
    public void testSkipPolicy() throws Exception {
        final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
        final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN);
        final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE);

        final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
        try (SyncBarrier b = new SyncBarrier()) {
            final Object greenReceiver = makeRegisteredReceiver(receiverGreenApp);
            final Object blueReceiver = makeRegisteredReceiver(receiverBlueApp);
            final Object yellowReceiver = makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW);
            final Object orangeReceiver = makeManifestReceiver(PACKAGE_ORANGE, CLASS_ORANGE);
            enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
                    List.of(greenReceiver, blueReceiver, yellowReceiver, orangeReceiver)));

            mSkipPolicy.setSkipReceiver(airplane.getAction(), greenReceiver);
            mSkipPolicy.setSkipReceiver(airplane.getAction(), orangeReceiver);
        }

        waitForIdle();
        // Verify that only blue and yellow receiver apps received the broadcast.
        verifyScheduleRegisteredReceiver(never(), receiverGreenApp, USER_SYSTEM);
        verifyScheduleRegisteredReceiver(receiverBlueApp, airplane);
        final ProcessRecord receiverYellowApp = mAms.getProcessRecordLocked(PACKAGE_YELLOW,
                getUidForPackage(PACKAGE_YELLOW));
        verifyScheduleReceiver(receiverYellowApp, airplane);
        final ProcessRecord receiverOrangeApp = mAms.getProcessRecordLocked(PACKAGE_ORANGE,
                getUidForPackage(PACKAGE_ORANGE));
        assertNull(receiverOrangeApp);
    }
}