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

Commit 523ab2ab authored by Winson Chung's avatar Winson Chung
Browse files

Add cached state to prevent blocking calls from SysUI->Shell

- WindowManagerGlobal has a shared lock which means that code in the
  shell main thread can deadlock with code running on the sysui main
  thread in rare cases (ie. display changes when folding/unfolding).

  Ideally there are no blocking calls between SysUI/Shell so this
  change caches state changes from the Bubbles side for querying from
  SysUI.  In particular, the expanded states and the suppressed states.

Bug: 190453559
Test: atest SystemUITests


Change-Id: I4e60b168a84618033604e7d097599b048a5886a1
parent 5d552869
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -284,6 +284,15 @@ public class Bubble implements BubbleViewProvider {
        return mTitle;
    }

    /**
     * @return the ShortcutInfo id if it exists, or the metadata shortcut id otherwise.
     */
    String getShortcutId() {
        return getShortcutInfo() != null
                ? getShortcutInfo().getId()
                : getMetadataShortcutId();
    }

    String getMetadataShortcutId() {
        return mMetadataShortcutId;
    }
+131 −31
Original line number Diff line number Diff line
@@ -93,6 +93,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -265,15 +266,7 @@ public class BubbleController {

    public void initialize() {
        mBubbleData.setListener(mBubbleDataListener);
        mBubbleData.setSuppressionChangedListener(bubble -> {
            // Make sure NoMan knows suppression state so that anyone querying it can tell.
            try {
                mBarService.onBubbleNotificationSuppressionChanged(bubble.getKey(),
                        !bubble.showInShade(), bubble.isSuppressed());
            } catch (RemoteException e) {
                // Bad things have happened
            }
        });
        mBubbleData.setSuppressionChangedListener(this::onBubbleNotificationSuppressionChanged);

        mBubbleData.setPendingIntentCancelledListener(bubble -> {
            if (bubble.getBubbleIntent() == null) {
@@ -401,6 +394,11 @@ public class BubbleController {
        return mImpl;
    }

    @VisibleForTesting
    public BubblesImpl.CachedState getImplCachedState() {
        return mImpl.mCachedState;
    }

    public ShellExecutor getMainExecutor() {
        return mMainExecutor;
    }
@@ -500,6 +498,18 @@ public class BubbleController {
        updateStack();
    }

    @VisibleForTesting
    public void onBubbleNotificationSuppressionChanged(Bubble bubble) {
        // Make sure NoMan knows suppression state so that anyone querying it can tell.
        try {
            mBarService.onBubbleNotificationSuppressionChanged(bubble.getKey(),
                    !bubble.showInShade(), bubble.isSuppressed());
        } catch (RemoteException e) {
            // Bad things have happened
        }
        mImpl.mCachedState.updateBubbleSuppressedState(bubble);
    }

    /** Called when the current user changes. */
    @VisibleForTesting
    public void onUserChanged(int newUserId) {
@@ -808,11 +818,6 @@ public class BubbleController {
        }
    }

    private boolean isBubbleExpanded(String key) {
        return isStackExpanded() && mBubbleData != null && mBubbleData.getSelectedBubble() != null
                && mBubbleData.getSelectedBubble().getKey().equals(key);
    }

    /** Promote the provided bubble from the overflow view. */
    public void promoteBubbleFromOverflow(Bubble bubble) {
        mLogger.log(bubble, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_BACK_TO_STACK);
@@ -1191,6 +1196,9 @@ public class BubbleController {

            mSysuiProxy.notifyInvalidateNotifications("BubbleData.Listener.applyUpdate");
            updateStack();

            // Update the cached state for queries from SysUI
            mImpl.mCachedState.update(update);
        }
    };

@@ -1364,25 +1372,124 @@ public class BubbleController {
    }

    private class BubblesImpl implements Bubbles {
        // Up-to-date cached state of bubbles data for SysUI to query from the calling thread
        @VisibleForTesting
        public class CachedState {
            private boolean mIsStackExpanded;
            private String mSelectedBubbleKey;
            private HashSet<String> mSuppressedBubbleKeys = new HashSet<>();
            private HashMap<String, String> mSuppressedGroupToNotifKeys = new HashMap<>();
            private HashMap<String, Bubble> mShortcutIdToBubble = new HashMap<>();

            private ArrayList<Bubble> mTmpBubbles = new ArrayList<>();

            /**
             * Updates the cached state based on the last full BubbleData change.
             */
            synchronized void update(BubbleData.Update update) {
                if (update.selectionChanged) {
                    mSelectedBubbleKey = update.selectedBubble != null
                            ? update.selectedBubble.getKey()
                            : null;
                }
                if (update.expandedChanged) {
                    mIsStackExpanded = update.expanded;
                }
                if (update.suppressedSummaryChanged) {
                    String summaryKey =
                            mBubbleData.getSummaryKey(update.suppressedSummaryGroup);
                    if (summaryKey != null) {
                        mSuppressedGroupToNotifKeys.put(update.suppressedSummaryGroup, summaryKey);
                    } else {
                        mSuppressedGroupToNotifKeys.remove(update.suppressedSummaryGroup);
                    }
                }

                mTmpBubbles.clear();
                mTmpBubbles.addAll(update.bubbles);
                mTmpBubbles.addAll(update.overflowBubbles);

                mSuppressedBubbleKeys.clear();
                mShortcutIdToBubble.clear();
                for (Bubble b : mTmpBubbles) {
                    mShortcutIdToBubble.put(b.getShortcutId(), b);
                    updateBubbleSuppressedState(b);
                }
            }

            /**
             * Updates a specific bubble suppressed state.  This is used mainly because notification
             * suppression changes don't go through the same BubbleData update mechanism.
             */
            synchronized void updateBubbleSuppressedState(Bubble b) {
                if (!b.showInShade()) {
                    mSuppressedBubbleKeys.add(b.getKey());
                } else {
                    mSuppressedBubbleKeys.remove(b.getKey());
                }
            }

            public synchronized boolean isStackExpanded() {
                return mIsStackExpanded;
            }

            public synchronized boolean isBubbleExpanded(String key) {
                return mIsStackExpanded && key.equals(mSelectedBubbleKey);
            }

            public synchronized boolean isBubbleNotificationSuppressedFromShade(String key,
                    String groupKey) {
                return mSuppressedBubbleKeys.contains(key)
                        || (mSuppressedGroupToNotifKeys.containsKey(groupKey)
                                && key.equals(mSuppressedGroupToNotifKeys.get(groupKey)));
            }

            @Nullable
            public synchronized Bubble getBubbleWithShortcutId(String id) {
                return mShortcutIdToBubble.get(id);
            }

            synchronized void dump(PrintWriter pw) {
                pw.println("BubbleImpl.CachedState state:");

                pw.println("mIsStackExpanded: " + mIsStackExpanded);
                pw.println("mSelectedBubbleKey: " + mSelectedBubbleKey);

                pw.print("mSuppressedBubbleKeys: ");
                pw.println(mSuppressedBubbleKeys.size());
                for (String key : mSuppressedBubbleKeys) {
                    pw.println("   suppressing: " + key);
                }

                pw.print("mSuppressedGroupToNotifKeys: ");
                pw.println(mSuppressedGroupToNotifKeys.size());
                for (String key : mSuppressedGroupToNotifKeys.keySet()) {
                    pw.println("   suppressing: " + key);
                }
            }
        }

        private CachedState mCachedState = new CachedState();

        @Override
        public boolean isBubbleNotificationSuppressedFromShade(String key, String groupKey) {
            return mMainExecutor.executeBlockingForResult(() -> {
                return BubbleController.this.isBubbleNotificationSuppressedFromShade(key, groupKey);
            }, Boolean.class);
            return mCachedState.isBubbleNotificationSuppressedFromShade(key, groupKey);
        }

        @Override
        public boolean isBubbleExpanded(String key) {
            return mMainExecutor.executeBlockingForResult(() -> {
                return BubbleController.this.isBubbleExpanded(key);
            }, Boolean.class);
            return mCachedState.isBubbleExpanded(key);
        }

        @Override
        public boolean isStackExpanded() {
            return mMainExecutor.executeBlockingForResult(() -> {
                return BubbleController.this.isStackExpanded();
            }, Boolean.class);
            return mCachedState.isStackExpanded();
        }

        @Override
        @Nullable
        public Bubble getBubbleWithShortcutId(String shortcutId) {
            return mCachedState.getBubbleWithShortcutId(shortcutId);
        }

        @Override
@@ -1424,14 +1531,6 @@ public class BubbleController {
            });
        }

        @Override
        @Nullable
        public Bubble getBubbleWithShortcutId(String shortcutId) {
            return mMainExecutor.executeBlockingForResult(() -> {
                return BubbleController.this.mBubbleData.getAnyBubbleWithShortcutId(shortcutId);
            }, Bubble.class);
        }

        @Override
        public void onTaskbarChanged(Bundle b) {
            mMainExecutor.execute(() -> {
@@ -1555,6 +1654,7 @@ public class BubbleController {
            try {
                mMainExecutor.executeBlocking(() -> {
                    BubbleController.this.dump(fd, pw, args);
                    mCachedState.dump(pw);
                });
            } catch (InterruptedException e) {
                Slog.e(TAG, "Failed to dump BubbleController in 2s");
+11 −1
Original line number Diff line number Diff line
@@ -73,6 +73,7 @@ public class BubbleData {
        boolean expandedChanged;
        boolean selectionChanged;
        boolean orderChanged;
        boolean suppressedSummaryChanged;
        boolean expanded;
        @Nullable BubbleViewProvider selectedBubble;
        @Nullable Bubble addedBubble;
@@ -81,6 +82,7 @@ public class BubbleData {
        @Nullable Bubble removedOverflowBubble;
        @Nullable Bubble suppressedBubble;
        @Nullable Bubble unsuppressedBubble;
        @Nullable String suppressedSummaryGroup;
        // Pair with Bubble and @DismissReason Integer
        final List<Pair<Bubble, Integer>> removedBubbles = new ArrayList<>();

@@ -103,7 +105,9 @@ public class BubbleData {
                    || removedOverflowBubble != null
                    || orderChanged
                    || suppressedBubble != null
                    || unsuppressedBubble != null;
                    || unsuppressedBubble != null
                    || suppressedSummaryChanged
                    || suppressedSummaryGroup != null;
        }

        void bubbleRemoved(Bubble bubbleToRemove, @DismissReason int reason) {
@@ -380,6 +384,9 @@ public class BubbleData {
     */
    void addSummaryToSuppress(String groupKey, String notifKey) {
        mSuppressedGroupKeys.put(groupKey, notifKey);
        mStateChange.suppressedSummaryChanged = true;
        mStateChange.suppressedSummaryGroup = groupKey;
        dispatchPendingChanges();
    }

    /**
@@ -397,6 +404,9 @@ public class BubbleData {
     */
    void removeSuppressedSummary(String groupKey) {
        mSuppressedGroupKeys.remove(groupKey);
        mStateChange.suppressedSummaryChanged = true;
        mStateChange.suppressedSummaryGroup = groupKey;
        dispatchPendingChanges();
    }

    /**
+1 −1
Original line number Diff line number Diff line
@@ -617,7 +617,7 @@ public class BubblesManager implements Dumpable {
     * cancel it (and hence the bubbles associated with it).
     *
     * @return true if we want to intercept the dismissal of the entry, else false.
     * @see Bubbles#handleDismissalInterception(BubbleEntry, List, IntConsumer)
     * @see Bubbles#handleDismissalInterception(BubbleEntry, List, IntConsumer, Executor)
     */
    public boolean handleDismissalInterception(NotificationEntry entry) {
        if (entry == null) {
+90 −75

File changed.

Preview size limit exceeded, changes collapsed.

Loading