Speed up the way we access ICU's locale data.

This patch makes creating a new NumberFormat or new SimpleDateFormat 2x faster.

Basically, the ResourceBundle mechanism is really expensive in several ways:

1. The two-level caching is unnecessary for locale data, and expensive because
   it burns through a lot of temporary objects.
2. The PrivilegedAction stuff is unnecessary and expensive because it too burns
   quite a few temporary objects (including an ArrayList for each call; should
   we consider removing support for SecurityManager so we can remove this cruft
   from our code?).
3. The caching in most cases doesn't cache anything useful; the ResourceBundles
   simply forward all questions straight to native code anyway, all we're
   caching is an unnecessary forwarding object (in a cache where lookups cost
   more than just creating a new unnecessary forwarding object would cost).

I've left CurrencyResourceBundle on the slow (ResourceBundle.getBundle) path
because I'm not yet sure how much of that path's semantics it relies on.

I still return LocaleResourceBundle instances (albeit via a much faster path)
but we should fix that. The native code returns an array which ResourceBundle
stuffs into a Hashtable and the calling code accesses via hash table lookups.
This despite the fact that the keys are a small fixed set known in advance.
We could make the native layer and the calling layer simpler and faster by
using a "struct", and doing so would make the middle layer go away completely.
diff --git a/libcore/icu/src/main/java/com/ibm/icu4jni/text/DecimalFormatSymbols.java b/libcore/icu/src/main/java/com/ibm/icu4jni/text/DecimalFormatSymbols.java
index 533c674..0ceb287 100644
--- a/libcore/icu/src/main/java/com/ibm/icu4jni/text/DecimalFormatSymbols.java
+++ b/libcore/icu/src/main/java/com/ibm/icu4jni/text/DecimalFormatSymbols.java
@@ -37,13 +37,7 @@
     
     public DecimalFormatSymbols(Locale locale) {
         this.loc = locale;
-        ResourceBundle bundle = AccessController.
-        doPrivileged(new PrivilegedAction<ResourceBundle>() {
-            public ResourceBundle run() {
-            return ResourceBundle.getBundle(
-                    "org.apache.harmony.luni.internal.locale.Locale", loc); //$NON-NLS-1$
-            }
-        });
+        ResourceBundle bundle = com.ibm.icu4jni.util.Resources.getLocaleInstance(locale);
         String pattern = bundle.getString("Number");
         this.addr = NativeDecimalFormat.openDecimalFormatImpl(
                 locale.toString(), pattern);
diff --git a/libcore/icu/src/main/java/com/ibm/icu4jni/util/Resources.java b/libcore/icu/src/main/java/com/ibm/icu4jni/util/Resources.java
index 7eeae7d..41bf3e3 100644
--- a/libcore/icu/src/main/java/com/ibm/icu4jni/util/Resources.java
+++ b/libcore/icu/src/main/java/com/ibm/icu4jni/util/Resources.java
@@ -16,34 +16,25 @@
 
 package com.ibm.icu4jni.util;
 
+import java.util.Arrays;
 import java.util.Enumeration;
 import java.util.ListResourceBundle;
+import java.util.Locale;
 import java.util.MissingResourceException;
 import java.util.ResourceBundle;
 import java.util.TimeZone;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.logging.Logger;
 
 /**
- * Helper class that delivers ResourceBundle instances expected by Harmony, but
- * with the data taken from ICU's database. This approach has a couple of
- * advantages:
- * <ol>
- * <li> We have less classes in the overall system, since we use different
- * instances for different ResourceBundles.
- * <li> We don't have these classes that consists of monstrous static arrays
- * with anymore.
- * <li> We have control over which values we load at which time or even cache
- * for later use.
- * <li> There is only one central place left in the system where I18N data needs
- * to be configured, namely ICU.
- * </ol>
- * Since we're mimicking the original Harmony ResourceBundle structures, most of
- * the Harmony code can stay the same. We basically just need to change the
- * ResourceBundle instantiation. Only the special case of the Locale bundles
- * needs some more tweaking, since we don't want to keep several hundred
- * timezone names in memory.
+ * Makes ICU data accessible to Java.
+ *
+ * TODO: finish removing the expensive ResourceBundle nonsense and rename this class.
  */
 public class Resources {
+    // A cache for the locale-specific data.
+    private static final ConcurrentHashMap<String, LocaleResourceBundle> localeInstanceCache =
+            new ConcurrentHashMap<String, LocaleResourceBundle>();
 
     /**
      * Cache for ISO language names.
@@ -66,41 +57,52 @@
     private static String[] availableTimezones = null;
 
     /**
-     * Creates ResourceBundle instance and fills it with ICU data.
-     * 
-     * @param bundleName The name of the requested Harmony resource bundle,
-     *            excluding the package name.
-     * @param locale The locale to use for the resources. A null value denotes
-     *            the default locale as configured in Java.
-     * @return The new ResourceBundle, or null, if no ResourceBundle was
-     *         created.
+     * Returns a LocaleResourceBundle corresponding to the given locale.
+     * TODO: return something that allows cheap static field lookup rather than
+     * expensive chained hash table lookup.
      */
+    public static ResourceBundle getLocaleInstance(Locale locale) {
+        if (locale == null) {
+            locale = Locale.getDefault();
+        }
+        String localeName = locale.toString();
+        LocaleResourceBundle bundle = localeInstanceCache.get(localeName);
+        if (bundle != null) {
+            return bundle;
+        }
+        bundle = makeLocaleResourceBundle(locale);
+        localeInstanceCache.put(localeName, bundle);
+        boolean absent = (localeInstanceCache.putIfAbsent(localeName, bundle) == null);
+        return absent ? bundle : localeInstanceCache.get(localeName);
+    }
+
+    private static LocaleResourceBundle makeLocaleResourceBundle(Locale locale) {
+        LocaleResourceBundle result = new LocaleResourceBundle(locale);
+        
+        // Anything not found in this ResourceBundle should be passed on to
+        // a parent ResourceBundle corresponding to the next-most-specific locale.
+        String country = locale.getCountry();
+        String language = locale.getLanguage();
+        if (locale.getVariant().length() > 0) {
+            result.setParent(getLocaleInstance(new Locale(language, country, "")));
+        } else if (country.length() > 0) {
+            result.setParent(getLocaleInstance(new Locale(language, "", "")));
+        } else if (language.length() > 0) {
+            result.setParent(getLocaleInstance(new Locale("", "", "")));
+        }
+        
+        return result;
+    }
+
+    // TODO: fix remaining caller and remove this.
     public static ResourceBundle getInstance(String bundleName, String locale) {
         if (locale == null) {
-            locale = java.util.Locale.getDefault().toString();
+            locale = Locale.getDefault().toString();
         }
-
-        if (bundleName.startsWith("Locale")) {
-            return new Locale(locale);
-        } else if (bundleName.startsWith("Country")) {
-            return new Country(locale);
-        } else if (bundleName.startsWith("Currency")) {
-            return new Currency(locale);
-        } else if (bundleName.startsWith("Language")) {
-            return new Language(locale);
-        } else if (bundleName.startsWith("Variant")) {
-            return new Variant(locale);
-        } else if (bundleName.equals("ISO3Countries")) {
-            return new ISO3Countries();
-        } else if (bundleName.equals("ISO3Languages")) {
-            return new ISO3Languages();
-        } else if (bundleName.equals("ISO4CurrenciesToDigits")) {
-            return new ISO4CurrenciesToDigits();
-        } else if (bundleName.equals("ISO4Currencies")) {
-            return new ISO4Currencies();
+        if (bundleName.startsWith("Currency")) {
+            return new CurrencyResourceBundle(locale);
         }
-
-        return null;
+        throw new AssertionError("bundle="+bundleName+" locale="+locale);
     }
 
     /**
@@ -174,13 +176,6 @@
     }
 
     /**
-     * Gets the name of the default locale.
-     */
-    private static String getDefaultLocaleName() {
-        return java.util.Locale.getDefault().toString();
-    }
-
-    /**
      * Initialization holder for default time zone names. This class will
      * be preloaded by the zygote to share the time and space costs of setting
      * up the list of time zone names, so although it looks like the lazy
@@ -190,7 +185,7 @@
         /**
          * Name of default locale at the time this class was initialized.
          */
-        private static final String locale = getDefaultLocaleName();
+        private static final String locale = Locale.getDefault().toString();
 
         /**
          * Names of time zones for the default locale.
@@ -260,7 +255,7 @@
      *         the TomeZone class.
      */
     public static String[][] getDisplayTimeZones(String locale) {
-        String defaultLocale = getDefaultLocaleName();
+        String defaultLocale = Locale.getDefault().toString();
         if (locale == null) {
             locale = defaultLocale;
         }
@@ -286,135 +281,16 @@
     // --- Specialized ResourceBundle subclasses ------------------------------
 
     /**
-     * Internal ResourceBundle mimicking the Harmony "ISO3Countries" bundle.
-     * Keys are the two-letter ISO country codes. Values are the three-letter
-     * ISO country abbreviations. An example entry is "US"->"USA".
-     */
-    private static final class ISO3Countries extends ResourceBundle {
-
-        @Override
-        public Enumeration<String> getKeys() {
-            // Won't get used
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        protected Object handleGetObject(String key) {
-            return getISO3CountryNative(key);
-        }
-
-    }
-
-    /**
-     * Internal ResourceBundle mimicking the Harmony "ISO3Languages" bundle.
-     * Keys are the two-letter ISO language codes. Values are the three-letter
-     * ISO language abbreviations. An example entry is "EN"->"ENG".
-     */
-    private static final class ISO3Languages extends ResourceBundle {
-
-        @Override
-        public Enumeration<String> getKeys() {
-            // Won't get used
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        protected Object handleGetObject(String key) {
-            return getISO3LanguageNative(key);
-        }
-
-    }
-
-    /**
-     * Internal ResourceBundle mimicking the Harmony "ISO4Currencies" bundle.
-     * Keys are the two-letter ISO language codes. Values are the three-letter
-     * ISO currency abbreviations. An example entry is "US"->"USD".
-     */
-    private static final class ISO4Currencies extends ResourceBundle {
-
-        @Override
-        public Enumeration<String> getKeys() {
-            // Won't get used
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        protected Object handleGetObject(String key) {
-            return getCurrencyCodeNative(key);
-        }
-
-    }
-
-    /**
-     * Internal ResourceBundle mimicking the Harmony "ISO4CurrenciesToDigits"
-     * bundle. Keys are the three-letter ISO currency codes. Values are strings
-     * containing the number of fraction digits to use for the currency. An
-     * example entry is "USD"->"2".
-     */
-    private static final class ISO4CurrenciesToDigits extends ResourceBundle {
-
-        @Override
-        public Enumeration<String> getKeys() {
-            // Won't get used
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        protected Object handleGetObject(String key) {
-            // In some places the triple-x code is used as the fall back
-            // currency. The harmony package returned -1 for this requested
-            // currency.
-            if ("XXX".equals(key)) {
-                return "-1";
-            }
-            int res = getFractionDigitsNative(key);
-            if (res < 0) {
-                throw new MissingResourceException("couldn't find resource.", 
-                        ISO4CurrenciesToDigits.class.getName(), key);
-            }
-            return Integer.toString(res);
-        }
-
-    }
-
-    /**
-     * Internal ResourceBundle mimicking the Harmony "Country_*" bundles. Keys
-     * are the two-letter ISO country codes. Values are the printable country
-     * names in terms of the specified locale. An example entry is "US"->"United
-     * States".
-     */
-    private static final class Country extends ResourceBundle {
-        private String locale;
-
-        public Country(String locale) {
-            super();
-            this.locale = locale;
-        }
-
-        @Override
-        public Enumeration<String> getKeys() {
-            // Won't get used
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        protected Object handleGetObject(String key) {
-            return getDisplayCountryNative(key, locale);
-        }
-
-    }
-
-    /**
      * Internal ResourceBundle mimicking the Harmony "Currency_*" bundles. Keys
      * are the three-letter ISO currency codes. Values are the printable
      * currency names in terms of the specified locale. An example entry is
      * "USD"->"$" (for inside the US) and "USD->"US$" (for outside the US).
      */
-    private static final class Currency extends ResourceBundle {
+    private static final class CurrencyResourceBundle extends ResourceBundle {
 
         private String locale;
 
-        public Currency(String locale) {
+        public CurrencyResourceBundle(String locale) {
             super();
             this.locale = locale;
         }
@@ -433,105 +309,64 @@
     }
 
     /**
-     * Internal ResourceBundle mimicking the Harmony "Language_*" bundles. Keys
-     * are the two-letter ISO language codes. Values are the printable language
-     * names in terms of the specified locale. An example entry is
-     * "en"->"English".
-     */
-    private static final class Language extends ResourceBundle {
-        private String locale;
-
-        public Language(String locale) {
-            super();
-            this.locale = locale;
-        }
-
-        @Override
-        public Enumeration<String> getKeys() {
-            // Won't get used
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        protected Object handleGetObject(String key) {
-            return getDisplayLanguageNative(key, locale);
-        }
-
-    }
-
-    /**
-     * Internal ResourceBundle mimicking the Harmony "Variant_*" bundles. Keys
-     * are a fixed set of variants codes known to Harmony. Values are the
-     * printable variant names in terms of the specified locale. An example
-     * entry is "EURO"->"Euro".
-     */
-    private static final class Variant extends ResourceBundle {
-
-        private String locale;
-
-        public Variant(String locale) {
-            super();
-            this.locale = locale;
-        }
-
-        @Override
-        public Enumeration<String> getKeys() {
-            // Won't get used
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        protected Object handleGetObject(String key) {
-            return getDisplayVariantNative(key, locale);
-        }
-
-    }
-
-    /**
-     * Internal ResourceBundle mimicking the Harmony "Locale_*" bundles. This is
-     * clearly the most complex case, because the content covers a wide range of
+     * Internal ResourceBundle mimicking the Harmony "Locale_*" bundles.
+     * The content covers a wide range of
      * data items, with values even being arrays in some cases. Note we are
      * cheating with the "timezones" entry, since we normally don't want to
      * waste our precious RAM on several thousand of these Strings.
      */
-    private static final class Locale extends ListResourceBundle {
-
-        private String locale;
-
-        public Locale(String locale) {
-            super();
+    private static final class LocaleResourceBundle extends ListResourceBundle {
+        private final Locale locale;
+        
+        public LocaleResourceBundle(Locale locale) {
             this.locale = locale;
         }
+        
+        // We can't set the superclass' locale field, so we need our own, and our own accessor.
+        @Override
+        public Locale getLocale() {
+            return locale;
+        }
 
         @Override
         protected Object[][] getContents() {
-            return getContentImpl(locale, false);
+            return getContentImpl(locale.toString());
         }
 
+        // Increase accessibility of this method so we can call it.
+        @Override
+        public void setParent(ResourceBundle bundle) {
+            this.parent = bundle;
+        }
+        
+        @Override
+        public String toString() {
+            StringBuilder result = new StringBuilder();
+            result.append("LocaleResourceBundle[locale=");
+            result.append(getLocale());
+            result.append(",contents=");
+            result.append(Arrays.deepToString(getContents()));
+            return result.toString();
+        }
     }
 
     // --- Native methods accessing ICU's database ----------------------------
 
-    private static native int getFractionDigitsNative(String currencyCode);
+    public static native String getDisplayCountryNative(String countryCode, String locale);
+    public static native String getDisplayLanguageNative(String languageCode, String locale);
+    public static native String getDisplayVariantNative(String variantCode, String locale);
 
-    private static native String getCurrencyCodeNative(String locale);
+    public static native String getISO3CountryNative(String locale);
+    public static native String getISO3LanguageNative(String locale);
 
-    private static native String getCurrencySymbolNative(String locale, String currencyCode);
+    public static native String getCurrencyCodeNative(String locale);
+    public static native String getCurrencySymbolNative(String locale, String currencyCode);
 
-    private static native String getDisplayCountryNative(String countryCode, String locale);
-
-    private static native String getDisplayLanguageNative(String languageCode, String locale);
-
-    private static native String getDisplayVariantNative(String variantCode, String locale);
-
-    private static native String getISO3CountryNative(String locale);
-
-    private static native String getISO3LanguageNative(String locale);
+    public static native int getCurrencyFractionDigitsNative(String currencyCode);
 
     private static native String[] getAvailableLocalesNative();
 
     private static native String[] getISOLanguagesNative();
-
     private static native String[] getISOCountriesNative();
 
     private static native void getTimeZonesNative(String[][] arrayToFill, String locale);
@@ -539,5 +374,5 @@
     private static native String getDisplayTimeZoneNative(String id, boolean isDST, int style,
             String locale);
 
-    private static native Object[][] getContentImpl(String locale, boolean needsTimeZones);
+    private static native Object[][] getContentImpl(String locale);
 }
diff --git a/libcore/icu/src/main/native/ResourceInterface.cpp b/libcore/icu/src/main/native/ResourceInterface.cpp
index 731cf3f..243d7c4 100644
--- a/libcore/icu/src/main/native/ResourceInterface.cpp
+++ b/libcore/icu/src/main/native/ResourceInterface.cpp
@@ -54,9 +54,9 @@
     int stringLength = string.length();
     jchar *res = (jchar *) malloc(sizeof(jchar) * (stringLength + 1));
     string.extract(res, stringLength+1, status);
-    if(U_FAILURE(status)) {
+    if (U_FAILURE(status)) {
         free(res);
-        LOGI("Error getting string for getJStringFromUnicodeString");
+        LOGE("Error getting string for getJStringFromUnicodeString: %s", u_errorName(status));
         status = U_ZERO_ERROR;
         return NULL;
     }
@@ -76,10 +76,7 @@
     env->DeleteLocalRef(element);
 } 
 
-static jint getFractionDigitsNative(JNIEnv* env, jclass clazz, 
-        jstring currencyCode) {
-    // LOGI("ENTER getFractionDigitsNative");
-    
+static jint getCurrencyFractionDigitsNative(JNIEnv* env, jclass clazz, jstring currencyCode) {
     UErrorCode status = U_ZERO_ERROR;
     
     NumberFormat *fmt = NumberFormat::createCurrencyInstance(status);
@@ -229,135 +226,77 @@
 
 static jstring getDisplayCountryNative(JNIEnv* env, jclass clazz, 
         jstring targetLocale, jstring locale) {
-    // LOGI("ENTER getDisplayCountryNative");
-    
-    UErrorCode status = U_ZERO_ERROR;
 
     Locale loc = getLocale(env, locale);
     Locale targetLoc = getLocale(env, targetLocale);
-    
+
     UnicodeString string;
     targetLoc.getDisplayCountry(loc, string);
-
-    jstring result = getJStringFromUnicodeString(env, string);
-
-    return result;
+    return getJStringFromUnicodeString(env, string);
 }
 
 static jstring getDisplayLanguageNative(JNIEnv* env, jclass clazz, 
         jstring targetLocale, jstring locale) {
-    // LOGI("ENTER getDisplayLanguageNative");
 
     Locale loc = getLocale(env, locale);
     Locale targetLoc = getLocale(env, targetLocale);
 
     UnicodeString string;
     targetLoc.getDisplayLanguage(loc, string);
-    
-    jstring result = getJStringFromUnicodeString(env, string);
-    
-    return result;
+    return getJStringFromUnicodeString(env, string);
 }
 
 static jstring getDisplayVariantNative(JNIEnv* env, jclass clazz, 
         jstring targetLocale, jstring locale) {
-    // LOGI("ENTER getDisplayVariantNative");
 
     Locale loc = getLocale(env, locale);
     Locale targetLoc = getLocale(env, targetLocale);
-    
+
     UnicodeString string;
     targetLoc.getDisplayVariant(loc, string);
-    
-    jstring result = getJStringFromUnicodeString(env, string);
-    
-    return result;
+    return getJStringFromUnicodeString(env, string);
 }
 
 static jstring getISO3CountryNative(JNIEnv* env, jclass clazz, jstring locale) {
-    // LOGI("ENTER getISO3CountryNative");
-
     Locale loc = getLocale(env, locale);
-    
-    const char *string = loc.getISO3Country();
-    
-    jstring result = env->NewStringUTF(string);
-    
-    return result;
+    return env->NewStringUTF(loc.getISO3Country());
 }
 
 static jstring getISO3LanguageNative(JNIEnv* env, jclass clazz, jstring locale) {
-    // LOGI("ENTER getISO3LanguageNative");
-
     Locale loc = getLocale(env, locale);
-    
-    const char *string = loc.getISO3Language();
-    
-    jstring result = env->NewStringUTF(string);
-    
+    return env->NewStringUTF(loc.getISO3Language());
+}
+
+static jobjectArray toStringArray(JNIEnv* env, const char* const* strings) {
+    size_t count = 0;
+    while (strings[count] != NULL) {
+        ++count;
+    }
+    jobjectArray result = env->NewObjectArray(count, string_class, NULL);
+    for (size_t i = 0; i < count; ++i) {
+        jstring s = env->NewStringUTF(strings[i]);
+        env->SetObjectArrayElement(result, i, s);
+        env->DeleteLocalRef(s);
+    }
     return result;
 }
 
 static jobjectArray getISOCountriesNative(JNIEnv* env, jclass clazz) {
-    // LOGI("ENTER getISOCountriesNative");
-
-    const char* const* strings = Locale::getISOCountries();
-    
-    int count = 0;
-    while(strings[count] != NULL) {
-        count++;
-    }
-  
-    jobjectArray result = env->NewObjectArray(count, string_class, NULL);
-
-    jstring res;
-    for(int i = 0; i < count; i++) {
-        res = env->NewStringUTF(strings[i]);
-        env->SetObjectArrayElement(result, i, res);
-        env->DeleteLocalRef(res);
-    }
-    return result;
+    return toStringArray(env, Locale::getISOCountries());
 }
 
 static jobjectArray getISOLanguagesNative(JNIEnv* env, jclass clazz) {
-    // LOGI("ENTER getISOLanguagesNative");
-    
-    const char* const* strings = Locale::getISOLanguages();
-    
-    const char *string = strings[0];
-    
-    int count = 0;
-    while(strings[count] != NULL) {
-        count++;
-    }
-    
-    jobjectArray result = env->NewObjectArray(count, string_class, NULL);
-
-    jstring res;
-    for(int i = 0; i < count; i++) {
-        res = env->NewStringUTF(strings[i]);
-        env->SetObjectArrayElement(result, i, res);
-        env->DeleteLocalRef(res);
-    }
-    return result;
+    return toStringArray(env, Locale::getISOLanguages());
 }
 
 static jobjectArray getAvailableLocalesNative(JNIEnv* env, jclass clazz) {
-    // LOGI("ENTER getAvailableLocalesNative");
-    
-    int count = uloc_countAvailable();
-    
+    size_t count = uloc_countAvailable();
     jobjectArray result = env->NewObjectArray(count, string_class, NULL);
-
-    jstring res;
-    const char * string;    
-    for(int i = 0; i < count; i++) {
-        string = uloc_getAvailable(i);
-        res = env->NewStringUTF(string);
-        env->SetObjectArrayElement(result, i, res);
-        env->DeleteLocalRef(res);
+    for (size_t i = 0; i < count; ++i) {
+        jstring s = env->NewStringUTF(uloc_getAvailable(i));
+        env->SetObjectArrayElement(result, i, s);
+        env->DeleteLocalRef(s);
     }
-
     return result;
 }
 
@@ -497,64 +436,53 @@
     return result;
 }
 
-static void getDayInitVector(JNIEnv *env, UResourceBundle *gregorian, int *values) {
-
-    UErrorCode status = U_ZERO_ERROR;
+static void getDayIntVector(JNIEnv *env, UResourceBundle *gregorian, int *values) {
 
     // get the First day of week and the minimal days in first week numbers
+    UErrorCode status = U_ZERO_ERROR;
     UResourceBundle *gregorianElems = ures_getByKey(gregorian, "DateTimeElements", NULL, &status);
-    if(U_FAILURE(status)) {
+    if (U_FAILURE(status)) {
         return;
     }
 
     int intVectSize;
-    const int *result;
-    result = ures_getIntVector(gregorianElems, &intVectSize, &status);
-    if(U_FAILURE(status)) {
+    const int* result = ures_getIntVector(gregorianElems, &intVectSize, &status);
+    if (U_FAILURE(status)) {
         ures_close(gregorianElems);
         return;
     }
-
-    if(intVectSize == 2) {
+    
+    if (intVectSize == 2) {
         values[0] = result[0];
         values[1] = result[1];
     }
 
     ures_close(gregorianElems);
-
 }
 
 static jobjectArray getAmPmMarkers(JNIEnv *env, UResourceBundle *gregorian) {
-    
-    jobjectArray amPmMarkers;
-    jstring pmU, amU;
-
     UErrorCode status = U_ZERO_ERROR;
-
-    UResourceBundle *gregorianElems;
-
-    gregorianElems = ures_getByKey(gregorian, "AmPmMarkers", NULL, &status);
-    if(U_FAILURE(status)) {
+    UResourceBundle *gregorianElems = ures_getByKey(gregorian, "AmPmMarkers", NULL, &status);
+    if (U_FAILURE(status)) {
         return NULL;
     }
 
-    int lengthAm, lengthPm;
-
     ures_resetIterator(gregorianElems);
 
+    int lengthAm, lengthPm;
     const jchar* am = ures_getStringByIndex(gregorianElems, 0, &lengthAm, &status);
     const jchar* pm = ures_getStringByIndex(gregorianElems, 1, &lengthPm, &status);
 
-    if(U_FAILURE(status)) {
+    if (U_FAILURE(status)) {
         ures_close(gregorianElems);
         return NULL;
     }
     
-    amPmMarkers = env->NewObjectArray(2, string_class, NULL);
-    amU = env->NewString(am, lengthAm);
+    jobjectArray amPmMarkers = env->NewObjectArray(2, string_class, NULL);
+    jstring amU = env->NewString(am, lengthAm);
     env->SetObjectArrayElement(amPmMarkers, 0, amU);
     env->DeleteLocalRef(amU);
-    pmU = env->NewString(pm, lengthPm);
+    jstring pmU = env->NewString(pm, lengthPm);
     env->SetObjectArrayElement(amPmMarkers, 1, pmU);
     env->DeleteLocalRef(pmU);
     ures_close(gregorianElems);
@@ -813,41 +741,26 @@
 }
 
 static jstring getDecimalPatternChars(JNIEnv *env, UResourceBundle *rootElems) {
-    
     UErrorCode status = U_ZERO_ERROR;
 
     int zeroL, digitL, decSepL, groupL, listL, percentL, permillL, expL, currSepL, minusL;
-    int patternLength;
-
-    jchar *patternChars;
 
     const jchar* zero = ures_getStringByIndex(rootElems, 4, &zeroL, &status);
-
     const jchar* digit = ures_getStringByIndex(rootElems, 5, &digitL, &status);
-
     const jchar* decSep = ures_getStringByIndex(rootElems, 0, &decSepL, &status);
-
     const jchar* group = ures_getStringByIndex(rootElems, 1, &groupL, &status);
-
     const jchar* list = ures_getStringByIndex(rootElems, 2, &listL, &status);
-
     const jchar* percent = ures_getStringByIndex(rootElems, 3, &percentL, &status);
-
     const jchar* permill = ures_getStringByIndex(rootElems, 8, &permillL, &status);
-
     const jchar* exp = ures_getStringByIndex(rootElems, 7, &expL, &status);
-
     const jchar* currSep = ures_getStringByIndex(rootElems, 0, &currSepL, &status);
-
     const jchar* minus = ures_getStringByIndex(rootElems, 6, &minusL, &status);
 
-    if(U_FAILURE(status)) {
+    if (U_FAILURE(status)) {
         return NULL;
     }
 
-
-    patternChars = (jchar *) malloc(11 * sizeof(jchar));
-
+    jchar patternChars[11];
     patternChars[0] = 0;
 
     u_strncat(patternChars, zero, 1);
@@ -861,11 +774,7 @@
     u_strncat(patternChars, currSep, 1);
     u_strncat(patternChars, minus, 1);
 
-    jstring decimalPatternChars = env->NewString(patternChars, 10);
-
-    free(patternChars);
-
-    return decimalPatternChars;
+    return env->NewString(patternChars, 10);
 }
 
 static jstring getIntCurrencyCode(JNIEnv *env, jclass clazz, jstring locale) {
@@ -891,32 +800,21 @@
 }
 
 static jstring getCurrencySymbol(JNIEnv *env, jclass clazz, jstring locale, jstring intCurrencySymbol) {
-
     jstring result = getCurrencySymbolNative(env, clazz, locale, intCurrencySymbol);
-    if(result == intCurrencySymbol) {
-        return NULL;
-    }
-    return result;
-
+    return (result == intCurrencySymbol) ? NULL : result;
 }
 
-static jobjectArray getContentImpl(JNIEnv* env, jclass clazz, 
-        jstring locale, jboolean needsTZ) {
-    
-    UErrorCode status = U_ZERO_ERROR;
-
+static jobjectArray getContentImpl(JNIEnv* env, jclass clazz, jstring locale) {
     const char *loc = env->GetStringUTFChars(locale, NULL);
-    UResourceBundle *root = ures_openU(NULL, loc, &status);
-
+    UErrorCode status = U_ZERO_ERROR;
+    UResourceBundle* root = ures_openU(NULL, loc, &status);
     env->ReleaseStringUTFChars(locale, loc);
-    if(U_FAILURE(status)) {
-        LOGI("Error getting resources");
+    if (U_FAILURE(status)) {
+        LOGE("Error getting resources: %s", u_errorName(status));
         status = U_ZERO_ERROR;
         return NULL;
     }
 
-
-
     jclass obj_class = env->FindClass("[Ljava/lang/Object;");
     jclass integer_class = env->FindClass("java/lang/Integer");
     jmethodID integerInit = env->GetMethodID(integer_class, "<init>", "(I)V");
@@ -952,7 +850,6 @@
 
     int counter = 0;
 
-    int firstDayVals[2] = {-1, -1};
 
     const jchar* nan = (const jchar *)NULL;
     const jchar* inf = (const jchar *)NULL;
@@ -981,7 +878,8 @@
 
 
     // adding the first day of week and minimal days in first week values
-    getDayInitVector(env, gregorian, firstDayVals);
+    int firstDayVals[2] = {-1, -1};
+    getDayIntVector(env, gregorian, firstDayVals);
     if((firstDayVals[0] != -1) && (firstDayVals[1] != -1)) {
         firstDayOfWeek = env->NewObject(integer_class, integerInit, firstDayVals[0]);
         minimalDaysInFirstWeek = env->NewObject(integer_class, integerInit, firstDayVals[1]);
@@ -1254,20 +1152,10 @@
     ures_close(root);
 
 
-    if(needsTZ == JNI_TRUE) {
-        counter++; //add empty timezone
-    }
-
-
-
     // collect all content and put it into an array
     result = env->NewObjectArray(counter, obj_class, NULL);
 
     int index = 0;
-    
-    if(needsTZ == JNI_TRUE) {
-        addObject(env, result, "timezones", NULL, index++);
-    }
     if(firstDayOfWeek != NULL && index < counter) {
         addObject(env, result, "First_Day", firstDayOfWeek, index++);
     }
@@ -1353,16 +1241,16 @@
 
 static JNINativeMethod gMethods[] = {
     /* name, signature, funcPtr */
-    {"getFractionDigitsNative", "(Ljava/lang/String;)I",                   
-            (void*) getFractionDigitsNative},
+    {"getCurrencyFractionDigitsNative", "(Ljava/lang/String;)I",
+            (void*) getCurrencyFractionDigitsNative},
     {"getCurrencyCodeNative", "(Ljava/lang/String;)Ljava/lang/String;",
             (void*) getCurrencyCodeNative},
     {"getCurrencySymbolNative", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
             (void*) getCurrencySymbolNative},
-    {"getDisplayCountryNative", 
+    {"getDisplayCountryNative",
             "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
             (void*) getDisplayCountryNative},
-    {"getDisplayLanguageNative", 
+    {"getDisplayLanguageNative",
             "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
             (void*) getDisplayLanguageNative},
     {"getDisplayVariantNative",
@@ -1380,14 +1268,14 @@
             (void*) getISOLanguagesNative},
     {"getAvailableLocalesNative", "()[Ljava/lang/String;",
             (void*) getAvailableLocalesNative},
-    {"getTimeZonesNative", 
+    {"getTimeZonesNative",
             "([[Ljava/lang/String;Ljava/lang/String;)V",
             (void*) getTimeZonesNative},
-    {"getDisplayTimeZoneNative", 
+    {"getDisplayTimeZoneNative",
             "(Ljava/lang/String;ZILjava/lang/String;)Ljava/lang/String;",
             (void*) getDisplayTimeZoneNative},
-    {"getContentImpl", 
-            "(Ljava/lang/String;Z)[[Ljava/lang/Object;",
+    {"getContentImpl",
+            "(Ljava/lang/String;)[[Ljava/lang/Object;",
             (void*) getContentImpl},
 };
 
diff --git a/libcore/luni/src/main/java/java/util/Calendar.java b/libcore/luni/src/main/java/java/util/Calendar.java
index 49a8c3f..3d1f4ad 100644
--- a/libcore/luni/src/main/java/java/util/Calendar.java
+++ b/libcore/luni/src/main/java/java/util/Calendar.java
@@ -691,7 +691,7 @@
         //                 .getTimeZone(timezone.getID()), locale);
         // setFirstDayOfWeek(icuCalendar.getFirstDayOfWeek());
         // setMinimalDaysInFirstWeek(icuCalendar.getMinimalDaysInFirstWeek());
-        ResourceBundle bundle = Locale.getBundle("Locale", locale); //$NON-NLS-1$
+        ResourceBundle bundle = com.ibm.icu4jni.util.Resources.getLocaleInstance(locale);
         setFirstDayOfWeek(((Integer) bundle.getObject("First_Day")).intValue()); //$NON-NLS-1$
         setMinimalDaysInFirstWeek(((Integer) bundle.getObject("Minimal_Days")) //$NON-NLS-1$
                 .intValue());
diff --git a/libcore/luni/src/main/java/java/util/Currency.java b/libcore/luni/src/main/java/java/util/Currency.java
index 79de49d..1673455 100644
--- a/libcore/luni/src/main/java/java/util/Currency.java
+++ b/libcore/luni/src/main/java/java/util/Currency.java
@@ -18,10 +18,14 @@
 package java.util;
 
 // BEGIN android-added
+import com.ibm.icu4jni.util.Resources;
+import java.util.logging.Logger;
 import org.apache.harmony.luni.util.Msg;
 // END android-added
 
+import java.security.AccessController;
 import java.io.Serializable;
+import java.security.PrivilegedAction;
 
 /**
  * This class represents a currency as identified in the ISO 4217 currency
@@ -31,25 +35,36 @@
 
     private static final long serialVersionUID = -158308464356906721L;
 
-    private static Hashtable<String, Currency> codesToCurrencies = new Hashtable<String, Currency>();
+    private static final Hashtable<String, Currency> codesToCurrencies = new Hashtable<String, Currency>();
 
-    private String currencyCode;
+    private final String currencyCode;
 
     // BEGIN android-added
-    private static String currencyVars = "EURO, HK, PREEURO"; //$NON-NLS-1$
-
+    // TODO: this isn't set if we're restored from serialized form,
+    // so getDefaultFractionDigits always returns 0!
     private transient int defaultFractionDigits;
     // END android-added
 
-    /**
-     * @param currencyCode
-     */
     private Currency(String currencyCode) {
+        // BEGIN android-changed
         this.currencyCode = currencyCode;
+
+        // In some places the code XXX is used as the fall back currency.
+        // The RI returns -1, but ICU defaults to 2 for unknown currencies.
+        if (currencyCode.equals("XXX")) {
+            this.defaultFractionDigits = -1;
+            return;
+        }
+
+        this.defaultFractionDigits = Resources.getCurrencyFractionDigitsNative(currencyCode);
+        if (defaultFractionDigits < 0) {
+            throw new IllegalArgumentException(Msg.getString("K0322", currencyCode));
+        }
+        // END android-changed
     }
 
     /**
-     * Returns the {@code Currency} instance for this currency code.
+     * Returns the {@code Currency} instance for the given currency code.
      * <p>
      *
      * @param currencyCode
@@ -61,29 +76,14 @@
      *             code.
      */
     public static Currency getInstance(String currencyCode) {
+        // BEGIN android-changed
         Currency currency = codesToCurrencies.get(currencyCode);
-
         if (currency == null) {
-            // BEGIN android-added
-            ResourceBundle bundle = Locale.getBundle(
-                    "ISO4CurrenciesToDigits", Locale.getDefault()); //$NON-NLS-1$
             currency = new Currency(currencyCode);
-
-            String defaultFractionDigits = null;
-            try {
-                defaultFractionDigits = bundle.getString(currencyCode);
-            } catch (MissingResourceException e) {
-                throw new IllegalArgumentException(
-                        org.apache.harmony.luni.util.Msg.getString(
-                                "K0322", currencyCode)); //$NON-NLS-1$
-            }
-            currency.defaultFractionDigits = Integer
-                    .parseInt(defaultFractionDigits);
-            // END android-added
             codesToCurrencies.put(currencyCode, currency);
         }
-
         return currency;
+        // END android-changed
     }
 
     /**
@@ -98,38 +98,20 @@
      */
     public static Currency getInstance(Locale locale) {
         // BEGIN android-changed
-        // com.ibm.icu.util.Currency currency = null;
-        // try {
-        //     currency = com.ibm.icu.util.Currency.getInstance(locale);
-        // } catch (IllegalArgumentException e) {
-        //     return null;
-        // }
-        // if (currency == null) {
-        //     throw new IllegalArgumentException(locale.getCountry());
-        // }
-        // String currencyCode = currency.getCurrencyCode();
         String country = locale.getCountry();
         String variant = locale.getVariant();
-        if (!variant.equals("") && currencyVars.indexOf(variant) > -1) { //$NON-NLS-1$
-            country = country + "_" + variant; //$NON-NLS-1$
+        if (variant.length() > 0 && "EURO, HK, PREEURO".indexOf(variant) != -1) {
+            country = country + "_" + variant;
         }
 
-        ResourceBundle bundle = Locale.getBundle(
-                "ISO4Currencies", Locale.getDefault()); //$NON-NLS-1$
-        String currencyCode = null;
-        try {
-            currencyCode = bundle.getString(country);
-        } catch (MissingResourceException e) {
-            throw new IllegalArgumentException(Msg.getString(
-                    "K0323", locale.toString())); //$NON-NLS-1$
-        }
-        // END android-changed
-
-        if (currencyCode.equals("None")) { //$NON-NLS-1$
+        String currencyCode = com.ibm.icu4jni.util.Resources.getCurrencyCodeNative(country);
+        if (currencyCode == null) {
+            throw new IllegalArgumentException(Msg.getString("K0323", locale.toString()));
+        } else if (currencyCode.equals("None")) {
             return null;
         }
-
         return getInstance(currencyCode);
+        // END android-changed
     }
 
     /**
@@ -176,40 +158,38 @@
      *         locale.
      */
     public String getSymbol(Locale locale) {
-        if (locale.getCountry().equals("")) { //$NON-NLS-1$
+        // BEGIN android-changed
+        if (locale.getCountry().length() == 0) {
             return currencyCode;
         }
 
-        // BEGIN android-changed
-        // return com.ibm.icu.util.Currency.getInstance(currencyCode).getSymbol(locale);
-        // check in the Locale bundle first, if the local has the same currency
-        ResourceBundle bundle = Locale.getBundle("Locale", locale); //$NON-NLS-1$
-        if (((String) bundle.getObject("IntCurrencySymbol")) //$NON-NLS-1$
-                .equals(currencyCode)) {
-            return (String) bundle.getObject("CurrencySymbol"); //$NON-NLS-1$
+        // Check the Locale bundle first, in case the locale has the same currency.
+        ResourceBundle localeBundle = com.ibm.icu4jni.util.Resources.getLocaleInstance(locale);
+        if (localeBundle.getString("IntCurrencySymbol").equals(currencyCode)) {
+            return localeBundle.getString("CurrencySymbol");
         }
 
-        // search for a Currency bundle
-        bundle = null;
+        // check if the currency bundle for this locale has an entry for this currency
         try {
-            bundle = Locale.getBundle("Currency", locale); //$NON-NLS-1$
+            ResourceBundle currencyBundle = getCurrencyBundle(locale);
+
+            // is the bundle found for a different country? (for instance the
+            // default locale's currency bundle)
+            if (!currencyBundle.getLocale().getCountry().equals(locale.getCountry())) {
+                // TODO: the fact I never see output from this when running the tests suggests we
+                // don't have a test for this. Does it ever even happen? If it does, can we just
+                // ask ICU a slightly different question to get the behavior we want?
+                Logger.global.info("currencyBundle " + currencyBundle + " for " + locale +
+                        " came back with locale " + currencyBundle.getLocale());
+                return currencyCode;
+            }
+
+            // Return the currency bundle's value, or currencyCode.
+            String result = (String) currencyBundle.handleGetObject(currencyCode);
+            return (result != null) ? result : currencyCode;
         } catch (MissingResourceException e) {
             return currencyCode;
         }
-
-        // is the bundle found for a different country? (for instance the
-        // default locale's currency bundle)
-        if (!bundle.getLocale().getCountry().equals(locale.getCountry())) {
-            return currencyCode;
-        }
-
-        // check if the currency bundle for this locale
-        // has an entry for this currency
-        String result = (String) bundle.handleGetObject(currencyCode);
-        if (result != null) {
-            return result;
-        }
-        return currencyCode;
         // END android-changed
     }
 
@@ -241,4 +221,14 @@
     private Object readResolve() {
         return getInstance(currencyCode);
     }
+
+    // TODO: remove this in favor of direct access (and no ResourceBundle cruft).
+    private static ResourceBundle getCurrencyBundle(final Locale locale) {
+        return AccessController.doPrivileged(new PrivilegedAction<ResourceBundle>() {
+            public ResourceBundle run() {
+                String bundle = "org.apache.harmony.luni.internal.locale.Currency";
+                return ResourceBundle.getBundle(bundle, locale);
+            }
+        });
+    }
 }
diff --git a/libcore/luni/src/main/java/java/util/Locale.java b/libcore/luni/src/main/java/java/util/Locale.java
index 1e97d3c..5337e48 100644
--- a/libcore/luni/src/main/java/java/util/Locale.java
+++ b/libcore/luni/src/main/java/java/util/Locale.java
@@ -25,7 +25,6 @@
 import java.io.ObjectStreamField;
 import java.io.Serializable;
 import java.security.AccessController;
-import java.security.PrivilegedAction;
 // import java.util.zip.ZipEntry;
 // import java.util.zip.ZipFile;
 
@@ -200,6 +199,7 @@
     private transient String countryCode;
     private transient String languageCode;
     private transient String variantCode;
+    private transient String cachedToStringResult;
 
     // BEGIN android-removed
     // private transient ULocale uLocale;
@@ -412,29 +412,18 @@
      *            the {@code Locale} for which the display name is retrieved.
      * @return a country name.
      */
-	public String getDisplayCountry(Locale locale) {
+    public String getDisplayCountry(Locale locale) {
         // BEGIN android-changed
-		// return ULocale.forLocale(this).getDisplayCountry(ULocale.forLocale(locale));
         if (countryCode.length() == 0) {
             return countryCode;
         }
-        try {
-            // First try the specified locale
-            ResourceBundle bundle = getBundle("Country", locale); //$NON-NLS-1$
-            String result = bundle.getString(this.toString());
-            if (result != null) {
-                return result;
-            }
-            // Now use the default locale
-            if (locale != Locale.getDefault()) {
-                bundle = getBundle("Country", Locale.getDefault()); //$NON-NLS-1$
-            }
-            return bundle.getString(countryCode);
-        } catch (MissingResourceException e) {
-            return countryCode;
+        String result = Resources.getDisplayCountryNative(toString(), locale.toString());
+        if (result == null) { // TODO: do we need to do this, or does ICU do it for us?
+            result = Resources.getDisplayCountryNative(toString(), Locale.getDefault().toString());
         }
+        return result;
         // END android-changed
-	}
+    }
 
     /**
      * Gets the full language name in the default {@code Locale} for the language code
@@ -456,29 +445,18 @@
      *            the {@code Locale} for which the display name is retrieved.
      * @return a language name.
      */
-	public String getDisplayLanguage(Locale locale) {
+    public String getDisplayLanguage(Locale locale) {
         // BEGIN android-changed
-        // return ULocale.forLocale(this).getDisplayLanguage(ULocale.forLocale(locale));
         if (languageCode.length() == 0) {
             return languageCode;
         }
-        try {
-            // First try the specified locale
-            ResourceBundle bundle = getBundle("Language", locale); //$NON-NLS-1$
-            String result = bundle.getString(this.toString());
-            if (result != null) {
-                return result;
-            }
-            // Now use the default locale
-            if (locale != Locale.getDefault()) {
-                bundle = getBundle("Language", Locale.getDefault()); //$NON-NLS-1$
-            }
-            return bundle.getString(languageCode);
-        } catch (MissingResourceException e) {
-            return languageCode;
+        String result = Resources.getDisplayLanguageNative(toString(), locale.toString());
+        if (result == null) { // TODO: do we need to do this, or does ICU do it for us?
+            result = Resources.getDisplayLanguageNative(toString(), Locale.getDefault().toString());
         }
+        return result;
         // END android-changed
-	}
+    }
 
     /**
      * Gets the full language, country, and variant names in the default {@code Locale}
@@ -547,29 +525,18 @@
      *            the {@code Locale} for which the display name is retrieved.
      * @return a variant name.
      */
-	public String getDisplayVariant(Locale locale) {
+    public String getDisplayVariant(Locale locale) {
         // BEGIN android-changed
-        // return ULocale.forLocale(this).getDisplayVariant(ULocale.forLocale(locale));
         if (variantCode.length() == 0) {
             return variantCode;
         }
-        try {
-            // First try the specified locale
-            ResourceBundle bundle = getBundle("Variant", locale); //$NON-NLS-1$
-            String result = bundle.getString(this.toString());
-            if (result != null) {
-                return result;
-            }
-            // Now use the default locale
-            if (locale != Locale.getDefault()) {
-                bundle = getBundle("Variant", Locale.getDefault()); //$NON-NLS-1$
-            }
-            return bundle.getString(variantCode);
-        } catch (MissingResourceException e) {
-            return variantCode;
+        String result = Resources.getDisplayVariantNative(toString(), locale.toString());
+        if (result == null) { // TODO: do we need to do this, or does ICU do it for us?
+            result = Resources.getDisplayVariantNative(toString(), Locale.getDefault().toString());
         }
+        return result;
         // END android-changed
-	}
+    }
 
     /**
      * Gets the three letter ISO country code which corresponds to the country
@@ -579,16 +546,14 @@
      * @throws MissingResourceException
      *                if there is no matching three letter ISO country code.
      */
-	public String getISO3Country() throws MissingResourceException {
+    public String getISO3Country() throws MissingResourceException {
         // BEGIN android-changed
-        // return ULocale.forLocale(this).getISO3Country();
         if (countryCode.length() == 0) {
-            return ""; //$NON-NLS-1$
+            return countryCode;
         }
-        ResourceBundle bundle = getBundle("ISO3Countries", this); //$NON-NLS-1$
-        return bundle.getString(this.toString());
+        return Resources.getISO3CountryNative(toString());
         // END android-changed
-	}
+    }
 
     /**
      * Gets the three letter ISO language code which corresponds to the language
@@ -598,16 +563,14 @@
      * @throws MissingResourceException
      *                if there is no matching three letter ISO language code.
      */
-	public String getISO3Language() throws MissingResourceException {
+    public String getISO3Language() throws MissingResourceException {
         // BEGIN android-changed
-        // return ULocale.forLocale(this).getISO3Language();
         if (languageCode.length() == 0) {
-            return ""; //$NON-NLS-1$
+            return languageCode;
         }
-        ResourceBundle bundle = getBundle("ISO3Languages", this); //$NON-NLS-1$
-        return bundle.getString(this.toString());
+        return Resources.getISO3LanguageNative(toString());
         // END android-changed
-	}
+    }
 
     /**
      * Gets the list of two letter ISO country codes which can be used as the
@@ -617,7 +580,6 @@
      */
     public static String[] getISOCountries() {
         // BEGIN android-changed
-        // return ULocale.getISOCountries();
         return Resources.getISOCountries();
         // END android-changed
     }
@@ -628,12 +590,11 @@
      *
      * @return an array of strings.
      */
-	public static String[] getISOLanguages() {
+    public static String[] getISOLanguages() {
         // BEGIN android-changed
-        // return ULocale.getISOLanguages();
         return Resources.getISOLanguages();
         // END android-changed
-	}
+    }
 
     /**
      * Gets the language code for this {@code Locale} or the empty string of no language
@@ -646,7 +607,7 @@
     }
 
     /**
-     * Gets the variant code for this {@code Locale} or an empty {@code String} of no variant
+     * Gets the variant code for this {@code Locale} or an empty {@code String} if no variant
      * was set.
      *
      * @return a variant code.
@@ -704,33 +665,31 @@
      */
     @Override
     public final String toString() {
-        StringBuilder result = new StringBuilder();
-        result.append(languageCode);
-        if (countryCode.length() > 0) {
-            result.append('_');
-            result.append(countryCode);
-        }
-        if (variantCode.length() > 0 && result.length() > 0) {
-            if (0 == countryCode.length()) {
-                result.append("__"); //$NON-NLS-1$
-            } else {
-                result.append('_');
-            }
-            result.append(variantCode);
-        }
-        return result.toString();
+        String result = cachedToStringResult;
+        return (result == null) ? (cachedToStringResult = toNewString()) : result;
     }
 
-    // BEGIN android-added
-    static ResourceBundle getBundle(final String clName, final Locale locale) {
-        return AccessController.doPrivileged(new PrivilegedAction<ResourceBundle>() {
-            public ResourceBundle run() {
-                return ResourceBundle.getBundle("org.apache.harmony.luni.internal.locale." //$NON-NLS-1$
-                        + clName, locale);
-            }
-        });
+    private String toNewString() {
+        // The string form of a locale that only has a variant is the empty string.
+        if (languageCode.length() == 0 && countryCode.length() == 0) {
+            return "";
+        }
+        // Otherwise, the output format is "ll_cc_variant", where language and country are always
+        // two letters, but the variant is an arbitrary length. A size of 11 characters has room
+        // for "en_US_POSIX", the largest "common" value. (In practice, the string form is almost
+        // always 5 characters: "ll_cc".)
+        StringBuilder result = new StringBuilder(11);
+        result.append(languageCode);
+        if (countryCode.length() > 0 || variantCode.length() > 0) {
+            result.append('_');
+        }
+        result.append(countryCode);
+        if (variantCode.length() > 0) {
+            result.append('_');
+        }
+        result.append(variantCode);
+        return result.toString();
     }
-    // END android-added
 
     private static final ObjectStreamField[] serialPersistentFields = {
             new ObjectStreamField("country", String.class), //$NON-NLS-1$
diff --git a/libcore/text/src/main/java/java/text/Format.java b/libcore/text/src/main/java/java/text/Format.java
index 8abe605..7660f8e 100644
--- a/libcore/text/src/main/java/java/text/Format.java
+++ b/libcore/text/src/main/java/java/text/Format.java
@@ -89,15 +89,8 @@
     }
 
     // BEGIN android-added
-    static ResourceBundle getBundle(final Locale locale) {
-        return AccessController
-                .doPrivileged(new PrivilegedAction<ResourceBundle>() {
-                    public ResourceBundle run() {
-                        return ResourceBundle
-                                .getBundle(
-                                        "org.apache.harmony.luni.internal.locale.Locale", locale); //$NON-NLS-1$
-                    }
-                });
+    static ResourceBundle getBundle(Locale locale) {
+        return com.ibm.icu4jni.util.Resources.getLocaleInstance(locale);
     }
     // END android-added