Fix handling of invalid locales in Date/DecimalFormatSymbols.

For locales whose language code is "und" we use Locale.ROOT
instead. This also fixes two other corner cases :

- We were using the wrong locale to fetch timezone strings
  when the input locale was null. we now use the same locale
  throughout by making sure we don't perform any subsititutions
  in LocaleData.get.

- Adds a clearer comment about the broken serialization
  behaviour.

bug: 15849709

(cherry picked from commit 043a1424a4e3bbb5abc9d9e11c9c088b20f4ca7d)

Change-Id: I716fb421fb8643dedebb3a7797a76ed1dd86c548
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DateFormatSymbolsTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DateFormatSymbolsTest.java
index 9fe3681..70e41a2 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DateFormatSymbolsTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DateFormatSymbolsTest.java
@@ -88,7 +88,7 @@
 
         Locale locale = new Locale("not exist language", "not exist country");
         DateFormatSymbols symbols = DateFormatSymbols.getInstance(locale);
-        assertNotNull(symbols);
+        assertEquals(DateFormatSymbols.getInstance(Locale.ROOT), symbols);
     }
 
     /**
diff --git a/luni/src/main/java/java/text/DateFormatSymbols.java b/luni/src/main/java/java/text/DateFormatSymbols.java
index 0d33d75..cb9fdac 100644
--- a/luni/src/main/java/java/text/DateFormatSymbols.java
+++ b/luni/src/main/java/java/text/DateFormatSymbols.java
@@ -102,7 +102,7 @@
      *            the locale.
      */
     public DateFormatSymbols(Locale locale) {
-        this.locale = locale;
+        this.locale = LocaleData.mapInvalidAndNullLocales(locale);
         this.localPatternChars = SimpleDateFormat.PATTERN_CHARS;
 
         this.localeData = LocaleData.get(locale);
@@ -152,7 +152,12 @@
 
     private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
         ois.defaultReadObject();
-        this.localeData = LocaleData.get(locale);
+
+        // NOTE: We don't serialize the locale we were created with, so we can't
+        // get back the localeData object we want. This is broken for callers that
+        // access this field directly (i.e, SimpleDateFormat). We should ideally
+        // have serialized the locale we were created with.
+        this.localeData = LocaleData.get(Locale.getDefault());
     }
 
     private void writeObject(ObjectOutputStream oos) throws IOException {
diff --git a/luni/src/main/java/java/text/DecimalFormatSymbols.java b/luni/src/main/java/java/text/DecimalFormatSymbols.java
index 1611594..6e25c1b 100644
--- a/luni/src/main/java/java/text/DecimalFormatSymbols.java
+++ b/luni/src/main/java/java/text/DecimalFormatSymbols.java
@@ -81,6 +81,7 @@
      *            the locale.
      */
     public DecimalFormatSymbols(Locale locale) {
+        locale = LocaleData.mapInvalidAndNullLocales(locale);
         LocaleData localeData = LocaleData.get(locale);
         this.zeroDigit = localeData.zeroDigit;
         this.digit = '#';
diff --git a/luni/src/main/java/libcore/icu/ICU.java b/luni/src/main/java/libcore/icu/ICU.java
index bb57f49..06e2205 100644
--- a/luni/src/main/java/libcore/icu/ICU.java
+++ b/luni/src/main/java/libcore/icu/ICU.java
@@ -432,7 +432,7 @@
   private static native String[] getISOLanguagesNative();
   private static native String[] getISOCountriesNative();
 
-  static native boolean initLocaleDataNative(String locale, LocaleData result);
+  static native boolean initLocaleDataNative(String languageTag, LocaleData result);
 
   /**
    * Takes a BCP-47 language tag (Locale.toLanguageTag()). e.g. en-US, not en_US
diff --git a/luni/src/main/java/libcore/icu/LocaleData.java b/luni/src/main/java/libcore/icu/LocaleData.java
index 845ba32..ec05b53 100644
--- a/luni/src/main/java/libcore/icu/LocaleData.java
+++ b/luni/src/main/java/libcore/icu/LocaleData.java
@@ -112,27 +112,36 @@
     private LocaleData() {
     }
 
+    public static Locale mapInvalidAndNullLocales(Locale locale) {
+        if (locale == null) {
+            return Locale.getDefault();
+        }
+
+        if ("und".equals(locale.toLanguageTag())) {
+            return Locale.ROOT;
+        }
+
+        return locale;
+    }
+
     /**
      * Returns a shared LocaleData for the given locale.
      */
     public static LocaleData get(Locale locale) {
-        if (locale == null) {
-            locale = Locale.getDefault();
-        }
-        String localeName = locale.toString();
+        final String languageTag = locale.toLanguageTag();
         synchronized (localeDataCache) {
-            LocaleData localeData = localeDataCache.get(localeName);
+            LocaleData localeData = localeDataCache.get(languageTag);
             if (localeData != null) {
                 return localeData;
             }
         }
         LocaleData newLocaleData = initLocaleData(locale);
         synchronized (localeDataCache) {
-            LocaleData localeData = localeDataCache.get(localeName);
+            LocaleData localeData = localeDataCache.get(languageTag);
             if (localeData != null) {
                 return localeData;
             }
-            localeDataCache.put(localeName, newLocaleData);
+            localeDataCache.put(languageTag, newLocaleData);
             return newLocaleData;
         }
     }
@@ -171,7 +180,7 @@
 
     private static LocaleData initLocaleData(Locale locale) {
         LocaleData localeData = new LocaleData();
-        if (!ICU.initLocaleDataNative(locale.toString(), localeData)) {
+        if (!ICU.initLocaleDataNative(locale.toLanguageTag(), localeData)) {
             throw new AssertionError("couldn't initialize LocaleData for locale " + locale);
         }
 
diff --git a/luni/src/main/native/libcore_icu_ICU.cpp b/luni/src/main/native/libcore_icu_ICU.cpp
index 733bf38..df26b39 100644
--- a/luni/src/main/native/libcore_icu_ICU.cpp
+++ b/luni/src/main/native/libcore_icu_ICU.cpp
@@ -513,16 +513,16 @@
   return true;
 }
 
-static jboolean ICU_initLocaleDataNative(JNIEnv* env, jclass, jstring javaLocaleName, jobject localeData) {
-    ScopedUtfChars localeName(env, javaLocaleName);
-    if (localeName.c_str() == NULL) {
+static jboolean ICU_initLocaleDataNative(JNIEnv* env, jclass, jstring javaLanguageTag, jobject localeData) {
+    ScopedUtfChars languageTag(env, javaLanguageTag);
+    if (languageTag.c_str() == NULL) {
         return JNI_FALSE;
     }
-    if (localeName.size() >= ULOC_FULLNAME_CAPACITY) {
+    if (languageTag.size() >= ULOC_FULLNAME_CAPACITY) {
         return JNI_FALSE; // ICU has a fixed-length limit.
     }
 
-    ScopedIcuLocale icuLocale(env, javaLocaleName);
+    ScopedIcuLocale icuLocale(env, javaLanguageTag);
     if (!icuLocale.valid()) {
       return JNI_FALSE;
     }
@@ -530,27 +530,27 @@
     // Get the DateTimePatterns.
     UErrorCode status = U_ZERO_ERROR;
     bool foundDateTimePatterns = false;
-    for (LocaleNameIterator it(localeName.c_str(), status); it.HasNext(); it.Up()) {
+    for (LocaleNameIterator it(icuLocale.locale().getBaseName(), status); it.HasNext(); it.Up()) {
       if (getDateTimePatterns(env, localeData, it.Get())) {
           foundDateTimePatterns = true;
           break;
       }
     }
     if (!foundDateTimePatterns) {
-        ALOGE("Couldn't find ICU DateTimePatterns for %s", localeName.c_str());
+        ALOGE("Couldn't find ICU DateTimePatterns for %s", languageTag.c_str());
         return JNI_FALSE;
     }
 
     // Get the "Yesterday", "Today", and "Tomorrow" strings.
     bool foundYesterdayTodayAndTomorrow = false;
-    for (LocaleNameIterator it(localeName.c_str(), status); it.HasNext(); it.Up()) {
+    for (LocaleNameIterator it(icuLocale.locale().getBaseName(), status); it.HasNext(); it.Up()) {
       if (getYesterdayTodayAndTomorrow(env, localeData, icuLocale.locale(), it.Get())) {
         foundYesterdayTodayAndTomorrow = true;
         break;
       }
     }
     if (!foundYesterdayTodayAndTomorrow) {
-      ALOGE("Couldn't find ICU yesterday/today/tomorrow for %s", localeName.c_str());
+      ALOGE("Couldn't find ICU yesterday/today/tomorrow for %s", languageTag.c_str());
       return JNI_FALSE;
     }
 
@@ -621,14 +621,14 @@
     setNumberPatterns(env, localeData, icuLocale.locale());
     setDecimalFormatSymbolsData(env, localeData, icuLocale.locale());
 
-    jstring countryCode = env->NewStringUTF(Locale::createFromName(localeName.c_str()).getCountry());
+    jstring countryCode = env->NewStringUTF(icuLocale.locale().getCountry());
     jstring internationalCurrencySymbol = ICU_getCurrencyCode(env, NULL, countryCode);
     env->DeleteLocalRef(countryCode);
     countryCode = NULL;
 
     jstring currencySymbol = NULL;
     if (internationalCurrencySymbol != NULL) {
-        currencySymbol = ICU_getCurrencySymbol(env, NULL, javaLocaleName, internationalCurrencySymbol);
+        currencySymbol = ICU_getCurrencySymbol(env, NULL, javaLanguageTag, internationalCurrencySymbol);
     } else {
         internationalCurrencySymbol = env->NewStringUTF("XXX");
     }