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

Commit 65145b2b authored by Cosmin Băieș's avatar Cosmin Băieș Committed by Android (Google) Code Review
Browse files

Merge "Handle ImeTracker.Token missing flows" into main

parents 637bb3ea df62c4fa
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -3961,6 +3961,7 @@ package android.view.inputmethod {
    method @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public java.util.List<android.view.inputmethod.InputMethodInfo> getInputMethodListAsUser(int);
    method public boolean hasActiveInputConnection(@Nullable android.view.View);
    method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public boolean hasPendingImeVisibilityRequests();
    method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public void hideSoftInputFromServerForTest();
    method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public boolean isCurrentRootView(@NonNull android.view.View);
    method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public boolean isInputMethodPickerShown();
    method @FlaggedApi("android.view.inputmethod.imm_userhandle_hostsidetests") @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public boolean isStylusHandwritingAvailableAsUser(@NonNull android.os.UserHandle);
+2 −2
Original line number Diff line number Diff line
@@ -453,7 +453,7 @@ class IInputMethodWrapper extends IInputMethod.Stub

    @BinderThread
    @Override
    public void showSoftInput(IBinder showInputToken, @Nullable ImeTracker.Token statsToken,
    public void showSoftInput(IBinder showInputToken, @NonNull ImeTracker.Token statsToken,
            @InputMethod.ShowFlags int flags, ResultReceiver resultReceiver) {
        ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_WRAPPER);
        mCaller.executeOrSendMessage(mCaller.obtainMessageIOOO(DO_SHOW_SOFT_INPUT,
@@ -462,7 +462,7 @@ class IInputMethodWrapper extends IInputMethod.Stub

    @BinderThread
    @Override
    public void hideSoftInput(IBinder hideInputToken, @Nullable ImeTracker.Token statsToken,
    public void hideSoftInput(IBinder hideInputToken, @NonNull ImeTracker.Token statsToken,
            int flags, ResultReceiver resultReceiver) {
        ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_WRAPPER);
        mCaller.executeOrSendMessage(mCaller.obtainMessageIOOO(DO_HIDE_SOFT_INPUT,
+131 −31
Original line number Diff line number Diff line
@@ -701,7 +701,13 @@ public class InputMethodService extends AbstractInputMethodService {
     */
    private IBinder mCurHideInputToken;

    /** The token tracking the current IME request or {@code null} otherwise. */
    /**
     * The token tracking the current IME request.
     *
     * <p> This exists as a workaround to changing the signatures of public methods. It will get
     * set to a {@code non-null} value before every call that uses it, stored locally inside the
     * callee, and immediately after reset to {@code null} from the callee.
     */
    @Nullable
    private ImeTracker.Token mCurStatsToken;

@@ -907,14 +913,13 @@ public class InputMethodService extends AbstractInputMethodService {
        @MainThread
        @Override
        public void hideSoftInputWithToken(int flags, ResultReceiver resultReceiver,
                IBinder hideInputToken, @Nullable ImeTracker.Token statsToken) {
                IBinder hideInputToken, @NonNull ImeTracker.Token statsToken) {
            mSystemCallingHideSoftInput = true;
            mCurHideInputToken = hideInputToken;
            mCurStatsToken = statsToken;
            try {
                hideSoftInput(flags, resultReceiver);
            } finally {
                mCurStatsToken = null;
                mCurHideInputToken = null;
                mSystemCallingHideSoftInput = false;
            }
@@ -926,23 +931,33 @@ public class InputMethodService extends AbstractInputMethodService {
        @MainThread
        @Override
        public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
            ImeTracker.forLogging().onProgress(
                    mCurStatsToken, ImeTracker.PHASE_IME_HIDE_SOFT_INPUT);
            if (DEBUG) Log.v(TAG, "hideSoftInput()");

            final var statsToken = mCurStatsToken != null ? mCurStatsToken
                    : createStatsToken(false /* show */,
                            SoftInputShowHideReason.HIDE_SOFT_INPUT_LEGACY_DIRECT,
                            ImeTracker.isFromUser(mRootView));
            mCurStatsToken = null;

            // TODO(b/148086656): Disallow IME developers from calling InputMethodImpl methods.
            if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R
                    && !mSystemCallingHideSoftInput) {
                Log.e(TAG, "IME shouldn't call hideSoftInput on itself."
                        + " Use requestHideSelf(int) itself");
                ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_IME_HIDE_SOFT_INPUT);
                return;
            }
            ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_HIDE_SOFT_INPUT);

            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.hideSoftInput");
            ImeTracing.getInstance().triggerServiceDump(
                    "InputMethodService.InputMethodImpl#hideSoftInput", mDumper,
                    null /* icProto */);
            final boolean wasVisible = isInputViewShown();
            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.hideSoftInput");

            mShowInputFlags = 0;
            mShowInputRequested = false;
            mCurStatsToken = statsToken;
            hideWindow();
            final boolean isVisible = isInputViewShown();
            final boolean visibilityChanged = isVisible != wasVisible;
@@ -963,14 +978,13 @@ public class InputMethodService extends AbstractInputMethodService {
        @Override
        public void showSoftInputWithToken(@InputMethod.ShowFlags int flags,
                ResultReceiver resultReceiver, IBinder showInputToken,
                @Nullable ImeTracker.Token statsToken) {
                @NonNull ImeTracker.Token statsToken) {
            mSystemCallingShowSoftInput = true;
            mCurShowInputToken = showInputToken;
            mCurStatsToken = statsToken;
            try {
                showSoftInput(flags, resultReceiver);
            } finally {
                mCurStatsToken = null;
                mCurShowInputToken = null;
                mSystemCallingShowSoftInput = false;
            }
@@ -982,16 +996,23 @@ public class InputMethodService extends AbstractInputMethodService {
        @MainThread
        @Override
        public void showSoftInput(@InputMethod.ShowFlags int flags, ResultReceiver resultReceiver) {
            ImeTracker.forLogging().onProgress(
                    mCurStatsToken, ImeTracker.PHASE_IME_SHOW_SOFT_INPUT);
            if (DEBUG) Log.v(TAG, "showSoftInput()");

            final var statsToken = mCurStatsToken != null ? mCurStatsToken
                    : createStatsToken(true /* show */,
                            SoftInputShowHideReason.SHOW_SOFT_INPUT_LEGACY_DIRECT,
                            ImeTracker.isFromUser(mRootView));
            mCurStatsToken = null;

            // TODO(b/148086656): Disallow IME developers from calling InputMethodImpl methods.
            if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R
                    && !mSystemCallingShowSoftInput) {
                Log.e(TAG, "IME shouldn't call showSoftInput on itself."
                        + " Use requestShowSelf(int) itself");
                ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_IME_SHOW_SOFT_INPUT);
                return;
            }
            ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_SHOW_SOFT_INPUT);

            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showSoftInput");
            ImeTracing.getInstance().triggerServiceDump(
@@ -999,11 +1020,12 @@ public class InputMethodService extends AbstractInputMethodService {
                    null /* icProto */);
            final boolean wasVisible = isInputViewShown();
            if (dispatchOnShowInputRequested(flags, false)) {
                ImeTracker.forLogging().onProgress(mCurStatsToken,
                ImeTracker.forLogging().onProgress(statsToken,
                        ImeTracker.PHASE_IME_ON_SHOW_SOFT_INPUT_TRUE);
                showWindow(true);
                mCurStatsToken = statsToken;
                showWindow(true /* showInput */);
            } else {
                ImeTracker.forLogging().onFailed(mCurStatsToken,
                ImeTracker.forLogging().onFailed(statsToken,
                        ImeTracker.PHASE_IME_ON_SHOW_SOFT_INPUT_TRUE);
            }
            setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
@@ -1895,21 +1917,23 @@ public class InputMethodService extends AbstractInputMethodService {
            if (showingInput) {
                // If we were last showing the soft keyboard, try to do so again.
                if (dispatchOnShowInputRequested(showFlags, true)) {
                    showWindow(true);
                    showWindowWithToken(true /* showInput */,
                            SoftInputShowHideReason.RESET_NEW_CONFIGURATION);
                    if (completions != null) {
                        mCurCompletions = completions;
                        onDisplayCompletions(completions);
                    }
                } else {
                    hideWindow();
                    hideWindowWithToken(SoftInputShowHideReason.RESET_NEW_CONFIGURATION);
                }
            } else if (mCandidatesVisibility == View.VISIBLE) {
                // If the candidates are currently visible, make sure the
                // window is shown for them.
                showWindow(false);
                showWindowWithToken(false /* showInput */,
                        SoftInputShowHideReason.RESET_NEW_CONFIGURATION);
            } else {
                // Otherwise hide the window.
                hideWindow();
                hideWindowWithToken(SoftInputShowHideReason.RESET_NEW_CONFIGURATION);
            }
            // If user uses hard keyboard, IME button should always be shown.
            boolean showing = onEvaluateInputViewShown();
@@ -2368,9 +2392,11 @@ public class InputMethodService extends AbstractInputMethodService {
            // has not asked for the input view to be shown, then we need
            // to update whether the window is shown.
            if (shown) {
                showWindow(false);
                showWindowWithToken(false /* showInput */,
                        SoftInputShowHideReason.UPDATE_CANDIDATES_VIEW_VISIBILITY);
            } else {
                hideWindow();
                hideWindowWithToken(
                        SoftInputShowHideReason.UPDATE_CANDIDATES_VIEW_VISIBILITY);
            }
        }
    }
@@ -3009,6 +3035,19 @@ public class InputMethodService extends AbstractInputMethodService {
        return result;
    }

    /**
     * Utility function that creates an IME request tracking token before
     * calling {@link #showWindow}.
     *
     * @param showInput whether the input window should be shown.
     * @param reason the reason why the IME request was created.
     */
    private void showWindowWithToken(boolean showInput, @SoftInputShowHideReason int reason) {
        mCurStatsToken = createStatsToken(true /* show */, reason,
                ImeTracker.isFromUser(mRootView));
        showWindow(showInput);
    }

    public void showWindow(boolean showInput) {
        if (DEBUG) Log.v(TAG, "Showing window: showInput=" + showInput
                + " mShowInputRequested=" + mShowInputRequested
@@ -3018,11 +3057,20 @@ public class InputMethodService extends AbstractInputMethodService {
                + " mInputStarted=" + mInputStarted
                + " mShowInputFlags=" + mShowInputFlags);

        final var statsToken = mCurStatsToken != null ? mCurStatsToken
                : createStatsToken(true /* show */,
                        SoftInputShowHideReason.SHOW_WINDOW_LEGACY_DIRECT,
                        ImeTracker.isFromUser(mRootView));
        mCurStatsToken = null;

        if (mInShowWindow) {
            Log.w(TAG, "Re-entrance in to showWindow");
            ImeTracker.forLogging().onCancelled(statsToken, ImeTracker.PHASE_IME_SHOW_WINDOW);
            return;
        }

        ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_SHOW_WINDOW);

        ImeTracing.getInstance().triggerServiceDump("InputMethodService#showWindow", mDumper,
                null /* icProto */);
        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showWindow");
@@ -3046,7 +3094,7 @@ public class InputMethodService extends AbstractInputMethodService {
        if (DEBUG) Log.v(TAG, "showWindow: draw decorView!");
        mWindow.show();
        mDecorViewWasVisible = true;
        applyVisibilityInInsetsConsumerIfNecessary(true);
        applyVisibilityInInsetsConsumerIfNecessary(true /* setVisible */, statsToken);
        cancelImeSurfaceRemoval();
        mInShowWindow = false;
        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
@@ -3137,13 +3185,15 @@ public class InputMethodService extends AbstractInputMethodService {
     * Applies the IME visibility in {@link android.view.ImeInsetsSourceConsumer}.
     *
     * @param setVisible {@code true} to make it visible, false to hide it.
     * @param statsToken the token tracking the current IME request.
     */
    private void applyVisibilityInInsetsConsumerIfNecessary(boolean setVisible) {
    private void applyVisibilityInInsetsConsumerIfNecessary(boolean setVisible,
            @NonNull ImeTracker.Token statsToken) {
        ImeTracing.getInstance().triggerServiceDump(
                "InputMethodService#applyVisibilityInInsetsConsumerIfNecessary", mDumper,
                null /* icProto */);
        mPrivOps.applyImeVisibilityAsync(setVisible
                ? mCurShowInputToken : mCurHideInputToken, setVisible, mCurStatsToken);
                ? mCurShowInputToken : mCurHideInputToken, setVisible, statsToken);
    }

    private void finishViews(boolean finishingInput) {
@@ -3159,12 +3209,35 @@ public class InputMethodService extends AbstractInputMethodService {
        mCandidatesViewStarted = false;
    }

    /**
     * Utility function that creates an IME request tracking token before
     * calling {@link #hideWindow}.
     *
     * @param reason the reason why the IME request was created.
     */
    private void hideWindowWithToken(@SoftInputShowHideReason int reason) {
        // TODO(b/303041796): this should be handled by ImeTracker.isFromUser after fixing it
        //  to work with onClickListeners
        final boolean isFromUser = ImeTracker.isFromUser(mRootView)
                || reason == SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_BACK_KEY;
        mCurStatsToken = createStatsToken(false /* show */, reason, isFromUser);
        hideWindow();
    }

    public void hideWindow() {
        if (DEBUG) Log.v(TAG, "CALL: hideWindow");

        final var statsToken = mCurStatsToken != null ? mCurStatsToken
                : createStatsToken(false /* show */,
                        SoftInputShowHideReason.HIDE_WINDOW_LEGACY_DIRECT,
                        ImeTracker.isFromUser(mRootView));
        mCurStatsToken = null;

        ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_HIDE_WINDOW);
        ImeTracing.getInstance().triggerServiceDump("InputMethodService#hideWindow", mDumper,
                null /* icProto */);
        setImeWindowStatus(0, mBackDisposition);
        applyVisibilityInInsetsConsumerIfNecessary(false);
        applyVisibilityInInsetsConsumerIfNecessary(false /* setVisible */, statsToken);
        mWindowVisible = false;
        finishViews(false /* finishingInput */);
        if (mDecorViewVisible) {
@@ -3440,9 +3513,14 @@ public class InputMethodService extends AbstractInputMethodService {

    private void requestHideSelf(@InputMethodManager.HideFlags int flags,
            @SoftInputShowHideReason int reason) {
        // TODO(b/303041796): this should be handled by ImeTracker.isFromUser after fixing it
        //  to work with onClickListeners
        final boolean isFromUser = ImeTracker.isFromUser(mRootView)
                || reason == SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_BACK_KEY;
        final var statsToken = createStatsToken(false /* show */, reason, isFromUser);
        ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestHideSelf", mDumper,
                null /* icProto */);
        mPrivOps.hideMySoftInput(flags, reason);
        mPrivOps.hideMySoftInput(statsToken, flags, reason);
    }

    /**
@@ -3450,9 +3528,16 @@ public class InputMethodService extends AbstractInputMethodService {
     * interact with it.
     */
    public final void requestShowSelf(@InputMethodManager.ShowFlags int flags) {
        requestShowSelf(flags, SoftInputShowHideReason.SHOW_SOFT_INPUT_FROM_IME);
    }

    private void requestShowSelf(@InputMethodManager.ShowFlags int flags,
            @SoftInputShowHideReason int reason) {
        final var statsToken = createStatsToken(true /* show */, reason,
                ImeTracker.isFromUser(mRootView));
        ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestShowSelf", mDumper,
                null /* icProto */);
        mPrivOps.showMySoftInput(flags);
        mPrivOps.showMySoftInput(statsToken, flags, reason);
    }

    private boolean handleBack(boolean doIt) {
@@ -3472,7 +3557,7 @@ public class InputMethodService extends AbstractInputMethodService {
                // If we have the window visible for some other reason --
                // most likely to show candidates -- then just get rid
                // of it.  This really shouldn't happen, but just in case...
                if (doIt) hideWindow();
                if (doIt) hideWindowWithToken(SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_BACK_KEY);
            }
            return true;
        }
@@ -3627,10 +3712,11 @@ public class InputMethodService extends AbstractInputMethodService {
            @InputMethodManager.HideFlags int hideFlags) {
        if (DEBUG) Log.v(TAG, "toggleSoftInput()");
        if (isInputViewShown()) {
            requestHideSelf(
                    hideFlags, SoftInputShowHideReason.HIDE_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT);
            requestHideSelf(hideFlags,
                    SoftInputShowHideReason.HIDE_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT);
        } else {
            requestShowSelf(showFlags);
            requestShowSelf(showFlags,
                    SoftInputShowHideReason.SHOW_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT);
        }
    }
    
@@ -4271,6 +4357,20 @@ public class InputMethodService extends AbstractInputMethodService {
                | (isInputViewShown() ? IME_VISIBLE : 0);
    }

    /**
     * Creates an IME request tracking token.
     *
     * @param show whether this is a show or a hide request.
     * @param reason the reason why the IME request was created.
     * @param isFromUser whether this request was created directly from user interaction.
     */
    @NonNull
    private ImeTracker.Token createStatsToken(boolean show, @SoftInputShowHideReason int reason,
            boolean isFromUser) {
        return ImeTracker.forLogging().onStart(show ? ImeTracker.TYPE_SHOW : ImeTracker.TYPE_HIDE,
                ImeTracker.ORIGIN_IME, reason, isFromUser);
    }

    /**
     * Performs a dump of the InputMethodService's internal state.  Override
     * to add your own information to the dump.
+2 −2
Original line number Diff line number Diff line
@@ -73,7 +73,7 @@ oneway interface IWindow {
     *
     * @param types internal insets types (WindowInsets.Type.InsetsType) to show
     * @param fromIme true if this request originated from IME (InputMethodService).
     * @param statsToken the token tracking the current IME show request or {@code null} otherwise.
     * @param statsToken the token tracking the current IME request or {@code null} otherwise.
     */
    void showInsets(int types, boolean fromIme, in @nullable ImeTracker.Token statsToken);

@@ -82,7 +82,7 @@ oneway interface IWindow {
     *
     * @param types internal insets types (WindowInsets.Type.InsetsType) to hide
     * @param fromIme true if this request originated from IME (InputMethodService).
     * @param statsToken the token tracking the current IME hide request or {@code null} otherwise.
     * @param statsToken the token tracking the current IME request or {@code null} otherwise.
     */
    void hideInsets(int types, boolean fromIme, in @nullable ImeTracker.Token statsToken);

+17 −17
Original line number Diff line number Diff line
@@ -21,9 +21,9 @@ import static android.view.ImeInsetsSourceConsumerProto.HAS_PENDING_REQUEST;
import static android.view.ImeInsetsSourceConsumerProto.INSETS_SOURCE_CONSUMER;
import static android.view.ImeInsetsSourceConsumerProto.IS_REQUESTED_VISIBLE_AWAITING_CONTROL;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.IBinder;
import android.os.Process;
import android.os.Trace;
import android.util.proto.ProtoOutputStream;
import android.view.SurfaceControl.Transaction;
@@ -70,7 +70,11 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
        if (!isShowRequested()) {
            mIsRequestedVisibleAwaitingControl = false;
            if (!running && !mHasPendingRequest) {
                notifyHidden(null /* statsToken */);
                final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE,
                        ImeTracker.ORIGIN_CLIENT,
                        SoftInputShowHideReason.HIDE_SOFT_INPUT_ON_ANIMATION_STATE_CHANGED,
                        mController.getHost().isHandlingPointerEvent() /* fromUser */);
                notifyHidden(statsToken);
                removeSurface();
            }
        }
@@ -144,9 +148,17 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {

    void requestHide(boolean fromIme, @Nullable ImeTracker.Token statsToken) {
        if (!fromIme) {
            // Create a new token to track the hide request when we have control,
            // as we use the passed in token for the insets animation already.
            final var notifyStatsToken = getControl() != null
                    ? ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE,
                        ImeTracker.ORIGIN_CLIENT,
                        SoftInputShowHideReason.HIDE_SOFT_INPUT_REQUEST_HIDE_WITH_CONTROL,
                        mController.getHost().isHandlingPointerEvent() /* fromUser */)
                    : statsToken;
            // The insets might be controlled by a remote target. Let the server know we are
            // requested to hide.
            notifyHidden(statsToken);
            notifyHidden(notifyStatsToken);
        }
        if (mAnimationState == ANIMATION_STATE_SHOW) {
            mHasPendingRequest = true;
@@ -157,21 +169,9 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
     * Notify {@link com.android.server.inputmethod.InputMethodManagerService} that
     * IME insets are hidden.
     *
     * @param statsToken the token tracking the current IME hide request or {@code null} otherwise.
     * @param statsToken the token tracking the current IME request or {@code null} otherwise.
     */
    private void notifyHidden(@Nullable ImeTracker.Token statsToken) {
        // Create a new stats token to track the hide request when:
        //  - we do not already have one, or
        //  - we do already have one, but we have control and use the passed in token
        //      for the insets animation already.
        if (statsToken == null || getControl() != null) {
            statsToken = ImeTracker.forLogging().onRequestHide(null /* component */,
                    Process.myUid(),
                    ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT,
                    SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API,
                    mController.getHost().isHandlingPointerEvent() /* fromUser */);
        }

    private void notifyHidden(@NonNull ImeTracker.Token statsToken) {
        ImeTracker.forLogging().onProgress(statsToken,
                ImeTracker.PHASE_CLIENT_INSETS_CONSUMER_NOTIFY_HIDDEN);

Loading