Merge "Use BCP-47 LanguageTag in IME/Spell-Checker."
diff --git a/api/current.txt b/api/current.txt
index 9d9b2f5..4bd0fc0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -728,6 +728,7 @@
     field public static final int label = 16842753; // 0x1010001
     field public static final int labelFor = 16843718; // 0x10103c6
     field public static final int labelTextSize = 16843317; // 0x1010235
+    field public static final int languageTag = 16844041; // 0x1010509
     field public static final int largeHeap = 16843610; // 0x101035a
     field public static final int largeScreens = 16843398; // 0x1010286
     field public static final int largestWidthLimitDp = 16843622; // 0x1010366
@@ -43213,7 +43214,8 @@
     method public java.lang.String getExtraValue();
     method public java.lang.String getExtraValueOf(java.lang.String);
     method public int getIconResId();
-    method public java.lang.String getLocale();
+    method public java.lang.String getLanguageTag();
+    method public deprecated java.lang.String getLocale();
     method public java.lang.String getMode();
     method public int getNameResId();
     method public boolean isAsciiCapable();
@@ -43228,6 +43230,7 @@
     method public android.view.inputmethod.InputMethodSubtype build();
     method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setIsAsciiCapable(boolean);
     method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setIsAuxiliary(boolean);
+    method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setLanguageTag(java.lang.String);
     method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setOverridesImplicitlyEnabledSubtype(boolean);
     method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeExtraValue(java.lang.String);
     method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeIconResId(int);
@@ -43291,7 +43294,8 @@
     method public java.lang.CharSequence getDisplayName(android.content.Context, java.lang.String, android.content.pm.ApplicationInfo);
     method public java.lang.String getExtraValue();
     method public java.lang.String getExtraValueOf(java.lang.String);
-    method public java.lang.String getLocale();
+    method public java.lang.String getLanguageTag();
+    method public deprecated java.lang.String getLocale();
     method public int getNameResId();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.view.textservice.SpellCheckerSubtype> CREATOR;
diff --git a/api/system-current.txt b/api/system-current.txt
index f9c8137..18dd49fe 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -822,6 +822,7 @@
     field public static final int label = 16842753; // 0x1010001
     field public static final int labelFor = 16843718; // 0x10103c6
     field public static final int labelTextSize = 16843317; // 0x1010235
+    field public static final int languageTag = 16844041; // 0x1010509
     field public static final int largeHeap = 16843610; // 0x101035a
     field public static final int largeScreens = 16843398; // 0x1010286
     field public static final int largestWidthLimitDp = 16843622; // 0x1010366
@@ -45554,7 +45555,8 @@
     method public java.lang.String getExtraValue();
     method public java.lang.String getExtraValueOf(java.lang.String);
     method public int getIconResId();
-    method public java.lang.String getLocale();
+    method public java.lang.String getLanguageTag();
+    method public deprecated java.lang.String getLocale();
     method public java.lang.String getMode();
     method public int getNameResId();
     method public boolean isAsciiCapable();
@@ -45569,6 +45571,7 @@
     method public android.view.inputmethod.InputMethodSubtype build();
     method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setIsAsciiCapable(boolean);
     method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setIsAuxiliary(boolean);
+    method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setLanguageTag(java.lang.String);
     method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setOverridesImplicitlyEnabledSubtype(boolean);
     method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeExtraValue(java.lang.String);
     method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeIconResId(int);
@@ -45632,7 +45635,8 @@
     method public java.lang.CharSequence getDisplayName(android.content.Context, java.lang.String, android.content.pm.ApplicationInfo);
     method public java.lang.String getExtraValue();
     method public java.lang.String getExtraValueOf(java.lang.String);
-    method public java.lang.String getLocale();
+    method public java.lang.String getLanguageTag();
+    method public deprecated java.lang.String getLocale();
     method public int getNameResId();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.view.textservice.SpellCheckerSubtype> CREATOR;
diff --git a/api/test-current.txt b/api/test-current.txt
index a59d03d..205b06d 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -728,6 +728,7 @@
     field public static final int label = 16842753; // 0x1010001
     field public static final int labelFor = 16843718; // 0x10103c6
     field public static final int labelTextSize = 16843317; // 0x1010235
+    field public static final int languageTag = 16844041; // 0x1010509
     field public static final int largeHeap = 16843610; // 0x101035a
     field public static final int largeScreens = 16843398; // 0x1010286
     field public static final int largestWidthLimitDp = 16843622; // 0x1010366
@@ -43215,7 +43216,8 @@
     method public java.lang.String getExtraValue();
     method public java.lang.String getExtraValueOf(java.lang.String);
     method public int getIconResId();
-    method public java.lang.String getLocale();
+    method public java.lang.String getLanguageTag();
+    method public deprecated java.lang.String getLocale();
     method public java.lang.String getMode();
     method public int getNameResId();
     method public boolean isAsciiCapable();
@@ -43230,6 +43232,7 @@
     method public android.view.inputmethod.InputMethodSubtype build();
     method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setIsAsciiCapable(boolean);
     method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setIsAuxiliary(boolean);
+    method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setLanguageTag(java.lang.String);
     method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setOverridesImplicitlyEnabledSubtype(boolean);
     method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeExtraValue(java.lang.String);
     method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeIconResId(int);
@@ -43293,7 +43296,8 @@
     method public java.lang.CharSequence getDisplayName(android.content.Context, java.lang.String, android.content.pm.ApplicationInfo);
     method public java.lang.String getExtraValue();
     method public java.lang.String getExtraValueOf(java.lang.String);
-    method public java.lang.String getLocale();
+    method public java.lang.String getLanguageTag();
+    method public deprecated java.lang.String getLocale();
     method public int getNameResId();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.view.textservice.SpellCheckerSubtype> CREATOR;
diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java
index fbaf51c..a42f4d9 100644
--- a/core/java/android/view/inputmethod/InputMethodSubtype.java
+++ b/core/java/android/view/inputmethod/InputMethodSubtype.java
@@ -16,6 +16,7 @@
 
 package android.view.inputmethod;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -50,6 +51,7 @@
  *
  * @attr ref android.R.styleable#InputMethod_Subtype_label
  * @attr ref android.R.styleable#InputMethod_Subtype_icon
+ * @attr ref android.R.styleable#InputMethod_Subtype_languageTag
  * @attr ref android.R.styleable#InputMethod_Subtype_imeSubtypeLocale
  * @attr ref android.R.styleable#InputMethod_Subtype_imeSubtypeMode
  * @attr ref android.R.styleable#InputMethod_Subtype_imeSubtypeExtraValue
@@ -60,6 +62,7 @@
  */
 public final class InputMethodSubtype implements Parcelable {
     private static final String TAG = InputMethodSubtype.class.getSimpleName();
+    private static final String LANGUAGE_TAG_NONE = "";
     private static final String EXTRA_VALUE_PAIR_SEPARATOR = ",";
     private static final String EXTRA_VALUE_KEY_VALUE_SEPARATOR = "=";
     // TODO: remove this
@@ -74,6 +77,7 @@
     private final int mSubtypeNameResId;
     private final int mSubtypeId;
     private final String mSubtypeLocale;
+    private final String mSubtypeLanguageTag;
     private final String mSubtypeMode;
     private final String mSubtypeExtraValue;
     private volatile HashMap<String, String> mExtraValueHashMapCache;
@@ -171,6 +175,15 @@
         private String mSubtypeLocale = "";
 
         /**
+         * @param languageTag is the BCP-47 Language Tag supported by this subtype.
+         */
+        public InputMethodSubtypeBuilder setLanguageTag(String languageTag) {
+            mSubtypeLanguageTag = languageTag == null ? LANGUAGE_TAG_NONE : languageTag;
+            return this;
+        }
+        private String mSubtypeLanguageTag = LANGUAGE_TAG_NONE;
+
+        /**
          * @param subtypeMode is the mode supported by this subtype.
          */
         public InputMethodSubtypeBuilder setSubtypeMode(String subtypeMode) {
@@ -271,6 +284,7 @@
         mSubtypeNameResId = builder.mSubtypeNameResId;
         mSubtypeIconResId = builder.mSubtypeIconResId;
         mSubtypeLocale = builder.mSubtypeLocale;
+        mSubtypeLanguageTag = builder.mSubtypeLanguageTag;
         mSubtypeMode = builder.mSubtypeMode;
         mSubtypeExtraValue = builder.mSubtypeExtraValue;
         mIsAuxiliary = builder.mIsAuxiliary;
@@ -291,6 +305,8 @@
         s = source.readString();
         mSubtypeLocale = s != null ? s : "";
         s = source.readString();
+        mSubtypeLanguageTag = s != null ? s : LANGUAGE_TAG_NONE;
+        s = source.readString();
         mSubtypeMode = s != null ? s : "";
         s = source.readString();
         mSubtypeExtraValue = s != null ? s : "";
@@ -318,21 +334,38 @@
     /**
      * @return The locale of the subtype. This method returns the "locale" string parameter passed
      * to the constructor.
+     *
+     * @deprecated Use {@link #getLanguageTag()} instead.
      */
+    @Deprecated
+    @NonNull
     public String getLocale() {
         return mSubtypeLocale;
     }
 
     /**
-     * @return The normalized {@link Locale} object of the subtype. The returned locale may or may
-     * not equal to "locale" string parameter passed to the constructor.
+     * @return the BCP-47 Language Tag of the subtype.  Returns an empty string when no Language Tag
+     * is specified.
      *
-     * <p>TODO: Consider to make this a public API.</p>
+     * @see Locale#forLanguageTag(String)
+     */
+    @NonNull
+    public String getLanguageTag() {
+        return mSubtypeLanguageTag;
+    }
+
+    /**
+     * @return {@link Locale} constructed from {@link #getLanguageTag()}. If the Language Tag is not
+     * specified, then try to construct from {@link #getLocale()}
+     *
+     * <p>TODO: Consider to make this a public API, or move this to support lib.</p>
      * @hide
      */
     @Nullable
     public Locale getLocaleObject() {
-        // TODO: Move the following method from InputMethodUtils to InputMethodSubtype.
+        if (!TextUtils.isEmpty(mSubtypeLanguageTag)) {
+            return Locale.forLanguageTag(mSubtypeLanguageTag);
+        }
         return InputMethodUtils.constructLocaleFromString(mSubtypeLocale);
     }
 
@@ -476,13 +509,14 @@
                 return (subtype.hashCode() == hashCode());
             }
             return (subtype.hashCode() == hashCode())
-                && (subtype.getLocale().equals(getLocale()))
-                && (subtype.getMode().equals(getMode()))
-                && (subtype.getExtraValue().equals(getExtraValue()))
-                && (subtype.isAuxiliary() == isAuxiliary())
-                && (subtype.overridesImplicitlyEnabledSubtype()
-                        == overridesImplicitlyEnabledSubtype())
-                && (subtype.isAsciiCapable() == isAsciiCapable());
+                    && (subtype.getLocale().equals(getLocale()))
+                    && (subtype.getLanguageTag().equals(getLanguageTag()))
+                    && (subtype.getMode().equals(getMode()))
+                    && (subtype.getExtraValue().equals(getExtraValue()))
+                    && (subtype.isAuxiliary() == isAuxiliary())
+                    && (subtype.overridesImplicitlyEnabledSubtype()
+                            == overridesImplicitlyEnabledSubtype())
+                    && (subtype.isAsciiCapable() == isAsciiCapable());
         }
         return false;
     }
@@ -497,6 +531,7 @@
         dest.writeInt(mSubtypeNameResId);
         dest.writeInt(mSubtypeIconResId);
         dest.writeString(mSubtypeLocale);
+        dest.writeString(mSubtypeLanguageTag);
         dest.writeString(mSubtypeMode);
         dest.writeString(mSubtypeExtraValue);
         dest.writeInt(mIsAuxiliary ? 1 : 0);
diff --git a/core/java/android/view/textservice/SpellCheckerInfo.java b/core/java/android/view/textservice/SpellCheckerInfo.java
index 491de78..471b6d4 100644
--- a/core/java/android/view/textservice/SpellCheckerInfo.java
+++ b/core/java/android/view/textservice/SpellCheckerInfo.java
@@ -117,6 +117,8 @@
                             a.getString(com.android.internal.R.styleable
                                     .SpellChecker_Subtype_subtypeLocale),
                             a.getString(com.android.internal.R.styleable
+                                    .SpellChecker_Subtype_languageTag),
+                            a.getString(com.android.internal.R.styleable
                                     .SpellChecker_Subtype_subtypeExtraValue),
                             a.getInt(com.android.internal.R.styleable
                                     .SpellChecker_Subtype_subtypeId, 0));
diff --git a/core/java/android/view/textservice/SpellCheckerSubtype.java b/core/java/android/view/textservice/SpellCheckerSubtype.java
index f2b03cc..df33698 100644
--- a/core/java/android/view/textservice/SpellCheckerSubtype.java
+++ b/core/java/android/view/textservice/SpellCheckerSubtype.java
@@ -18,6 +18,7 @@
 
 import com.android.internal.inputmethod.InputMethodUtils;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -40,6 +41,7 @@
  * @see SpellCheckerInfo
  *
  * @attr ref android.R.styleable#SpellChecker_Subtype_label
+ * @attr ref android.R.styleable#SpellChecker_Subtype_languageTag
  * @attr ref android.R.styleable#SpellChecker_Subtype_subtypeLocale
  * @attr ref android.R.styleable#SpellChecker_Subtype_subtypeExtraValue
  * @attr ref android.R.styleable#SpellChecker_Subtype_subtypeId
@@ -49,11 +51,13 @@
     private static final String EXTRA_VALUE_PAIR_SEPARATOR = ",";
     private static final String EXTRA_VALUE_KEY_VALUE_SEPARATOR = "=";
     private static final int SUBTYPE_ID_NONE = 0;
+    private static final String SUBTYPE_LANGUAGE_TAG_NONE = "";
 
     private final int mSubtypeId;
     private final int mSubtypeHashCode;
     private final int mSubtypeNameResId;
     private final String mSubtypeLocale;
+    private final String mSubtypeLanguageTag;
     private final String mSubtypeExtraValue;
     private HashMap<String, String> mExtraValueHashMapCache;
 
@@ -66,14 +70,17 @@
      *
      * @param nameId The name of the subtype
      * @param locale The locale supported by the subtype
+     * @param languageTag The BCP-47 Language Tag associated with this subtype.
      * @param extraValue The extra value of the subtype
      * @param subtypeId The subtype ID that is supposed to be stable during package update.
      *
      * @hide
      */
-    public SpellCheckerSubtype(int nameId, String locale, String extraValue, int subtypeId) {
+    public SpellCheckerSubtype(int nameId, String locale, String languageTag, String extraValue,
+            int subtypeId) {
         mSubtypeNameResId = nameId;
         mSubtypeLocale = locale != null ? locale : "";
+        mSubtypeLanguageTag = languageTag != null ? languageTag : SUBTYPE_LANGUAGE_TAG_NONE;
         mSubtypeExtraValue = extraValue != null ? extraValue : "";
         mSubtypeId = subtypeId;
         mSubtypeHashCode = mSubtypeId != SUBTYPE_ID_NONE ?
@@ -91,7 +98,7 @@
      * to instantiate {@link SpellCheckerSubtype} object.
      */
     public SpellCheckerSubtype(int nameId, String locale, String extraValue) {
-        this(nameId, locale, extraValue, SUBTYPE_ID_NONE);
+        this(nameId, locale, SUBTYPE_LANGUAGE_TAG_NONE, extraValue, SUBTYPE_ID_NONE);
     }
 
     SpellCheckerSubtype(Parcel source) {
@@ -100,6 +107,8 @@
         s = source.readString();
         mSubtypeLocale = s != null ? s : "";
         s = source.readString();
+        mSubtypeLanguageTag = s != null ? s : "";
+        s = source.readString();
         mSubtypeExtraValue = s != null ? s : "";
         mSubtypeId = source.readInt();
         mSubtypeHashCode = mSubtypeId != SUBTYPE_ID_NONE ?
@@ -115,12 +124,27 @@
 
     /**
      * @return the locale of the subtype
+     *
+     * @deprecated Use {@link #getLanguageTag()} instead.
      */
+    @Deprecated
+    @NonNull
     public String getLocale() {
         return mSubtypeLocale;
     }
 
     /**
+     * @return the BCP-47 Language Tag of the subtype.  Returns an empty string when no Language Tag
+     * is specified.
+     *
+     * @see Locale#forLanguageTag(String)
+     */
+    @NonNull
+    public String getLanguageTag() {
+        return mSubtypeLanguageTag;
+    }
+
+    /**
      * @return the extra value of the subtype
      */
     public String getExtraValue() {
@@ -182,20 +206,24 @@
             return (subtype.hashCode() == hashCode())
                     && (subtype.getNameResId() == getNameResId())
                     && (subtype.getLocale().equals(getLocale()))
+                    && (subtype.getLanguageTag().equals(getLanguageTag()))
                     && (subtype.getExtraValue().equals(getExtraValue()));
         }
         return false;
     }
 
     /**
-     * @return The normalized {@link Locale} object of the subtype. The returned locale may or may
-     * not equal to "locale" string parameter passed to the constructor.
+     * @return {@link Locale} constructed from {@link #getLanguageTag()}. If the Language Tag is not
+     * specified, then try to construct from {@link #getLocale()}
      *
-     * <p>TODO: Consider to make this a public API.</p>
+     * <p>TODO: Consider to make this a public API, or move this to support lib.</p>
      * @hide
      */
     @Nullable
     public Locale getLocaleObject() {
+        if (!TextUtils.isEmpty(mSubtypeLanguageTag)) {
+            return Locale.forLanguageTag(mSubtypeLanguageTag);
+        }
         return InputMethodUtils.constructLocaleFromString(mSubtypeLocale);
     }
 
@@ -234,6 +262,7 @@
     public void writeToParcel(Parcel dest, int parcelableFlags) {
         dest.writeInt(mSubtypeNameResId);
         dest.writeString(mSubtypeLocale);
+        dest.writeString(mSubtypeLanguageTag);
         dest.writeString(mSubtypeExtraValue);
         dest.writeInt(mSubtypeId);
     }
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 9bca3d6..786554c 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3042,7 +3042,8 @@
         <attr name="imeSubtypeLocale" format="string" />
         <!-- The mode of the subtype. This string can be a mode (e.g. voice, keyboard...) and this
              string will be passed to the IME when the framework calls the IME with the
-             subtype.  -->
+             subtype.  {@link android.view.inputmethod.InputMethodSubtype#getLocale()} returns the
+             value specified in this attribute.  -->
         <attr name="imeSubtypeMode" format="string" />
         <!-- Set true if the subtype is auxiliary.  An auxiliary subtype won't be shown in the
              input method selection list in the settings app.
@@ -3067,6 +3068,9 @@
              this subtype. This is important because many password fields only allow
              ASCII-characters. -->
         <attr name="isAsciiCapable" format="boolean" />
+        <!-- The BCP-47 Language Tag of the subtype.  This replaces
+        {@link android.R.styleable#InputMethod_Subtype_imeSubtypeLocale}.  -->
+        <attr name="languageTag" format="string" />
     </declare-styleable>
 
     <!-- Use <code>spell-checker</code> as the root tag of the XML resource that
@@ -3090,7 +3094,8 @@
         <attr name="label" />
         <!-- The locale of the subtype. This string should be a locale (e.g. en_US, fr_FR...)
              This is also used by the framework to know the supported locales
-             of the spell checker.  -->
+             of the spell checker. {@link android.view.textservice.SpellCheckerSubtype#getLocale()}
+             returns the value specified in this attribute.  -->
         <attr name="subtypeLocale" format="string" />
         <!-- The extra value of the subtype. This string can be any string and will be passed to
              the SpellChecker.  -->
@@ -3102,6 +3107,9 @@
              {@code Arrays.hashCode(new Object[] {subtypeLocale, extraValue}) will be used instead.
               -->
         <attr name="subtypeId" />
+        <!-- The BCP-47 Language Tag of the subtype.  This replaces
+        {@link android.R.styleable#SpellChecker_Subtype_subtypeLocale}.  -->
+        <attr name="languageTag" />
     </declare-styleable>
 
     <!-- Use <code>accessibility-service</code> as the root tag of the XML resource that
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index b6b2e20..addeb05 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2683,6 +2683,7 @@
     <public type="attr" name="encryptionAware" />
     <public type="attr" name="preferenceFragmentStyle" />
     <public type="attr" name="canControlMagnification" />
+    <public type="attr" name="languageTag" />
 
     <public type="style" name="Theme.Material.DayNight" />
     <public type="style" name="Theme.Material.DayNight.DarkActionBar" />
diff --git a/core/tests/coretests/src/android/view/textservice/SpellCheckerSubtypeTest.java b/core/tests/coretests/src/android/view/textservice/SpellCheckerSubtypeTest.java
index 73fdb10..4a1c414 100644
--- a/core/tests/coretests/src/android/view/textservice/SpellCheckerSubtypeTest.java
+++ b/core/tests/coretests/src/android/view/textservice/SpellCheckerSubtypeTest.java
@@ -30,11 +30,16 @@
  */
 public class SpellCheckerSubtypeTest extends InstrumentationTestCase {
     private static final int SUBTYPE_SUBTYPE_ID_NONE = 0;
+    private static final String SUBTYPE_SUBTYPE_LOCALE_STRING_NONE = "";
+    private static final String SUBTYPE_SUBTYPE_LANGUAGE_TAG_NONE = "";
+
     private static final String SUBTYPE_SUBTYPE_LOCALE_STRING_A = "en_GB";
+    private static final String SUBTYPE_SUBTYPE_LANGUAGE_TAG_A = "en-GB";
     private static final int SUBTYPE_NAME_RES_ID_A = 0x12345;
     private static final String SUBTYPE_EXTRA_VALUE_A = "Key1=Value1,Key2=Value2";
     private static final int SUBTYPE_SUBTYPE_ID_A = 42;
     private static final String SUBTYPE_SUBTYPE_LOCALE_STRING_B = "en_IN";
+    private static final String SUBTYPE_SUBTYPE_LANGUAGE_TAG_B = "en-IN";
     private static final int SUBTYPE_NAME_RES_ID_B = 0x54321;
     private static final String SUBTYPE_EXTRA_VALUE_B = "Key3=Value3,Key4=Value4";
     private static final int SUBTYPE_SUBTYPE_ID_B = -42;
@@ -60,9 +65,11 @@
     @SmallTest
     public void testSubtypeWithNoSubtypeId() throws Exception {
         final SpellCheckerSubtype subtype = new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A,
-                SUBTYPE_SUBTYPE_LOCALE_STRING_A, SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE);
+                SUBTYPE_SUBTYPE_LOCALE_STRING_A, SUBTYPE_SUBTYPE_LANGUAGE_TAG_A,
+                SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE);
         assertEquals(SUBTYPE_NAME_RES_ID_A, subtype.getNameResId());
         assertEquals(SUBTYPE_SUBTYPE_LOCALE_STRING_A, subtype.getLocale());
+        assertEquals(SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, subtype.getLanguageTag());
         assertEquals("Value1", subtype.getExtraValueOf("Key1"));
         assertEquals("Value2", subtype.getExtraValueOf("Key2"));
         // Historically we have used SpellCheckerSubtype#hashCode() to track which subtype is
@@ -75,6 +82,7 @@
         final SpellCheckerSubtype clonedSubtype = cloneViaParcel(subtype);
         assertEquals(SUBTYPE_NAME_RES_ID_A, clonedSubtype.getNameResId());
         assertEquals(SUBTYPE_SUBTYPE_LOCALE_STRING_A, clonedSubtype.getLocale());
+        assertEquals(SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, clonedSubtype.getLanguageTag());
         assertEquals("Value1", clonedSubtype.getExtraValueOf("Key1"));
         assertEquals("Value2", clonedSubtype.getExtraValueOf("Key2"));
         assertEquals(
@@ -84,10 +92,12 @@
 
     public void testSubtypeWithSubtypeId() throws Exception {
         final SpellCheckerSubtype subtype = new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A,
-                SUBTYPE_SUBTYPE_LOCALE_STRING_A, SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A);
+                SUBTYPE_SUBTYPE_LOCALE_STRING_A, SUBTYPE_SUBTYPE_LANGUAGE_TAG_A,
+                SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A);
 
         assertEquals(SUBTYPE_NAME_RES_ID_A, subtype.getNameResId());
         assertEquals(SUBTYPE_SUBTYPE_LOCALE_STRING_A, subtype.getLocale());
+        assertEquals(SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, subtype.getLanguageTag());
         assertEquals("Value1", subtype.getExtraValueOf("Key1"));
         assertEquals("Value2", subtype.getExtraValueOf("Key2"));
         // Similar to "SubtypeId" in InputMethodSubtype, "SubtypeId" in SpellCheckerSubtype enables
@@ -97,6 +107,7 @@
         final SpellCheckerSubtype clonedSubtype = cloneViaParcel(subtype);
         assertEquals(SUBTYPE_NAME_RES_ID_A, clonedSubtype.getNameResId());
         assertEquals(SUBTYPE_SUBTYPE_LOCALE_STRING_A, clonedSubtype.getLocale());
+        assertEquals(SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, clonedSubtype.getLanguageTag());
         assertEquals("Value1", clonedSubtype.getExtraValueOf("Key1"));
         assertEquals("Value2", clonedSubtype.getExtraValueOf("Key2"));
         assertEquals(SUBTYPE_SUBTYPE_ID_A, clonedSubtype.hashCode());
@@ -104,30 +115,42 @@
 
     @SmallTest
     public void testGetLocaleObject() throws Exception {
-        assertEquals(new Locale("en"), new SpellCheckerSubtype(
-                SUBTYPE_NAME_RES_ID_A, "en", SUBTYPE_EXTRA_VALUE_A).getLocaleObject());
-        assertEquals(new Locale("en", "US"), new SpellCheckerSubtype(
-                SUBTYPE_NAME_RES_ID_A, "en_US", SUBTYPE_EXTRA_VALUE_A).getLocaleObject());
-        assertEquals(new Locale("en", "US", "POSIX"), new SpellCheckerSubtype(
-                SUBTYPE_NAME_RES_ID_A, "en_US_POSIX", SUBTYPE_EXTRA_VALUE_A).getLocaleObject());
+        assertEquals(new Locale("en", "GB"),
+                new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, "en_GB",
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_NONE, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_NONE).getLocaleObject());
+        assertEquals(new Locale("en", "GB"),
+                new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_NONE,
+                        "en-GB", SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_NONE).getLocaleObject());
 
-        // Special rewrite rule for "tl" for versions of Android earlier than Lollipop that did not
-        // support three letter language codes, and used "tl" (Tagalog) as the language string for
-        // "fil" (Filipino).
-        assertEquals(new Locale("fil"), new SpellCheckerSubtype(
-                SUBTYPE_NAME_RES_ID_A, "tl", SUBTYPE_EXTRA_VALUE_A).getLocaleObject());
-        assertEquals(new Locale("fil", "PH"), new SpellCheckerSubtype(
-                SUBTYPE_NAME_RES_ID_A, "tl_PH", SUBTYPE_EXTRA_VALUE_A).getLocaleObject());
-        assertEquals(new Locale("fil", "PH", "POSIX"), new SpellCheckerSubtype(
-                SUBTYPE_NAME_RES_ID_A, "tl_PH_POSIX", SUBTYPE_EXTRA_VALUE_A).getLocaleObject());
+        // If neither locale string nor language tag is specified,
+        // {@link SpellCheckerSubtype#getLocaleObject} returns null.
+        assertNull(
+                new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_NONE,
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_NONE, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_NONE).getLocaleObject());
 
-        // So far rejecting invalid/unexpected locale strings is out of the scope.
-        assertEquals(new Locale("a"), new SpellCheckerSubtype(
-                SUBTYPE_NAME_RES_ID_A, "a", SUBTYPE_EXTRA_VALUE_A).getLocaleObject());
-        assertEquals(new Locale("a b c"), new SpellCheckerSubtype(
-                SUBTYPE_NAME_RES_ID_A, "a b c", SUBTYPE_EXTRA_VALUE_A).getLocaleObject());
-        assertEquals(new Locale("en-US"), new SpellCheckerSubtype(
-                SUBTYPE_NAME_RES_ID_A, "en-US", SUBTYPE_EXTRA_VALUE_A).getLocaleObject());
+        // If both locale string and language tag are specified,
+        // {@link SpellCheckerSubtype#getLocaleObject} uses language tag.
+        assertEquals(new Locale("en", "GB"),
+                new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, "en_US", "en-GB",
+                        SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE).getLocaleObject());
+
+        // Make sure that "tl_PH" is rewritten to "fil_PH" for spell checkers that need to support
+        // Android KitKat and prior, which do not support 3-letter language codes.
+        assertEquals(new Locale("fil", "PH"),
+                new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, "tl_PH",
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_NONE, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_NONE).getLocaleObject());
+
+        // "languageTag" attribute is available in Android N and later, where 3-letter country codes
+        // are guaranteed to be available.  It's developers' responsibility for specifying a valid
+        // country subtags here and we do not rewrite "tl" to "fil" for simplicity.
+        assertEquals("tl",
+                new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_NONE,
+                        "tl-PH", SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE)
+                        .getLocaleObject().getLanguage());
     }
 
     @SmallTest
@@ -156,50 +179,83 @@
         // If subtype ID is 0 (== SUBTYPE_SUBTYPE_ID_NONE), we keep the same behavior.
         assertEquals(
                 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
-                        SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE),
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_NONE),
                 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
-                        SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE));
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_NONE));
         assertNotEqual(
                 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
-                        SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE),
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_NONE),
                 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_B, SUBTYPE_SUBTYPE_LOCALE_STRING_B,
-                        SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE));
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_NONE));
         assertNotEqual(
                 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
-                        SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE),
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_NONE),
                 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_B,
-                        SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE));
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_NONE));
         assertNotEqual(
                 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
-                        SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE),
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_NONE),
                 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
-                        SUBTYPE_EXTRA_VALUE_B, SUBTYPE_SUBTYPE_ID_NONE));
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_B, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_NONE));
+        assertNotEqual(
+                new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_NONE),
+                new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_B,
+                        SUBTYPE_SUBTYPE_ID_NONE));
 
         // If subtype ID is not 0, we test the equality based only on the subtype ID.
         assertEquals(
                 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
-                        SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A),
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_A),
                 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
-                        SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A));
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_A));
         assertEquals(
                 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
-                        SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A),
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_A),
                 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_B, SUBTYPE_SUBTYPE_LOCALE_STRING_B,
-                        SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A));
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_A));
         assertEquals(
                 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
-                        SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A),
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_A),
                 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_B,
-                        SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A));
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_A));
         assertEquals(
                 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
-                        SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A),
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_A),
                 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
-                        SUBTYPE_EXTRA_VALUE_B, SUBTYPE_SUBTYPE_ID_A));
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_B, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_A));
+        assertEquals(
+                new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_A),
+                new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_B,
+                        SUBTYPE_SUBTYPE_ID_A));
         assertNotEqual(
                 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
-                        SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A),
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_A),
                 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
-                        SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_B));
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_B));
     }
+
 }
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 45c1ed2..0282a72 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -3536,6 +3536,7 @@
         private static final String ATTR_LABEL = "label";
         private static final String ATTR_ICON = "icon";
         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";
         private static final String ATTR_IME_SUBTYPE_EXTRA_VALUE = "imeSubtypeExtraValue";
         private static final String ATTR_IS_AUXILIARY = "isAuxiliary";
@@ -3629,6 +3630,8 @@
                         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());
+                        out.attribute(null, ATTR_IME_SUBTYPE_LANGUAGE_TAG,
+                                subtype.getLanguageTag());
                         out.attribute(null, ATTR_IME_SUBTYPE_MODE, subtype.getMode());
                         out.attribute(null, ATTR_IME_SUBTYPE_EXTRA_VALUE, subtype.getExtraValue());
                         out.attribute(null, ATTR_IS_AUXILIARY,
@@ -3690,6 +3693,8 @@
                                 parser.getAttributeValue(null, ATTR_LABEL));
                         final String imeSubtypeLocale =
                                 parser.getAttributeValue(null, ATTR_IME_SUBTYPE_LOCALE);
+                        final String languageTag =
+                                parser.getAttributeValue(null, ATTR_IME_SUBTYPE_LANGUAGE_TAG);
                         final String imeSubtypeMode =
                                 parser.getAttributeValue(null, ATTR_IME_SUBTYPE_MODE);
                         final String imeSubtypeExtraValue =
@@ -3700,6 +3705,7 @@
                                 .setSubtypeNameResId(label)
                                 .setSubtypeIconResId(icon)
                                 .setSubtypeLocale(imeSubtypeLocale)
+                                .setLanguageTag(languageTag)
                                 .setSubtypeMode(imeSubtypeMode)
                                 .setSubtypeExtraValue(imeSubtypeExtraValue)
                                 .setIsAuxiliary(isAuxiliary)