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

Commit 1e72ef28 authored by Lan Wei's avatar Lan Wei
Browse files

Avoid bad InputConnection call crashing the app

Update InputConnection#getTextBeforeCursor(int, int) and
InputConnection#getTextAfterCursor(int, int) API to
specify that the paramter {@code n} must be non-negative.
If the IME using the API incorrectly, throw
IllegalArgumentException.

Also update these two APIs that return nullable result (no
behaivor change). For editor App, return null for bad case.

Test: atest BaseInputConnectionTest#testInvalidGetTextBeforeOrAfterCursorRequest
Test: atest InputConnectionWrapperTest#testInvalidGetTextBeforeOrAfterCursorRequest

BUG: 169114026

Change-Id: I95169735198f8363c981a61e20234dfebfd645b1
parent 64cd208b
Loading
Loading
Loading
Loading
+6 −6
Original line number Diff line number Diff line
@@ -57227,8 +57227,8 @@ package android.view.inputmethod {
    method public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int);
    method public android.os.Handler getHandler();
    method public CharSequence getSelectedText(int);
    method public CharSequence getTextAfterCursor(int, int);
    method public CharSequence getTextBeforeCursor(int, int);
    method @Nullable public CharSequence getTextAfterCursor(@IntRange(from=0) int, int);
    method @Nullable public CharSequence getTextBeforeCursor(@IntRange(from=0) int, int);
    method public boolean performContextMenuAction(int);
    method public boolean performEditorAction(int);
    method public boolean performPrivateCommand(String, android.os.Bundle);
@@ -57454,8 +57454,8 @@ package android.view.inputmethod {
    method public android.os.Handler getHandler();
    method public CharSequence getSelectedText(int);
    method @Nullable public default android.view.inputmethod.SurroundingText getSurroundingText(@IntRange(from=0) int, @IntRange(from=0) int, int);
    method public CharSequence getTextAfterCursor(int, int);
    method public CharSequence getTextBeforeCursor(int, int);
    method @Nullable public CharSequence getTextAfterCursor(@IntRange(from=0) int, int);
    method @Nullable public CharSequence getTextBeforeCursor(@IntRange(from=0) int, int);
    method public boolean performContextMenuAction(int);
    method public boolean performEditorAction(int);
    method public boolean performPrivateCommand(String, android.os.Bundle);
@@ -57489,8 +57489,8 @@ package android.view.inputmethod {
    method public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int);
    method public android.os.Handler getHandler();
    method public CharSequence getSelectedText(int);
    method public CharSequence getTextAfterCursor(int, int);
    method public CharSequence getTextBeforeCursor(int, int);
    method @Nullable public CharSequence getTextAfterCursor(@IntRange(from=0) int, int);
    method @Nullable public CharSequence getTextBeforeCursor(@IntRange(from=0) int, int);
    method public boolean performContextMenuAction(int);
    method public boolean performEditorAction(int);
    method public boolean performPrivateCommand(String, android.os.Bundle);
+10 −2
Original line number Diff line number Diff line
@@ -502,7 +502,10 @@ public class BaseInputConnection implements InputConnection {
     * The default implementation returns the given amount of text from the
     * current cursor position in the buffer.
     */
    public CharSequence getTextBeforeCursor(int length, int flags) {
    @Nullable
    public CharSequence getTextBeforeCursor(@IntRange(from = 0) int length, int flags) {
        if (length < 0) return null;

        final Editable content = getEditable();
        if (content == null) return null;

@@ -558,7 +561,10 @@ public class BaseInputConnection implements InputConnection {
     * The default implementation returns the given amount of text from the
     * current cursor position in the buffer.
     */
    public CharSequence getTextAfterCursor(int length, int flags) {
    @Nullable
    public CharSequence getTextAfterCursor(@IntRange(from = 0) int length, int flags) {
        if (length < 0) return null;

        final Editable content = getEditable();
        if (content == null) return null;

@@ -594,6 +600,8 @@ public class BaseInputConnection implements InputConnection {
    @Nullable
    public SurroundingText getSurroundingText(
            @IntRange(from = 0) int beforeLength, @IntRange(from = 0)  int afterLength, int flags) {
        if (beforeLength < 0 || afterLength < 0) return null;

        final Editable content = getEditable();
        if (content == null) return null;

+14 −4
Original line number Diff line number Diff line
@@ -27,6 +27,8 @@ import android.text.TextUtils;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;

import com.android.internal.util.Preconditions;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@@ -186,13 +188,15 @@ public interface InputConnection {
     * the current line, and specifically do not return 0 characters unless
     * the cursor is really at the start of the text.</p>
     *
     * @param n The expected length of the text.
     * @param n The expected length of the text. This must be non-negative.
     * @param flags Supplies additional options controlling how the text is
     * returned. May be either 0 or {@link #GET_TEXT_WITH_STYLES}.
     * @return the text before the cursor position; the length of the
     * returned text might be less than <var>n</var>.
     * @throws IllegalArgumentException if {@code n} is negative.
     */
    CharSequence getTextBeforeCursor(int n, int flags);
    @Nullable
    CharSequence getTextBeforeCursor(@IntRange(from = 0) int n, int flags);

    /**
     * Get <var>n</var> characters of text after the current cursor
@@ -228,14 +232,16 @@ public interface InputConnection {
     * the current line, and specifically do not return 0 characters unless
     * the cursor is really at the end of the text.</p>
     *
     * @param n The expected length of the text.
     * @param n The expected length of the text. This must be non-negative.
     * @param flags Supplies additional options controlling how the text is
     * returned. May be either 0 or {@link #GET_TEXT_WITH_STYLES}.
     *
     * @return the text after the cursor position; the length of the
     * returned text might be less than <var>n</var>.
     * @throws IllegalArgumentException if {@code n} is negative.
     */
    CharSequence getTextAfterCursor(int n, int flags);
    @Nullable
    CharSequence getTextAfterCursor(@IntRange(from = 0) int n, int flags);

    /**
     * Gets the selected text, if any.
@@ -307,11 +313,15 @@ public interface InputConnection {
     * editor can't comply with the request for some reason, or the application does not implement
     * this method. The length of the returned text might be less than the sum of
     * <var>beforeLength</var> and <var>afterLength</var> .
     * @throws IllegalArgumentException if {@code beforeLength} or {@code afterLength} is negative.
     */
    @Nullable
    default SurroundingText getSurroundingText(
            @IntRange(from = 0) int beforeLength, @IntRange(from = 0) int afterLength,
            @GetTextType int flags) {
        Preconditions.checkArgumentNonnegative(beforeLength);
        Preconditions.checkArgumentNonnegative(afterLength);

        CharSequence textBeforeCursor = getTextBeforeCursor(beforeLength, flags);
        if (textBeforeCursor == null) {
            textBeforeCursor = "";
+14 −2
Original line number Diff line number Diff line
@@ -16,11 +16,14 @@

package android.view.inputmethod;

import android.annotation.IntRange;
import android.annotation.Nullable;
import android.os.Bundle;
import android.os.Handler;
import android.view.KeyEvent;

import com.android.internal.util.Preconditions;

/**
 * <p>Wrapper class for proxying calls to another InputConnection.  Subclass and have fun!
 */
@@ -74,18 +77,24 @@ public class InputConnectionWrapper implements InputConnection {
    /**
     * {@inheritDoc}
     * @throws NullPointerException if the target is {@code null}.
     * @throws IllegalArgumentException if {@code length} is negative.
     */
    @Nullable
    @Override
    public CharSequence getTextBeforeCursor(int n, int flags) {
    public CharSequence getTextBeforeCursor(@IntRange(from = 0) int n, int flags) {
        Preconditions.checkArgumentNonnegative(n);
        return mTarget.getTextBeforeCursor(n, flags);
    }

    /**
     * {@inheritDoc}
     * @throws NullPointerException if the target is {@code null}.
     * @throws IllegalArgumentException if {@code length} is negative.
     */
    @Nullable
    @Override
    public CharSequence getTextAfterCursor(int n, int flags) {
    public CharSequence getTextAfterCursor(@IntRange(from = 0) int n, int flags) {
        Preconditions.checkArgumentNonnegative(n);
        return mTarget.getTextAfterCursor(n, flags);
    }

@@ -101,10 +110,13 @@ public class InputConnectionWrapper implements InputConnection {
    /**
     * {@inheritDoc}
     * @throws NullPointerException if the target is {@code null}.
     * @throws IllegalArgumentException if {@code beforeLength} or {@code afterLength} is negative.
     */
    @Nullable
    @Override
    public SurroundingText getSurroundingText(int beforeLength, int afterLength, int flags) {
        Preconditions.checkArgumentNonnegative(beforeLength);
        Preconditions.checkArgumentNonnegative(afterLength);
        return mTarget.getSurroundingText(beforeLength, afterLength, flags);
    }

+17 −6
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.internal.view;

import android.annotation.AnyThread;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.inputmethodservice.AbstractInputMethodService;
@@ -106,9 +107,13 @@ public class InputConnectionWrapper implements InputConnection {
        return null;
    }

    /**
     * See {@link InputConnection#getTextAfterCursor(int, int)}.
     */
    @Nullable
    @AnyThread
    public CharSequence getTextAfterCursor(int length, int flags) {
        if (mCancellationGroup.isCanceled()) {
    public CharSequence getTextAfterCursor(@IntRange(from = 0) int length, int flags) {
        if (length < 0 || mCancellationGroup.isCanceled()) {
            return null;
        }

@@ -122,9 +127,13 @@ public class InputConnectionWrapper implements InputConnection {
        return getResultOrNull(value, "getTextAfterCursor()");
    }

    /**
     * See {@link InputConnection#getTextBeforeCursor(int, int)}.
     */
    @Nullable
    @AnyThread
    public CharSequence getTextBeforeCursor(int length, int flags) {
        if (mCancellationGroup.isCanceled()) {
    public CharSequence getTextBeforeCursor(@IntRange(from = 0) int length, int flags) {
        if (length < 0 || mCancellationGroup.isCanceled()) {
            return null;
        }

@@ -171,10 +180,12 @@ public class InputConnectionWrapper implements InputConnection {
     * not support this protocol.
     */
    @AnyThread
    public SurroundingText getSurroundingText(int beforeLength, int afterLength, int flags) {
        if (mCancellationGroup.isCanceled()) {
    public SurroundingText getSurroundingText(
            @IntRange(from = 0) int beforeLength, @IntRange(from = 0) int afterLength, int flags) {
        if (beforeLength < 0 || afterLength < 0 || mCancellationGroup.isCanceled()) {
            return null;
        }

        if (isMethodMissing(MissingMethodFlags.GET_SURROUNDING_TEXT)) {
            // This method is not implemented.
            return null;
Loading