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

Commit b486f41e authored by Christopher Tate's avatar Christopher Tate
Browse files

Fix waitForBroadcastIdle()

Any ongoing deferrals are fast-forwarded for immediate deliverability,
to minimize the wait time.  Also adds detailed queue state description
to the wait-for-broadcast-idle shell command's output:

Waiting for queue background to become idle...
0 parallel; 0 ordered, 9 deferred
Waiting for queue background to become idle...
0 parallel; 1 in flight, 0 ordered, 8 deferred
...
Waiting for queue background to become idle...
0 parallel; 1 in flight, 5 ordered, 2 deferrals in alarm recipients
All broadcast queues are idle!

Fixes: 123999376
Test: atest CtsVoiceSettingsTestCases
Test: atest CtsBackgroundRestrictionsTestCases
Test: atest CtsAppSecurityHostTestCases:StorageHostTest
Change-Id: Ia0ada70b621131f8bad8e8340083347c5574e4af
parent b6f498a2
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -18100,8 +18100,10 @@ public class ActivityManagerService extends IActivityManager.Stub
                    if (!queue.isIdle()) {
                        final String msg = "Waiting for queue " + queue + " to become idle...";
                        pw.println(msg);
                        pw.println(queue.describeState());
                        pw.flush();
                        Slog.v(TAG, msg);
                        queue.cancelDeferrals();
                        idle = false;
                    }
                }
+65 −11
Original line number Diff line number Diff line
@@ -65,6 +65,14 @@ public class BroadcastDispatcher {
            broadcasts.add(br);
        }

        int size() {
            return broadcasts.size();
        }

        boolean isEmpty() {
            return broadcasts.isEmpty();
        }

        void writeToProto(ProtoOutputStream proto, long fieldId) {
            for (BroadcastRecord br : broadcasts) {
                br.writeToProto(proto, fieldId);
@@ -252,22 +260,48 @@ public class BroadcastDispatcher {
        synchronized (mLock) {
            return mCurrentBroadcast == null
                    && mOrderedBroadcasts.isEmpty()
                    && mDeferredBroadcasts.isEmpty()
                    && mAlarmBroadcasts.isEmpty();
                    && isDeferralsListEmpty(mDeferredBroadcasts)
                    && isDeferralsListEmpty(mAlarmBroadcasts);
        }
    }

    private static int pendingInDeferralsList(ArrayList<Deferrals> list) {
        int pending = 0;
        final int numEntries = list.size();
        for (int i = 0; i < numEntries; i++) {
            pending += list.get(i).size();
        }
        return pending;
    }

    private static boolean isDeferralsListEmpty(ArrayList<Deferrals> list) {
        return pendingInDeferralsList(list) == 0;
    }

    /**
     * Not quite the traditional size() measurement; includes any in-process but
     * not yet retired active outbound broadcast.
     * Strictly for logging, describe the currently pending contents in a human-
     * readable way
     */
    public int totalUndelivered() {
        synchronized (mLock) {
            return mAlarmBroadcasts.size()
                    + mDeferredBroadcasts.size()
                    + mOrderedBroadcasts.size()
                    + (mCurrentBroadcast == null ? 0 : 1);
    public String describeStateLocked() {
        final StringBuilder sb = new StringBuilder(128);
        if (mCurrentBroadcast != null) {
            sb.append("1 in flight, ");
        }
        sb.append(mOrderedBroadcasts.size());
        sb.append(" ordered");
        int n = pendingInDeferralsList(mAlarmBroadcasts);
        if (n > 0) {
            sb.append(", ");
            sb.append(n);
            sb.append(" deferrals in alarm recipients");
        }
        n = pendingInDeferralsList(mDeferredBroadcasts);
        if (n > 0) {
            sb.append(", ");
            sb.append(n);
            sb.append(" deferred");
        }
        return sb.toString();
    }

    // ----------------------------------
@@ -579,6 +613,26 @@ public class BroadcastDispatcher {
        }
    }

    /**
     * Cancel all current deferrals; that is, make all currently-deferred broadcasts
     * immediately deliverable.  Used by the wait-for-broadcast-idle mechanism.
     */
    public void cancelDeferrals() {
        synchronized (mLock) {
            zeroDeferralTimes(mAlarmBroadcasts);
            zeroDeferralTimes(mDeferredBroadcasts);
        }
    }

    private static void zeroDeferralTimes(ArrayList<Deferrals> list) {
        final int num = list.size();
        for (int i = 0; i < num; i++) {
            Deferrals d = list.get(i);
            // Safe to do this in-place because it won't break ordering
            d.deferUntil = d.deferredBy = 0;
        }
    }

    // ----------------------------------

    /**
+16 −3
Original line number Diff line number Diff line
@@ -923,8 +923,8 @@ public final class BroadcastQueue {

        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "processNextBroadcast ["
                + mQueueName + "]: "
                + mParallelBroadcasts.size() + " parallel broadcasts, "
                + mDispatcher.totalUndelivered() + " ordered broadcasts");
                + mParallelBroadcasts.size() + " parallel broadcasts; "
                + mDispatcher.describeStateLocked());

        mService.updateCpuStats();

@@ -1822,11 +1822,24 @@ public final class BroadcastQueue {
                record.intent == null ? "" : record.intent.getAction());
    }

    final boolean isIdle() {
    boolean isIdle() {
        return mParallelBroadcasts.isEmpty() && mDispatcher.isEmpty()
                && (mPendingBroadcast == null);
    }

    // Used by wait-for-broadcast-idle : fast-forward all current deferrals to
    // be immediately deliverable.
    void cancelDeferrals() {
        mDispatcher.cancelDeferrals();
    }

    String describeState() {
        synchronized (mService) {
            return mParallelBroadcasts.size() + " parallel; "
                    + mDispatcher.describeStateLocked();
        }
    }

    void writeToProto(ProtoOutputStream proto, long fieldId) {
        long token = proto.start(fieldId);
        proto.write(BroadcastQueueProto.QUEUE_NAME, mQueueName);