Extended locales in AAPT / AssetManager.

Support 3 letter language codes, script codes &
variants. The bulk of the changes are related to
the implementation of command line filtering of
locales etc. The previous code assumed that the
value of each "axis" (locale, density, size etc.)
could be represented by a 4 byte type. This is
no longer the case.

This change introduces a new class, AaptLocaleValue
which holds a (normalized) locale parsed from a
directory name or a filter string. This class takes
responsibility for parsing locales as well as
writing them to ResTable_config structures, which is
their representation in the resource table.

This includes minor changes at the java / JNI level
for AssetManager. We now call locale.toLanguageTag()
to give the native layer a well formed BCP-47 tag.
I've removed some duplicated parsing code in
AssetManager.cpp and replaced them with functions on
ResTable_config. The native getLocales function has
been changed to return well formed BCP-47 locales as
well, so that the corresponding java function can use
Locale.forLanguageTag to construct a Locale object
out of it.

Finally, this change introduces default and copy
constructors for ResTable_config to prevent having
to memset() the associated memory to 0 on every
stack allocation.

Change-Id: I899a56a9a182ee6be52b9389d1ae59266f5482e9
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index aa96f0e..de00f71 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1625,10 +1625,7 @@
 
             String locale = null;
             if (mConfiguration.locale != null) {
-                locale = mConfiguration.locale.getLanguage();
-                if (mConfiguration.locale.getCountry() != null) {
-                    locale += "-" + mConfiguration.locale.getCountry();
-                }
+                locale = mConfiguration.locale.toLanguageTag();
             }
             int width, height;
             if (mMetrics.widthPixels >= mMetrics.heightPixels) {
diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h
index e79011f..610465e 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -1053,7 +1053,7 @@
 
     // The ISO-15924 short name for the script corresponding to this
     // configuration. (eg. Hant, Latn, etc.). Interpreted in conjunction with
-    // the locale field
+    // the locale field.
     char localeScript[4];
 
     // A single BCP-47 variant subtag. Will vary in length between 5 and 8
@@ -1118,14 +1118,23 @@
     bool match(const ResTable_config& settings) const;
 
     // Get the string representation of the locale component of this
-    // Config. This will contain the language along with the prefixed script,
-    // region and variant of this config, separated by underscores.
+    // Config. The maximum size of this representation will be
+    // |RESTABLE_MAX_LOCALE_LEN| (including a terminating '\0').
     //
-    // 'r' is the region prefix, 's' is the script prefix and 'v' is the
-    // variant prefix.
-    //
-    // Example: en_rUS, en_sLatn_rUS, en_vPOSIX.
-    void getLocale(char str[RESTABLE_MAX_LOCALE_LEN]) const;
+    // Example: en-US, en-Latn-US, en-POSIX.
+    void getBcp47Locale(char* out) const;
+
+    // Sets the values of language, region, script and variant to the
+    // well formed BCP-47 locale contained in |in|. The input locale is
+    // assumed to be valid and no validation is performed.
+    void setBcp47Locale(const char* in);
+
+    inline void clearLocale() {
+        locale = 0;
+        memset(localeScript, 0, sizeof(localeScript));
+        memset(localeVariant, 0, sizeof(localeVariant));
+    }
+
     // Get the 2 or 3 letter language code of this configuration. Trailing
     // bytes are set to '\0'.
     size_t unpackLanguage(char language[4]) const;
@@ -1133,12 +1142,16 @@
     // bytes are set to '\0'.
     size_t unpackRegion(char region[4]) const;
 
-    // Sets the language code of this configuration from |language|. If |language|
-    // is a 2 letter code, the trailing byte is expected to be '\0'.
-    void packLanguage(const char language[3]);
-    // Sets the region code of this configuration from |region|. If |region|
-    // is a 2 letter code, the trailing byte is expected to be '\0'.
-    void packRegion(const char region[3]);
+    // Sets the language code of this configuration to the first three
+    // chars at |language|.
+    //
+    // If |language| is a 2 letter code, the trailing byte must be '\0' or
+    // the BCP-47 separator '-'.
+    void packLanguage(const char* language);
+    // Sets the region code of this configuration to the first three bytes
+    // at |region|. If |region| is a 2 letter code, the trailing byte must be '\0'
+    // or the BCP-47 separator '-'.
+    void packRegion(const char* region);
 
     // Returns a positive integer if this config is more specific than |o|
     // with respect to their locales, a negative integer if |o| is more specific
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 501b83e..9cea68c 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -463,17 +463,8 @@
     if (locale) {
         setLocaleLocked(locale);
     } else if (config.language[0] != 0) {
-        char spec[9];
-        spec[0] = config.language[0];
-        spec[1] = config.language[1];
-        if (config.country[0] != 0) {
-            spec[2] = '_';
-            spec[3] = config.country[0];
-            spec[4] = config.country[1];
-            spec[5] = 0;
-        } else {
-            spec[3] = 0;
-        }
+        char spec[RESTABLE_MAX_LOCALE_LEN];
+        config.getBcp47Locale(spec);
         setLocaleLocked(spec);
     } else {
         updateResourceParamsLocked();
@@ -733,20 +724,11 @@
         return;
     }
 
-    size_t llen = mLocale ? strlen(mLocale) : 0;
-    mConfig->language[0] = 0;
-    mConfig->language[1] = 0;
-    mConfig->country[0] = 0;
-    mConfig->country[1] = 0;
-    if (llen >= 2) {
-        mConfig->language[0] = mLocale[0];
-        mConfig->language[1] = mLocale[1];
+    if (mLocale) {
+        mConfig->setBcp47Locale(mLocale);
+    } else {
+        mConfig->clearLocale();
     }
-    if (llen >= 5) {
-        mConfig->country[0] = mLocale[3];
-        mConfig->country[1] = mLocale[4];
-    }
-    mConfig->size = sizeof(*mConfig);
 
     res->setParameters(mConfig);
 }
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 9e544e9..94d150a 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -1566,9 +1566,9 @@
   return 0;
 }
 
-/* static */ void packLanguageOrRegion(const char in[3], const char base,
+/* static */ void packLanguageOrRegion(const char* in, const char base,
         char out[2]) {
-  if (in[2] == 0) {
+  if (in[2] == 0 || in[2] == '-') {
       out[0] = in[0];
       out[1] = in[1];
   } else {
@@ -1582,11 +1582,11 @@
 }
 
 
-void ResTable_config::packLanguage(const char language[3]) {
+void ResTable_config::packLanguage(const char* language) {
     packLanguageOrRegion(language, 'a', this->language);
 }
 
-void ResTable_config::packRegion(const char region[3]) {
+void ResTable_config::packRegion(const char* region) {
     packLanguageOrRegion(region, '0', this->country);
 }
 
@@ -2294,7 +2294,7 @@
     return true;
 }
 
-void ResTable_config::getLocale(char str[RESTABLE_MAX_LOCALE_LEN]) const {
+void ResTable_config::getBcp47Locale(char str[RESTABLE_MAX_LOCALE_LEN]) const {
     memset(str, 0, RESTABLE_MAX_LOCALE_LEN);
 
     // This represents the "any" locale value, which has traditionally been
@@ -2305,34 +2305,83 @@
 
     size_t charsWritten = 0;
     if (language[0]) {
-        unpackLanguage(str);
-    }
-
-    if (country[0]) {
-        if (charsWritten) {
-            str[charsWritten++] = '_';
-            str[charsWritten++] = 'r';
-        }
-        charsWritten += unpackRegion(str + charsWritten);
+        charsWritten += unpackLanguage(str);
     }
 
     if (localeScript[0]) {
         if (charsWritten) {
-            str[charsWritten++] = '_';
-            str[charsWritten++] = '_s';
+            str[charsWritten++] = '-';
         }
         memcpy(str + charsWritten, localeScript, sizeof(localeScript));
+        charsWritten += sizeof(localeScript);
+    }
+
+    if (country[0]) {
+        if (charsWritten) {
+            str[charsWritten++] = '-';
+        }
+        charsWritten += unpackRegion(str + charsWritten);
     }
 
     if (localeVariant[0]) {
         if (charsWritten) {
-            str[charsWritten++] = '_';
-            str[charsWritten++] = 'v';
+            str[charsWritten++] = '-';
         }
         memcpy(str + charsWritten, localeVariant, sizeof(localeVariant));
     }
 }
 
+/* static */ inline bool assignLocaleComponent(ResTable_config* config,
+        const char* start, size_t size) {
+
+  switch (size) {
+       case 0:
+           return false;
+       case 2:
+       case 3:
+           config->language[0] ? config->packRegion(start) : config->packLanguage(start);
+           break;
+       case 4:
+           config->localeScript[0] = toupper(start[0]);
+           for (size_t i = 1; i < 4; ++i) {
+               config->localeScript[i] = tolower(start[i]);
+           }
+           break;
+       case 5:
+       case 6:
+       case 7:
+       case 8:
+           for (size_t i = 0; i < size; ++i) {
+               config->localeVariant[i] = tolower(start[i]);
+           }
+           break;
+       default:
+           return false;
+  }
+
+  return true;
+}
+
+void ResTable_config::setBcp47Locale(const char* in) {
+    locale = 0;
+    memset(localeScript, 0, sizeof(localeScript));
+    memset(localeVariant, 0, sizeof(localeVariant));
+
+    const char* separator = in;
+    const char* start = in;
+    while ((separator = strchr(start, '-')) != NULL) {
+        const size_t size = separator - start;
+        if (!assignLocaleComponent(this, start, size)) {
+            fprintf(stderr, "Invalid BCP-47 locale string: %s", in);
+        }
+
+        start = (separator + 1);
+    }
+
+    const size_t size = in + strlen(in) - start;
+    assignLocaleComponent(this, start, size);
+}
+
 String8 ResTable_config::toString() const {
     String8 res;
 
@@ -2345,7 +2394,7 @@
         res.appendFormat("%dmnc", dtohs(mnc));
     }
     char localeStr[RESTABLE_MAX_LOCALE_LEN];
-    getLocale(localeStr);
+    getBcp47Locale(localeStr);
     res.append(localeStr);
 
     if ((screenLayout&MASK_LAYOUTDIR) != 0) {
@@ -5100,18 +5149,20 @@
                 const size_t L = type->configs.size();
                 for (size_t l=0; l<L; l++) {
                     const ResTable_type* config = type->configs[l];
-                    const ResTable_config* cfg = &config->config;
+                    ResTable_config cfg;
+                    memset(&cfg, 0, sizeof(ResTable_config));
+                    cfg.copyFromDtoH(config->config);
                     // only insert unique
                     const size_t M = configs->size();
                     size_t m;
                     for (m=0; m<M; m++) {
-                        if (0 == (*configs)[m].compare(*cfg)) {
+                        if (0 == (*configs)[m].compare(cfg)) {
                             break;
                         }
                     }
                     // if we didn't find it
                     if (m == M) {
-                        configs->add(*cfg);
+                        configs->add(cfg);
                     }
                 }
             }
@@ -5129,7 +5180,7 @@
 
     char locale[RESTABLE_MAX_LOCALE_LEN];
     for (size_t i=0; i<I; i++) {
-        configs[i].getLocale(locale);
+        configs[i].getBcp47Locale(locale);
         const size_t J = locales->size();
         size_t j;
         for (j=0; j<J; j++) {
@@ -5753,7 +5804,7 @@
     }
 #if 0
     char localeStr[RESTABLE_MAX_LOCALE_LEN];
-    mParams.getLocale(localeStr);
+    mParams.getBcp47Locale(localeStr);
     printf("mParams=%s,\n" localeStr);
 #endif
     size_t pgCount = mPackageGroups.size();
diff --git a/libs/androidfw/tests/ResourceTypes_test.cpp b/libs/androidfw/tests/ResourceTypes_test.cpp
index eadfe00..4888b4a 100644
--- a/libs/androidfw/tests/ResourceTypes_test.cpp
+++ b/libs/androidfw/tests/ResourceTypes_test.cpp
@@ -146,5 +146,40 @@
     EXPECT_TRUE(r.isMoreSpecificThan(l));
 }
 
+TEST(ResourceTypesTest, setLocale) {
+    ResTable_config test;
+    test.setBcp47Locale("en-US");
+    EXPECT_EQ('e', test.language[0]);
+    EXPECT_EQ('n', test.language[1]);
+    EXPECT_EQ('U', test.country[0]);
+    EXPECT_EQ('S', test.country[1]);
+    EXPECT_EQ(0, test.localeScript[0]);
+    EXPECT_EQ(0, test.localeVariant[0]);
+
+    test.setBcp47Locale("eng-419");
+    char out[4] = { 1, 1, 1, 1};
+    test.unpackLanguage(out);
+    EXPECT_EQ('e', out[0]);
+    EXPECT_EQ('n', out[1]);
+    EXPECT_EQ('g', out[2]);
+    EXPECT_EQ(0, out[3]);
+    memset(out, 1, 4);
+    test.unpackRegion(out);
+    EXPECT_EQ('4', out[0]);
+    EXPECT_EQ('1', out[1]);
+    EXPECT_EQ('9', out[2]);
+
+
+    test.setBcp47Locale("en-Latn-419");
+    memset(out, 1, 4);
+    EXPECT_EQ('e', test.language[0]);
+    EXPECT_EQ('n', test.language[1]);
+
+    EXPECT_EQ(0, memcmp("Latn", test.localeScript, 4));
+    test.unpackRegion(out);
+    EXPECT_EQ('4', out[0]);
+    EXPECT_EQ('1', out[1]);
+    EXPECT_EQ('9', out[2]);
+}
 
 }  // namespace android.
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index 6a3c506..f9a2d19 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -149,205 +149,506 @@
 // =========================================================================
 // =========================================================================
 
-status_t
-AaptGroupEntry::parseNamePart(const String8& part, int* axis, uint32_t* value)
+/* static */ void AaptLocaleValue::splitAndLowerCase(const char* const chars,
+        Vector<String8>* parts, const char separator) {
+    const char *p = chars;
+    const char *q;
+    while (NULL != (q = strchr(p, separator))) {
+         String8 val(p, q - p);
+         val.toLower();
+         parts->add(val);
+         p = q+1;
+    }
+
+    if (p < chars + strlen(chars)) {
+        String8 val(p);
+        val.toLower();
+        parts->add(val);
+    }
+}
+
+/* static */
+inline bool isAlpha(const String8& string) {
+     const size_t length = string.length();
+     for (size_t i = 0; i < length; ++i) {
+          if (!isalpha(string[i])) {
+              return false;
+          }
+     }
+
+     return true;
+}
+
+/* static */
+inline bool isNumber(const String8& string) {
+     const size_t length = string.length();
+     for (size_t i = 0; i < length; ++i) {
+          if (!isdigit(string[i])) {
+              return false;
+          }
+     }
+
+     return true;
+}
+
+void AaptLocaleValue::setLanguage(const char* languageChars) {
+     size_t i = 0;
+     while ((*languageChars) != '\0') {
+          language[i++] = tolower(*languageChars);
+          languageChars++;
+     }
+}
+
+void AaptLocaleValue::setRegion(const char* regionChars) {
+    size_t i = 0;
+    while ((*regionChars) != '\0') {
+         region[i++] = toupper(*regionChars);
+         regionChars++;
+    }
+}
+
+void AaptLocaleValue::setScript(const char* scriptChars) {
+    size_t i = 0;
+    while ((*scriptChars) != '\0') {
+         if (i == 0) {
+             script[i++] = toupper(*scriptChars);
+         } else {
+             script[i++] = tolower(*scriptChars);
+         }
+         scriptChars++;
+    }
+}
+
+void AaptLocaleValue::setVariant(const char* variantChars) {
+     size_t i = 0;
+     while ((*variantChars) != '\0') {
+          variant[i++] = *variantChars;
+          variantChars++;
+     }
+}
+
+bool AaptLocaleValue::initFromFilterString(const String8& str) {
+     // A locale (as specified in the filter) is an underscore separated name such
+     // as "en_US", "en_Latn_US", or "en_US_POSIX".
+     Vector<String8> parts;
+     splitAndLowerCase(str.string(), &parts, '_');
+
+     const int numTags = parts.size();
+     bool valid = false;
+     if (numTags >= 1) {
+         const String8& lang = parts[0];
+         if (isAlpha(lang) && (lang.length() == 2 || lang.length() == 3)) {
+             setLanguage(lang.string());
+             valid = true;
+         }
+     }
+
+     if (!valid || numTags == 1) {
+         return valid;
+     }
+
+     // At this point, valid == true && numTags > 1.
+     const String8& part2 = parts[1];
+     if ((part2.length() == 2 && isAlpha(part2)) ||
+         (part2.length() == 3 && isNumber(part2))) {
+         setRegion(part2.string());
+     } else if (part2.length() == 4 && isAlpha(part2)) {
+         setScript(part2.string());
+     } else if (part2.length() >= 5 && part2.length() <= 8) {
+         setVariant(part2.string());
+     } else {
+         valid = false;
+     }
+
+     if (!valid || numTags == 2) {
+         return valid;
+     }
+
+     // At this point, valid == true && numTags > 1.
+     const String8& part3 = parts[2];
+     if (((part3.length() == 2 && isAlpha(part3)) ||
+         (part3.length() == 3 && isNumber(part3))) && script[0]) {
+         setRegion(part3.string());
+     } else if (part3.length() >= 5 && part3.length() <= 8) {
+         setVariant(part3.string());
+     } else {
+         valid = false;
+     }
+
+     if (!valid || numTags == 3) {
+         return valid;
+     }
+
+     const String8& part4 = parts[3];
+     if (part4.length() >= 5 && part4.length() <= 8) {
+         setVariant(part4.string());
+     } else {
+         valid = false;
+     }
+
+     if (!valid || numTags > 4) {
+         return false;
+     }
+
+     return true;
+}
+
+int AaptLocaleValue::initFromDirName(const Vector<String8>& parts, const int startIndex) {
+    const int size = parts.size();
+    int currentIndex = startIndex;
+
+    String8 part = parts[currentIndex];
+    if (part[0] == 'b' && part[1] == '+') {
+        // This is a "modified" BCP-47 language tag. Same semantics as BCP-47 tags,
+        // except that the separator is "+" and not "-".
+        Vector<String8> subtags;
+        AaptLocaleValue::splitAndLowerCase(part.string(), &subtags, '+');
+        subtags.removeItemsAt(0);
+        if (subtags.size() == 1) {
+            setLanguage(subtags[0]);
+        } else if (subtags.size() == 2) {
+            setLanguage(subtags[0]);
+
+            // The second tag can either be a region, a variant or a script.
+            switch (subtags[1].size()) {
+                case 2:
+                case 3:
+                    setRegion(subtags[1]);
+                    break;
+                case 4:
+                    setScript(subtags[1]);
+                    break;
+                case 5:
+                case 6:
+                case 7:
+                case 8:
+                    setVariant(subtags[1]);
+                    break;
+                default:
+                    fprintf(stderr, "ERROR: Invalid BCP-47 tag in directory name %s\n",
+                            part.string());
+                    return -1;
+            }
+        } else if (subtags.size() == 3) {
+            // The language is always the first subtag.
+            setLanguage(subtags[0]);
+
+            // The second subtag can either be a script or a region code.
+            // If its size is 4, it's a script code, else it's a region code.
+            bool hasRegion = false;
+            if (subtags[1].size() == 4) {
+                setScript(subtags[1]);
+            } else if (subtags[1].size() == 2 || subtags[1].size() == 3) {
+                setRegion(subtags[1]);
+                hasRegion = true;
+            } else {
+                fprintf(stderr, "ERROR: Invalid BCP-47 tag in directory name %s\n", part.string());
+                return -1;
+            }
+
+            // The third tag can either be a region code (if the second tag was
+            // a script), else a variant code.
+            if (subtags[2].size() > 4) {
+                setVariant(subtags[2]);
+            } else {
+                setRegion(subtags[2]);
+            }
+        } else if (subtags.size() == 4) {
+            setLanguage(subtags[0]);
+            setScript(subtags[1]);
+            setRegion(subtags[2]);
+            setVariant(subtags[3]);
+        } else {
+            fprintf(stderr, "ERROR: Invalid BCP-47 tag in directory name: %s\n", part.string());
+            return -1;
+        }
+
+        return ++currentIndex;
+    } else {
+        if ((part.length() == 2 || part.length() == 3) && isAlpha(part)) {
+            setLanguage(part);
+            if (++currentIndex == size) {
+                return size;
+            }
+        } else {
+            return currentIndex;
+        }
+
+        part = parts[currentIndex];
+        if (part.string()[0] == 'r' && part.length() == 3) {
+            setRegion(part.string() + 1);
+            if (++currentIndex == size) {
+                return size;
+            }
+        }
+    }
+
+    return currentIndex;
+}
+
+
+String8 AaptLocaleValue::toDirName() const {
+    String8 dirName("");
+    if (language[0]) {
+        dirName += language;
+    } else {
+        return dirName;
+    }
+
+    if (script[0]) {
+        dirName += "-s";
+        dirName += script;
+    }
+
+    if (region[0]) {
+        dirName += "-r";
+        dirName += region;
+    }
+
+    if (variant[0]) {
+        dirName += "-v";
+        dirName += variant;
+    }
+
+    return dirName;
+}
+
+void AaptLocaleValue::initFromResTable(const ResTable_config& config) {
+    config.unpackLanguage(language);
+    config.unpackRegion(region);
+    if (config.localeScript[0]) {
+        memcpy(script, config.localeScript, sizeof(config.localeScript));
+    }
+
+    if (config.localeVariant[0]) {
+        memcpy(variant, config.localeVariant, sizeof(config.localeVariant));
+    }
+}
+
+void AaptLocaleValue::writeTo(ResTable_config* out) const {
+    out->packLanguage(language);
+    out->packRegion(region);
+
+    if (script[0]) {
+        memcpy(out->localeScript, script, sizeof(out->localeScript));
+    }
+
+    if (variant[0]) {
+        memcpy(out->localeVariant, variant, sizeof(out->localeVariant));
+    }
+}
+
+
+/* static */ bool
+AaptGroupEntry::parseFilterNamePart(const String8& part, int* axis, AxisValue* value)
 {
     ResTable_config config;
+    memset(&config, 0, sizeof(ResTable_config));
 
     // IMSI - MCC
     if (getMccName(part.string(), &config)) {
         *axis = AXIS_MCC;
-        *value = config.mcc;
-        return 0;
+        value->intValue = config.mcc;
+        return true;
     }
 
     // IMSI - MNC
     if (getMncName(part.string(), &config)) {
         *axis = AXIS_MNC;
-        *value = config.mnc;
-        return 0;
+        value->intValue = config.mnc;
+        return true;
     }
 
     // locale - language
-    if (part.length() == 2 && isalpha(part[0]) && isalpha(part[1])) {
-        *axis = AXIS_LANGUAGE;
-        *value = part[1] << 8 | part[0];
-        return 0;
-    }
-
-    // locale - language_REGION
-    if (part.length() == 5 && isalpha(part[0]) && isalpha(part[1])
-            && part[2] == '_' && isalpha(part[3]) && isalpha(part[4])) {
-        *axis = AXIS_LANGUAGE;
-        *value = (part[4] << 24) | (part[3] << 16) | (part[1] << 8) | (part[0]);
-        return 0;
+    if (value->localeValue.initFromFilterString(part)) {
+        *axis = AXIS_LOCALE;
+        return true;
     }
 
     // layout direction
     if (getLayoutDirectionName(part.string(), &config)) {
         *axis = AXIS_LAYOUTDIR;
-        *value = (config.screenLayout&ResTable_config::MASK_LAYOUTDIR);
-        return 0;
+        value->intValue = (config.screenLayout&ResTable_config::MASK_LAYOUTDIR);
+        return true;
     }
 
     // smallest screen dp width
     if (getSmallestScreenWidthDpName(part.string(), &config)) {
         *axis = AXIS_SMALLESTSCREENWIDTHDP;
-        *value = config.smallestScreenWidthDp;
-        return 0;
+        value->intValue = config.smallestScreenWidthDp;
+        return true;
     }
 
     // screen dp width
     if (getScreenWidthDpName(part.string(), &config)) {
         *axis = AXIS_SCREENWIDTHDP;
-        *value = config.screenWidthDp;
-        return 0;
+        value->intValue = config.screenWidthDp;
+        return true;
     }
 
     // screen dp height
     if (getScreenHeightDpName(part.string(), &config)) {
         *axis = AXIS_SCREENHEIGHTDP;
-        *value = config.screenHeightDp;
-        return 0;
+        value->intValue = config.screenHeightDp;
+        return true;
     }
 
     // screen layout size
     if (getScreenLayoutSizeName(part.string(), &config)) {
         *axis = AXIS_SCREENLAYOUTSIZE;
-        *value = (config.screenLayout&ResTable_config::MASK_SCREENSIZE);
-        return 0;
+        value->intValue = (config.screenLayout&ResTable_config::MASK_SCREENSIZE);
+        return true;
     }
 
     // screen layout long
     if (getScreenLayoutLongName(part.string(), &config)) {
         *axis = AXIS_SCREENLAYOUTLONG;
-        *value = (config.screenLayout&ResTable_config::MASK_SCREENLONG);
-        return 0;
+        value->intValue = (config.screenLayout&ResTable_config::MASK_SCREENLONG);
+        return true;
     }
 
     // orientation
     if (getOrientationName(part.string(), &config)) {
         *axis = AXIS_ORIENTATION;
-        *value = config.orientation;
-        return 0;
+        value->intValue = config.orientation;
+        return true;
     }
 
     // ui mode type
     if (getUiModeTypeName(part.string(), &config)) {
         *axis = AXIS_UIMODETYPE;
-        *value = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
-        return 0;
+        value->intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
+        return true;
     }
 
     // ui mode night
     if (getUiModeNightName(part.string(), &config)) {
         *axis = AXIS_UIMODENIGHT;
-        *value = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
-        return 0;
+        value->intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
+        return true;
     }
 
     // density
     if (getDensityName(part.string(), &config)) {
         *axis = AXIS_DENSITY;
-        *value = config.density;
-        return 0;
+        value->intValue = config.density;
+        return true;
     }
 
     // touchscreen
     if (getTouchscreenName(part.string(), &config)) {
         *axis = AXIS_TOUCHSCREEN;
-        *value = config.touchscreen;
-        return 0;
+        value->intValue = config.touchscreen;
+        return true;
     }
 
     // keyboard hidden
     if (getKeysHiddenName(part.string(), &config)) {
         *axis = AXIS_KEYSHIDDEN;
-        *value = config.inputFlags;
-        return 0;
+        value->intValue = config.inputFlags;
+        return true;
     }
 
     // keyboard
     if (getKeyboardName(part.string(), &config)) {
         *axis = AXIS_KEYBOARD;
-        *value = config.keyboard;
-        return 0;
+        value->intValue = config.keyboard;
+        return true;
     }
 
     // navigation hidden
     if (getNavHiddenName(part.string(), &config)) {
         *axis = AXIS_NAVHIDDEN;
-        *value = config.inputFlags;
+        value->intValue = config.inputFlags;
         return 0;
     }
 
     // navigation
     if (getNavigationName(part.string(), &config)) {
         *axis = AXIS_NAVIGATION;
-        *value = config.navigation;
-        return 0;
+        value->intValue = config.navigation;
+        return true;
     }
 
     // screen size
     if (getScreenSizeName(part.string(), &config)) {
         *axis = AXIS_SCREENSIZE;
-        *value = config.screenSize;
-        return 0;
+        value->intValue = config.screenSize;
+        return true;
     }
 
     // version
     if (getVersionName(part.string(), &config)) {
         *axis = AXIS_VERSION;
-        *value = config.version;
-        return 0;
+        value->intValue = config.version;
+        return true;
     }
 
-    return 1;
+    return false;
 }
 
-uint32_t
+AxisValue
 AaptGroupEntry::getConfigValueForAxis(const ResTable_config& config, int axis)
 {
+    AxisValue value;
     switch (axis) {
         case AXIS_MCC:
-            return config.mcc;
+            value.intValue = config.mcc;
+            break;
         case AXIS_MNC:
-            return config.mnc;
-        case AXIS_LANGUAGE:
-            return (((uint32_t)config.country[1]) << 24) | (((uint32_t)config.country[0]) << 16)
-                | (((uint32_t)config.language[1]) << 8) | (config.language[0]);
+            value.intValue = config.mnc;
+            break;
+        case AXIS_LOCALE:
+            value.localeValue.initFromResTable(config);
+            break;
         case AXIS_LAYOUTDIR:
-            return config.screenLayout&ResTable_config::MASK_LAYOUTDIR;
+            value.intValue = config.screenLayout&ResTable_config::MASK_LAYOUTDIR;
+            break;
         case AXIS_SCREENLAYOUTSIZE:
-            return config.screenLayout&ResTable_config::MASK_SCREENSIZE;
+            value.intValue = config.screenLayout&ResTable_config::MASK_SCREENSIZE;
+            break;
         case AXIS_ORIENTATION:
-            return config.orientation;
+            value.intValue = config.orientation;
+            break;
         case AXIS_UIMODETYPE:
-            return (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
+            value.intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
+            break;
         case AXIS_UIMODENIGHT:
-            return (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
+            value.intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
+            break;
         case AXIS_DENSITY:
-            return config.density;
+            value.intValue = config.density;
+            break;
         case AXIS_TOUCHSCREEN:
-            return config.touchscreen;
+            value.intValue = config.touchscreen;
+            break;
         case AXIS_KEYSHIDDEN:
-            return config.inputFlags;
+            value.intValue = config.inputFlags;
+            break;
         case AXIS_KEYBOARD:
-            return config.keyboard;
+            value.intValue = config.keyboard;
+            break;
         case AXIS_NAVIGATION:
-            return config.navigation;
+            value.intValue = config.navigation;
+            break;
         case AXIS_SCREENSIZE:
-            return config.screenSize;
+            value.intValue = config.screenSize;
+            break;
         case AXIS_SMALLESTSCREENWIDTHDP:
-            return config.smallestScreenWidthDp;
+            value.intValue = config.smallestScreenWidthDp;
+            break;
         case AXIS_SCREENWIDTHDP:
-            return config.screenWidthDp;
+            value.intValue = config.screenWidthDp;
+            break;
         case AXIS_SCREENHEIGHTDP:
-            return config.screenHeightDp;
+            value.intValue = config.screenHeightDp;
+            break;
         case AXIS_VERSION:
-            return config.version;
+            value.intValue = config.version;
+            break;
     }
-    return 0;
+
+    return value;
 }
 
 bool
@@ -371,24 +672,14 @@
     mParamsChanged = true;
 
     Vector<String8> parts;
+    AaptLocaleValue::splitAndLowerCase(dir, &parts, '-');
 
-    String8 mcc, mnc, loc, layoutsize, layoutlong, orient, den;
+    String8 mcc, mnc, layoutsize, layoutlong, orient, den;
     String8 touch, key, keysHidden, nav, navHidden, size, layoutDir, vers;
     String8 uiModeType, uiModeNight, smallestwidthdp, widthdp, heightdp;
 
-    const char *p = dir;
-    const char *q;
-    while (NULL != (q = strchr(p, '-'))) {
-        String8 val(p, q-p);
-        val.toLower();
-        parts.add(val);
-        //printf("part: %s\n", parts[parts.size()-1].string());
-        p = q+1;
-    }
-    String8 val(p);
-    val.toLower();
-    parts.add(val);
-    //printf("part: %s\n", parts[parts.size()-1].string());
+    AaptLocaleValue locale;
+    int numLocaleComponents = 0;
 
     const int N = parts.size();
     int index = 0;
@@ -429,38 +720,18 @@
         }
         part = parts[index];
     } else {
-        //printf("not mcc: %s\n", part.string());
+        //printf("not mnc: %s\n", part.string());
     }
 
-    // locale - language
-    if (part.length() == 2 && isalpha(part[0]) && isalpha(part[1])) {
-        loc = part;
-
-        index++;
-        if (index == N) {
-            goto success;
-        }
-        part = parts[index];
-    } else {
-        //printf("not language: %s\n", part.string());
+    index = locale.initFromDirName(parts, index);
+    if (index == -1) {
+        return false;
+    }
+    if (index >= N){
+        goto success;
     }
 
-    // locale - region
-    if (loc.length() > 0
-            && part.length() == 3 && part[0] == 'r' && part[0] && part[1]) {
-        loc += "-";
-        part.toUpper();
-        loc += part.string() + 1;
-
-        index++;
-        if (index == N) {
-            goto success;
-        }
-        part = parts[index];
-    } else {
-        //printf("not region: %s\n", part.string());
-    }
-
+    part = parts[index];
     if (getLayoutDirectionName(part.string())) {
         layoutDir = part;
 
@@ -679,7 +950,7 @@
 success:
     this->mcc = mcc;
     this->mnc = mnc;
-    this->locale = loc;
+    this->locale = locale;
     this->screenLayoutSize = layoutsize;
     this->screenLayoutLong = layoutlong;
     this->smallestScreenWidthDp = smallestwidthdp;
@@ -711,7 +982,7 @@
     s += ",";
     s += this->mnc;
     s += ",";
-    s += this->locale;
+    s += locale.toDirName();
     s += ",";
     s += layoutDirection;
     s += ",";
@@ -765,12 +1036,15 @@
         }
         s += mnc;
     }
-    if (this->locale != "") {
-        if (s.length() > 0) {
-            s += "-";
-        }
-        s += locale;
+
+    const String8 localeComponent = locale.toDirName();
+    if (localeComponent != "") {
+         if (s.length() > 0) {
+             s += "-";
+         }
+         s += localeComponent;
     }
+
     if (this->layoutDirection != "") {
         if (s.length() > 0) {
             s += "-";
@@ -942,55 +1216,6 @@
     return true;
 }
 
-/*
- * Does this directory name fit the pattern of a locale dir ("en-rUS" or
- * "default")?
- *
- * TODO: Should insist that the first two letters are lower case, and the
- * second two are upper.
- */
-bool AaptGroupEntry::getLocaleName(const char* fileName,
-                                   ResTable_config* out)
-{
-    if (strcmp(fileName, kWildcardName) == 0
-            || strcmp(fileName, kDefaultLocale) == 0) {
-        if (out) {
-            out->language[0] = 0;
-            out->language[1] = 0;
-            out->country[0] = 0;
-            out->country[1] = 0;
-        }
-        return true;
-    }
-
-    if (strlen(fileName) == 2 && isalpha(fileName[0]) && isalpha(fileName[1])) {
-        if (out) {
-            out->language[0] = fileName[0];
-            out->language[1] = fileName[1];
-            out->country[0] = 0;
-            out->country[1] = 0;
-        }
-        return true;
-    }
-
-    if (strlen(fileName) == 5 &&
-        isalpha(fileName[0]) &&
-        isalpha(fileName[1]) &&
-        fileName[2] == '-' &&
-        isalpha(fileName[3]) &&
-        isalpha(fileName[4])) {
-        if (out) {
-            out->language[0] = fileName[0];
-            out->language[1] = fileName[1];
-            out->country[0] = fileName[3];
-            out->country[1] = fileName[4];
-        }
-        return true;
-    }
-
-    return false;
-}
-
 bool AaptGroupEntry::getLayoutDirectionName(const char* name, ResTable_config* out)
 {
     if (strcmp(name, kWildcardName) == 0) {
@@ -1496,18 +1721,18 @@
     return v;
 }
 
-const ResTable_config& AaptGroupEntry::toParams() const
+const ResTable_config AaptGroupEntry::toParams() const
 {
     if (!mParamsChanged) {
         return mParams;
     }
 
     mParamsChanged = false;
-    ResTable_config& params(mParams);
-    memset(&params, 0, sizeof(params));
+    ResTable_config& params = mParams;
+    memset(&params, 0, sizeof(ResTable_config));
     getMccName(mcc.string(), &params);
     getMncName(mnc.string(), &params);
-    getLocaleName(locale.string(), &params);
+    locale.writeTo(&params);
     getLayoutDirectionName(layoutDirection.string(), &params);
     getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), &params);
     getScreenWidthDpName(screenWidthDp.string(), &params);
@@ -1992,7 +2217,9 @@
 
 AaptAssets::AaptAssets()
     : AaptDir(String8(), String8()),
-      mChanged(false), mHaveIncludedAssets(false), mRes(NULL)
+      mHavePrivateSymbols(false),
+      mChanged(false), mHaveIncludedAssets(false),
+      mRes(NULL)
 {
 }
 
@@ -2505,9 +2732,9 @@
             // If our preferred density is hdpi but we only have mdpi and xhdpi resources, we
             // pick xhdpi.
             uint32_t preferredDensity = 0;
-            const SortedVector<uint32_t>* preferredConfigs = prefFilter.configsForAxis(AXIS_DENSITY);
+            const SortedVector<AxisValue>* preferredConfigs = prefFilter.configsForAxis(AXIS_DENSITY);
             if (preferredConfigs != NULL && preferredConfigs->size() > 0) {
-                preferredDensity = (*preferredConfigs)[0];
+                preferredDensity = (*preferredConfigs)[0].intValue;
             }
 
             // Now deal with preferred configurations.
diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h
index 9cc9007..336d08b 100644
--- a/tools/aapt/AaptAssets.h
+++ b/tools/aapt/AaptAssets.h
@@ -13,7 +13,6 @@
 #include <utils/RefBase.h>
 #include <utils/SortedVector.h>
 #include <utils/String8.h>
-#include <utils/String8.h>
 #include <utils/Vector.h>
 #include "ZipFile.h"
 
@@ -34,8 +33,7 @@
     AXIS_NONE = 0,
     AXIS_MCC = 1,
     AXIS_MNC,
-    AXIS_LANGUAGE,
-    AXIS_REGION,
+    AXIS_LOCALE,
     AXIS_SCREENLAYOUTSIZE,
     AXIS_SCREENLAYOUTLONG,
     AXIS_ORIENTATION,
@@ -58,6 +56,73 @@
     AXIS_END = AXIS_VERSION,
 };
 
+struct AaptLocaleValue {
+     char language[4];
+     char region[4];
+     char script[4];
+     char variant[8];
+
+     AaptLocaleValue() {
+         memset(this, 0, sizeof(AaptLocaleValue));
+     }
+
+     // Initialize this AaptLocaleValue from a config string.
+     bool initFromFilterString(const String8& config);
+
+     int initFromDirName(const Vector<String8>& parts, const int startIndex);
+
+     // Initialize this AaptLocaleValue from a ResTable_config.
+     void initFromResTable(const ResTable_config& config);
+
+     void writeTo(ResTable_config* out) const;
+
+     String8 toDirName() const;
+
+     int compare(const AaptLocaleValue& other) const {
+         return memcmp(this, &other, sizeof(AaptLocaleValue));
+     }
+
+     static void splitAndLowerCase(const char* const chars, Vector<String8>* parts,
+             const char separator);
+
+     inline bool operator<(const AaptLocaleValue& o) const { return compare(o) < 0; }
+     inline bool operator<=(const AaptLocaleValue& o) const { return compare(o) <= 0; }
+     inline bool operator==(const AaptLocaleValue& o) const { return compare(o) == 0; }
+     inline bool operator!=(const AaptLocaleValue& o) const { return compare(o) != 0; }
+     inline bool operator>=(const AaptLocaleValue& o) const { return compare(o) >= 0; }
+     inline bool operator>(const AaptLocaleValue& o) const { return compare(o) > 0; }
+private:
+     void setLanguage(const char* language);
+     void setRegion(const char* language);
+     void setScript(const char* script);
+     void setVariant(const char* variant);
+};
+
+struct AxisValue {
+    // Used for all axes except AXIS_LOCALE, which is represented
+    // as a AaptLocaleValue value.
+    int intValue;
+    AaptLocaleValue localeValue;
+
+    AxisValue() : intValue(0) {
+    }
+
+    inline int compare(const AxisValue &other) const  {
+        if (intValue != other.intValue) {
+            return intValue - other.intValue;
+        }
+
+        return localeValue.compare(other.localeValue);
+    }
+
+    inline bool operator<(const AxisValue& o) const { return compare(o) < 0; }
+    inline bool operator<=(const AxisValue& o) const { return compare(o) <= 0; }
+    inline bool operator==(const AxisValue& o) const { return compare(o) == 0; }
+    inline bool operator!=(const AxisValue& o) const { return compare(o) != 0; }
+    inline bool operator>=(const AxisValue& o) const { return compare(o) >= 0; }
+    inline bool operator>(const AxisValue& o) const { return compare(o) > 0; }
+};
+
 /**
  * This structure contains a specific variation of a single file out
  * of all the variations it can have that we can have.
@@ -65,22 +130,38 @@
 struct AaptGroupEntry
 {
 public:
-    AaptGroupEntry() : mParamsChanged(true) { }
-    AaptGroupEntry(const String8& _locale, const String8& _vendor)
-        : locale(_locale), vendor(_vendor), mParamsChanged(true) { }
+    AaptGroupEntry() : mParamsChanged(true) {
+        memset(&mParams, 0, sizeof(ResTable_config));
+    }
 
     bool initFromDirName(const char* dir, String8* resType);
 
-    static status_t parseNamePart(const String8& part, int* axis, uint32_t* value);
+    static bool parseFilterNamePart(const String8& part, int* axis, AxisValue* value);
 
-    static uint32_t getConfigValueForAxis(const ResTable_config& config, int axis);
+    static AxisValue getConfigValueForAxis(const ResTable_config& config, int axis);
 
     static bool configSameExcept(const ResTable_config& config,
             const ResTable_config& otherConfig, int axis);
 
+    int compare(const AaptGroupEntry& o) const;
+
+    const ResTable_config toParams() const;
+
+    inline bool operator<(const AaptGroupEntry& o) const { return compare(o) < 0; }
+    inline bool operator<=(const AaptGroupEntry& o) const { return compare(o) <= 0; }
+    inline bool operator==(const AaptGroupEntry& o) const { return compare(o) == 0; }
+    inline bool operator!=(const AaptGroupEntry& o) const { return compare(o) != 0; }
+    inline bool operator>=(const AaptGroupEntry& o) const { return compare(o) >= 0; }
+    inline bool operator>(const AaptGroupEntry& o) const { return compare(o) > 0; }
+
+    String8 toString() const;
+    String8 toDirName(const String8& resType) const;
+
+    const String8& getVersionString() const { return version; }
+
+private:
     static bool getMccName(const char* name, ResTable_config* out = NULL);
     static bool getMncName(const char* name, ResTable_config* out = NULL);
-    static bool getLocaleName(const char* name, ResTable_config* out = NULL);
     static bool getScreenLayoutSizeName(const char* name, ResTable_config* out = NULL);
     static bool getScreenLayoutLongName(const char* name, ResTable_config* out = NULL);
     static bool getOrientationName(const char* name, ResTable_config* out = NULL);
@@ -99,26 +180,9 @@
     static bool getLayoutDirectionName(const char* name, ResTable_config* out = NULL);
     static bool getVersionName(const char* name, ResTable_config* out = NULL);
 
-    int compare(const AaptGroupEntry& o) const;
-
-    const ResTable_config& toParams() const;
-
-    inline bool operator<(const AaptGroupEntry& o) const { return compare(o) < 0; }
-    inline bool operator<=(const AaptGroupEntry& o) const { return compare(o) <= 0; }
-    inline bool operator==(const AaptGroupEntry& o) const { return compare(o) == 0; }
-    inline bool operator!=(const AaptGroupEntry& o) const { return compare(o) != 0; }
-    inline bool operator>=(const AaptGroupEntry& o) const { return compare(o) >= 0; }
-    inline bool operator>(const AaptGroupEntry& o) const { return compare(o) > 0; }
-
-    String8 toString() const;
-    String8 toDirName(const String8& resType) const;
-
-    const String8& getVersionString() const { return version; }
-
-private:
     String8 mcc;
     String8 mnc;
-    String8 locale;
+    AaptLocaleValue locale;
     String8 vendor;
     String8 smallestScreenWidthDp;
     String8 screenWidthDp;
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 48e3125..8e856b7 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -600,6 +600,7 @@
     // the API version because key resources like icons will have an implicit
     // version if they are using newer config types like density.
     ResTable_config config;
+    memset(&config, 0, sizeof(ResTable_config));
     config.language[0] = 'e';
     config.language[1] = 'n';
     config.country[0] = 'U';
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index a390e42..c923bc2 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -80,6 +80,7 @@
     ResourceDirIterator(const sp<ResourceTypeSet>& set, const String8& resType)
         : mResType(resType), mSet(set), mSetPos(0), mGroupPos(0)
     {
+        memset(&mParams, 0, sizeof(ResTable_config));
     }
 
     inline const sp<AaptGroup>& getGroup() const { return mGroup; }
diff --git a/tools/aapt/ResourceFilter.cpp b/tools/aapt/ResourceFilter.cpp
index 8cfd2a5..e8a2be4 100644
--- a/tools/aapt/ResourceFilter.cpp
+++ b/tools/aapt/ResourceFilter.cpp
@@ -28,8 +28,8 @@
             mContainsPseudo = true;
         }
         int axis;
-        uint32_t value;
-        if (AaptGroupEntry::parseNamePart(part, &axis, &value)) {
+        AxisValue value;
+        if (!AaptGroupEntry::parseFilterNamePart(part, &axis, &value)) {
             fprintf(stderr, "Invalid configuration: %s\n", arg);
             fprintf(stderr, "                       ");
             for (int i=0; i<p-arg; i++) {
@@ -44,15 +44,20 @@
 
         ssize_t index = mData.indexOfKey(axis);
         if (index < 0) {
-            mData.add(axis, SortedVector<uint32_t>());
+            mData.add(axis, SortedVector<AxisValue>());
         }
-        SortedVector<uint32_t>& sv = mData.editValueFor(axis);
+        SortedVector<AxisValue>& sv = mData.editValueFor(axis);
         sv.add(value);
-        // if it's a locale with a region, also match an unmodified locale of the
-        // same language
-        if (axis == AXIS_LANGUAGE) {
-            if (value & 0xffff0000) {
-                sv.add(value & 0x0000ffff);
+
+        // If it's a locale with a region, script or variant, we should also match an
+        // unmodified locale of the same language
+        if (axis == AXIS_LOCALE) {
+            if (value.localeValue.region[0] || value.localeValue.script[0] ||
+                value.localeValue.variant[0]) {
+                AxisValue copy;
+                memcpy(copy.localeValue.language, value.localeValue.language,
+                       sizeof(value.localeValue.language));
+                sv.add(copy);
             }
         }
         p = q;
@@ -70,9 +75,9 @@
 }
 
 bool
-ResourceFilter::match(int axis, uint32_t value) const
+ResourceFilter::match(int axis, const AxisValue& value) const
 {
-    if (value == 0) {
+    if (value.intValue == 0 && (value.localeValue.language[0] == 0)) {
         // they didn't specify anything so take everything
         return true;
     }
@@ -81,7 +86,7 @@
         // we didn't request anything on this axis so take everything
         return true;
     }
-    const SortedVector<uint32_t>& sv = mData.valueAt(index);
+    const SortedVector<AxisValue>& sv = mData.valueAt(index);
     return sv.indexOf(value) >= 0;
 }
 
@@ -102,7 +107,7 @@
     return true;
 }
 
-const SortedVector<uint32_t>* ResourceFilter::configsForAxis(int axis) const
+const SortedVector<AxisValue>* ResourceFilter::configsForAxis(int axis) const
 {
     ssize_t index = mData.indexOfKey(axis);
     if (index < 0) {
diff --git a/tools/aapt/ResourceFilter.h b/tools/aapt/ResourceFilter.h
index 647b7bb..0d127ba 100644
--- a/tools/aapt/ResourceFilter.h
+++ b/tools/aapt/ResourceFilter.h
@@ -19,14 +19,15 @@
     ResourceFilter() : mData(), mContainsPseudo(false) {}
     status_t parse(const char* arg);
     bool isEmpty() const;
-    bool match(int axis, uint32_t value) const;
     bool match(int axis, const ResTable_config& config) const;
     bool match(const ResTable_config& config) const;
-    const SortedVector<uint32_t>* configsForAxis(int axis) const;
+    const SortedVector<AxisValue>* configsForAxis(int axis) const;
     inline bool containsPseudo() const { return mContainsPseudo; }
 
 private:
-    KeyedVector<int,SortedVector<uint32_t> > mData;
+    bool match(int axis, const AxisValue& value) const;
+
+    KeyedVector<int,SortedVector<AxisValue> > mData;
     bool mContainsPseudo;
 };
 
diff --git a/tools/aapt/ResourceIdCache.h b/tools/aapt/ResourceIdCache.h
index 65f7781..e6bcda2 100644
--- a/tools/aapt/ResourceIdCache.h
+++ b/tools/aapt/ResourceIdCache.h
@@ -7,7 +7,6 @@
 #define RESOURCE_ID_CACHE_H
 
 namespace android {
-class android::String16;
 
 class ResourceIdCache {
 public:
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 02a74b1..aff0088 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -1292,8 +1292,8 @@
                 curIsStyled = true;
             } else if (strcmp16(block.getElementName(&len), string16.string()) == 0) {
                 // Note the existence and locale of every string we process
-                char rawLocale[16];
-                curParams.getLocale(rawLocale);
+                char rawLocale[RESTABLE_MAX_LOCALE_LEN];
+                curParams.getBcp47Locale(rawLocale);
                 String8 locale(rawLocale);
                 String16 name;
                 String16 translatable;