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

Commit 42c11e7a authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Make IMM more robust for window focus stealing"

parents ffed5c82 2553e488
Loading
Loading
Loading
Loading
+31 −49
Original line number Original line Diff line number Diff line
@@ -281,10 +281,10 @@ public final class InputMethodManager {
    boolean mActive = false;
    boolean mActive = false;


    /**
    /**
     * Set whenever this client becomes inactive, to know we need to reset
     * {@code true} if next {@link #onPostWindowFocus(View, View, int, boolean, int)} needs to
     * state with the IME the next time we receive focus.
     * restart input.
     */
     */
    boolean mHasBeenInactive = true;
    boolean mRestartOnNextWindowFocus = true;


    /**
    /**
     * As reported by IME through InputConnection.
     * As reported by IME through InputConnection.
@@ -489,7 +489,7 @@ public final class InputMethodManager {
                            // Some other client has starting using the IME, so note
                            // Some other client has starting using the IME, so note
                            // that this happened and make sure our own editor's
                            // that this happened and make sure our own editor's
                            // state is reset.
                            // state is reset.
                            mHasBeenInactive = true;
                            mRestartOnNextWindowFocus = true;
                            try {
                            try {
                                // Note that finishComposingText() is allowed to run
                                // Note that finishComposingText() is allowed to run
                                // even when we are not active.
                                // even when we are not active.
@@ -500,7 +500,7 @@ public final class InputMethodManager {
                        // Check focus again in case that "onWindowFocus" is called before
                        // Check focus again in case that "onWindowFocus" is called before
                        // handling this message.
                        // handling this message.
                        if (mServedView != null && mServedView.hasWindowFocus()) {
                        if (mServedView != null && mServedView.hasWindowFocus()) {
                            if (checkFocusNoStartInput(mHasBeenInactive)) {
                            if (checkFocusNoStartInput(mRestartOnNextWindowFocus)) {
                                final int reason = active ?
                                final int reason = active ?
                                        InputMethodClient.START_INPUT_REASON_ACTIVATED_BY_IMMS :
                                        InputMethodClient.START_INPUT_REASON_ACTIVATED_BY_IMMS :
                                        InputMethodClient.START_INPUT_REASON_DEACTIVATED_BY_IMMS;
                                        InputMethodClient.START_INPUT_REASON_DEACTIVATED_BY_IMMS;
@@ -1336,7 +1336,14 @@ public final class InputMethodManager {
                        windowFlags, tba, servedContext, missingMethodFlags,
                        windowFlags, tba, servedContext, missingMethodFlags,
                        view.getContext().getApplicationInfo().targetSdkVersion);
                        view.getContext().getApplicationInfo().targetSdkVersion);
                if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
                if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
                if (res != null) {
                if (res == null) {
                    Log.wtf(TAG, "startInputOrWindowGainedFocus must not return"
                            + " null. startInputReason="
                            + InputMethodClient.getStartInputReason(startInputReason)
                            + " editorInfo=" + tba
                            + " controlFlags=#" + Integer.toHexString(controlFlags));
                    return false;
                }
                if (res.id != null) {
                if (res.id != null) {
                    setInputChannelLocked(res.channel);
                    setInputChannelLocked(res.channel);
                    mBindSequence = res.sequence;
                    mBindSequence = res.sequence;
@@ -1344,38 +1351,13 @@ public final class InputMethodManager {
                    mCurId = res.id;
                    mCurId = res.id;
                    mNextUserActionNotificationSequenceNumber =
                    mNextUserActionNotificationSequenceNumber =
                            res.userActionNotificationSequenceNumber;
                            res.userActionNotificationSequenceNumber;
                    } else {
                } else if (res.channel != null && res.channel != mCurChannel) {
                        if (res.channel != null && res.channel != mCurChannel) {
                    res.channel.dispose();
                    res.channel.dispose();
                }
                }
                        if (mCurMethod == null) {
                switch (res.result) {
                            // This means there is no input method available.
                    case InputBindResult.ResultCode.ERROR_NOT_IME_TARGET_WINDOW:
                            if (DEBUG) Log.v(TAG, "ABORT input: no input method!");
                        mRestartOnNextWindowFocus = true;
                            return true;
                        break;
                        }
                    }
                } else {
                    if (startInputReason
                            == InputMethodClient.START_INPUT_REASON_WINDOW_FOCUS_GAIN) {
                        // We are here probably because of an obsolete window-focus-in message sent
                        // to windowGainingFocus.  Since IMMS determines whether a Window can have
                        // IME focus or not by using the latest window focus state maintained in the
                        // WMS, this kind of race condition cannot be avoided.  One obvious example
                        // would be that we have already received a window-focus-out message but the
                        // UI thread is still handling previous window-focus-in message here.
                        // TODO: InputBindResult should have the error code.
                        if (DEBUG) Log.w(TAG, "startInputOrWindowGainedFocus failed. "
                                + "Window focus may have already been lost. "
                                + "win=" + windowGainingFocus + " view=" + dumpViewInfo(view));
                        if (!mActive) {
                            // mHasBeenInactive is a latch switch to forcefully refresh IME focus
                            // state when an inactive (mActive == false) client is gaining window
                            // focus. In case we have unnecessary disable the latch due to this
                            // spurious wakeup, we re-enable the latch here.
                            // TODO: Come up with more robust solution.
                            mHasBeenInactive = true;
                        }
                    }
                }
                }
                if (mCurMethod != null && mCompletions != null) {
                if (mCurMethod != null && mCompletions != null) {
                    try {
                    try {
@@ -1551,9 +1533,9 @@ public final class InputMethodManager {
                    + " softInputMode=" + InputMethodClient.softInputModeToString(softInputMode)
                    + " softInputMode=" + InputMethodClient.softInputModeToString(softInputMode)
                    + " first=" + first + " flags=#"
                    + " first=" + first + " flags=#"
                    + Integer.toHexString(windowFlags));
                    + Integer.toHexString(windowFlags));
            if (mHasBeenInactive) {
            if (mRestartOnNextWindowFocus) {
                if (DEBUG) Log.v(TAG, "Has been inactive!  Starting fresh");
                if (DEBUG) Log.v(TAG, "Restarting due to mRestartOnNextWindowFocus");
                mHasBeenInactive = false;
                mRestartOnNextWindowFocus = false;
                forceNewFocus = true;
                forceNewFocus = true;
            }
            }
            focusInLocked(focusedView != null ? focusedView : rootView);
            focusInLocked(focusedView != null ? focusedView : rootView);
@@ -2485,7 +2467,7 @@ public final class InputMethodManager {
        p.println("  mMainLooper=" + mMainLooper);
        p.println("  mMainLooper=" + mMainLooper);
        p.println("  mIInputContext=" + mIInputContext);
        p.println("  mIInputContext=" + mIInputContext);
        p.println("  mActive=" + mActive
        p.println("  mActive=" + mActive
                + " mHasBeenInactive=" + mHasBeenInactive
                + " mRestartOnNextWindowFocus=" + mRestartOnNextWindowFocus
                + " mBindSequence=" + mBindSequence
                + " mBindSequence=" + mBindSequence
                + " mCurId=" + mCurId);
                + " mCurId=" + mCurId);
        p.println("  mFullscreenMode=" + mFullscreenMode);
        p.println("  mFullscreenMode=" + mFullscreenMode);
+1 −0
Original line number Original line Diff line number Diff line
@@ -56,6 +56,7 @@ interface IInputMethodManager {
            in ResultReceiver resultReceiver);
            in ResultReceiver resultReceiver);
    // If windowToken is null, this just does startInput().  Otherwise this reports that a window
    // If windowToken is null, this just does startInput().  Otherwise this reports that a window
    // has gained focus, and if 'attribute' is non-null then also does startInput.
    // has gained focus, and if 'attribute' is non-null then also does startInput.
    // @NonNull
    InputBindResult startInputOrWindowGainedFocus(
    InputBindResult startInputOrWindowGainedFocus(
            /* @InputMethodClient.StartInputReason */ int startInputReason,
            /* @InputMethodClient.StartInputReason */ int startInputReason,
            in IInputMethodClient client, in IBinder windowToken, int controlFlags,
            in IInputMethodClient client, in IBinder windowToken, int controlFlags,
+196 −7
Original line number Original line Diff line number Diff line
@@ -16,16 +16,133 @@


package com.android.internal.view;
package com.android.internal.view;


import static java.lang.annotation.RetentionPolicy.SOURCE;

import android.annotation.IntDef;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Parcelable;
import android.os.UserHandle;
import android.view.InputChannel;
import android.view.InputChannel;


import java.lang.annotation.Retention;

/**
/**
 * Bundle of information returned by input method manager about a successful
 * Bundle of information returned by input method manager about a successful
 * binding to an input method.
 * binding to an input method.
 */
 */
public final class InputBindResult implements Parcelable {
public final class InputBindResult implements Parcelable {
    static final String TAG = "InputBindResult";

    @Retention(SOURCE)
    @IntDef({
            ResultCode.SUCCESS_WITH_IME_SESSION,
            ResultCode.SUCCESS_WAITING_IME_SESSION,
            ResultCode.SUCCESS_WAITING_IME_BINDING,
            ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY,
            ResultCode.ERROR_NULL,
            ResultCode.ERROR_NO_IME,
            ResultCode.ERROR_INVALID_PACKAGE_NAME,
            ResultCode.ERROR_SYSTEM_NOT_READY,
            ResultCode.ERROR_IME_NOT_CONNECTED,
            ResultCode.ERROR_INVALID_USER,
            ResultCode.ERROR_NULL_EDITOR_INFO,
            ResultCode.ERROR_NOT_IME_TARGET_WINDOW,
    })
    public @interface ResultCode {
        /**
         * Indicates that everything in this result object including {@link #method} is valid.
         */
        int SUCCESS_WITH_IME_SESSION = 0;
        /**
         * Indicates that this is a temporary binding until the
         * {@link android.inputmethodservice.InputMethodService} (IMS) establishes a valid session
         * to {@link com.android.server.InputMethodManagerService} (IMMS).
         *
         * <p>Note that in this state the IMS is already bound to IMMS but the logical session
         * is not yet established on top of the IPC channel.</p>
         *
         * <p>Some of fields such as {@link #channel} is not yet available.</p>
         *
         * @see android.inputmethodservice.InputMethodService##onCreateInputMethodSessionInterface()
         **/
        int SUCCESS_WAITING_IME_SESSION = 1;
        /**
         * Indicates that this is a temporary binding until the
         * {@link android.inputmethodservice.InputMethodService} (IMS) establishes a valid session
         * to {@link com.android.server.InputMethodManagerService} (IMMS).
         *
         * <p>Note that in this state the IMMS has already initiated a connection to the IMS but
         * the binding process is not completed yet.</p>
         *
         * <p>Some of fields such as {@link #channel} is not yet available.</p>
         * @see android.content.ServiceConnection#onServiceConnected(ComponentName, IBinder)
         */
        int SUCCESS_WAITING_IME_BINDING = 2;
        /**
         * Indicates that this is not intended for starting input but just for reporting window
         * focus change from the application process.
         *
         * <p>All other fields do not have meaningful value.</p>
         */
        int SUCCESS_REPORT_WINDOW_FOCUS_ONLY = 3;
        /**
         * Indicates somehow
         * {@link com.android.server.InputMethodManagerService#startInputOrWindowGainedFocus} is
         * trying to return null {@link InputBindResult}, which must never happen.
         */
        int ERROR_NULL = 4;
        /**
         * Indicates that {@link com.android.server.InputMethodManagerService} recognizes no IME.
         */
        int ERROR_NO_IME = 5;
        /**
         * Indicates that {@link android.view.inputmethod.EditorInfo#packageName} does not match
         * the caller UID.
         *
         * @see android.view.inputmethod.EditorInfo#packageName
         */
        int ERROR_INVALID_PACKAGE_NAME = 6;
        /**
         * Indicates that the system is still in an early stage of the boot process and any 3rd
         * party application is not allowed to run.
         *
         * @see com.android.server.SystemService#PHASE_THIRD_PARTY_APPS_CAN_START
         */
        int ERROR_SYSTEM_NOT_READY = 7;
        /**
         * Indicates that {@link com.android.server.InputMethodManagerService} tried to connect to
         * an {@link android.inputmethodservice.InputMethodService} but failed.
         *
         * @see android.content.Context#bindServiceAsUser(Intent, ServiceConnection, int, UserHandle)
         */
        int ERROR_IME_NOT_CONNECTED = 8;
        /**
         * Indicates that the caller is not the foreground user (or does not have
         * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission).
         */
        int ERROR_INVALID_USER = 9;
        /**
         * Indicates that the caller should have specified non-null
         * {@link android.view.inputmethod.EditorInfo}.
         */
        int ERROR_NULL_EDITOR_INFO = 10;
        /**
         * Indicates that the target window the client specified cannot be the IME target right now.
         *
         * <p>Due to the asynchronous nature of Android OS, we cannot completely avoid this error.
         * The client should try to restart input when its {@link android.view.Window} is focused
         * again.</p>
         *
         * @see com.android.server.wm.WindowManagerService#inputMethodClientHasFocus(IInputMethodClient)
         */
        int ERROR_NOT_IME_TARGET_WINDOW = 11;
    }

    @ResultCode
    public final int result;


    /**
    /**
     * The input method service.
     * The input method service.
@@ -53,8 +170,10 @@ public final class InputBindResult implements Parcelable {
     */
     */
    public final int userActionNotificationSequenceNumber;
    public final int userActionNotificationSequenceNumber;


    public InputBindResult(IInputMethodSession _method, InputChannel _channel,
    public InputBindResult(@ResultCode int _result,
            IInputMethodSession _method, InputChannel _channel,
            String _id, int _sequence, int _userActionNotificationSequenceNumber) {
            String _id, int _sequence, int _userActionNotificationSequenceNumber) {
        result = _result;
        method = _method;
        method = _method;
        channel = _channel;
        channel = _channel;
        id = _id;
        id = _id;
@@ -63,6 +182,7 @@ public final class InputBindResult implements Parcelable {
    }
    }


    InputBindResult(Parcel source) {
    InputBindResult(Parcel source) {
        result = source.readInt();
        method = IInputMethodSession.Stub.asInterface(source.readStrongBinder());
        method = IInputMethodSession.Stub.asInterface(source.readStrongBinder());
        if (source.readInt() != 0) {
        if (source.readInt() != 0) {
            channel = InputChannel.CREATOR.createFromParcel(source);
            channel = InputChannel.CREATOR.createFromParcel(source);
@@ -76,9 +196,9 @@ public final class InputBindResult implements Parcelable {


    @Override
    @Override
    public String toString() {
    public String toString() {
        return "InputBindResult{" + method + " " + id
        return "InputBindResult{result=" + getResultString() + " method="+ method + " id=" + id
                + " sequence:" + sequence
                + " sequence=" + sequence
                + " userActionNotificationSequenceNumber:" + userActionNotificationSequenceNumber
                + " userActionNotificationSequenceNumber=" + userActionNotificationSequenceNumber
                + "}";
                + "}";
    }
    }


@@ -90,6 +210,7 @@ public final class InputBindResult implements Parcelable {
     */
     */
    @Override
    @Override
    public void writeToParcel(Parcel dest, int flags) {
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(result);
        dest.writeStrongInterface(method);
        dest.writeStrongInterface(method);
        if (channel != null) {
        if (channel != null) {
            dest.writeInt(1);
            dest.writeInt(1);
@@ -122,4 +243,72 @@ public final class InputBindResult implements Parcelable {
    public int describeContents() {
    public int describeContents() {
        return channel != null ? channel.describeContents() : 0;
        return channel != null ? channel.describeContents() : 0;
    }
    }

    public String getResultString() {
        switch (result) {
            case ResultCode.SUCCESS_WITH_IME_SESSION:
                return "SUCCESS_WITH_IME_SESSION";
            case ResultCode.SUCCESS_WAITING_IME_SESSION:
                return "SUCCESS_WAITING_IME_SESSION";
            case ResultCode.SUCCESS_WAITING_IME_BINDING:
                return "SUCCESS_WAITING_IME_BINDING";
            case ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY:
                return "SUCCESS_REPORT_WINDOW_FOCUS_ONLY";
            case ResultCode.ERROR_NULL:
                return "ERROR_NULL";
            case ResultCode.ERROR_NO_IME:
                return "ERROR_NO_IME";
            case ResultCode.ERROR_INVALID_PACKAGE_NAME:
                return "ERROR_INVALID_PACKAGE_NAME";
            case ResultCode.ERROR_SYSTEM_NOT_READY:
                return "ERROR_SYSTEM_NOT_READY";
            case ResultCode.ERROR_IME_NOT_CONNECTED:
                return "ERROR_IME_NOT_CONNECTED";
            case ResultCode.ERROR_INVALID_USER:
                return "ERROR_INVALID_USER";
            case ResultCode.ERROR_NULL_EDITOR_INFO:
                return "ERROR_NULL_EDITOR_INFO";
            case ResultCode.ERROR_NOT_IME_TARGET_WINDOW:
                return "ERROR_NOT_IME_TARGET_WINDOW";
            default:
                return "Unknown(" + result + ")";
        }
    }

    private static InputBindResult error(@ResultCode int result) {
        return new InputBindResult(result, null, null, null, -1, -1);
    }

    /**
     * Predefined error object for {@link ResultCode#ERROR_NULL}.
     */
    public static final InputBindResult NULL = error(ResultCode.ERROR_NULL);
    /**
     * Predefined error object for {@link ResultCode#NO_IME}.
     */
    public static final InputBindResult NO_IME = error(ResultCode.ERROR_NO_IME);
    /**
     * Predefined error object for {@link ResultCode#ERROR_INVALID_PACKAGE_NAME}.
     */
    public static final InputBindResult INVALID_PACKAGE_NAME =
            error(ResultCode.ERROR_INVALID_PACKAGE_NAME);
    /**
     * Predefined error object for {@link ResultCode#ERROR_NULL_EDITOR_INFO}.
     */
    public static final InputBindResult NULL_EDITOR_INFO = error(ResultCode.ERROR_NULL_EDITOR_INFO);
    /**
     * Predefined error object for {@link ResultCode#ERROR_NOT_IME_TARGET_WINDOW}.
     */
    public static final InputBindResult NOT_IME_TARGET_WINDOW =
            error(ResultCode.ERROR_NOT_IME_TARGET_WINDOW);
    /**
     * Predefined error object for {@link ResultCode#ERROR_IME_NOT_CONNECTED}.
     */
    public static final InputBindResult IME_NOT_CONNECTED =
            error(ResultCode.ERROR_IME_NOT_CONNECTED);
    /**
     * Predefined error object for {@link ResultCode#ERROR_INVALID_USER}.
     */
    public static final InputBindResult INVALID_USER = error(ResultCode.ERROR_INVALID_USER);

}
}
+47 −26

File changed.

Preview size limit exceeded, changes collapsed.