Fix internal variables and equals in SuggestionSpan

Bug: 4443922

- Instantiating other package's class for the name is complicated, so we changed the internal value for the notification target class from Class to String.
- Implement equals

Change-Id: Iaf7682be777f0027d33c9a3be4609ac01b6950ad
diff --git a/api/current.txt b/api/current.txt
index 94e4210..2f5b9b4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -19386,7 +19386,6 @@
     method public int describeContents();
     method public int getFlags();
     method public java.lang.String getLocale();
-    method public java.lang.Class<?> getNotificationTargetClass();
     method public int getSpanTypeId();
     method public java.lang.String[] getSuggestions();
     method public void writeToParcel(android.os.Parcel, int);
diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java
index effa5c8..240ad9b 100644
--- a/core/java/android/text/style/SuggestionSpan.java
+++ b/core/java/android/text/style/SuggestionSpan.java
@@ -22,7 +22,6 @@
 import android.os.SystemClock;
 import android.text.ParcelableSpan;
 import android.text.TextUtils;
-import android.util.Log;
 
 import java.util.Arrays;
 import java.util.Locale;
@@ -31,8 +30,6 @@
  * Holds suggestion candidates of words under this span.
  */
 public class SuggestionSpan implements ParcelableSpan {
-    private static final String TAG = SuggestionSpan.class.getSimpleName();
-
     /**
      * Flag for indicating that the input is verbatim. TextView refers to this flag to determine
      * how it displays a word with SuggestionSpan.
@@ -56,7 +53,7 @@
     private final int mFlags;
     private final String[] mSuggestions;
     private final String mLocaleString;
-    private final Class<?> mNotificationTargetClass;
+    private final String mNotificationTargetClassName;
     private final int mHashCode;
 
     /*
@@ -100,25 +97,20 @@
         } else {
             mLocaleString = locale.toString();
         }
-        mNotificationTargetClass = notificationTargetClass;
+        if (notificationTargetClass != null) {
+            mNotificationTargetClassName = notificationTargetClass.getCanonicalName();
+        } else {
+            mNotificationTargetClassName = "";
+        }
         mHashCode = hashCodeInternal(
-                mFlags, mSuggestions, mLocaleString, mNotificationTargetClass);
+                mFlags, mSuggestions, mLocaleString, mNotificationTargetClassName);
     }
 
     public SuggestionSpan(Parcel src) {
         mSuggestions = src.readStringArray();
         mFlags = src.readInt();
         mLocaleString = src.readString();
-        Class<?> tempClass = null;
-        try {
-            final String className = src.readString();
-            if (!TextUtils.isEmpty(className)) {
-                tempClass = Class.forName(className);
-            }
-        } catch (ClassNotFoundException e) {
-            Log.i(TAG, "Invalid class name was created.");
-        }
-        mNotificationTargetClass = tempClass;
+        mNotificationTargetClassName = src.readString();
         mHashCode = src.readInt();
     }
 
@@ -137,13 +129,16 @@
     }
 
     /**
-     * @return The class to notify. The class of the original IME package will receive
+     * @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.
+     *
+     * @hide
      */
-    public Class<?> getNotificationTargetClass() {
-        return mNotificationTargetClass;
+    public String getNotificationTargetClassName() {
+        return mNotificationTargetClassName;
     }
 
     public int getFlags() {
@@ -160,9 +155,7 @@
         dest.writeStringArray(mSuggestions);
         dest.writeInt(mFlags);
         dest.writeString(mLocaleString);
-        dest.writeString(mNotificationTargetClass != null
-                ? mNotificationTargetClass.getCanonicalName()
-                : "");
+        dest.writeString(mNotificationTargetClassName);
         dest.writeInt(mHashCode);
     }
 
@@ -172,17 +165,22 @@
     }
 
     @Override
+    public boolean equals(Object o) {
+        if (o instanceof SuggestionSpan) {
+            return ((SuggestionSpan)o).hashCode() == mHashCode;
+        }
+        return false;
+    }
+
+    @Override
     public int hashCode() {
         return mHashCode;
     }
 
     private static int hashCodeInternal(int flags, String[] suggestions,String locale,
-            Class<?> notificationTargetClass) {
-        final String cls = notificationTargetClass != null
-                ? notificationTargetClass.getCanonicalName()
-                : "";
-        return Arrays.hashCode(
-                new Object[] {SystemClock.uptimeMillis(), flags, suggestions, locale, cls});
+            String notificationTargetClassName) {
+        return Arrays.hashCode(new Object[] {SystemClock.uptimeMillis(), flags, suggestions, locale,
+                notificationTargetClassName});
     }
 
     public static final Parcelable.Creator<SuggestionSpan> CREATOR =
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 7361ef7..465fd2e 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -1021,8 +1021,9 @@
             final InputMethodInfo currentImi = mMethodMap.get(mCurMethodId);
             for (int i = 0; i < spans.length; ++i) {
                 SuggestionSpan ss = spans[i];
-                if (ss.getNotificationTargetClass() != null) {
+                if (!TextUtils.isEmpty(ss.getNotificationTargetClassName())) {
                     mSecureSuggestionSpans.put(ss, currentImi);
+                    final InputMethodInfo targetImi = mSecureSuggestionSpans.get(ss);
                 }
             }
         }
@@ -1035,11 +1036,11 @@
             if (targetImi != null) {
                 final String[] suggestions = span.getSuggestions();
                 if (index < 0 || index >= suggestions.length) return false;
-                final Class<?> c = span.getNotificationTargetClass();
+                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(), c.getCanonicalName());
+                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]);
@@ -1145,6 +1146,7 @@
         }
     }
 
+    @Override
     public boolean showSoftInput(IInputMethodClient client, int flags,
             ResultReceiver resultReceiver) {
         int uid = Binder.getCallingUid();
@@ -1210,6 +1212,7 @@
         return res;
     }
 
+    @Override
     public boolean hideSoftInput(IInputMethodClient client, int flags,
             ResultReceiver resultReceiver) {
         int uid = Binder.getCallingUid();
@@ -1272,6 +1275,7 @@
         return res;
     }
 
+    @Override
     public void windowGainedFocus(IInputMethodClient client, IBinder windowToken,
             boolean viewHasFocus, boolean isTextEditor, int softInputMode,
             boolean first, int windowFlags) {
@@ -1373,6 +1377,7 @@
         }
     }
 
+    @Override
     public void showInputMethodPickerFromClient(IInputMethodClient client) {
         synchronized (mMethodMap) {
             if (mCurClient == null || client == null
@@ -1387,10 +1392,12 @@
         }
     }
 
+    @Override
     public void setInputMethod(IBinder token, String id) {
         setInputMethodWithSubtypeId(token, id, NOT_A_SUBTYPE_ID);
     }
 
+    @Override
     public void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) {
         synchronized (mMethodMap) {
             if (subtype != null) {
@@ -1402,6 +1409,7 @@
         }
     }
 
+    @Override
     public void showInputMethodAndSubtypeEnablerFromClient(
             IInputMethodClient client, String inputMethodId) {
         synchronized (mMethodMap) {
@@ -1524,6 +1532,7 @@
         }
     }
 
+    @Override
     public void hideMySoftInput(IBinder token, int flags) {
         synchronized (mMethodMap) {
             if (token == null || mCurToken != token) {
@@ -1540,6 +1549,7 @@
         }
     }
 
+    @Override
     public void showMySoftInput(IBinder token, int flags) {
         synchronized (mMethodMap) {
             if (token == null || mCurToken != token) {
@@ -1576,6 +1586,7 @@
         }
     }
 
+    @Override
     public boolean handleMessage(Message msg) {
         HandlerCaller.SomeArgs args;
         switch (msg.what) {
@@ -1919,6 +1930,7 @@
             }
 
             AlertDialog.OnClickListener adocl = new AlertDialog.OnClickListener() {
+                @Override
                 public void onClick(DialogInterface dialog, int which) {
                     hideInputMethodMenu();
                 }
@@ -1930,6 +1942,7 @@
             mDialogBuilder = new AlertDialog.Builder(context)
                     .setTitle(com.android.internal.R.string.select_input_method)
                     .setOnCancelListener(new OnCancelListener() {
+                        @Override
                         public void onCancel(DialogInterface dialog) {
                             hideInputMethodMenu();
                         }
@@ -1940,6 +1953,7 @@
 
             mDialogBuilder.setSingleChoiceItems(mItems, checkedItem,
                     new AlertDialog.OnClickListener() {
+                        @Override
                         public void onClick(DialogInterface dialog, int which) {
                             synchronized (mMethodMap) {
                                 if (mIms == null || mIms.length <= which
@@ -1964,6 +1978,7 @@
                 mDialogBuilder.setPositiveButton(
                         com.android.internal.R.string.configure_input_methods,
                         new DialogInterface.OnClickListener() {
+                            @Override
                             public void onClick(DialogInterface dialog, int whichButton) {
                                 showConfigureInputMethods();
                             }
@@ -1999,6 +2014,7 @@
 
     // ----------------------------------------------------------------------
 
+    @Override
     public boolean setInputMethodEnabled(String id, boolean enabled) {
         synchronized (mMethodMap) {
             if (mContext.checkCallingOrSelfPermission(
@@ -2329,6 +2345,7 @@
     /**
      * @return Return the current subtype of this input method.
      */
+    @Override
     public InputMethodSubtype getCurrentInputMethodSubtype() {
         boolean subtypeIsSelected = false;
         try {
@@ -2411,6 +2428,7 @@
         }
     }
 
+    @Override
     public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) {
         synchronized (mMethodMap) {
             if (subtype != null && mCurMethodId != null) {