Make additional IME Subtype ID persistent.

It turns out that IME subtypes specified to
InputMethodManager#setAdditionalInputMethodSubtypes() are stored in the
presistent storate without subtype IDs.  As a result, when the system is
rebooted, the system would no longer consider those additional subtypes
as enabled due to subtype ID mismatch, until the IME re-adds those
additional subtypes again with the original subtype IDs.

Bug: 28104337
Change-Id: I1445213e0b83d76631a839b974ec1ab9b28ad7d2
diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java
index a42f4d9..dc433b1 100644
--- a/core/java/android/view/inputmethod/InputMethodSubtype.java
+++ b/core/java/android/view/inputmethod/InputMethodSubtype.java
@@ -68,6 +68,7 @@
     // TODO: remove this
     private static final String EXTRA_KEY_UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME =
             "UntranslatableReplacementStringInSubtypeName";
+    private static final int SUBTYPE_ID_NONE = 0;
 
     private final boolean mIsAuxiliary;
     private final boolean mOverridesImplicitlyEnabledSubtype;
@@ -157,13 +158,13 @@
          * track of enabled subtypes by ID. When the IME package gets upgraded, enabled IDs will
          * stay enabled even if other attributes are different. If the ID is unspecified or 0,
          * Arrays.hashCode(new Object[] {locale, mode, extraValue,
-         * isAuxiliary, overridesImplicitlyEnabledSubtype}) will be used instead.
+         * isAuxiliary, overridesImplicitlyEnabledSubtype, isAsciiCapable}) will be used instead.
          */
         public InputMethodSubtypeBuilder setSubtypeId(int subtypeId) {
             mSubtypeId = subtypeId;
             return this;
         }
-        private int mSubtypeId = 0;
+        private int mSubtypeId = SUBTYPE_ID_NONE;
 
         /**
          * @param subtypeLocale is the locale supported by this subtype.
@@ -268,7 +269,7 @@
      * subtypes by ID. When the IME package gets upgraded, enabled IDs will stay enabled even if
      * other attributes are different. If the ID is unspecified or 0,
      * Arrays.hashCode(new Object[] {locale, mode, extraValue,
-     * isAuxiliary, overridesImplicitlyEnabledSubtype}) will be used instead.
+     * isAuxiliary, overridesImplicitlyEnabledSubtype, isAsciiCapable}) will be used instead.
      */
     public InputMethodSubtype(int nameId, int iconId, String locale, String mode, String extraValue,
             boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype, int id) {
@@ -293,9 +294,12 @@
         mIsAsciiCapable = builder.mIsAsciiCapable;
         // If hashCode() of this subtype is 0 and you want to specify it as an id of this subtype,
         // just specify 0 as this subtype's id. Then, this subtype's id is treated as 0.
-        mSubtypeHashCode = mSubtypeId != 0 ? mSubtypeId : hashCodeInternal(mSubtypeLocale,
-                mSubtypeMode, mSubtypeExtraValue, mIsAuxiliary, mOverridesImplicitlyEnabledSubtype,
-                mIsAsciiCapable);
+        if (mSubtypeId != SUBTYPE_ID_NONE) {
+            mSubtypeHashCode = mSubtypeId;
+        } else {
+            mSubtypeHashCode = hashCodeInternal(mSubtypeLocale, mSubtypeMode, mSubtypeExtraValue,
+                    mIsAuxiliary, mOverridesImplicitlyEnabledSubtype, mIsAsciiCapable);
+        }
     }
 
     InputMethodSubtype(Parcel source) {
@@ -501,6 +505,22 @@
         return mSubtypeHashCode;
     }
 
+    /**
+     * @hide
+     * @return {@code true} if a valid subtype ID exists.
+     */
+    public final boolean hasSubtypeId() {
+        return mSubtypeId != SUBTYPE_ID_NONE;
+    }
+
+    /**
+     * @hide
+     * @return subtype ID. {@code 0} means that not subtype ID is specified.
+     */
+    public final int getSubtypeId() {
+        return mSubtypeId;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (o instanceof InputMethodSubtype) {
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index ac7872a..09445f1 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -3648,6 +3648,7 @@
         private static final String ATTR_ID = "id";
         private static final String ATTR_LABEL = "label";
         private static final String ATTR_ICON = "icon";
+        private static final String ATTR_IME_SUBTYPE_ID = "subtypeId";
         private static final String ATTR_IME_SUBTYPE_LOCALE = "imeSubtypeLocale";
         private static final String ATTR_IME_SUBTYPE_LANGUAGE_TAG = "languageTag";
         private static final String ATTR_IME_SUBTYPE_MODE = "imeSubtypeMode";
@@ -3741,6 +3742,10 @@
                     for (int i = 0; i < N; ++i) {
                         final InputMethodSubtype subtype = subtypesList.get(i);
                         out.startTag(null, NODE_SUBTYPE);
+                        if (subtype.hasSubtypeId()) {
+                            out.attribute(null, ATTR_IME_SUBTYPE_ID,
+                                    String.valueOf(subtype.getSubtypeId()));
+                        }
                         out.attribute(null, ATTR_ICON, String.valueOf(subtype.getIconResId()));
                         out.attribute(null, ATTR_LABEL, String.valueOf(subtype.getNameResId()));
                         out.attribute(null, ATTR_IME_SUBTYPE_LOCALE, subtype.getLocale());
@@ -3819,7 +3824,7 @@
                                 parser.getAttributeValue(null, ATTR_IS_AUXILIARY)));
                         final boolean isAsciiCapable = "1".equals(String.valueOf(
                                 parser.getAttributeValue(null, ATTR_IS_ASCII_CAPABLE)));
-                        final InputMethodSubtype subtype = new InputMethodSubtypeBuilder()
+                        final InputMethodSubtypeBuilder builder = new InputMethodSubtypeBuilder()
                                 .setSubtypeNameResId(label)
                                 .setSubtypeIconResId(icon)
                                 .setSubtypeLocale(imeSubtypeLocale)
@@ -3827,9 +3832,13 @@
                                 .setSubtypeMode(imeSubtypeMode)
                                 .setSubtypeExtraValue(imeSubtypeExtraValue)
                                 .setIsAuxiliary(isAuxiliary)
-                                .setIsAsciiCapable(isAsciiCapable)
-                                .build();
-                        tempSubtypesArray.add(subtype);
+                                .setIsAsciiCapable(isAsciiCapable);
+                        final String subtypeIdString =
+                                parser.getAttributeValue(null, ATTR_IME_SUBTYPE_ID);
+                        if (subtypeIdString != null) {
+                            builder.setSubtypeId(Integer.valueOf(subtypeIdString));
+                        }
+                        tempSubtypesArray.add(builder.build());
                     }
                 }
             } catch (XmlPullParserException | IOException | NumberFormatException e) {