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

Commit 5957d63c authored by mrulhania's avatar mrulhania
Browse files

Simplify selection toolbar state management

Refactors selection toolbar state management
by leveraging binder ordering guarantees for
show/hide/dismiss calls. This change also
removes the onToolbarShowTimeout callback.
Also cleans up widget token and keyed selection
toolbar with a UID on the server side.

Fix: 423124353
Test: manual
Flag: EXEMPT bug fix
Change-Id: Ife9e8cbaca8fec5a17a5637823310a7ca7edd8de
parent 42ebb848
Loading
Loading
Loading
Loading
+38 −84
Original line number Diff line number Diff line
@@ -16,18 +16,12 @@

package android.service.selectiontoolbar;

import static android.view.selectiontoolbar.ISelectionToolbarCallback.ERROR_DO_NOT_ALLOW_MULTIPLE_TOOL_BAR;
import static android.view.selectiontoolbar.ISelectionToolbarCallback.ERROR_UNKNOWN_WIDGET_TOKEN;
import static android.view.selectiontoolbar.SelectionToolbarManager.NO_TOOLBAR_ID;

import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.view.selectiontoolbar.ShowInfo;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.UUID;

/**
 * The default implementation of {@link SelectionToolbarRenderService}.
@@ -43,110 +37,70 @@ public final class DefaultSelectionToolbarRenderService extends SelectionToolbar

    // TODO(b/215497659): handle remove if the client process dies.
    // Only show one toolbar, dismiss the old ones and remove from cache
    // Maps uid -> (widget token, toolbar instance)
    private final SparseArray<Pair<Long, RemoteSelectionToolbar>> mToolbarCache =
            new SparseArray<>();

    /**
     * Only allow one package to create one toolbar.
     */
    private boolean isToolbarShown(int uid, ShowInfo showInfo) {
        if (showInfo.widgetToken != NO_TOOLBAR_ID) {
            return true;
        }
        return mToolbarCache.contains(uid);
    }
    // Maps uid -> toolbar instance
    private final SparseArray<RemoteSelectionToolbar> mToolbarCache = new SparseArray<>();

    @Override
    public void onShow(int callingUid, ShowInfo showInfo,
    public void onShow(int uid, ShowInfo showInfo,
            SelectionToolbarRenderService.RemoteCallbackWrapper callbackWrapper) {
        if (isToolbarShown(callingUid, showInfo)) {
        RemoteSelectionToolbar existingToolbar = mToolbarCache.get(uid);
        if (existingToolbar != null) {
            // TODO can we remove this check and just update the widget with dismissing?
            Slog.e(TAG, "Do not allow multiple toolbar for the app.");
            callbackWrapper.onError(ERROR_DO_NOT_ALLOW_MULTIPLE_TOOL_BAR, showInfo.sequenceNumber);
            Slog.e(TAG, "Do not allow multiple toolbar for the uid : " + uid);
            return;
        }
        long widgetToken = showInfo.widgetToken == NO_TOOLBAR_ID
                ? UUID.randomUUID().getMostSignificantBits()
                : showInfo.widgetToken;

        if (mToolbarCache.indexOfKey(callingUid) < 0) {
            RemoteSelectionToolbar toolbar = new RemoteSelectionToolbar(callingUid, this,
                    widgetToken, showInfo,
                    callbackWrapper, this::transferTouch, this::onPasteAction);
            mToolbarCache.put(callingUid, new Pair<>(widgetToken, toolbar));
        }
        Slog.v(TAG, "onShow() for " + widgetToken);
        Pair<Long, RemoteSelectionToolbar> toolbarPair = mToolbarCache.get(callingUid);
        if (toolbarPair.first == widgetToken) {
            toolbarPair.second.show(showInfo);
        } else {
            Slog.w(TAG, "onShow() for unknown " + widgetToken);
            callbackWrapper.onError(ERROR_UNKNOWN_WIDGET_TOKEN, showInfo.sequenceNumber);
        }
    }

    @Override
    public void onHide(long widgetToken) {
        RemoteSelectionToolbar toolbar = getRemoteSelectionToolbarByToken(widgetToken);
        if (toolbar != null) {
            Slog.v(TAG, "onHide() for " + widgetToken);
            toolbar.hide(widgetToken);
        }
        RemoteSelectionToolbar toolbar = new RemoteSelectionToolbar(uid, this,
                showInfo, callbackWrapper, this::transferTouch, this::onPasteAction);
        mToolbarCache.put(uid, toolbar);
        toolbar.show(showInfo);
        Slog.v(TAG, "onShow() for uid: " + uid);
    }

    @Override
    public void onDismiss(long widgetToken) {
        RemoteSelectionToolbar toolbar = getRemoteSelectionToolbarByToken(widgetToken);
    public void onHide(int uid) {
        RemoteSelectionToolbar toolbar = mToolbarCache.get(uid);
        if (toolbar != null) {
            Slog.v(TAG, "onDismiss() for " + widgetToken);
            toolbar.dismiss(widgetToken);
            removeRemoteSelectionToolbarByTokenLocked(widgetToken);
            Slog.v(TAG, "onHide() for uid: " + uid);
            toolbar.hide(uid);
        }
    }

    @Override
    public void onUidDied(int callingUid) {
        Slog.w(TAG, "onUidDied for callingUid = " + callingUid);
        Pair<Long, RemoteSelectionToolbar> toolbarPair = mToolbarCache.removeReturnOld(callingUid);
        if (toolbarPair != null) {
            RemoteSelectionToolbar remoteToolbar = toolbarPair.second;
            remoteToolbar.dismiss(toolbarPair.first);
        }
    public void onDismiss(int uid) {
        Slog.v(TAG, "onDismiss() for uid: " + uid);
        removeAndDismissToolbar(uid);
    }

    private RemoteSelectionToolbar getRemoteSelectionToolbarByToken(long widgetToken) {
        for (int i = 0; i < mToolbarCache.size(); i++) {
            Pair<Long, RemoteSelectionToolbar> toolbarPair = mToolbarCache.valueAt(i);
            if (toolbarPair.first == widgetToken) {
                return toolbarPair.second;
            }
    private void removeAndDismissToolbar(int uid) {
        RemoteSelectionToolbar toolbar = mToolbarCache.removeReturnOld(uid);
        if (toolbar != null) {
            toolbar.dismiss(uid);
        }
        return null;
    }

    private void removeRemoteSelectionToolbarByTokenLocked(long widgetToken) {
        for (int i = 0; i < mToolbarCache.size(); i++) {
            Pair<Long, RemoteSelectionToolbar> toolbarPair = mToolbarCache.valueAt(i);
            if (toolbarPair.first == widgetToken) {
                mToolbarCache.remove(mToolbarCache.keyAt(i));
                return;
            }
        }
    @Override
    public void onUidDied(int uid) {
        Slog.w(TAG, "onUidDied for uid: " + uid);
        removeAndDismissToolbar(uid);
    }

    @Override
    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
        int size = mToolbarCache.size();
        pw.print("number selectionToolbar: "); pw.println(size);
        pw.print("number selectionToolbar: ");
        pw.println(size);
        String pfx = "  ";
        for (int i = 0; i < size; i++) {
            pw.print("#"); pw.println(i);
            int callingUid = mToolbarCache.keyAt(i);
            pw.print(pfx); pw.print("callingUid: "); pw.println(callingUid);
            Pair<Long, RemoteSelectionToolbar> toolbarPair = mToolbarCache.valueAt(i);
            RemoteSelectionToolbar selectionToolbar = toolbarPair.second;
            pw.print(pfx); pw.print("selectionToolbar: ");
            pw.print("#");
            pw.println(i);
            int uid = mToolbarCache.keyAt(i);
            pw.print(pfx);
            pw.print("uid: ");
            pw.println(uid);
            RemoteSelectionToolbar selectionToolbar = mToolbarCache.get(uid);
            pw.print(pfx);
            pw.print("selectionToolbar: ");
            selectionToolbar.dump(pfx, pw);
            pw.println();
        }
+4 −4
Original line number Diff line number Diff line
@@ -26,8 +26,8 @@ import android.view.selectiontoolbar.ShowInfo;
 */
oneway interface ISelectionToolbarRenderService {
    void onConnected(in IBinder callback);
    void onShow(int callingUid, in ShowInfo showInfo, in ISelectionToolbarCallback callback);
    void onHide(long widgetToken);
    void onDismiss(int callingUid, long widgetToken);
    void onUidDied(int callingUid);
    void onShow(int uid, in ShowInfo showInfo, in ISelectionToolbarCallback callback);
    void onHide(int uid);
    void onDismiss(int uid);
    void onUidDied(int uid);
}
+25 −26
Original line number Diff line number Diff line
@@ -80,7 +80,11 @@ public final class RemoteSelectionToolbar {
    private static final int MIN_OVERFLOW_SIZE = 2;
    private static final int MAX_OVERFLOW_SIZE = 4;

    private int mCallingUid;
    private static final int TOOLBAR_STATE_SHOWN = 1;
    private static final int TOOLBAR_STATE_HIDDEN = 2;
    private static final int TOOLBAR_STATE_DISMISSED = 3;

    private final int mUid;

    private final Context mContext;

@@ -121,7 +125,6 @@ public final class RemoteSelectionToolbar {
    private final int mLineHeight;
    private final int mIconTextSpacing;

    private final long mSelectionToolbarToken;
    private IBinder mHostInputToken;
    private final SelectionToolbarRenderService.RemoteCallbackWrapper mCallbackWrapper;
    private final SelectionToolbarRenderService.TransferTouchListener mTransferTouchListener;
@@ -148,9 +151,8 @@ public final class RemoteSelectionToolbar {
        }
    };

    private boolean mDismissed = true; // tracks whether this popup is dismissed or dismissing.
    private boolean mHidden; // tracks whether this popup is hidden or hiding.

    // Tracks this selection toolbar state.
    private int mState = TOOLBAR_STATE_DISMISSED;
    /* Calculated sizes for panels and overflow button. */
    private final Size mOverflowButtonSize;
    private Size mOverflowPanelSize;  // Should be null when there is no overflow.
@@ -170,13 +172,12 @@ public final class RemoteSelectionToolbar {
    private final Rect mTempContentRectForRoot = new Rect();
    private final int[] mTempCoords = new int[2];

    public RemoteSelectionToolbar(int callingUid, Context context, long selectionToolbarToken,
    public RemoteSelectionToolbar(int uid, Context context,
            ShowInfo showInfo, SelectionToolbarRenderService.RemoteCallbackWrapper callbackWrapper,
            SelectionToolbarRenderService.TransferTouchListener transferTouchListener,
            SelectionToolbarRenderService.OnPasteActionCallback onPasteActionCallback) {
        mCallingUid = callingUid;
        mUid = uid;
        mContext = wrapContext(context, showInfo);
        mSelectionToolbarToken = selectionToolbarToken;
        mCallbackWrapper = callbackWrapper;
        mTransferTouchListener = transferTouchListener;
        mOnPasteActionCallback = onPasteActionCallback;
@@ -261,7 +262,7 @@ public final class RemoteSelectionToolbar {
                if (tag instanceof ToolbarMenuItem toolbarMenuItem) {
                    if (toolbarMenuItem.itemId == R.id.paste
                            || toolbarMenuItem.itemId == R.id.pasteAsPlainText) {
                        mOnPasteActionCallback.onPasteAction(mCallingUid);
                        mOnPasteActionCallback.onPasteAction(mUid);
                    }
                    mCallbackWrapper.onMenuItemClicked(toolbarMenuItem.itemIndex);
                }
@@ -289,7 +290,6 @@ public final class RemoteSelectionToolbar {
                mRelativeCoordsForToolbar.y + mPopupHeight);
        WidgetInfo widgetInfo = new WidgetInfo();
        widgetInfo.sequenceNumber = mSequenceNumber;
        widgetInfo.widgetToken = mSelectionToolbarToken;
        widgetInfo.contentRect = mTempContentRect;
        widgetInfo.surfacePackage = getSurfacePackage();
        return widgetInfo;
@@ -350,12 +350,11 @@ public final class RemoteSelectionToolbar {
    private void show(Rect contentRectOnScreen) {
        Objects.requireNonNull(contentRectOnScreen);

        mHidden = false;
        mDismissed = false;
        cancelDismissAndHideAnimations();
        cancelOverflowAnimations();
        refreshCoordinatesAndOverflowDirection(contentRectOnScreen);
        preparePopupContent();
        mState = TOOLBAR_STATE_SHOWN;
        mCallbackWrapper.onShown(createWidgetInfo());
        // TODO(b/215681595): Use Choreographer to coordinate for show between different thread
        mShowAnimation.start();
@@ -364,33 +363,34 @@ public final class RemoteSelectionToolbar {
    /**
     * Dismiss the specified selection toolbar.
     */
    public void dismiss(long floatingToolbarToken) {
        debugLog("dismiss for " + floatingToolbarToken);
        if (mDismissed) {
    public void dismiss(int uid) {
        debugLog("dismiss for uid: " + uid);
        if (mState == TOOLBAR_STATE_DISMISSED) {
            return;
        }
        mHidden = false;
        mDismissed = true;

        mHideAnimation.cancel();
        mDismissAnimation.start();
        mState = TOOLBAR_STATE_DISMISSED;
    }

    /**
     * Hide the specified selection toolbar.
     */
    public void hide(long floatingToolbarToken) {
        debugLog("hide for " + floatingToolbarToken);
    public void hide(int uid) {
        debugLog("hide for uid: " + uid);
        if (!isShowing()) {
            return;
        }

        mHidden = true;
        mHideAnimation.start();
        mState = TOOLBAR_STATE_HIDDEN;
    }

    public boolean isShowing() {
        return !mDismissed && !mHidden;
        return mState == TOOLBAR_STATE_SHOWN;
    }

    public boolean isHidden() {
        return mState == TOOLBAR_STATE_HIDDEN;
    }

    private void updateCoordinates(Rect contentRectOnScreen) {
@@ -1437,9 +1437,8 @@ public final class RemoteSelectionToolbar {
     * Dumps information about this class.
     */
    public void dump(String prefix, PrintWriter pw) {
        pw.print(prefix); pw.print("toolbar token: "); pw.println(mSelectionToolbarToken);
        pw.print(prefix); pw.print("dismissed: "); pw.println(mDismissed);
        pw.print(prefix); pw.print("hidden: "); pw.println(mHidden);
        pw.print(prefix); pw.print("dismissed: "); pw.println(mState == TOOLBAR_STATE_DISMISSED);
        pw.print(prefix); pw.print("hidden: "); pw.println(mState == TOOLBAR_STATE_HIDDEN);
        pw.print(prefix); pw.print("popup width: "); pw.println(mPopupWidth);
        pw.print(prefix); pw.print("popup height: "); pw.println(mPopupHeight);
        pw.print(prefix); pw.print("relative coords: "); pw.println(mRelativeCoordsForToolbar);
+18 −18
Original line number Diff line number Diff line
@@ -69,23 +69,23 @@ public abstract class SelectionToolbarRenderService extends Service {
                        new SparseArray<>();

                @Override
                public void onShow(int callingUid, ShowInfo showInfo,
                public void onShow(int uid, ShowInfo showInfo,
                        ISelectionToolbarCallback callback) {
                    RemoteCallbackWrapper remoteCallbackWrapper;
                    synchronized (mCache) {
                        remoteCallbackWrapper = mCache.get(callingUid);
                        remoteCallbackWrapper = mCache.get(uid);
                        if (remoteCallbackWrapper == null) {
                            try {
                                DeathRecipient deathRecipient = () -> {
                                    synchronized (mCache) {
                                        mCache.remove(callingUid);
                                        mCache.remove(uid);
                                    }
                                    onUidDied(callingUid);
                                    onUidDied(uid);
                                };
                                callback.asBinder().linkToDeath(deathRecipient, 0);
                                remoteCallbackWrapper = new RemoteCallbackWrapper(callback,
                                        deathRecipient);
                                mCache.put(callingUid, remoteCallbackWrapper);
                                mCache.put(uid, remoteCallbackWrapper);
                            } catch (RemoteException e) {
                                Log.e(TAG, "ISelectionToolbarCallback has already died");
                                return;
@@ -93,23 +93,23 @@ public abstract class SelectionToolbarRenderService extends Service {
                        }
                    }
                    mHandler.sendMessage(obtainMessage(SelectionToolbarRenderService::onShow,
                            SelectionToolbarRenderService.this, callingUid, showInfo,
                            SelectionToolbarRenderService.this, uid, showInfo,
                            remoteCallbackWrapper));
                }

                @Override
                public void onHide(long widgetToken) {
                public void onHide(int uid) {
                    mHandler.sendMessage(obtainMessage(SelectionToolbarRenderService::onHide,
                            SelectionToolbarRenderService.this, widgetToken));
                            SelectionToolbarRenderService.this, uid));
                }

                @Override
                public void onDismiss(int callingUid, long widgetToken) {
                public void onDismiss(int uid) {
                    mHandler.sendMessage(obtainMessage(SelectionToolbarRenderService::onDismiss,
                            SelectionToolbarRenderService.this, widgetToken));
                            SelectionToolbarRenderService.this, uid));
                    synchronized (mCache) {
                        RemoteCallbackWrapper remoteCallbackWrapper =
                                mCache.removeReturnOld(callingUid);
                                mCache.removeReturnOld(uid);
                        if (remoteCallbackWrapper != null) {
                            remoteCallbackWrapper.unlinkToDeath();
                        }
@@ -124,9 +124,9 @@ public abstract class SelectionToolbarRenderService extends Service {
                }

                @Override
                public void onUidDied(int callingUid) {
                public void onUidDied(int uid) {
                    mHandler.sendMessage(obtainMessage(SelectionToolbarRenderService::onUidDied,
                            SelectionToolbarRenderService.this, callingUid));
                            SelectionToolbarRenderService.this, uid));
                }
            };

@@ -180,23 +180,23 @@ public abstract class SelectionToolbarRenderService extends Service {
    /**
     * Called when showing the selection toolbar.
     */
    public abstract void onShow(int callingUid, ShowInfo showInfo,
    public abstract void onShow(int uid, ShowInfo showInfo,
            RemoteCallbackWrapper callbackWrapper);

    /**
     * Called when hiding the selection toolbar.
     */
    public abstract void onHide(long widgetToken);
    public abstract void onHide(int uid);

    /**
     * Called when dismissing the selection toolbar.
     */
    public abstract void onDismiss(long widgetToken);
    public abstract void onDismiss(int uid);

    /**
     * Called when the client process dies.
     */
    public abstract void onUidDied(int callingUid);
    public abstract void onUidDied(int uid);

    /**
     * Callback to notify the client toolbar events.
@@ -272,6 +272,6 @@ public abstract class SelectionToolbarRenderService extends Service {
        /**
         * Notify the service to the paste action.
         */
        void onPasteAction(int callingUid);
        void onPasteAction(int uid);
    }
}
+0 −12
Original line number Diff line number Diff line
@@ -25,20 +25,8 @@ import android.view.selectiontoolbar.WidgetInfo;
 * @hide
 */
oneway interface ISelectionToolbarCallback {

    /**
     * The error code that do not allow to create multiple toolbar.
     */
    const int ERROR_DO_NOT_ALLOW_MULTIPLE_TOOL_BAR = 1;

    /**
     * The error code that the widget token is unknown or invalid.
     */
    const int ERROR_UNKNOWN_WIDGET_TOKEN = 2;

    void onShown(in WidgetInfo info);
    void onWidgetUpdated(in WidgetInfo info);
    void onToolbarShowTimeout();
    void onMenuItemClicked(int itemIndex);
    void onError(int errorCode, int sequenceNumber);
}
Loading