Merge "Fix native crashes when APKs can't be opened."
diff --git a/core/java/com/android/internal/app/LocalePicker.java b/core/java/com/android/internal/app/LocalePicker.java
index 043964f..6973615 100644
--- a/core/java/com/android/internal/app/LocalePicker.java
+++ b/core/java/com/android/internal/app/LocalePicker.java
@@ -37,6 +37,8 @@
 
 import java.text.Collator;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 import java.util.Locale;
 import java.util.ArrayList;
 
@@ -108,8 +110,9 @@
             final int layoutId, final int fieldId, final boolean isInDeveloperMode) {
         final Resources resources = context.getResources();
 
-        ArrayList<String> localeList = new ArrayList<String>(Arrays.asList(
-                Resources.getSystem().getAssets().getLocales()));
+        String[] locales = Resources.getSystem().getAssets().getLocales();
+        List<String> localeList = new ArrayList<String>(locales.length);
+        Collections.addAll(localeList, locales);
         if (isInDeveloperMode) {
             if (!localeList.contains("zz_ZZ")) {
                 localeList.add("zz_ZZ");
@@ -120,78 +123,61 @@
          *	}
          */
         }
-        String[] locales = new String[localeList.size()];
-        locales = localeList.toArray(locales);
 
+        Collections.sort(localeList);
         final String[] specialLocaleCodes = resources.getStringArray(R.array.special_locale_codes);
         final String[] specialLocaleNames = resources.getStringArray(R.array.special_locale_names);
-        Arrays.sort(locales);
-        final int origSize = locales.length;
-        final LocaleInfo[] preprocess = new LocaleInfo[origSize];
-        int finalSize = 0;
-        for (int i = 0 ; i < origSize; i++ ) {
-            final String s = locales[i];
-            final int len = s.length();
-            if (len == 5) {
-                String language = s.substring(0, 2);
-                String country = s.substring(3, 5);
-                final Locale l = new Locale(language, country);
 
-                if (finalSize == 0) {
+        final ArrayList<LocaleInfo> localeInfos = new ArrayList<LocaleInfo>(localeList.size());
+        for (String locale : localeList) {
+            final Locale l = Locale.forLanguageTag(locale.replace('_', '-'));
+            if (l == null || "und".equals(l.getLanguage())) {
+                continue;
+            }
+
+            if (localeInfos.isEmpty()) {
+                if (DEBUG) {
+                    Log.v(TAG, "adding initial "+ toTitleCase(l.getDisplayLanguage(l)));
+                }
+                localeInfos.add(new LocaleInfo(toTitleCase(l.getDisplayLanguage(l)), l));
+            } else {
+                // check previous entry:
+                //  same lang and a country -> upgrade to full name and
+                //    insert ours with full name
+                //  diff lang -> insert ours with lang-only name
+                final LocaleInfo previous = localeInfos.get(localeInfos.size() - 1);
+                if (previous.locale.getLanguage().equals(l.getLanguage()) &&
+                        !previous.locale.getLanguage().equals("zz")) {
                     if (DEBUG) {
-                        Log.v(TAG, "adding initial "+ toTitleCase(l.getDisplayLanguage(l)));
+                        Log.v(TAG, "backing up and fixing " + previous.label + " to " +
+                                getDisplayName(previous.locale, specialLocaleCodes, specialLocaleNames));
                     }
-                    preprocess[finalSize++] =
-                            new LocaleInfo(toTitleCase(l.getDisplayLanguage(l)), l);
+                    previous.label = toTitleCase(getDisplayName(
+                            previous.locale, specialLocaleCodes, specialLocaleNames));
+                    if (DEBUG) {
+                        Log.v(TAG, "  and adding "+ toTitleCase(
+                                getDisplayName(l, specialLocaleCodes, specialLocaleNames)));
+                    }
+                    localeInfos.add(new LocaleInfo(toTitleCase(
+                            getDisplayName(l, specialLocaleCodes, specialLocaleNames)), l));
                 } else {
-                    // check previous entry:
-                    //  same lang and a country -> upgrade to full name and
-                    //    insert ours with full name
-                    //  diff lang -> insert ours with lang-only name
-                    if (preprocess[finalSize-1].locale.getLanguage().equals(
-                            language) &&
-                            !preprocess[finalSize-1].locale.getLanguage().equals("zz")) {
-                        if (DEBUG) {
-                            Log.v(TAG, "backing up and fixing "+
-                                    preprocess[finalSize-1].label+" to "+
-                                    getDisplayName(preprocess[finalSize-1].locale,
-                                            specialLocaleCodes, specialLocaleNames));
-                        }
-                        preprocess[finalSize-1].label = toTitleCase(
-                                getDisplayName(preprocess[finalSize-1].locale,
-                                        specialLocaleCodes, specialLocaleNames));
-                        if (DEBUG) {
-                            Log.v(TAG, "  and adding "+ toTitleCase(
-                                    getDisplayName(l, specialLocaleCodes, specialLocaleNames)));
-                        }
-                        preprocess[finalSize++] =
-                                new LocaleInfo(toTitleCase(
-                                        getDisplayName(
-                                                l, specialLocaleCodes, specialLocaleNames)), l);
+                    String displayName;
+                    if (locale.equals("zz_ZZ")) {
+                        displayName = "[Developer] Accented English";
+                    } else if (locale.equals("zz_ZY")) {
+                        displayName = "[Developer] Fake Bi-Directional";
                     } else {
-                        String displayName;
-                        if (s.equals("zz_ZZ")) {
-                            displayName = "[Developer] Accented English";
-                        } else if (s.equals("zz_ZY")) {
-                            displayName = "[Developer] Fake Bi-Directional";
-                        } else {
-                            displayName = toTitleCase(l.getDisplayLanguage(l));
-                        }
-                        if (DEBUG) {
-                            Log.v(TAG, "adding "+displayName);
-                        }
-                        preprocess[finalSize++] = new LocaleInfo(displayName, l);
+                        displayName = toTitleCase(l.getDisplayLanguage(l));
                     }
+                    if (DEBUG) {
+                        Log.v(TAG, "adding "+displayName);
+                    }
+                    localeInfos.add(new LocaleInfo(displayName, l));
                 }
             }
         }
 
-        final LocaleInfo[] localeInfos = new LocaleInfo[finalSize];
-        for (int i = 0; i < finalSize; i++) {
-            localeInfos[i] = preprocess[i];
-        }
-        Arrays.sort(localeInfos);
-
+        Collections.sort(localeInfos);
         final LayoutInflater inflater =
                 (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
         return new ArrayAdapter<LocaleInfo>(context, layoutId, fieldId, localeInfos) {
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index a61901b..e8caa0c1 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -391,8 +391,8 @@
         property_get("ro.product.locale.language", propLang, "en");
         property_get("ro.product.locale.region", propRegn, "US");
     }
-    strncat(language, propLang, 2);
-    strncat(region, propRegn, 2);
+    strncat(language, propLang, 3);
+    strncat(region, propRegn, 3);
     //ALOGD("language=%s region=%s\n", language, region);
 }
 
@@ -490,6 +490,8 @@
     char profile_backoff[sizeof("-Xprofile-backoff:") + PROPERTY_VALUE_MAX];
     char profile_top_k_threshold[sizeof("-Xprofile-top-k-threshold:") + PROPERTY_VALUE_MAX];
     char profile_top_k_change_threshold[sizeof("-Xprofile-top-k-change-threshold:") + PROPERTY_VALUE_MAX];
+    char profile_type[sizeof("-Xprofile-type:") + PROPERTY_VALUE_MAX];
+    char profile_max_stack_depth[sizeof("-Xprofile-max-stack-depth:")+PROPERTY_VALUE_MAX];
     char langOption[sizeof("-Duser.language=") + 3];
     char regionOption[sizeof("-Duser.region=") + 3];
     char lockProfThresholdBuf[sizeof("-Xlockprofthreshold:") + sizeof(propBuf)];
@@ -872,6 +874,20 @@
             opt.optionString = profile_top_k_change_threshold;
             mOptions.add(opt);
         }
+
+        // Type of profile data.
+        strcpy(profile_type, "-Xprofile-type:");
+        if (property_get("dalvik.vm.profiler.type", profile_type+15, NULL) > 0) {
+            opt.optionString = profile_type;
+            mOptions.add(opt);
+        }
+
+        // Depth of bounded stack data
+        strcpy(profile_max_stack_depth, "-Xprofile-max-stack-depth:");
+        if (property_get("dalvik.vm.profile.max-stack-depth", profile_max_stack_depth+26, NULL) > 0) {
+           opt.optionString = profile_max_stack_depth;
+           mOptions.add(opt);
+        }
     }
 
     initArgs.version = JNI_VERSION_1_4;
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 87164ca..482dfc8 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -347,6 +347,15 @@
     setLocaleLocked(locale);
 }
 
+
+static const char kFilPrefix[] = "fil";
+static const char kTlPrefix[] = "tl";
+
+// The sizes of the prefixes, excluding the 0 suffix.
+// char.
+static const int kFilPrefixLen = sizeof(kFilPrefix) - 1;
+static const int kTlPrefixLen = sizeof(kTlPrefix) - 1;
+
 void AssetManager::setLocaleLocked(const char* locale)
 {
     if (mLocale != NULL) {
@@ -355,8 +364,44 @@
         //mZipSet.purgeLocale();
         delete[] mLocale;
     }
-    mLocale = strdupNew(locale);
 
+
+    // If we're attempting to set a locale that starts with "fil",
+    // we should convert it to "tl" for backwards compatibility since
+    // we've been using "tl" instead of "fil" prior to L.
+    //
+    // If the resource table already has entries for "fil", we use that
+    // instead of attempting a fallback.
+    if (strncmp(locale, kFilPrefix, kFilPrefixLen) == 0) {
+        Vector<String8> locales;
+        getLocales(&locales);
+        const size_t localesSize = locales.size();
+        bool hasFil = false;
+        for (size_t i = 0; i < localesSize; ++i) {
+            if (locales[i].find(kFilPrefix) == 0) {
+                hasFil = true;
+                break;
+            }
+        }
+
+
+        if (!hasFil) {
+            const size_t newLocaleLen = strlen(locale);
+            // This isn't a bug. We really do want mLocale to be 1 byte
+            // shorter than locale, because we're replacing "fil-" with
+            // "tl-".
+            mLocale = new char[newLocaleLen];
+            // Copy over "tl".
+            memcpy(mLocale, kTlPrefix, kTlPrefixLen);
+            // Copy the rest of |locale|, including the terminating '\0'.
+            memcpy(mLocale + kTlPrefixLen, locale + kFilPrefixLen,
+                   newLocaleLen - kFilPrefixLen + 1);
+            updateResourceParamsLocked();
+            return;
+        }
+    }
+
+    mLocale = strdupNew(locale);
     updateResourceParamsLocked();
 }
 
@@ -741,6 +786,16 @@
     if (res != NULL) {
         res->getLocales(locales);
     }
+
+    const size_t numLocales = locales->size();
+    for (size_t i = 0; i < numLocales; ++i) {
+        const String8& localeStr = locales->itemAt(i);
+        if (localeStr.find(kTlPrefix) == 0) {
+            String8 replaced("fil");
+            replaced += (localeStr.string() + kTlPrefixLen);
+            locales->editItemAt(i) = replaced;
+        }
+    }
 }
 
 /*
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 2c828c1e..2f3b1f7 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -1597,9 +1597,9 @@
       out[0] = in[0];
       out[1] = in[1];
   } else {
-      uint8_t first = (in[0] - base) & 0x00ef;
-      uint8_t second = (in[1] - base) & 0x00ef;
-      uint8_t third = (in[2] - base) & 0x00ef;
+      uint8_t first = (in[0] - base) & 0x007f;
+      uint8_t second = (in[1] - base) & 0x007f;
+      uint8_t third = (in[2] - base) & 0x007f;
 
       out[0] = (0x80 | (third << 2) | (second >> 3));
       out[1] = ((second << 5) | first);
diff --git a/libs/androidfw/tests/ResourceTypes_test.cpp b/libs/androidfw/tests/ResourceTypes_test.cpp
index 4888b4a..139f868 100644
--- a/libs/androidfw/tests/ResourceTypes_test.cpp
+++ b/libs/androidfw/tests/ResourceTypes_test.cpp
@@ -75,6 +75,30 @@
      EXPECT_EQ(0, out[3]);
 }
 
+TEST(ResourceTypesTest, ResourceConfig_packAndUnpack3LetterLanguageAtOffset16) {
+     ResTable_config config;
+     config.packLanguage("tgp");
+
+     // We had a bug where we would accidentally mask
+     // the 5th bit of both bytes
+     //
+     // packed[0] = 1011 1100
+     // packed[1] = 1101 0011
+     //
+     // which is equivalent to:
+     // 1  [0]   [1]   [2]
+     // 1-01111-00110-10011
+     EXPECT_EQ(0xbc, config.language[0]);
+     EXPECT_EQ(0xd3, config.language[1]);
+
+     char out[4] = { 1, 1, 1, 1};
+     config.unpackLanguage(out);
+     EXPECT_EQ('t', out[0]);
+     EXPECT_EQ('g', out[1]);
+     EXPECT_EQ('p', out[2]);
+     EXPECT_EQ(0, out[3]);
+}
+
 TEST(ResourceTypesTest, ResourceConfig_packAndUnpack3LetterRegion) {
      ResTable_config config;
      config.packRegion("419");
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 06a8f4c..4599bd6 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -1184,6 +1184,7 @@
         HashSet<String> existingFiles = new HashSet<String>();
         String directory = "/sdcard/DCIM/.thumbnails";
         String [] files = (new File(directory)).list();
+        Cursor c = null;
         if (files == null)
             files = new String[0];
 
@@ -1193,7 +1194,7 @@
         }
 
         try {
-            Cursor c = mMediaProvider.query(
+            c = mMediaProvider.query(
                     mPackageName,
                     mThumbsUri,
                     new String [] { "_data" },
@@ -1218,11 +1219,12 @@
             }
 
             Log.v(TAG, "/pruneDeadThumbnailFiles... " + c);
+        } catch (RemoteException e) {
+            // We will soon be killed...
+        } finally {
             if (c != null) {
                 c.close();
             }
-        } catch (RemoteException e) {
-            // We will soon be killed...
         }
     }
 
diff --git a/services/java/com/android/server/pm/SELinuxMMAC.java b/services/java/com/android/server/pm/SELinuxMMAC.java
index c78249b..81302b9 100644
--- a/services/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/java/com/android/server/pm/SELinuxMMAC.java
@@ -346,31 +346,21 @@
      */
     public static boolean assignSeinfoValue(PackageParser.Package pkg) {
 
-        /*
-         * Non system installed apps should be treated the same. This
-         * means that any post-loaded apk will be assigned the default
-         * tag, if one exists in the policy, else null, without respect
-         * to the signing key.
-         */
-        if (((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) ||
-            ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0)) {
+        // We just want one of the signatures to match.
+        for (Signature s : pkg.mSignatures) {
+            if (s == null)
+                continue;
 
-            // We just want one of the signatures to match.
-            for (Signature s : pkg.mSignatures) {
-                if (s == null)
-                    continue;
+            Policy policy = sSigSeinfo.get(s);
+            if (policy != null) {
+                String seinfo = policy.checkPolicy(pkg.packageName);
+                if (seinfo != null) {
+                    pkg.applicationInfo.seinfo = seinfo;
+                    if (DEBUG_POLICY_INSTALL)
+                        Slog.i(TAG, "package (" + pkg.packageName +
+                               ") labeled with seinfo=" + seinfo);
 
-                Policy policy = sSigSeinfo.get(s);
-                if (policy != null) {
-                    String seinfo = policy.checkPolicy(pkg.packageName);
-                    if (seinfo != null) {
-                        pkg.applicationInfo.seinfo = seinfo;
-                        if (DEBUG_POLICY_INSTALL)
-                            Slog.i(TAG, "package (" + pkg.packageName +
-                                   ") labeled with seinfo=" + seinfo);
-
-                        return true;
-                    }
+                    return true;
                 }
             }
         }