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

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

Merge "Deprecate MissingMethodFlags to preserve invocation order"

parents 0ad1e6c8 e8424ef6
Loading
Loading
Loading
Loading
+3 −10
Original line number Diff line number Diff line
@@ -32,7 +32,6 @@ import android.view.InputChannel;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputConnectionInspector;
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodSession;
import android.view.inputmethod.InputMethodSubtype;
@@ -190,10 +189,8 @@ class IInputMethodWrapper extends IInputMethod.Stub
                final EditorInfo info = (EditorInfo) args.arg3;
                final CancellationGroup cancellationGroup = (CancellationGroup) args.arg4;
                final boolean restarting = args.argi5 == 1;
                final int missingMethod = args.argi6;
                final InputConnection ic = inputContext != null
                        ? new RemoteInputConnection(
                                mTarget, inputContext, missingMethod, cancellationGroup)
                        ? new RemoteInputConnection(mTarget, inputContext, cancellationGroup)
                        : null;
                info.makeCompatible(mTargetSdkVersion);
                inputMethod.dispatchStartInputWithToken(ic, info, restarting, startInputToken);
@@ -295,11 +292,8 @@ class IInputMethodWrapper extends IInputMethod.Stub
            Log.e(TAG, "bindInput must be paired with unbindInput.");
        }
        mCancellationGroup = new CancellationGroup();
        // This IInputContext is guaranteed to implement all the methods.
        final int missingMethodFlags = 0;
        InputConnection ic = new RemoteInputConnection(mTarget,
                IInputContext.Stub.asInterface(binding.getConnectionToken()), missingMethodFlags,
                mCancellationGroup);
                IInputContext.Stub.asInterface(binding.getConnectionToken()), mCancellationGroup);
        InputBinding nu = new InputBinding(ic, binding);
        mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_INPUT_CONTEXT, nu));
    }
@@ -320,14 +314,13 @@ class IInputMethodWrapper extends IInputMethod.Stub
    @BinderThread
    @Override
    public void startInput(IBinder startInputToken, IInputContext inputContext,
            @InputConnectionInspector.MissingMethodFlags final int missingMethods,
            EditorInfo attribute, boolean restarting) {
        if (mCancellationGroup == null) {
            Log.e(TAG, "startInput must be called after bindInput.");
            mCancellationGroup = new CancellationGroup();
        }
        mCaller.executeOrSendMessage(mCaller.obtainMessageOOOOII(DO_START_INPUT, startInputToken,
                inputContext, attribute, mCancellationGroup, restarting ? 1 : 0, missingMethods));
                inputContext, attribute, mCancellationGroup, restarting ? 1 : 0, 0 /* unused */));
    }

    @BinderThread
+2 −46
Original line number Diff line number Diff line
@@ -29,8 +29,6 @@ import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputConnectionInspector;
import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.SurroundingText;

@@ -87,9 +85,6 @@ final class RemoteInputConnection implements InputConnection {
    @NonNull
    private final InputMethodServiceInternalHolder mImsInternal;

    @MissingMethodFlags
    private final int mMissingMethods;

    /**
     * Signaled when the system decided to take away IME focus from the target app.
     *
@@ -101,11 +96,9 @@ final class RemoteInputConnection implements InputConnection {

    RemoteInputConnection(
            @NonNull WeakReference<InputMethodServiceInternal> inputMethodService,
            IInputContext inputContext, @MissingMethodFlags int missingMethods,
            @NonNull CancellationGroup cancellationGroup) {
            IInputContext inputContext, @NonNull CancellationGroup cancellationGroup) {
        mImsInternal = new InputMethodServiceInternalHolder(inputMethodService);
        mInvoker = IInputContextInvoker.create(inputContext);
        mMissingMethods = missingMethods;
        mCancellationGroup = cancellationGroup;
    }

@@ -163,10 +156,6 @@ final class RemoteInputConnection implements InputConnection {
            return null;
        }

        if (isMethodMissing(MissingMethodFlags.GET_SELECTED_TEXT)) {
            // This method is not implemented.
            return null;
        }
        final CompletableFuture<CharSequence> value = mInvoker.getSelectedText(flags);
        final CharSequence result = CompletableFutureUtil.getResultOrNull(
                value, TAG, "getSelectedText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
@@ -200,10 +189,6 @@ final class RemoteInputConnection implements InputConnection {
            return null;
        }

        if (isMethodMissing(MissingMethodFlags.GET_SURROUNDING_TEXT)) {
            // This method is not implemented.
            return null;
        }
        final CompletableFuture<SurroundingText> value = mInvoker.getSurroundingText(beforeLength,
                afterLength, flags);
        final SurroundingText result = CompletableFutureUtil.getResultOrNull(
@@ -284,10 +269,6 @@ final class RemoteInputConnection implements InputConnection {

    @AnyThread
    public boolean commitCorrection(CorrectionInfo correctionInfo) {
        if (isMethodMissing(MissingMethodFlags.COMMIT_CORRECTION)) {
            // This method is not implemented.
            return false;
        }
        return mInvoker.commitCorrection(correctionInfo);
    }

@@ -308,10 +289,6 @@ final class RemoteInputConnection implements InputConnection {

    @AnyThread
    public boolean setComposingRegion(int start, int end) {
        if (isMethodMissing(MissingMethodFlags.SET_COMPOSING_REGION)) {
            // This method is not implemented.
            return false;
        }
        return mInvoker.setComposingRegion(start, end);
    }

@@ -360,10 +337,6 @@ final class RemoteInputConnection implements InputConnection {

    @AnyThread
    public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
        if (isMethodMissing(MissingMethodFlags.DELETE_SURROUNDING_TEXT_IN_CODE_POINTS)) {
            // This method is not implemented.
            return false;
        }
        return mInvoker.deleteSurroundingTextInCodePoints(beforeLength, afterLength);
    }

@@ -389,11 +362,6 @@ final class RemoteInputConnection implements InputConnection {
            return false;
        }

        if (isMethodMissing(MissingMethodFlags.REQUEST_CURSOR_UPDATES)) {
            // This method is not implemented.
            return false;
        }

        final InputMethodServiceInternal ims = mImsInternal.getAndWarnIfNull();
        if (ims == null) {
            return false;
@@ -423,11 +391,6 @@ final class RemoteInputConnection implements InputConnection {
            return false;
        }

        if (isMethodMissing(MissingMethodFlags.COMMIT_CONTENT)) {
            // This method is not implemented.
            return false;
        }

        if ((flags & InputConnection.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) {
            final InputMethodServiceInternal imsInternal = mImsInternal.getAndWarnIfNull();
            if (imsInternal == null) {
@@ -450,17 +413,10 @@ final class RemoteInputConnection implements InputConnection {
        return mInvoker.setImeConsumesInput(imeConsumesInput);
    }

    @AnyThread
    private boolean isMethodMissing(@MissingMethodFlags final int methodFlag) {
        return (mMissingMethods & methodFlag) == methodFlag;
    }

    @AnyThread
    @Override
    public String toString() {
        return "RemoteInputConnection{idHash=#"
                + Integer.toHexString(System.identityHashCode(this))
                + " mMissingMethods="
                + InputConnectionInspector.getMissingMethodFlagsAsString(mMissingMethods) + "}";
                + Integer.toHexString(System.identityHashCode(this)) + "}";
    }
}
+17 −16
Original line number Diff line number Diff line
@@ -274,10 +274,7 @@ public interface InputConnection {
     *
     * @param flags Supplies additional options controlling how the text is
     * returned. May be either {@code 0} or {@link #GET_TEXT_WITH_STYLES}.
     * @return the text that is currently selected, if any, or null if
     * no text is selected. In {@link android.os.Build.VERSION_CODES#N} and
     * later, returns false when the target application does not implement
     * this method.
     * @return the text that is currently selected, if any, or {@code null} if no text is selected.
     */
    CharSequence getSelectedText(int flags);

@@ -483,7 +480,8 @@ public interface InputConnection {
     *        If this is greater than the number of existing characters between the cursor and
     *        the end of the text, then this method does not fail but deletes all the characters in
     *        that range.
     * @return true on success, false if the input connection is no longer valid.  Returns
     * @return {@code true} on success, {@code false} if the input connection is no longer valid.
     *         Before Android {@link android.os.Build.VERSION_CODES#TIRAMISU}, this API returned
     *         {@code false} when the target application does not implement this method.
     */
    boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength);
@@ -573,9 +571,10 @@ public interface InputConnection {
     *
     * @param start the position in the text at which the composing region begins
     * @param end the position in the text at which the composing region ends
     * @return true on success, false if the input connection is no longer
     * valid. In {@link android.os.Build.VERSION_CODES#N} and later, false is returned when the
     * target application does not implement this method.
     * @return {@code true} on success, {@code false} if the input connection is no longer valid.
     *         Since Android {@link android.os.Build.VERSION_CODES#N} until
     *         {@link android.os.Build.VERSION_CODES#TIRAMISU}, this API returned {@code false} when
     *         the target application does not implement this method.
     */
    boolean setComposingRegion(int start, int end);

@@ -686,9 +685,10 @@ public interface InputConnection {
     * in progress.</p>
     *
     * @param correctionInfo Detailed information about the correction.
     * @return true on success, false if the input connection is no longer valid.
     * In {@link android.os.Build.VERSION_CODES#N} and later, returns false
     * when the target application does not implement this method.
     * @return {@code true} on success, {@code false} if the input connection is no longer valid.
     *         Since Android {@link android.os.Build.VERSION_CODES#N} until
     *         {@link android.os.Build.VERSION_CODES#TIRAMISU}, this API returned {@code false} when
     *         the target application does not implement this method.
     */
    boolean commitCorrection(CorrectionInfo correctionInfo);

@@ -924,10 +924,11 @@ public interface InputConnection {
     * {@link #CURSOR_UPDATE_MONITOR}. Pass {@code 0} to disable the effect of
     * {@link #CURSOR_UPDATE_MONITOR}.
     * @return {@code true} if the request is scheduled. {@code false} to indicate that when the
     * application will not call
     * {@link InputMethodManager#updateCursorAnchorInfo(android.view.View, CursorAnchorInfo)}.
     * In {@link android.os.Build.VERSION_CODES#N} and later, returns {@code false} also when the
     * target application does not implement this method.
     *         application will not call {@link InputMethodManager#updateCursorAnchorInfo(
     *         android.view.View, CursorAnchorInfo)}.
     *         Since Android {@link android.os.Build.VERSION_CODES#N} until
     *         {@link android.os.Build.VERSION_CODES#TIRAMISU}, this API returned {@code false} when
     *         the target application does not implement this method.
     */
    boolean requestCursorUpdates(int cursorUpdateMode);

+0 −293
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.view.inputmethod;

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

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;

import java.lang.annotation.Retention;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;

/**
 * @hide
 */
public final class InputConnectionInspector {

    @Retention(SOURCE)
    @IntDef({MissingMethodFlags.GET_SELECTED_TEXT,
            MissingMethodFlags.SET_COMPOSING_REGION,
            MissingMethodFlags.COMMIT_CORRECTION,
            MissingMethodFlags.REQUEST_CURSOR_UPDATES,
            MissingMethodFlags.DELETE_SURROUNDING_TEXT_IN_CODE_POINTS,
            MissingMethodFlags.GET_HANDLER,
            MissingMethodFlags.CLOSE_CONNECTION,
            MissingMethodFlags.COMMIT_CONTENT,
            MissingMethodFlags.GET_SURROUNDING_TEXT
    })
    public @interface MissingMethodFlags {
        /**
         * {@link InputConnection#getSelectedText(int)} is available in
         * {@link android.os.Build.VERSION_CODES#GINGERBREAD} and later.
         */
        int GET_SELECTED_TEXT = 1 << 0;
        /**
         * {@link InputConnection#setComposingRegion(int, int)} is available in
         * {@link android.os.Build.VERSION_CODES#GINGERBREAD} and later.
         */
        int SET_COMPOSING_REGION = 1 << 1;
        /**
         * {@link InputConnection#commitCorrection(CorrectionInfo)} is available in
         * {@link android.os.Build.VERSION_CODES#HONEYCOMB} and later.
         */
        int COMMIT_CORRECTION = 1 << 2;
        /**
         * {@link InputConnection#requestCursorUpdates(int)} is available in
         * {@link android.os.Build.VERSION_CODES#LOLLIPOP} and later.
         */
        int REQUEST_CURSOR_UPDATES = 1 << 3;
        /**
         * {@link InputConnection#deleteSurroundingTextInCodePoints(int, int)}} is available in
         * {@link android.os.Build.VERSION_CODES#N} and later.
         */
        int DELETE_SURROUNDING_TEXT_IN_CODE_POINTS = 1 << 4;
        /**
         * {@link InputConnection#deleteSurroundingTextInCodePoints(int, int)}} is available in
         * {@link android.os.Build.VERSION_CODES#N} and later.
         */
        int GET_HANDLER = 1 << 5;
        /**
         * {@link InputConnection#closeConnection()}} is available in
         * {@link android.os.Build.VERSION_CODES#N} and later.
         */
        int CLOSE_CONNECTION = 1 << 6;
        /**
         * {@link InputConnection#commitContent(InputContentInfo, int, Bundle)} is available in
         * {@link android.os.Build.VERSION_CODES#N} MR-1 and later.
         */
        int COMMIT_CONTENT = 1 << 7;
        /**
         * {@link InputConnection#getSurroundingText(int, int, int)} is available in
         * {@link android.os.Build.VERSION_CODES#S} and later.
         */
        int GET_SURROUNDING_TEXT = 1 << 8;
    }

    private static final Map<Class, Integer> sMissingMethodsMap = Collections.synchronizedMap(
            new WeakHashMap<>());

    @MissingMethodFlags
    public static int getMissingMethodFlags(@Nullable final InputConnection ic) {
        if (ic == null) {
            return 0;
        }
        // Optimization for a known class.
        if (ic instanceof BaseInputConnection) {
            return 0;
        }
        // Optimization for a known class.
        if (ic instanceof InputConnectionWrapper) {
            return ((InputConnectionWrapper) ic).getMissingMethodFlags();
        }
        return getMissingMethodFlagsInternal(ic.getClass());
    }

    @MissingMethodFlags
    public static int getMissingMethodFlagsInternal(@NonNull final Class clazz) {
        final Integer cachedFlags = sMissingMethodsMap.get(clazz);
        if (cachedFlags != null) {
            return cachedFlags;
        }
        int flags = 0;
        if (!hasGetSelectedText(clazz)) {
            flags |= MissingMethodFlags.GET_SELECTED_TEXT;
        }
        if (!hasSetComposingRegion(clazz)) {
            flags |= MissingMethodFlags.SET_COMPOSING_REGION;
        }
        if (!hasCommitCorrection(clazz)) {
            flags |= MissingMethodFlags.COMMIT_CORRECTION;
        }
        if (!hasRequestCursorUpdate(clazz)) {
            flags |= MissingMethodFlags.REQUEST_CURSOR_UPDATES;
        }
        if (!hasDeleteSurroundingTextInCodePoints(clazz)) {
            flags |= MissingMethodFlags.DELETE_SURROUNDING_TEXT_IN_CODE_POINTS;
        }
        if (!hasGetHandler(clazz)) {
            flags |= MissingMethodFlags.GET_HANDLER;
        }
        if (!hasCloseConnection(clazz)) {
            flags |= MissingMethodFlags.CLOSE_CONNECTION;
        }
        if (!hasCommitContent(clazz)) {
            flags |= MissingMethodFlags.COMMIT_CONTENT;
        }
        if (!hasGetSurroundingText(clazz)) {
            flags |= MissingMethodFlags.GET_SURROUNDING_TEXT;
        }
        sMissingMethodsMap.put(clazz, flags);
        return flags;
    }

    private static boolean hasGetSelectedText(@NonNull final Class clazz) {
        try {
            final Method method = clazz.getMethod("getSelectedText", int.class);
            return !Modifier.isAbstract(method.getModifiers());
        } catch (NoSuchMethodException e) {
            return false;
        }
    }

    private static boolean hasSetComposingRegion(@NonNull final Class clazz) {
        try {
            final Method method = clazz.getMethod("setComposingRegion", int.class, int.class);
            return !Modifier.isAbstract(method.getModifiers());
        } catch (NoSuchMethodException e) {
            return false;
        }
    }

    private static boolean hasCommitCorrection(@NonNull final Class clazz) {
        try {
            final Method method = clazz.getMethod("commitCorrection", CorrectionInfo.class);
            return !Modifier.isAbstract(method.getModifiers());
        } catch (NoSuchMethodException e) {
            return false;
        }
    }

    private static boolean hasRequestCursorUpdate(@NonNull final Class clazz) {
        try {
            final Method method = clazz.getMethod("requestCursorUpdates", int.class);
            return !Modifier.isAbstract(method.getModifiers());
        } catch (NoSuchMethodException e) {
            return false;
        }
    }

    private static boolean hasDeleteSurroundingTextInCodePoints(@NonNull final Class clazz) {
        try {
            final Method method = clazz.getMethod("deleteSurroundingTextInCodePoints", int.class,
                    int.class);
            return !Modifier.isAbstract(method.getModifiers());
        } catch (NoSuchMethodException e) {
            return false;
        }
    }

    private static boolean hasGetHandler(@NonNull final Class clazz) {
        try {
            final Method method = clazz.getMethod("getHandler");
            return !Modifier.isAbstract(method.getModifiers());
        } catch (NoSuchMethodException e) {
            return false;
        }
    }

    private static boolean hasCloseConnection(@NonNull final Class clazz) {
        try {
            final Method method = clazz.getMethod("closeConnection");
            return !Modifier.isAbstract(method.getModifiers());
        } catch (NoSuchMethodException e) {
            return false;
        }
    }

    private static boolean hasCommitContent(@NonNull final Class clazz) {
        try {
            final Method method = clazz.getMethod("commitContent", InputContentInfo.class,
                    int.class, Bundle.class);
            return !Modifier.isAbstract(method.getModifiers());
        } catch (NoSuchMethodException e) {
            return false;
        }
    }

    private static boolean hasGetSurroundingText(@NonNull final Class clazz) {
        try {
            final Method method = clazz.getMethod("getSurroundingText", int.class, int.class,
                    int.class);
            return !Modifier.isAbstract(method.getModifiers());
        } catch (NoSuchMethodException e) {
            return false;
        }
    }

    public static String getMissingMethodFlagsAsString(@MissingMethodFlags final int flags) {
        final StringBuilder sb = new StringBuilder();
        boolean isEmpty = true;
        if ((flags & MissingMethodFlags.GET_SELECTED_TEXT) != 0) {
            sb.append("getSelectedText(int)");
            isEmpty = false;
        }
        if ((flags & MissingMethodFlags.SET_COMPOSING_REGION) != 0) {
            if (!isEmpty) {
                sb.append(",");
            }
            sb.append("setComposingRegion(int, int)");
            isEmpty = false;
        }
        if ((flags & MissingMethodFlags.COMMIT_CORRECTION) != 0) {
            if (!isEmpty) {
                sb.append(",");
            }
            sb.append("commitCorrection(CorrectionInfo)");
            isEmpty = false;
        }
        if ((flags & MissingMethodFlags.REQUEST_CURSOR_UPDATES) != 0) {
            if (!isEmpty) {
                sb.append(",");
            }
            sb.append("requestCursorUpdate(int)");
            isEmpty = false;
        }
        if ((flags & MissingMethodFlags.DELETE_SURROUNDING_TEXT_IN_CODE_POINTS) != 0) {
            if (!isEmpty) {
                sb.append(",");
            }
            sb.append("deleteSurroundingTextInCodePoints(int, int)");
            isEmpty = false;
        }
        if ((flags & MissingMethodFlags.GET_HANDLER) != 0) {
            if (!isEmpty) {
                sb.append(",");
            }
            sb.append("getHandler()");
        }
        if ((flags & MissingMethodFlags.CLOSE_CONNECTION) != 0) {
            if (!isEmpty) {
                sb.append(",");
            }
            sb.append("closeConnection()");
        }
        if ((flags & MissingMethodFlags.COMMIT_CONTENT) != 0) {
            if (!isEmpty) {
                sb.append(",");
            }
            sb.append("commitContent(InputContentInfo, Bundle)");
        }
        return sb.toString();
    }
}
+0 −12
Original line number Diff line number Diff line
@@ -30,8 +30,6 @@ import com.android.internal.util.Preconditions;
public class InputConnectionWrapper implements InputConnection {
    private InputConnection mTarget;
    final boolean mMutable;
    @InputConnectionInspector.MissingMethodFlags
    private int mMissingMethodFlags;

    /**
     * Initializes a wrapper.
@@ -46,7 +44,6 @@ public class InputConnectionWrapper implements InputConnection {
    public InputConnectionWrapper(InputConnection target, boolean mutable) {
        mMutable = mutable;
        mTarget = target;
        mMissingMethodFlags = InputConnectionInspector.getMissingMethodFlags(target);
    }

    /**
@@ -63,15 +60,6 @@ public class InputConnectionWrapper implements InputConnection {
            throw new SecurityException("not mutable");
        }
        mTarget = target;
        mMissingMethodFlags = InputConnectionInspector.getMissingMethodFlags(target);
    }

    /**
     * @hide
     */
    @InputConnectionInspector.MissingMethodFlags
    public int getMissingMethodFlags() {
        return mMissingMethodFlags;
    }

    /**
Loading