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

Commit 17ace29a authored by Yohei Yukawa's avatar Yohei Yukawa
Browse files

Deprecate SuggestionSpan#ACTION_SUGGESTION_PICKED

This CL deprecates SuggestionSpan#ACTION_SUGGESTION_PICKED and related
constants [1].

There are multiple security concerns, open questions about
compatibility, and maintainance challanges in this protocol.

IME developers can implement their own suggestion picker UI on top of
CursorAnchorInfo API to achieve safer, should give more flexible UI
options, better security, and better compatibility.

 [1]: Ia539de0acf66053e0349daec459d75e36805f6bf
      f9f01008

Fix: 123160396
Test: make -j checkbuild
Change-Id: I6d39e838ae47488055162cd44b5f553f68869b17
parent d3e0fd93
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -46827,15 +46827,15 @@ package android.text.style {
    method public void setFlags(int);
    method public void updateDrawState(android.text.TextPaint);
    method public void writeToParcel(android.os.Parcel, int);
    field public static final String ACTION_SUGGESTION_PICKED = "android.text.style.SUGGESTION_PICKED";
    field @Deprecated public static final String ACTION_SUGGESTION_PICKED = "android.text.style.SUGGESTION_PICKED";
    field public static final android.os.Parcelable.Creator<android.text.style.SuggestionSpan> CREATOR;
    field public static final int FLAG_AUTO_CORRECTION = 4; // 0x4
    field public static final int FLAG_EASY_CORRECT = 1; // 0x1
    field public static final int FLAG_MISSPELLED = 2; // 0x2
    field public static final int SUGGESTIONS_MAX_SIZE = 5; // 0x5
    field public static final String SUGGESTION_SPAN_PICKED_AFTER = "after";
    field public static final String SUGGESTION_SPAN_PICKED_BEFORE = "before";
    field public static final String SUGGESTION_SPAN_PICKED_HASHCODE = "hashcode";
    field @Deprecated public static final String SUGGESTION_SPAN_PICKED_AFTER = "after";
    field @Deprecated public static final String SUGGESTION_SPAN_PICKED_BEFORE = "before";
    field @Deprecated public static final String SUGGESTION_SPAN_PICKED_HASHCODE = "hashcode";
  }
  public class SuperscriptSpan extends android.text.style.MetricAffectingSpan implements android.text.ParcelableSpan {
+42 −60
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.os.Parcel;
@@ -31,7 +30,6 @@ import android.text.ParcelableSpan;
import android.text.TextPaint;
import android.text.TextUtils;
import android.util.Log;
import android.view.inputmethod.InputMethodManager;
import android.widget.TextView;

import java.util.Arrays;
@@ -72,9 +70,37 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {
     */
    public static final int FLAG_AUTO_CORRECTION = 0x0004;

    /**
     * This action is deprecated in {@link android.os.Build.VERSION_CODES#Q}.
     *
     * @deprecated For IMEs to receive this kind of user interaction signals, implement IMEs' own
     *             suggestion picker UI instead of relying on {@link SuggestionSpan}. To retrieve
     *             bounding boxes for each character of the composing text, use
     *             {@link android.view.inputmethod.CursorAnchorInfo}.
     */
    @Deprecated
    public static final String ACTION_SUGGESTION_PICKED = "android.text.style.SUGGESTION_PICKED";

    /**
     * This is deprecated in {@link android.os.Build.VERSION_CODES#Q}.
     *
     * @deprecated See {@link #ACTION_SUGGESTION_PICKED}.
     */
    @Deprecated
    public static final String SUGGESTION_SPAN_PICKED_AFTER = "after";
    /**
     * This is deprecated in {@link android.os.Build.VERSION_CODES#Q}.
     *
     * @deprecated See {@link #ACTION_SUGGESTION_PICKED}.
     */
    @Deprecated
    public static final String SUGGESTION_SPAN_PICKED_BEFORE = "before";
    /**
     * This is deprecated in {@link android.os.Build.VERSION_CODES#Q}.
     *
     * @deprecated See {@link #ACTION_SUGGESTION_PICKED}.
     */
    @Deprecated
    public static final String SUGGESTION_SPAN_PICKED_HASHCODE = "hashcode";

    public static final int SUGGESTIONS_MAX_SIZE = 5;
@@ -97,8 +123,6 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {
    private final String mLocaleStringForCompatibility;
    @NonNull
    private final String mLanguageTag;
    private final String mNotificationTargetClassName;
    private final String mNotificationTargetPackageName;
    private final int mHashCode;

    @UnsupportedAppUsage
@@ -137,7 +161,9 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {
     * {@link SuggestionSpan#SUGGESTIONS_MAX_SIZE} will be considered. Null values not permitted.
     * @param flags Additional flags indicating how this span is handled in TextView
     * @param notificationTargetClass if not null, this class will get notified when the user
     * selects one of the suggestions.
     *                                selects one of the suggestions.  On Android
     *                                {@link android.os.Build.VERSION_CODES#Q} and later this
     *                                parameter is always ignored.
     */
    public SuggestionSpan(Context context, Locale locale, String[] suggestions, int flags,
            Class<?> notificationTargetClass) {
@@ -156,20 +182,7 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {
        }
        mLocaleStringForCompatibility = sourceLocale == null ? "" : sourceLocale.toString();
        mLanguageTag = sourceLocale == null ? "" : sourceLocale.toLanguageTag();

        if (context != null) {
            mNotificationTargetPackageName = context.getPackageName();
        } else {
            mNotificationTargetPackageName = null;
        }

        if (notificationTargetClass != null) {
            mNotificationTargetClassName = notificationTargetClass.getCanonicalName();
        } else {
            mNotificationTargetClassName = "";
        }
        mHashCode = hashCodeInternal(mSuggestions, mLanguageTag, mLocaleStringForCompatibility,
                mNotificationTargetClassName);
        mHashCode = hashCodeInternal(mSuggestions, mLanguageTag, mLocaleStringForCompatibility);

        initStyle(context);
    }
@@ -215,8 +228,6 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {
        mFlags = src.readInt();
        mLocaleStringForCompatibility = src.readString();
        mLanguageTag = src.readString();
        mNotificationTargetClassName = src.readString();
        mNotificationTargetPackageName = src.readString();
        mHashCode = src.readInt();
        mEasyCorrectUnderlineColor = src.readInt();
        mEasyCorrectUnderlineThickness = src.readFloat();
@@ -260,17 +271,15 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {
    }

    /**
     * @return The name of the class to notify. The class of the original IME package will receive
     * a notification when the user selects one of the suggestions. The notification will include
     * the original string, the suggested replacement string as well as the hashCode of this span.
     * The class will get notified by an intent that has those information.
     * This is an internal API because only the framework should know the class name.
     * @return {@code null}.
     *
     * @hide
     * @deprecated Do not use. Always returns {@code null}.
     */
    @Deprecated
    @UnsupportedAppUsage
    public String getNotificationTargetClassName() {
        return mNotificationTargetClassName;
        return null;
    }

    public int getFlags() {
@@ -297,8 +306,6 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {
        dest.writeInt(mFlags);
        dest.writeString(mLocaleStringForCompatibility);
        dest.writeString(mLanguageTag);
        dest.writeString(mNotificationTargetClassName);
        dest.writeString(mNotificationTargetPackageName);
        dest.writeInt(mHashCode);
        dest.writeInt(mEasyCorrectUnderlineColor);
        dest.writeFloat(mEasyCorrectUnderlineThickness);
@@ -332,9 +339,9 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {
    }

    private static int hashCodeInternal(String[] suggestions, @NonNull String languageTag,
            @NonNull String localeStringForCompatibility, String notificationTargetClassName) {
            @NonNull String localeStringForCompatibility) {
        return Arrays.hashCode(new Object[] {Long.valueOf(SystemClock.uptimeMillis()), suggestions,
                languageTag, localeStringForCompatibility, notificationTargetClassName});
                languageTag, localeStringForCompatibility});
    }

    public static final Parcelable.Creator<SuggestionSpan> CREATOR =
@@ -390,39 +397,14 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {
    }

    /**
     * Notifies a suggestion selection.
     * Does nothing.
     *
     * @deprecated this is deprecated in {@link android.os.Build.VERSION_CODES#Q}.
     * @hide
     */
    @UnsupportedAppUsage
    @Deprecated
    public void notifySelection(Context context, String original, int index) {
        final Intent intent = new Intent();

        if (context == null || mNotificationTargetClassName == null) {
            return;
        }
        // Ensures that only a class in the original IME package will receive the
        // notification.
        if (mSuggestions == null || index < 0 || index >= mSuggestions.length) {
            Log.w(TAG, "Unable to notify the suggestion as the index is out of range index=" + index
                    + " length=" + mSuggestions.length);
            return;
        }

        // The package name is not mandatory (legacy from JB), and if the package name
        // is missing, we try to notify the suggestion through the input method manager.
        if (mNotificationTargetPackageName != null) {
            intent.setClassName(mNotificationTargetPackageName, mNotificationTargetClassName);
            intent.setAction(SuggestionSpan.ACTION_SUGGESTION_PICKED);
            intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_BEFORE, original);
            intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_AFTER, mSuggestions[index]);
            intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_HASHCODE, hashCode());
            context.sendBroadcast(intent);
        } else {
            InputMethodManager imm = context.getSystemService(InputMethodManager.class);
            if (imm != null) {
                imm.notifySuggestionPicked(this, original, index);
            }
        }
        Log.w(TAG, "notifySelection() is deprecated.  Does nothing.");
    }
}
+18 −12
Original line number Diff line number Diff line
@@ -980,24 +980,30 @@ public final class InputMethodManager {
        InputMethodPrivilegedOperationsRegistry.get(imeToken).updateStatusIcon(null, 0);
    }

    /** @hide */
    /**
     * This hidden API is deprecated in {@link android.os.Build.VERSION_CODES#Q}. Does nothing.
     *
     * @param spans will be ignored.
     *
     * @deprecated Do not use.
     * @hide
     */
    @Deprecated
    @UnsupportedAppUsage
    public void registerSuggestionSpansForNotification(SuggestionSpan[] spans) {
        try {
            mService.registerSuggestionSpansForNotification(spans);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
        Log.w(TAG, "registerSuggestionSpansForNotification() is deprecated.  Does nothing.");
    }

    /** @hide */
    /**
     * This hidden API is deprecated in {@link android.os.Build.VERSION_CODES#Q}. Does nothing.
     *
     * @deprecated Do not use.
     * @hide
     */
    @Deprecated
    @UnsupportedAppUsage
    public void notifySuggestionPicked(SuggestionSpan span, String originalString, int index) {
        try {
            mService.notifySuggestionPicked(span, originalString, index);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
        Log.w(TAG, "notifySuggestionPicked() is deprecated.  Does nothing.");
    }

    /**
+0 −4
Original line number Diff line number Diff line
@@ -2901,10 +2901,6 @@ public class Editor {
            }
        }

        // Notify source IME of the suggestion pick. Do this before swapping texts.
        targetSuggestionSpan.notifySelection(
                mTextView.getContext(), originalText, suggestionInfo.mSuggestionIndex);

        // Swap text content between actual text and Suggestion span
        final int suggestionStart = suggestionInfo.mSuggestionStart;
        final int suggestionEnd = suggestionInfo.mSuggestionEnd;
+0 −3
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@
package com.android.internal.view;

import android.os.ResultReceiver;
import android.text.style.SuggestionSpan;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
import android.view.inputmethod.EditorInfo;
@@ -65,8 +64,6 @@ interface IInputMethodManager {
            int displayId);
    void showInputMethodAndSubtypeEnablerFromClient(in IInputMethodClient client, String topId);
    boolean isInputMethodPickerShownForTest();
    void registerSuggestionSpansForNotification(in SuggestionSpan[] spans);
    boolean notifySuggestionPicked(in SuggestionSpan span, String originalString, int index);
    InputMethodSubtype getCurrentInputMethodSubtype();
    boolean setCurrentInputMethodSubtype(in InputMethodSubtype subtype);
    void setAdditionalInputMethodSubtypes(String id, in InputMethodSubtype[] subtypes);
Loading