diff options
| author | 2019-01-21 09:24:26 -0800 | |
|---|---|---|
| committer | 2019-01-21 09:24:26 -0800 | |
| commit | 17ace29a10c8eaa5553acad0ebb62aee3efbd303 (patch) | |
| tree | 2658b16a1f635392e2e5eb8414f9912b6ec71002 | |
| parent | d3e0fd9395151fa031661d56d9c13c9e7cc8d2b4 (diff) | |
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
f9f01008624e2d28c15a90d942fa36f98c8c967d
Fix: 123160396
Test: make -j checkbuild
Change-Id: I6d39e838ae47488055162cd44b5f553f68869b17
8 files changed, 64 insertions, 154 deletions
diff --git a/api/current.txt b/api/current.txt index 6122e5208985..d58b76ff76fb 100644 --- a/api/current.txt +++ b/api/current.txt @@ -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 { diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java index 7a586815199c..dd073e9142b1 100644 --- a/core/java/android/text/style/SuggestionSpan.java +++ b/core/java/android/text/style/SuggestionSpan.java @@ -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."); } } diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 1ba7d8ed5db2..86c5f188ffab 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -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."); } /** diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 55364ec1017d..4a60b6a8185a 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -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; diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index 70f4ed2d529e..356d178cc4eb 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -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); diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java index 7f7528dd3de0..78688edb3f88 100644 --- a/core/java/com/android/internal/widget/EditableInputConnection.java +++ b/core/java/com/android/internal/widget/EditableInputConnection.java @@ -18,9 +18,7 @@ package com.android.internal.widget; import android.os.Bundle; import android.text.Editable; -import android.text.Spanned; import android.text.method.KeyListener; -import android.text.style.SuggestionSpan; import android.util.Log; import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.CompletionInfo; @@ -173,12 +171,6 @@ public class EditableInputConnection extends BaseInputConnection { if (mTextView == null) { return super.commitText(text, newCursorPosition); } - if (text instanceof Spanned) { - Spanned spanned = ((Spanned) text); - SuggestionSpan[] spans = spanned.getSpans(0, text.length(), SuggestionSpan.class); - mIMM.registerSuggestionSpansForNotification(spans); - } - mTextView.resetErrorChangedFlag(); boolean success = super.commitText(text, newCursorPosition); mTextView.hideErrorIfUnchanged(); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 0210da3e2160..6c3cc58a1bd6 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -2513,54 +2513,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } - @Override - public void registerSuggestionSpansForNotification(SuggestionSpan[] spans) { - synchronized (mMethodMap) { - if (!calledFromValidUserLocked(!PER_PROFILE_IME_ENABLED)) { - return; - } - final InputMethodInfo currentImi = mMethodMap.get(mCurMethodId); - for (int i = 0; i < spans.length; ++i) { - SuggestionSpan ss = spans[i]; - if (!TextUtils.isEmpty(ss.getNotificationTargetClassName())) { - mSecureSuggestionSpans.put(ss, currentImi); - } - } - } - } - - @Override - public boolean notifySuggestionPicked(SuggestionSpan span, String originalString, int index) { - synchronized (mMethodMap) { - if (!calledFromValidUserLocked(!PER_PROFILE_IME_ENABLED)) { - return false; - } - final InputMethodInfo targetImi = mSecureSuggestionSpans.get(span); - // TODO: Do not send the intent if the process of the targetImi is already dead. - if (targetImi != null) { - final String[] suggestions = span.getSuggestions(); - if (index < 0 || index >= suggestions.length) return false; - final String className = span.getNotificationTargetClassName(); - final Intent intent = new Intent(); - // Ensures that only a class in the original IME package will receive the - // notification. - intent.setClassName(targetImi.getPackageName(), className); - intent.setAction(SuggestionSpan.ACTION_SUGGESTION_PICKED); - intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_BEFORE, originalString); - intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_AFTER, suggestions[index]); - intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_HASHCODE, span.hashCode()); - final long ident = Binder.clearCallingIdentity(); - try { - mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT); - } finally { - Binder.restoreCallingIdentity(ident); - } - return true; - } - } - return false; - } - void updateFromSettingsLocked(boolean enabledMayChange) { updateInputMethodsFromSettingsLocked(enabledMayChange); updateKeyboardFromSettingsLocked(); diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java index f304cebddb25..6f0c5e83b4fe 100644 --- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java @@ -53,7 +53,6 @@ import android.os.ShellCallback; import android.os.UserHandle; import android.provider.Settings; import android.text.TextUtils; -import android.text.style.SuggestionSpan; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; @@ -1525,20 +1524,6 @@ public final class MultiClientInputMethodManagerService { @BinderThread @Override - public void registerSuggestionSpansForNotification(SuggestionSpan[] suggestionSpans) { - reportNotSupported(); - } - - @BinderThread - @Override - public boolean notifySuggestionPicked( - SuggestionSpan span, String originalString, int index) { - reportNotSupported(); - return false; - } - - @BinderThread - @Override public InputMethodSubtype getCurrentInputMethodSubtype() { reportNotSupported(); return null; |