Improve performance of LocaleList with Resources

We allow each individual Resources object to select the best
Locale for the given APK. This allows one update to the configuration
instead of multiple updates, once the locale is chosen.

The Java locale is selected from the app context's locale.

Bug:28625993
Bug:27325465
Change-Id: I99e1e53f522e560f3b80bbd1e1c605f552dbdff0
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index dff0769..30753c1 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -62,6 +62,7 @@
 import android.os.Environment;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.LocaleList;
 import android.os.Looper;
 import android.os.Message;
 import android.os.MessageQueue;
@@ -130,6 +131,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 import java.util.TimeZone;
@@ -4684,6 +4686,8 @@
                     + config);
 
             mResourcesManager.applyConfigurationToResourcesLocked(config, compat);
+            updateLocaleListFromAppContext(mInitialApplication.getApplicationContext(),
+                    mResourcesManager.getConfiguration().getLocales());
 
             if (mConfiguration == null) {
                 mConfiguration = new Configuration();
@@ -4989,6 +4993,24 @@
         return insInfo.nativeLibraryDir;
     }
 
+    /**
+     * The LocaleList set for the app's resources may have been shuffled so that the preferred
+     * Locale is at position 0. We must find the index of this preferred Locale in the
+     * original LocaleList.
+     */
+    private void updateLocaleListFromAppContext(Context context, LocaleList newLocaleList) {
+        final Locale bestLocale = context.getResources().getConfiguration().getLocales().get(0);
+        final int newLocaleListSize = newLocaleList.size();
+        for (int i = 0; i < newLocaleListSize; i++) {
+            if (bestLocale.equals(newLocaleList.get(i))) {
+                LocaleList.setDefault(newLocaleList, i);
+                return;
+            }
+        }
+        throw new AssertionError("chosen locale " + bestLocale + " must be present in LocaleList: "
+                + newLocaleList.toLanguageTags());
+    }
+
     private void handleBindApplication(AppBindData data) {
         // Register the UI Thread as a sensitive thread to the runtime.
         VMRuntime.registerSensitiveThread();
@@ -5047,6 +5069,24 @@
          */
         TimeZone.setDefault(null);
 
+        /*
+         * Set the LocaleList. This may change once we create the App Context.
+         */
+        LocaleList.setDefault(data.config.getLocales());
+
+        synchronized (mResourcesManager) {
+            /*
+             * Update the system configuration since its preloaded and might not
+             * reflect configuration changes. The configuration object passed
+             * in AppBindData can be safely assumed to be up to date
+             */
+            mResourcesManager.applyConfigurationToResourcesLocked(data.config, data.compatInfo);
+            mCurDefaultDisplayDpi = data.config.densityDpi;
+
+            // This calls mResourcesManager so keep it within the synchronized block.
+            applyCompatConfiguration(mCurDefaultDisplayDpi);
+        }
+
         data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
 
         /**
@@ -5174,25 +5214,8 @@
         }
 
         final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
-        synchronized (mResourcesManager) {
-            /*
-             * Initialize the default locales in this process for the reasons we set the time zone.
-             *
-             * We do this through ResourcesManager, since we need to do locale negotiation.
-             */
-            mResourcesManager.setDefaultLocalesLocked(data.config.getLocales());
-
-            /*
-             * Update the system configuration since its preloaded and might not
-             * reflect configuration changes. The configuration object passed
-             * in AppBindData can be safely assumed to be up to date
-             */
-            mResourcesManager.applyConfigurationToResourcesLocked(data.config, data.compatInfo);
-            mCurDefaultDisplayDpi = data.config.densityDpi;
-
-            // This calls mResourcesManager so keep it within the synchronized block.
-            applyCompatConfiguration(mCurDefaultDisplayDpi);
-        }
+        updateLocaleListFromAppContext(appContext,
+                mResourcesManager.getConfiguration().getLocales());
 
         if (!Process.isIsolated() && !"android".equals(appContext.getPackageName())) {
             // This cache location probably points at credential-encrypted
@@ -5895,6 +5918,9 @@
                     // immediately, because upon returning the view
                     // hierarchy will be informed about it.
                     if (mResourcesManager.applyConfigurationToResourcesLocked(newConfig, null)) {
+                        updateLocaleListFromAppContext(mInitialApplication.getApplicationContext(),
+                                mResourcesManager.getConfiguration().getLocales());
+
                         // This actually changed the resources!  Tell
                         // everyone about it.
                         if (mPendingConfiguration == null ||
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index b4e9db8..25a8b66 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -67,10 +67,6 @@
                 }
             };
 
-    private String[] mSystemLocales = null;
-    private final HashSet<String> mNonSystemLocales = new HashSet<>();
-    private boolean mHasNonSystemLocales = false;
-
     /**
      * The global compatibility settings.
      */
@@ -479,12 +475,7 @@
      */
     private Resources getOrCreateResources(@Nullable IBinder activityToken,
             @NonNull ResourcesKey key, @NonNull ClassLoader classLoader) {
-        final boolean findSystemLocales;
-        final boolean hasNonSystemLocales;
         synchronized (this) {
-            findSystemLocales = (mSystemLocales == null || mSystemLocales.length == 0);
-            hasNonSystemLocales = mHasNonSystemLocales;
-
             if (DEBUG) {
                 Throwable here = new Throwable();
                 here.fillInStackTrace();
@@ -538,24 +529,7 @@
         // If we're here, we didn't find a suitable ResourcesImpl to use, so create one now.
         ResourcesImpl resourcesImpl = createResourcesImpl(key);
 
-        final String[] systemLocales = findSystemLocales
-                ? AssetManager.getSystem().getLocales() : null;
-        final String[] nonSystemLocales = resourcesImpl.getAssets().getNonSystemLocales();
-
-        // Avoid checking for non-pseudo-locales if we already know there were some from a previous
-        // Resources. The default value (for when hasNonSystemLocales is true) doesn't matter,
-        // since mHasNonSystemLocales will also be true, and thus isPseudoLocalesOnly would not be
-        // able to affect mHasNonSystemLocales.
-        final boolean isPseudoLocalesOnly = hasNonSystemLocales ||
-                LocaleList.isPseudoLocalesOnly(nonSystemLocales);
-
         synchronized (this) {
-            if (mSystemLocales == null || mSystemLocales.length == 0) {
-                mSystemLocales = systemLocales;
-            }
-            mNonSystemLocales.addAll(Arrays.asList(nonSystemLocales));
-            mHasNonSystemLocales = mHasNonSystemLocales || !isPseudoLocalesOnly;
-
             ResourcesImpl existingResourcesImpl = findResourcesImplForKeyLocked(key);
             if (existingResourcesImpl != null) {
                 if (DEBUG) {
@@ -745,23 +719,6 @@
         }
     }
 
-    /* package */ void setDefaultLocalesLocked(@NonNull LocaleList locales) {
-        if (mSystemLocales == null) {
-            throw new RuntimeException("ResourcesManager is not ready to negotiate locales.");
-        }
-        final int bestLocale;
-        if (mHasNonSystemLocales) {
-            bestLocale = locales.getFirstMatchIndexWithEnglishSupported(mNonSystemLocales);
-        } else {
-            // We fallback to system locales if there was no locale specifically supported by the
-            // assets. This is to properly support apps that only rely on the shared system assets
-            // and don't need assets of their own.
-            bestLocale = locales.getFirstMatchIndexWithEnglishSupported(mSystemLocales);
-        }
-        // set it for Java, this also affects newly created Resources
-        LocaleList.setDefault(locales, bestLocale);
-    }
-
     public final boolean applyConfigurationToResourcesLocked(@NonNull Configuration config,
                                                              @Nullable CompatibilityInfo compat) {
         try {
@@ -786,30 +743,7 @@
                         | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
             }
 
-            Configuration localeAdjustedConfig = config;
-            final LocaleList configLocales = config.getLocales();
-            if (!configLocales.isEmpty()) {
-                setDefaultLocalesLocked(configLocales);
-                final LocaleList adjustedLocales = LocaleList.getAdjustedDefault();
-                if (adjustedLocales
-                        != configLocales) { // has the same result as .equals() in this case
-                    // The first locale in the list was not chosen. So we create a modified
-                    // configuration with the adjusted locales (which moves the chosen locale to the
-                    // front).
-                    localeAdjustedConfig = new Configuration();
-                    localeAdjustedConfig.setTo(config);
-                    localeAdjustedConfig.setLocales(adjustedLocales);
-                    // Also adjust the locale list in mResConfiguration, so that the Resources
-                    // created later would have the same locale list.
-                    if (!mResConfiguration.getLocales().equals(adjustedLocales)) {
-                        mResConfiguration.setLocales(adjustedLocales);
-                        changes |= ActivityInfo.CONFIG_LOCALE;
-                    }
-                }
-            }
-
-            Resources.updateSystemConfiguration(localeAdjustedConfig, defaultDisplayMetrics,
-                    compat);
+            Resources.updateSystemConfiguration(config, defaultDisplayMetrics, compat);
 
             ApplicationPackageManager.configurationChanged();
             //Slog.i(TAG, "Configuration changed in " + currentPackageName());
@@ -821,7 +755,7 @@
                 ResourcesImpl r = mResourceImpls.valueAt(i).get();
                 if (r != null) {
                     if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
-                            + r + " config to: " + localeAdjustedConfig);
+                            + r + " config to: " + config);
                     int displayId = key.mDisplayId;
                     boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
                     DisplayMetrics dm = defaultDisplayMetrics;
@@ -830,7 +764,7 @@
                         if (tmpConfig == null) {
                             tmpConfig = new Configuration();
                         }
-                        tmpConfig.setTo(localeAdjustedConfig);
+                        tmpConfig.setTo(config);
                         if (!isDefaultDisplay) {
                             dm = getDisplayMetrics(displayId);
                             applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
@@ -840,7 +774,7 @@
                         }
                         r.updateConfiguration(tmpConfig, dm, compat);
                     } else {
-                        r.updateConfiguration(localeAdjustedConfig, dm, compat);
+                        r.updateConfiguration(config, dm, compat);
                     }
                     //Slog.i(TAG, "Updated app resources " + v.getKey()
                     //        + " " + r + ": " + r.getConfiguration());
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index c1aac85..f6445e6 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -1446,7 +1446,7 @@
      *
      * @return The locale list.
      */
-    public LocaleList getLocales() {
+    public @NonNull LocaleList getLocales() {
         fixUpLocaleList();
         return mLocaleList;
     }
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index dada612..32a2795 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -330,18 +330,43 @@
                 // doing the conversion here.  This impl should be okay because
                 // we make sure to return a compatible display in the places
                 // where there are public APIs to retrieve the display...  but
-                // it would be cleaner and more maintainble to just be
+                // it would be cleaner and more maintainable to just be
                 // consistently dealing with a compatible display everywhere in
                 // the framework.
                 mCompatibilityInfo.applyToDisplayMetrics(mMetrics);
 
                 final @Config int configChanges = calcConfigChanges(config);
 
+                // If even after the update there are no Locales set, grab the default locales.
                 LocaleList locales = mConfiguration.getLocales();
                 if (locales.isEmpty()) {
-                    locales = LocaleList.getAdjustedDefault();
+                    locales = LocaleList.getDefault();
                     mConfiguration.setLocales(locales);
                 }
+
+                if ((configChanges & ActivityInfo.CONFIG_LOCALE) != 0) {
+                    if (locales.size() > 1) {
+                        // The LocaleList has changed. We must query the AssetManager's available
+                        // Locales and figure out the best matching Locale in the new LocaleList.
+                        String[] availableLocales = mAssets.getNonSystemLocales();
+                        if (LocaleList.isPseudoLocalesOnly(availableLocales)) {
+                            // No app defined locales, so grab the system locales.
+                            availableLocales = mAssets.getLocales();
+                            if (LocaleList.isPseudoLocalesOnly(availableLocales)) {
+                                availableLocales = null;
+                            }
+                        }
+
+                        if (availableLocales != null) {
+                            final Locale bestLocale = locales.getFirstMatchWithEnglishSupported(
+                                    availableLocales);
+                            if (bestLocale != null && bestLocale != locales.get(0)) {
+                                mConfiguration.setLocales(new LocaleList(bestLocale, locales));
+                            }
+                        }
+                    }
+                }
+
                 if (mConfiguration.densityDpi != Configuration.DENSITY_DPI_UNDEFINED) {
                     mMetrics.densityDpi = mConfiguration.densityDpi;
                     mMetrics.density =
@@ -370,7 +395,7 @@
                 }
 
                 mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
-                        adjustLanguageTag(locales.get(0).toLanguageTag()),
+                        adjustLanguageTag(mConfiguration.getLocales().get(0).toLanguageTag()),
                         mConfiguration.orientation,
                         mConfiguration.touchscreen,
                         mConfiguration.densityDpi, mConfiguration.keyboard,
diff --git a/core/java/android/os/LocaleList.java b/core/java/android/os/LocaleList.java
index 8136796..60b618a 100644
--- a/core/java/android/os/LocaleList.java
+++ b/core/java/android/os/LocaleList.java
@@ -409,6 +409,14 @@
     }
 
     /**
+     * {@hide}
+     */
+    public int getFirstMatchIndex(String[] supportedLocales) {
+        return computeFirstMatchIndex(Arrays.asList(supportedLocales),
+                false /* assume English is not supported */);
+    }
+
+    /**
      * Same as getFirstMatch(), but with English assumed to be supported, even if it's not.
      * {@hide}
      */
@@ -437,7 +445,11 @@
      * Assumes that there is no repetition in the input.
      * {@hide}
      */
-    public static boolean isPseudoLocalesOnly(String[] supportedLocales) {
+    public static boolean isPseudoLocalesOnly(@Nullable String[] supportedLocales) {
+        if (supportedLocales == null) {
+            return true;
+        }
+
         if (supportedLocales.length > NUM_PSEUDO_LOCALES + 1) {
             // This is for optimization. Since there's no repetition in the input, if we have more
             // than the number of pseudo-locales plus one for the empty string, it's guaranteed
diff --git a/core/tests/coretests/apks/locales/Android.mk b/core/tests/coretests/apks/locales/Android.mk
new file mode 100644
index 0000000..9cb13dd
--- /dev/null
+++ b/core/tests/coretests/apks/locales/Android.mk
@@ -0,0 +1,8 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := locales
+
+include $(FrameworkCoreTests_BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/locales/AndroidManifest.xml b/core/tests/coretests/apks/locales/AndroidManifest.xml
new file mode 100644
index 0000000..8a1f575
--- /dev/null
+++ b/core/tests/coretests/apks/locales/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.frameworks.coretests.locales">
+
+    <application android:hasCode="false">
+    </application>
+</manifest>
diff --git a/core/tests/coretests/apks/locales/res/values-pl/values.xml b/core/tests/coretests/apks/locales/res/values-pl/values.xml
new file mode 100644
index 0000000..22237fa8
--- /dev/null
+++ b/core/tests/coretests/apks/locales/res/values-pl/values.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+    <string name="text">Text (pl)</string>
+</resources>
diff --git a/core/tests/coretests/apks/locales/res/values/values.xml b/core/tests/coretests/apks/locales/res/values/values.xml
new file mode 100644
index 0000000..cb990d2
--- /dev/null
+++ b/core/tests/coretests/apks/locales/res/values/values.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+    <string name="text">Text</string>
+</resources>
diff --git a/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java b/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java
new file mode 100644
index 0000000..e85666e
--- /dev/null
+++ b/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.content.res;
+
+import android.os.FileUtils;
+import android.os.LocaleList;
+import android.support.test.filters.SmallTest;
+import android.test.AndroidTestCase;
+import android.util.DisplayMetrics;
+import android.view.Display;
+
+import com.android.frameworks.coretests.R;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Locale;
+
+public class ResourcesLocaleTest extends AndroidTestCase {
+
+    private String extractApkAndGetPath(int id) throws Exception {
+        final Resources resources = getContext().getResources();
+        try (InputStream is = resources.openRawResource(id)) {
+            File path = new File(getContext().getFilesDir(), resources.getResourceEntryName(id));
+            FileUtils.copyToFileOrThrow(is, path);
+            return path.getAbsolutePath();
+        }
+    }
+
+    private Resources createResourcesWithApk(int rawApkId) throws Exception {
+        final AssetManager assets = new AssetManager();
+        assertTrue(assets.addAssetPath(extractApkAndGetPath(rawApkId)) != 0);
+
+        final DisplayMetrics dm = new DisplayMetrics();
+        dm.setToDefaults();
+        return new Resources(assets, dm, new Configuration());
+    }
+
+    private static void ensureNoLanguage(Resources resources, String language) {
+        final String[] supportedLocales = resources.getAssets().getNonSystemLocales();
+        for (String languageTag : supportedLocales) {
+            if ("en-XA".equals(languageTag)) {
+                continue;
+            }
+            assertFalse(
+                    "supported locales: " + Arrays.toString(supportedLocales),
+                    language.equals(Locale.forLanguageTag(languageTag).getLanguage()));
+        }
+    }
+
+    @SmallTest
+    public void testEnglishIsAlwaysConsideredSupported() throws Exception {
+        final Resources resources = createResourcesWithApk(R.raw.locales);
+        ensureNoLanguage(resources, "en");
+
+        final LocaleList preferredLocales = LocaleList.forLanguageTags("en-US,pl-PL");
+        final Configuration config = new Configuration();
+        config.setLocales(preferredLocales);
+
+        resources.updateConfiguration(config, null);
+
+        // The APK we loaded has default and Polish languages. If English is first in the list,
+        // always take it the default (assumed to be English).
+        assertEquals(Locale.forLanguageTag("en-US"),
+                resources.getConfiguration().getLocales().get(0));
+    }
+
+    @SmallTest
+    public void testSelectFirstSupportedLanguage() throws Exception {
+        final Resources resources = createResourcesWithApk(R.raw.locales);
+        ensureNoLanguage(resources, "fr");
+
+        final LocaleList preferredLocales = LocaleList.forLanguageTags("fr-FR,pl-PL");
+        final Configuration config = new Configuration();
+        config.setLocales(preferredLocales);
+
+        resources.updateConfiguration(config, null);
+
+        // The APK we loaded has default and Polish languages. We expect the Polish language to
+        // therefore be chosen.
+        assertEquals(Locale.forLanguageTag("pl-PL"),
+                resources.getConfiguration().getLocales().get(0));
+    }
+}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7a59703..b80487f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -18325,21 +18325,21 @@
                 EventLog.writeEvent(EventLogTags.CONFIGURATION_CHANGED, changes);
 
                 if (!initLocale && !values.getLocales().isEmpty() && values.userSetLocale) {
-                    final Locale locale;
-                    if (values.getLocales().size() == 1) {
-                        // This is an optimization to avoid the JNI call when the result of
-                        // getFirstMatch() does not depend on the supported locales.
-                        locale = values.getLocales().get(0);
-                    } else {
+                    final LocaleList locales = values.getLocales();
+                    int bestLocaleIndex = 0;
+                    if (locales.size() > 1) {
                         if (mSupportedSystemLocales == null) {
                             mSupportedSystemLocales =
                                     Resources.getSystem().getAssets().getLocales();
                         }
-                        locale = values.getLocales().getFirstMatch(mSupportedSystemLocales);
+                        bestLocaleIndex = Math.max(0,
+                                locales.getFirstMatchIndex(mSupportedSystemLocales));
                     }
-                    SystemProperties.set("persist.sys.locale", locale.toLanguageTag());
+                    SystemProperties.set("persist.sys.locale",
+                            locales.get(bestLocaleIndex).toLanguageTag());
+                    LocaleList.setDefault(locales, bestLocaleIndex);
                     mHandler.sendMessage(mHandler.obtainMessage(SEND_LOCALE_TO_MOUNT_DAEMON_MSG,
-                            locale));
+                            locales.get(bestLocaleIndex)));
                 }
 
                 mConfigurationSeq++;