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

Commit 67621c2b authored by Sudheer Shanka's avatar Sudheer Shanka
Browse files

Kill the cached process if it sends excessive broadcasts.

Bug: 327496592
Test: atest services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
Change-Id: Ic8fa58fe8ae490776731a317e0f9377dcfedaa2a
parent a7a47f65
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -487,6 +487,15 @@ public final class ApplicationExitInfo implements Parcelable {
     */
    public static final int SUBREASON_FREEZER_BINDER_ASYNC_FULL = 31;

    /**
     * The process was killed because it was sending too many broadcasts while it is in the
     * Cached state. This would be set only when the reason is {@link #REASON_OTHER}.
     *
     * For internal use only.
     * @hide
     */
    public static final int SUBREASON_EXCESSIVE_OUTGOING_BROADCASTS_WHILE_CACHED = 32;

    // If there is any OEM code which involves additional app kill reasons, it should
    // be categorized in {@link #REASON_OTHER}, with subreason code starting from 1000.

@@ -665,6 +674,7 @@ public final class ApplicationExitInfo implements Parcelable {
        SUBREASON_EXCESSIVE_BINDER_OBJECTS,
        SUBREASON_OOM_KILL,
        SUBREASON_FREEZER_BINDER_ASYNC_FULL,
        SUBREASON_EXCESSIVE_OUTGOING_BROADCASTS_WHILE_CACHED,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface SubReason {}
@@ -1396,6 +1406,8 @@ public final class ApplicationExitInfo implements Parcelable {
                return "OOM KILL";
            case SUBREASON_FREEZER_BINDER_ASYNC_FULL:
                return "FREEZER BINDER ASYNC FULL";
            case SUBREASON_EXCESSIVE_OUTGOING_BROADCASTS_WHILE_CACHED:
                return "EXCESSIVE_OUTGOING_BROADCASTS_WHILE_CACHED";
            default:
                return "UNKNOWN";
        }
+1 −1
Original line number Diff line number Diff line
@@ -298,7 +298,7 @@ public class BroadcastConstants {
     * For {@link BroadcastQueueModernImpl}: Maximum number of outgoing broadcasts from a
     * freezable process that will be allowed before killing the process.
     */
    public long MAX_FROZEN_OUTGOING_BROADCASTS = DEFAULT_MAX_FROZEN_OUTGOING_BROADCASTS;
    public int MAX_FROZEN_OUTGOING_BROADCASTS = DEFAULT_MAX_FROZEN_OUTGOING_BROADCASTS;
    private static final String KEY_MAX_FROZEN_OUTGOING_BROADCASTS =
            "max_frozen_outgoing_broadcasts";
    private static final int DEFAULT_MAX_FROZEN_OUTGOING_BROADCASTS = 32;
+4 −0
Original line number Diff line number Diff line
@@ -277,6 +277,10 @@ class BroadcastProcessQueue {
        mOutgoingBroadcasts.clear();
    }

    public void clearOutgoingBroadcasts() {
        mOutgoingBroadcasts.clear();
    }

    /**
     * Enqueue the given broadcast to be dispatched to this process at some
     * future point in time. The target receiver is indicated by the given index
+12 −3
Original line number Diff line number Diff line
@@ -166,7 +166,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
    /**
     * Map from UID to per-process broadcast queues. If a UID hosts more than
     * one process, each additional process is stored as a linked list using
     * {@link BroadcastProcessQueue#next}.
     * {@link BroadcastProcessQueue#processNameNext}.
     *
     * @see #getProcessQueue
     * @see #getOrCreateProcessQueue
@@ -661,6 +661,10 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
        final BroadcastProcessQueue queue = getProcessQueue(app);
        if (queue != null) {
            setQueueProcess(queue, app);
            // Outgoing broadcasts should be cleared when the process dies but there have been
            // issues due to AMS not always informing the BroadcastQueue of process deaths.
            // So, clear them when a new process starts as well.
            queue.clearOutgoingBroadcasts();
        }

        boolean didSomething = false;
@@ -730,6 +734,8 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
                demoteFromRunningLocked(queue);
            }

            queue.clearOutgoingBroadcasts();

            // Skip any pending registered receivers, since the old process
            // would never be around to receive them
            boolean didSomething = queue.forEachMatchingBroadcast((r, i) -> {
@@ -781,8 +787,11 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
            final BroadcastProcessQueue queue = getOrCreateProcessQueue(
                    r.callerApp.processName, r.callerApp.uid);
            if (queue.getOutgoingBroadcastCount() >= mConstants.MAX_FROZEN_OUTGOING_BROADCASTS) {
                // TODO: Kill the process if the outgoing broadcasts count is
                // beyond a certain limit.
                r.callerApp.killLocked("Too many outgoing broadcasts in cached state",
                        ApplicationExitInfo.REASON_OTHER,
                        ApplicationExitInfo.SUBREASON_EXCESSIVE_OUTGOING_BROADCASTS_WHILE_CACHED,
                        true /* noisy */);
                return;
            }
            queue.enqueueOutgoingBroadcast(r);
            mHistory.onBroadcastFrozenLocked(r);
+31 −0
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
@@ -57,6 +58,7 @@ import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.ApplicationExitInfo;
import android.app.BackgroundStartPrivileges;
import android.app.BroadcastOptions;
import android.app.IApplicationThread;
@@ -239,6 +241,7 @@ public class BroadcastQueueTest extends BaseBroadcastQueueTest {
        mConstants.TIMEOUT = 200;
        mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT = 0;
        mConstants.PENDING_COLD_START_CHECK_INTERVAL_MILLIS = 500;
        mConstants.MAX_FROZEN_OUTGOING_BROADCASTS = 10;
    }

    @After
@@ -2368,6 +2371,34 @@ public class BroadcastQueueTest extends BaseBroadcastQueueTest {
        verifyScheduleReceiver(times(1), receiverYellowApp, timeTick);
    }

    @Test
    @RequiresFlagsEnabled(Flags.FLAG_DEFER_OUTGOING_BROADCASTS)
    public void testKillProcess_excessiveOutgoingBroadcastsWhileCached() throws Exception {
        final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
        setProcessFreezable(callerApp, true /* pendingFreeze */, false /* frozen */);
        waitForIdle();

        final int count = mConstants.MAX_FROZEN_OUTGOING_BROADCASTS + 1;
        for (int i = 0; i < count; ++i) {
            final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK + "_" + i);
            enqueueBroadcast(makeBroadcastRecord(timeTick, callerApp, List.of(
                    makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE))));
        }
        // Verify that we invoke the call to freeze the caller app.
        verify(mAms.mOomAdjuster.mCachedAppOptimizer, atLeastOnce())
                .freezeAppAsyncImmediateLSP(callerApp);

        // Verify that the caller process is killed
        assertTrue(callerApp.isKilled());
        verify(mProcessList).noteAppKill(same(callerApp),
                eq(ApplicationExitInfo.REASON_OTHER),
                eq(ApplicationExitInfo.SUBREASON_EXCESSIVE_OUTGOING_BROADCASTS_WHILE_CACHED),
                any(String.class));

        waitForIdle();
        assertNull(mAms.getProcessRecordLocked(PACKAGE_BLUE, getUidForPackage(PACKAGE_BLUE)));
    }

    private long getReceiverScheduledTime(@NonNull BroadcastRecord r, @NonNull Object receiver) {
        for (int i = 0; i < r.receivers.size(); ++i) {
            if (isReceiverEquals(receiver, r.receivers.get(i))) {