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

Commit 71249db6 authored by Christopher Tate's avatar Christopher Tate
Browse files

Implement alarm precedence for broadcast delivery

Rather than queueing alarm-triggered broadcasts with the rest of the
outbound ordered broadcasts, we now give them delivery precedence over
the general queue, to minimize parasitic execution under the alarm
wakelock.  A new DeviceConfig flag can optionally be used to override
the platform default and select between immediate alarm delivery and the
previous behavior.

Bug: 161537193
Test: atest CtsAlarmManagerTestCases
Change-Id: Ic18654427a07831dfbe7ec5edace36999cf9973f
parent 21d8b0c7
Loading
Loading
Loading
Loading
+31 −0
Original line number Diff line number Diff line
@@ -146,6 +146,7 @@ final class ActivityManagerConstants extends ContentObserver {
    static final String KEY_NETWORK_ACCESS_TIMEOUT_MS = "network_access_timeout_ms";

    private static final int DEFAULT_MAX_CACHED_PROCESSES = 32;
    private static final boolean DEFAULT_PRIORITIZE_ALARM_BROADCASTS = true;
    private static final long DEFAULT_FGSERVICE_MIN_SHOWN_TIME = 2*1000;
    private static final long DEFAULT_FGSERVICE_MIN_REPORT_TIME = 3*1000;
    private static final long DEFAULT_FGSERVICE_SCREEN_ON_BEFORE_TIME = 1*1000;
@@ -325,6 +326,14 @@ final class ActivityManagerConstants extends ContentObserver {
     */
    private static final String KEY_PROCESS_KILL_TIMEOUT = "process_kill_timeout";

    /**
     * {@code true} to send in-flight alarm broadcasts ahead of non-alarms; {@code false}
     * to queue alarm broadcasts identically to non-alarms [i.e. the pre-U behavior]; or
     * {@code null} or empty string in order to fall back to whatever the build-time default
     * was for the device.
     */
    private static final String KEY_PRIORITIZE_ALARM_BROADCASTS = "prioritize_alarm_broadcasts";

    private static final String KEY_DEFER_BOOT_COMPLETED_BROADCAST =
            "defer_boot_completed_broadcast";

@@ -666,6 +675,12 @@ final class ActivityManagerConstants extends ContentObserver {
    volatile @BroadcastConstants.DeferBootCompletedBroadcastType int mDeferBootCompletedBroadcast =
            DEFAULT_DEFER_BOOT_COMPLETED_BROADCAST;

    /**
     * Whether alarm broadcasts are delivered immediately, or queued along with the rest
     * of the pending ordered broadcasts.
     */
    volatile boolean mPrioritizeAlarmBroadcasts = DEFAULT_PRIORITIZE_ALARM_BROADCASTS;

    /**
     * How long the Context.startForegroundService() grace period is to get around to
     * calling Service.startForeground() before we generate ANR.
@@ -977,6 +992,9 @@ final class ActivityManagerConstants extends ContentObserver {
                            case KEY_PROCESS_KILL_TIMEOUT:
                                updateProcessKillTimeout();
                                break;
                            case KEY_PRIORITIZE_ALARM_BROADCASTS:
                                updatePrioritizeAlarmBroadcasts();
                                break;
                            case KEY_DEFER_BOOT_COMPLETED_BROADCAST:
                                updateDeferBootCompletedBroadcast();
                                break;
@@ -1446,6 +1464,17 @@ final class ActivityManagerConstants extends ContentObserver {
        }
    }

    private void updatePrioritizeAlarmBroadcasts() {
        // Flag value can be something that evaluates to `true` or `false`,
        // or empty/null.  If it's empty/null, the platform default is used.
        final String flag = DeviceConfig.getString(
                DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                KEY_PRIORITIZE_ALARM_BROADCASTS,
                "");
        mPrioritizeAlarmBroadcasts = TextUtils.isEmpty(flag)
                ? DEFAULT_PRIORITIZE_ALARM_BROADCASTS
                : Boolean.parseBoolean(flag);
    }
    private void updateDeferBootCompletedBroadcast() {
        mDeferBootCompletedBroadcast = DeviceConfig.getInt(
                DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -1786,6 +1815,8 @@ final class ActivityManagerConstants extends ContentObserver {
        pw.print("="); pw.println(mComponentAliasOverrides);
        pw.print("  "); pw.print(KEY_DEFER_BOOT_COMPLETED_BROADCAST);
        pw.print("="); pw.println(mDeferBootCompletedBroadcast);
        pw.print("  "); pw.print(KEY_PRIORITIZE_ALARM_BROADCASTS);
        pw.print("="); pw.println(mPrioritizeAlarmBroadcasts);
        pw.print("  "); pw.print(KEY_NO_KILL_CACHED_PROCESSES_UNTIL_BOOT_COMPLETED);
        pw.print("="); pw.println(mNoKillCachedProcessesUntilBootCompleted);
        pw.print("  "); pw.print(KEY_NO_KILL_CACHED_PROCESSES_POST_BOOT_COMPLETED_DURATION_MILLIS);
+73 −25
Original line number Diff line number Diff line
@@ -181,7 +181,7 @@ public class BroadcastDispatcher {
                for (int i = 0; i < numEntries; i++) {
                    if (recipientUid == mDeferredBroadcasts.get(i).uid) {
                        Deferrals d = mDeferredBroadcasts.remove(i);
                        mAlarmBroadcasts.add(d);
                        mAlarmDeferrals.add(d);
                        break;
                    }
                }
@@ -201,10 +201,10 @@ public class BroadcastDispatcher {

                // No longer an alarm target, so resume ordinary deferral policy
                if (newCount <= 0) {
                    final int numEntries = mAlarmBroadcasts.size();
                    final int numEntries = mAlarmDeferrals.size();
                    for (int i = 0; i < numEntries; i++) {
                        if (recipientUid == mAlarmBroadcasts.get(i).uid) {
                            Deferrals d = mAlarmBroadcasts.remove(i);
                        if (recipientUid == mAlarmDeferrals.get(i).uid) {
                            Deferrals d = mAlarmDeferrals.remove(i);
                            insertLocked(mDeferredBroadcasts, d);
                            break;
                        }
@@ -234,7 +234,13 @@ public class BroadcastDispatcher {
    // General deferrals not holding up alarms
    private final ArrayList<Deferrals> mDeferredBroadcasts = new ArrayList<>();
    // Deferrals that *are* holding up alarms; ordered by alarm dispatch time
    private final ArrayList<Deferrals> mAlarmBroadcasts = new ArrayList<>();
    private final ArrayList<Deferrals> mAlarmDeferrals = new ArrayList<>();
    // Under the "deliver alarm broadcasts immediately" policy, the queue of
    // upcoming alarm broadcasts.  These are always delivered first - if the
    // policy is changed on the fly from immediate-alarm-delivery to the previous
    // in-order-queueing behavior, pending immediate alarm deliveries will drain
    // and then the behavior settle into the pre-U semantics.
    private final ArrayList<BroadcastRecord> mAlarmQueue = new ArrayList<>();

    // Next outbound broadcast, established by getNextBroadcastLocked()
    private BroadcastRecord mCurrentBroadcast;
@@ -528,8 +534,9 @@ public class BroadcastDispatcher {
        synchronized (mLock) {
            return mCurrentBroadcast == null
                    && mOrderedBroadcasts.isEmpty()
                    && mAlarmQueue.isEmpty()
                    && isDeferralsListEmpty(mDeferredBroadcasts)
                    && isDeferralsListEmpty(mAlarmBroadcasts);
                    && isDeferralsListEmpty(mAlarmDeferrals);
        }
    }

@@ -557,7 +564,13 @@ public class BroadcastDispatcher {
        }
        sb.append(mOrderedBroadcasts.size());
        sb.append(" ordered");
        int n = pendingInDeferralsList(mAlarmBroadcasts);
        int n = mAlarmQueue.size();
        if (n > 0) {
            sb.append(", ");
            sb.append(n);
            sb.append(" alarms");
        }
        n = pendingInDeferralsList(mAlarmDeferrals);
        if (n > 0) {
            sb.append(", ");
            sb.append(n);
@@ -592,8 +605,16 @@ public class BroadcastDispatcher {
    // ----------------------------------
    // BroadcastQueue operation support
    void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
        final ArrayList<BroadcastRecord> queue =
                (r.alarm && mQueue.mService.mConstants.mPrioritizeAlarmBroadcasts)
                        ? mAlarmQueue
                        : mOrderedBroadcasts;

        if (r.receivers == null || r.receivers.isEmpty()) {
            mOrderedBroadcasts.add(r);
            // Fast no-op path for broadcasts that won't actually be dispatched to
            // receivers - we still need to handle completion callbacks and historical
            // records, but we don't need to consider the fancy cases.
            queue.add(r);
            return;
        }

@@ -622,7 +643,8 @@ public class BroadcastDispatcher {
                return;
            }
        } else {
            mOrderedBroadcasts.add(r);
            // Ordinary broadcast, so put it on the appropriate queue and carry on
            queue.add(r);
        }
    }

@@ -652,10 +674,13 @@ public class BroadcastDispatcher {
    BroadcastRecord replaceBroadcastLocked(BroadcastRecord r, String typeForLogging) {
        // Simple case, in the ordinary queue.
        BroadcastRecord old = replaceBroadcastLocked(mOrderedBroadcasts, r, typeForLogging);

        // ... or possibly in the simple alarm queue
        if (old == null) {
            old = replaceBroadcastLocked(mAlarmQueue, r, typeForLogging);
        }
        // If we didn't find it, less-simple:  in a deferral queue?
        if (old == null) {
            old = replaceDeferredBroadcastLocked(mAlarmBroadcasts, r, typeForLogging);
            old = replaceDeferredBroadcastLocked(mAlarmDeferrals, r, typeForLogging);
        }
        if (old == null) {
            old = replaceDeferredBroadcastLocked(mDeferredBroadcasts, r, typeForLogging);
@@ -705,6 +730,10 @@ public class BroadcastDispatcher {
        // "yes we would do something" circumstance
        boolean didSomething = cleanupBroadcastListDisabledReceiversLocked(mOrderedBroadcasts,
                packageName, filterByClasses, userId, doit);
        if (doit || !didSomething) {
            didSomething = cleanupBroadcastListDisabledReceiversLocked(mAlarmQueue,
                    packageName, filterByClasses, userId, doit);
        }
        if (doit || !didSomething) {
            ArrayList<BroadcastRecord> lockedBootCompletedBroadcasts = new ArrayList<>();
            for (int u = 0, usize = mUser2Deferred.size(); u < usize; u++) {
@@ -731,7 +760,7 @@ public class BroadcastDispatcher {
                    packageName, filterByClasses, userId, doit);
        }
        if (doit || !didSomething) {
            didSomething |= cleanupDeferralsListDisabledReceiversLocked(mAlarmBroadcasts,
            didSomething |= cleanupDeferralsListDisabledReceiversLocked(mAlarmDeferrals,
                    packageName, filterByClasses, userId, doit);
        }
        if (doit || !didSomething) {
@@ -781,12 +810,15 @@ public class BroadcastDispatcher {
        if (mCurrentBroadcast != null) {
            mCurrentBroadcast.dumpDebug(proto, fieldId);
        }
        for (Deferrals d : mAlarmBroadcasts) {
        for (Deferrals d : mAlarmDeferrals) {
            d.dumpDebug(proto, fieldId);
        }
        for (BroadcastRecord br : mOrderedBroadcasts) {
            br.dumpDebug(proto, fieldId);
        }
        for (BroadcastRecord br : mAlarmQueue) {
            br.dumpDebug(proto, fieldId);
        }
        for (Deferrals d : mDeferredBroadcasts) {
            d.dumpDebug(proto, fieldId);
        }
@@ -816,23 +848,33 @@ public class BroadcastDispatcher {
            return mCurrentBroadcast;
        }

        final boolean someQueued = !mOrderedBroadcasts.isEmpty();

        BroadcastRecord next = null;

        // Alarms in flight take precedence over everything else.  This queue
        // will be non-empty only when the relevant policy is in force, but if
        // policy has changed on the fly we still need to drain this before we
        // settle into the legacy behavior.
        if (!mAlarmQueue.isEmpty()) {
            next = mAlarmQueue.remove(0);
        }

        // Next in precedence are deferred BOOT_COMPLETED broadcasts
        if (next == null) {
            next = dequeueDeferredBootCompletedBroadcast();
        }

        if (next == null && !mAlarmBroadcasts.isEmpty()) {
            next = popLocked(mAlarmBroadcasts);
        // Alarm-related deferrals are next in precedence...
        if (next == null && !mAlarmDeferrals.isEmpty()) {
            next = popLocked(mAlarmDeferrals);
            if (DEBUG_BROADCAST_DEFERRAL && next != null) {
                Slog.i(TAG, "Next broadcast from alarm targets: " + next);
            }
        }

        final boolean someQueued = !mOrderedBroadcasts.isEmpty();

        if (next == null && !mDeferredBroadcasts.isEmpty()) {
            // We're going to deliver either:
            // A this point we're going to deliver either:
            // 1. the next "overdue" deferral; or
            // 2. the next ordinary ordered broadcast; *or*
            // 3. the next not-yet-overdue deferral.
@@ -937,7 +979,7 @@ public class BroadcastDispatcher {
                    scheduleDeferralCheckLocked(true);
                } else {
                    // alarm-related: strict order-encountered
                    mAlarmBroadcasts.add(d);
                    mAlarmDeferrals.add(d);
                }
            } else {
                // We're already deferring, but something was slow again.  Reset the
@@ -999,7 +1041,7 @@ public class BroadcastDispatcher {
     * immediately deliverable.  Used by the wait-for-broadcast-idle mechanism.
     */
    public void cancelDeferralsLocked() {
        zeroDeferralTimes(mAlarmBroadcasts);
        zeroDeferralTimes(mAlarmDeferrals);
        zeroDeferralTimes(mDeferredBroadcasts);
    }

@@ -1023,7 +1065,7 @@ public class BroadcastDispatcher {
        Deferrals d = findUidLocked(uid, mDeferredBroadcasts);
        // ...but if not there, also check alarm-prioritized deferrals
        if (d == null) {
            d = findUidLocked(uid, mAlarmBroadcasts);
            d = findUidLocked(uid, mAlarmDeferrals);
        }
        return d;
    }
@@ -1035,7 +1077,7 @@ public class BroadcastDispatcher {
    private boolean removeDeferral(Deferrals d) {
        boolean didRemove = mDeferredBroadcasts.remove(d);
        if (!didRemove) {
            didRemove = mAlarmBroadcasts.remove(d);
            didRemove = mAlarmDeferrals.remove(d);
        }
        return didRemove;
    }
@@ -1103,14 +1145,20 @@ public class BroadcastDispatcher {
        } else {
            pw.println("  (null)");
        }
        printed |= dumper.didPrint();

        dumper.setHeading("Active alarm broadcasts");
        dumper.setLabel("Active Alarm Broadcast");
        for (BroadcastRecord br : mAlarmQueue) {
            dumper.dump(br);
        }
        printed |= dumper.didPrint();

        dumper.setHeading("Active ordered broadcasts");
        dumper.setLabel("Active Ordered Broadcast");
        for (Deferrals d : mAlarmBroadcasts) {
        for (Deferrals d : mAlarmDeferrals) {
            d.dumpLocked(dumper);
        }
        printed |= dumper.didPrint();

        for (BroadcastRecord br : mOrderedBroadcasts) {
            dumper.dump(br);
        }