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

Commit 17814cc1 authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

Apply negative delay to "urgent" broadcasts.

When choosing which "runnable" process to promote into a "running"
slot, we currently use a single scoring dimension of a "runnable at"
timestamp, and always start with the longest-waiting process.

However, in the case of a process with urgent broadcasts, we could
still end up waiting behind many other non-urgent processes that
just happened to enqueue themselves earlier.  (This is particularly
evident with BOOT_COMPLETED broadcasts.)

Future changes will improve execution and starvation guarantees
in this area, such as by reserving certain "runnable" slots for
processes with urgent work.

This change also improves test flakiness by treating broadcasts sent
from the shell as if the sender was "instrumented", since many tests
rely on sending broadcasts through the shell.

Bug: 253906105
Test: atest FrameworksMockingServicesTests:BroadcastRecordTest
Test: atest FrameworksMockingServicesTests:BroadcastQueueTest
Test: atest FrameworksMockingServicesTests:BroadcastQueueModernImplTest
Change-Id: I12f49d4135d3cab18dcadb42f37a9367e8638585
parent 2d4f558d
Loading
Loading
Loading
Loading
+15 −2
Original line number Diff line number Diff line
@@ -167,7 +167,7 @@ public class BroadcastConstants {
     */
    public long DELAY_NORMAL_MILLIS = DEFAULT_DELAY_NORMAL_MILLIS;
    private static final String KEY_DELAY_NORMAL_MILLIS = "bcast_delay_normal_millis";
    private static final long DEFAULT_DELAY_NORMAL_MILLIS = 1_000;
    private static final long DEFAULT_DELAY_NORMAL_MILLIS = 0;

    /**
     * For {@link BroadcastQueueModernImpl}: Delay to apply to broadcasts
@@ -175,7 +175,16 @@ public class BroadcastConstants {
     */
    public long DELAY_CACHED_MILLIS = DEFAULT_DELAY_CACHED_MILLIS;
    private static final String KEY_DELAY_CACHED_MILLIS = "bcast_delay_cached_millis";
    private static final long DEFAULT_DELAY_CACHED_MILLIS = 10_000;
    private static final long DEFAULT_DELAY_CACHED_MILLIS = 0;

    /**
     * For {@link BroadcastQueueModernImpl}: Delay to apply to urgent
     * broadcasts, typically a negative value to indicate they should be
     * executed before most other pending broadcasts.
     */
    public long DELAY_URGENT_MILLIS = DEFAULT_DELAY_URGENT_MILLIS;
    private static final String KEY_DELAY_URGENT_MILLIS = "bcast_delay_urgent_millis";
    private static final long DEFAULT_DELAY_URGENT_MILLIS = -120_000;

    /**
     * For {@link BroadcastQueueModernImpl}: Maximum number of complete
@@ -313,6 +322,8 @@ public class BroadcastConstants {
                    DEFAULT_DELAY_NORMAL_MILLIS);
            DELAY_CACHED_MILLIS = getDeviceConfigLong(KEY_DELAY_CACHED_MILLIS,
                    DEFAULT_DELAY_CACHED_MILLIS);
            DELAY_URGENT_MILLIS = getDeviceConfigLong(KEY_DELAY_URGENT_MILLIS,
                    DEFAULT_DELAY_URGENT_MILLIS);
            MAX_HISTORY_COMPLETE_SIZE = getDeviceConfigInt(KEY_MAX_HISTORY_COMPLETE_SIZE,
                    DEFAULT_MAX_HISTORY_COMPLETE_SIZE);
            MAX_HISTORY_SUMMARY_SIZE = getDeviceConfigInt(KEY_MAX_HISTORY_SUMMARY_SIZE,
@@ -354,6 +365,8 @@ public class BroadcastConstants {
                    TimeUtils.formatDuration(DELAY_NORMAL_MILLIS)).println();
            pw.print(KEY_DELAY_CACHED_MILLIS,
                    TimeUtils.formatDuration(DELAY_CACHED_MILLIS)).println();
            pw.print(KEY_DELAY_URGENT_MILLIS,
                    TimeUtils.formatDuration(DELAY_URGENT_MILLIS)).println();
            pw.print(KEY_MAX_HISTORY_COMPLETE_SIZE, MAX_HISTORY_COMPLETE_SIZE).println();
            pw.print(KEY_MAX_HISTORY_SUMMARY_SIZE, MAX_HISTORY_SUMMARY_SIZE).println();
            pw.decreaseIndent();
+17 −18
Original line number Diff line number Diff line
@@ -668,17 +668,18 @@ class BroadcastProcessQueue {
                return;
            }

            // If we have too many broadcasts pending, bypass any delays that
            // might have been applied above to aid draining
            if (mPending.size() + mPendingUrgent.size() >= constants.MAX_PENDING_BROADCASTS) {
                mRunnableAt = runnableAt;
                mRunnableAtReason = REASON_MAX_PENDING;
                return;
            }

            if (mCountForeground > 0) {
                mRunnableAt = runnableAt;
                mRunnableAt = runnableAt + constants.DELAY_URGENT_MILLIS;
                mRunnableAtReason = REASON_CONTAINS_FOREGROUND;
            } else if (mCountInteractive > 0) {
                mRunnableAt = runnableAt + constants.DELAY_URGENT_MILLIS;
                mRunnableAtReason = REASON_CONTAINS_INTERACTIVE;
            } else if (mCountInstrumented > 0) {
                mRunnableAt = runnableAt + constants.DELAY_URGENT_MILLIS;
                mRunnableAtReason = REASON_CONTAINS_INSTRUMENTED;
            } else if (mProcessInstrumented) {
                mRunnableAt = runnableAt + constants.DELAY_URGENT_MILLIS;
                mRunnableAtReason = REASON_INSTRUMENTED;
            } else if (mCountOrdered > 0) {
                mRunnableAt = runnableAt;
                mRunnableAtReason = REASON_CONTAINS_ORDERED;
@@ -688,18 +689,9 @@ class BroadcastProcessQueue {
            } else if (mCountPrioritized > 0) {
                mRunnableAt = runnableAt;
                mRunnableAtReason = REASON_CONTAINS_PRIORITIZED;
            } else if (mCountInteractive > 0) {
                mRunnableAt = runnableAt;
                mRunnableAtReason = REASON_CONTAINS_INTERACTIVE;
            } else if (mCountResultTo > 0) {
                mRunnableAt = runnableAt;
                mRunnableAtReason = REASON_CONTAINS_RESULT_TO;
            } else if (mCountInstrumented > 0) {
                mRunnableAt = runnableAt;
                mRunnableAtReason = REASON_CONTAINS_INSTRUMENTED;
            } else if (mProcessInstrumented) {
                mRunnableAt = runnableAt;
                mRunnableAtReason = REASON_INSTRUMENTED;
            } else if (mProcessCached) {
                mRunnableAt = runnableAt + constants.DELAY_CACHED_MILLIS;
                mRunnableAtReason = REASON_CACHED;
@@ -707,6 +699,13 @@ class BroadcastProcessQueue {
                mRunnableAt = runnableAt + constants.DELAY_NORMAL_MILLIS;
                mRunnableAtReason = REASON_NORMAL;
            }

            // If we have too many broadcasts pending, bypass any delays that
            // might have been applied above to aid draining
            if (mPending.size() + mPendingUrgent.size() >= constants.MAX_PENDING_BROADCASTS) {
                mRunnableAt = runnableAt;
                mRunnableAtReason = REASON_MAX_PENDING;
            }
        } else {
            mRunnableAt = Long.MAX_VALUE;
            mRunnableAtReason = REASON_EMPTY;
+13 −3
Original line number Diff line number Diff line
@@ -78,7 +78,7 @@ final class BroadcastRecord extends Binder {
    final int callingPid;   // the pid of who sent this
    final int callingUid;   // the uid of who sent this
    final boolean callerInstantApp; // caller is an Instant App?
    final boolean callerInstrumented; // caller is being instrumented
    final boolean callerInstrumented; // caller is being instrumented?
    final boolean ordered;  // serialize the send to receivers?
    final boolean sticky;   // originated from existing sticky data?
    final boolean alarm;    // originated from an alarm triggering?
@@ -366,8 +366,7 @@ final class BroadcastRecord extends Binder {
        callingPid = _callingPid;
        callingUid = _callingUid;
        callerInstantApp = _callerInstantApp;
        callerInstrumented = (_callerApp != null)
                ? (_callerApp.getActiveInstrumentation() != null) : false;
        callerInstrumented = isCallerInstrumented(_callerApp, _callingUid);
        resolvedType = _resolvedType;
        requiredPermissions = _requiredPermissions;
        excludedPermissions = _excludedPermissions;
@@ -675,6 +674,17 @@ final class BroadcastRecord extends Binder {
        return (newIntent != null) ? newIntent : intent;
    }

    static boolean isCallerInstrumented(@Nullable ProcessRecord callerApp, int callingUid) {
        switch (UserHandle.getAppId(callingUid)) {
            case android.os.Process.ROOT_UID:
            case android.os.Process.SHELL_UID:
                // Broadcasts sent via "shell" are typically invoked by test
                // suites, so we treat them as if the caller was instrumented
                return true;
        }
        return (callerApp != null) ? (callerApp.getActiveInstrumentation() != null) : false;
    }

    /**
     * Return if given receivers list has more than one traunch of priorities.
     */
+7 −5
Original line number Diff line number Diff line
@@ -26,6 +26,8 @@ import static com.android.server.am.BroadcastQueueTest.PACKAGE_YELLOW;
import static com.android.server.am.BroadcastQueueTest.getUidForPackage;
import static com.android.server.am.BroadcastQueueTest.makeManifestReceiver;

import static com.google.common.truth.Truth.assertThat;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -278,7 +280,7 @@ public class BroadcastQueueModernImplTest {
        final long notCachedRunnableAt = queue.getRunnableAt();
        queue.setProcessCached(true);
        final long cachedRunnableAt = queue.getRunnableAt();
        assertTrue(cachedRunnableAt > notCachedRunnableAt);
        assertThat(cachedRunnableAt).isGreaterThan(notCachedRunnableAt);
        assertEquals(ProcessList.SCHED_GROUP_BACKGROUND, queue.getPreferredSchedulingGroupLocked());
    }

@@ -306,13 +308,13 @@ public class BroadcastQueueModernImplTest {
        // (b) the next one up is the fg-priority broadcast despite its later enqueue time
        queue.setProcessCached(false);
        assertTrue(queue.isRunnable());
        assertEquals(airplaneRecord.enqueueTime, queue.getRunnableAt());
        assertThat(queue.getRunnableAt()).isAtMost(airplaneRecord.enqueueClockTime);
        assertEquals(ProcessList.SCHED_GROUP_DEFAULT, queue.getPreferredSchedulingGroupLocked());
        assertEquals(queue.peekNextBroadcastRecord(), airplaneRecord);

        queue.setProcessCached(true);
        assertTrue(queue.isRunnable());
        assertEquals(airplaneRecord.enqueueTime, queue.getRunnableAt());
        assertThat(queue.getRunnableAt()).isAtMost(airplaneRecord.enqueueClockTime);
        assertEquals(ProcessList.SCHED_GROUP_DEFAULT, queue.getPreferredSchedulingGroupLocked());
        assertEquals(queue.peekNextBroadcastRecord(), airplaneRecord);
    }
@@ -356,12 +358,12 @@ public class BroadcastQueueModernImplTest {

        mConstants.MAX_PENDING_BROADCASTS = 128;
        queue.invalidateRunnableAt();
        assertTrue(queue.getRunnableAt() > airplaneRecord.enqueueTime);
        assertThat(queue.getRunnableAt()).isGreaterThan(airplaneRecord.enqueueTime);
        assertEquals(BroadcastProcessQueue.REASON_NORMAL, queue.getRunnableAtReason());

        mConstants.MAX_PENDING_BROADCASTS = 1;
        queue.invalidateRunnableAt();
        assertTrue(queue.getRunnableAt() == airplaneRecord.enqueueTime);
        assertThat(queue.getRunnableAt()).isAtMost(airplaneRecord.enqueueTime);
        assertEquals(BroadcastProcessQueue.REASON_MAX_PENDING, queue.getRunnableAtReason());
    }