Loading include/androidfw/ResourceTypes.h +84 −4 Original line number Diff line number Diff line Loading @@ -808,6 +808,19 @@ struct ResTable_package uint32_t lastPublicKey; }; // The most specific locale can consist of: // // - a 3 char language code // - a 3 char region code prefixed by a 'r' // - a 4 char script code prefixed by a 's' // - a 8 char variant code prefixed by a 'v' // // each separated by a single char separator, which sums up to a total of 24 // chars, (25 include the string terminator) rounded up to 28 to be 4 byte // aligned. #define RESTABLE_MAX_LOCALE_LEN 28 /** * Describes a particular resource configuration. */ Loading @@ -828,10 +841,42 @@ struct ResTable_config union { struct { // \0\0 means "any". Otherwise, en, fr, etc. // This field can take three different forms: // - \0\0 means "any". // // - Two 7 bit ascii values interpreted as ISO-639-1 language // codes ('fr', 'en' etc. etc.). The high bit for both bytes is // zero. // // - A single 16 bit little endian packed value representing an // ISO-639-2 3 letter language code. This will be of the form: // // {1, t, t, t, t, t, s, s, s, s, s, f, f, f, f, f} // // bit[0, 4] = first letter of the language code // bit[5, 9] = second letter of the language code // bit[10, 14] = third letter of the language code. // bit[15] = 1 always // // For backwards compatibility, languages that have unambiguous // two letter codes are represented in that format. // // The layout is always bigendian irrespective of the runtime // architecture. char language[2]; // \0\0 means "any". Otherwise, US, CA, etc. // This field can take three different forms: // - \0\0 means "any". // // - Two 7 bit ascii values interpreted as 2 letter region // codes ('US', 'GB' etc.). The high bit for both bytes is zero. // // - An UN M.49 3 digit region code. For simplicity, these are packed // in the same manner as the language codes, though we should need // only 10 bits to represent them, instead of the 15. // // The layout is always bigendian irrespective of the runtime // architecture. char country[2]; }; uint32_t locale; Loading Loading @@ -1006,6 +1051,15 @@ struct ResTable_config uint32_t screenSizeDp; }; // The ISO-15924 short name for the script corresponding to this // configuration. (eg. Hant, Latn, etc.). Interpreted in conjunction with // the locale field char localeScript[4]; // A single BCP-47 variant subtag. Will vary in length between 5 and 8 // chars. Interpreted in conjunction with the locale field. char localeVariant[8]; void copyFromDeviceNoSwap(const ResTable_config& o); void copyFromDtoH(const ResTable_config& o); Loading Loading @@ -1063,7 +1117,33 @@ struct ResTable_config // settings is the requested settings bool match(const ResTable_config& settings) const; void getLocale(char str[6]) 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. // // '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; // Get the 2 or 3 letter language code of this configuration. Trailing // bytes are set to '\0'. size_t unpackLanguage(char language[4]) const; // Get the 2 or 3 letter language code of this configuration. Trailing // 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]); // 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 // and 0 if they're equally specific. int isLocaleMoreSpecificThan(const ResTable_config &o) const; String8 toString() const; }; Loading libs/androidfw/ResourceTypes.cpp +195 −44 Original line number Diff line number Diff line Loading @@ -66,11 +66,6 @@ namespace android { // size measured in sizeof(uint32_t) #define IDMAP_HEADER_SIZE (ResTable::IDMAP_HEADER_SIZE_BYTES / sizeof(uint32_t)) static void printToLogFunc(int32_t cookie, const char* txt) { ALOGV("[cookie=%d] %s", cookie, txt); } // Standard C isspace() is only required to look at the low byte of its input, so // produces incorrect results for UTF-16 characters. For safety's sake, assume that // any high-byte UTF-16 code point is not whitespace. Loading Loading @@ -1565,6 +1560,71 @@ void ResTable_config::copyFromDeviceNoSwap(const ResTable_config& o) { } } /* static */ size_t unpackLanguageOrRegion(const char in[2], const char base, char out[4]) { if (in[0] & 0x80) { // The high bit is "1", which means this is a packed three letter // language code. // The smallest 5 bits of the second char are the first alphabet. const uint8_t first = in[1] & 0x1f; // The last three bits of the second char and the first two bits // of the first char are the second alphabet. const uint8_t second = ((in[1] & 0xe0) >> 5) + ((in[0] & 0x03) << 3); // Bits 3 to 7 (inclusive) of the first char are the third alphabet. const uint8_t third = (in[0] & 0x7c) >> 2; out[0] = first + base; out[1] = second + base; out[2] = third + base; out[3] = 0; return 3; } if (in[0]) { memcpy(out, in, 2); memset(out + 2, 0, 2); return 2; } memset(out, 0, 4); return 0; } /* static */ void packLanguageOrRegion(const char in[3], const char base, char out[2]) { if (in[2] == 0) { 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; out[0] = (0x80 | (third << 2) | (second >> 3)); out[1] = ((second << 5) | first); } } void ResTable_config::packLanguage(const char language[3]) { packLanguageOrRegion(language, 'a', this->language); } void ResTable_config::packRegion(const char region[3]) { packLanguageOrRegion(region, '0', this->country); } size_t ResTable_config::unpackLanguage(char language[4]) const { return unpackLanguageOrRegion(this->language, 'a', language); } size_t ResTable_config::unpackRegion(char region[4]) const { return unpackLanguageOrRegion(this->country, '0', region); } void ResTable_config::copyFromDtoH(const ResTable_config& o) { copyFromDeviceNoSwap(o); size = sizeof(ResTable_config); Loading Loading @@ -1594,10 +1654,30 @@ void ResTable_config::swapHtoD() { screenHeightDp = htods(screenHeightDp); } /* static */ inline int compareLocales(const ResTable_config &l, const ResTable_config &r) { if (l.locale != r.locale) { // NOTE: This is the old behaviour with respect to comparison orders. // The diff value here doesn't make much sense (given our bit packing scheme) // but it's stable, and that's all we need. return l.locale - r.locale; } // The language & region are equal, so compare the scripts and variants. int script = memcmp(l.localeScript, r.localeScript, sizeof(l.localeScript)); if (script) { return script; } // The language, region and script are equal, so compare variants. // // This should happen very infrequently (if at all.) return memcmp(l.localeVariant, r.localeVariant, sizeof(l.localeVariant)); } int ResTable_config::compare(const ResTable_config& o) const { int32_t diff = (int32_t)(imsi - o.imsi); if (diff != 0) return diff; diff = (int32_t)(locale - o.locale); diff = compareLocales(*this, o); if (diff != 0) return diff; diff = (int32_t)(screenType - o.screenType); if (diff != 0) return diff; Loading @@ -1624,18 +1704,15 @@ int ResTable_config::compareLogical(const ResTable_config& o) const { if (mnc != o.mnc) { return mnc < o.mnc ? -1 : 1; } if (language[0] != o.language[0]) { return language[0] < o.language[0] ? -1 : 1; } if (language[1] != o.language[1]) { return language[1] < o.language[1] ? -1 : 1; } if (country[0] != o.country[0]) { return country[0] < o.country[0] ? -1 : 1; int diff = compareLocales(*this, o); if (diff < 0) { return -1; } if (country[1] != o.country[1]) { return country[1] < o.country[1] ? -1 : 1; if (diff > 0) { return 1; } if ((screenLayout & MASK_LAYOUTDIR) != (o.screenLayout & MASK_LAYOUTDIR)) { return (screenLayout & MASK_LAYOUTDIR) < (o.screenLayout & MASK_LAYOUTDIR) ? -1 : 1; } Loading Loading @@ -1682,7 +1759,6 @@ int ResTable_config::diff(const ResTable_config& o) const { int diffs = 0; if (mcc != o.mcc) diffs |= CONFIG_MCC; if (mnc != o.mnc) diffs |= CONFIG_MNC; if (locale != o.locale) diffs |= CONFIG_LOCALE; if (orientation != o.orientation) diffs |= CONFIG_ORIENTATION; if (density != o.density) diffs |= CONFIG_DENSITY; if (touchscreen != o.touchscreen) diffs |= CONFIG_TOUCHSCREEN; Loading @@ -1697,9 +1773,44 @@ int ResTable_config::diff(const ResTable_config& o) const { if (uiMode != o.uiMode) diffs |= CONFIG_UI_MODE; if (smallestScreenWidthDp != o.smallestScreenWidthDp) diffs |= CONFIG_SMALLEST_SCREEN_SIZE; if (screenSizeDp != o.screenSizeDp) diffs |= CONFIG_SCREEN_SIZE; const int diff = compareLocales(*this, o); if (diff) diffs |= CONFIG_LOCALE; return diffs; } int ResTable_config::isLocaleMoreSpecificThan(const ResTable_config& o) const { if (locale || o.locale) { if (language[0] != o.language[0]) { if (!language[0]) return -1; if (!o.language[0]) return 1; } if (country[0] != o.country[0]) { if (!country[0]) return -1; if (!o.country[0]) return 1; } } // There isn't a well specified "importance" order between variants and // scripts. We can't easily tell whether, say "en-Latn-US" is more or less // specific than "en-US-POSIX". // // We therefore arbitrarily decide to give priority to variants over // scripts since it seems more useful to do so. We will consider // "en-US-POSIX" to be more specific than "en-Latn-US". const int score = ((localeScript[0] != 0) ? 1 : 0) + ((localeVariant[0] != 0) ? 2 : 0); const int oScore = ((o.localeScript[0] != 0) ? 1 : 0) + ((o.localeVariant[0] != 0) ? 2 : 0); return score - oScore; } bool ResTable_config::isMoreSpecificThan(const ResTable_config& o) const { // The order of the following tests defines the importance of one // configuration parameter over another. Those tests first are more Loading @@ -1717,14 +1828,13 @@ bool ResTable_config::isMoreSpecificThan(const ResTable_config& o) const { } if (locale || o.locale) { if (language[0] != o.language[0]) { if (!language[0]) return false; if (!o.language[0]) return true; const int diff = isLocaleMoreSpecificThan(o); if (diff < 0) { return false; } if (country[0] != o.country[0]) { if (!country[0]) return false; if (!o.country[0]) return true; if (diff > 0) { return true; } } Loading Loading @@ -1860,6 +1970,18 @@ bool ResTable_config::isBetterThan(const ResTable_config& o, } } if (localeScript[0] || o.localeScript[0]) { if (localeScript[0] != o.localeScript[0] && requested->localeScript[0]) { return localeScript[0]; } } if (localeVariant[0] || o.localeVariant[0]) { if (localeVariant[0] != o.localeVariant[0] && requested->localeVariant[0]) { return localeVariant[0]; } } if (screenLayout || o.screenLayout) { if (((screenLayout^o.screenLayout) & MASK_LAYOUTDIR) != 0 && (requested->screenLayout & MASK_LAYOUTDIR)) { Loading Loading @@ -2080,17 +2202,23 @@ bool ResTable_config::match(const ResTable_config& settings) const { } } if (locale != 0) { // Don't consider the script & variants when deciding matches. // // If we two configs differ only in their script or language, they // can be weeded out in the isMoreSpecificThan test. if (language[0] != 0 && (language[0] != settings.language[0] || language[1] != settings.language[1])) { return false; } if (country[0] != 0 && (country[0] != settings.country[0] || country[1] != settings.country[1])) { return false; } } if (screenConfig != 0) { const int layoutDir = screenLayout&MASK_LAYOUTDIR; const int setLayoutDir = settings.screenLayout&MASK_LAYOUTDIR; Loading Loading @@ -2192,16 +2320,42 @@ bool ResTable_config::match(const ResTable_config& settings) const { return true; } void ResTable_config::getLocale(char str[6]) const { memset(str, 0, 6); void ResTable_config::getLocale(char str[RESTABLE_MAX_LOCALE_LEN]) const { memset(str, 0, RESTABLE_MAX_LOCALE_LEN); // This represents the "any" locale value, which has traditionally been // represented by the empty string. if (!language[0] && !country[0]) { return; } size_t charsWritten = 0; if (language[0]) { str[0] = language[0]; str[1] = language[1]; unpackLanguage(str); } if (country[0]) { str[2] = '_'; str[3] = country[0]; str[4] = country[1]; if (charsWritten) { str[charsWritten++] = '_'; str[charsWritten++] = 'r'; } charsWritten += unpackRegion(str + charsWritten); } if (localeScript[0]) { if (charsWritten) { str[charsWritten++] = '_'; str[charsWritten++] = '_s'; } memcpy(str + charsWritten, localeScript, sizeof(localeScript)); } if (localeVariant[0]) { if (charsWritten) { str[charsWritten++] = '_'; str[charsWritten++] = 'v'; } memcpy(str + charsWritten, localeVariant, sizeof(localeVariant)); } } Loading @@ -2216,14 +2370,10 @@ String8 ResTable_config::toString() const { if (res.size() > 0) res.append("-"); res.appendFormat("%dmnc", dtohs(mnc)); } if (language[0] != 0) { if (res.size() > 0) res.append("-"); res.append(language, 2); } if (country[0] != 0) { if (res.size() > 0) res.append("-"); res.append(country, 2); } char localeStr[RESTABLE_MAX_LOCALE_LEN]; getLocale(localeStr); res.append(localeStr); if ((screenLayout&MASK_LAYOUTDIR) != 0) { if (res.size() > 0) res.append("-"); switch (screenLayout&ResTable_config::MASK_LAYOUTDIR) { Loading Loading @@ -5002,8 +5152,9 @@ void ResTable::getLocales(Vector<String8>* locales) const getConfigurations(&configs); ALOGV("called getConfigurations size=%d", (int)configs.size()); const size_t I = configs.size(); char locale[RESTABLE_MAX_LOCALE_LEN]; for (size_t i=0; i<I; i++) { char locale[6]; configs[i].getLocale(locale); const size_t J = locales->size(); size_t j; Loading Loading @@ -5663,9 +5814,9 @@ void ResTable::print(bool inclValues) const printf("mError=0x%x (%s)\n", mError, strerror(mError)); } #if 0 printf("mParams=%c%c-%c%c,\n", mParams.language[0], mParams.language[1], mParams.country[0], mParams.country[1]); char localeStr[RESTABLE_MAX_LOCALE_LEN]; mParams.getLocale(localeStr); printf("mParams=%s,\n" localeStr); #endif size_t pgCount = mPackageGroups.size(); printf("Package Groups (%d)\n", (int)pgCount); Loading Loading @@ -5794,7 +5945,7 @@ void ResTable::print(bool inclValues) const continue; } uint16_t esize = dtohs(ent->size); uintptr_t esize = dtohs(ent->size); if ((esize&0x3) != 0) { printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void*)esize); continue; Loading libs/androidfw/tests/Android.mk +2 −1 Original line number Diff line number Diff line Loading @@ -5,7 +5,8 @@ include $(CLEAR_VARS) # Build the unit tests. test_src_files := \ ObbFile_test.cpp \ ZipUtils_test.cpp ZipUtils_test.cpp \ ResourceTypes_test.cpp shared_libraries := \ libandroidfw \ Loading libs/androidfw/tests/ResourceTypes_test.cpp 0 → 100644 +150 −0 Original line number Diff line number Diff line /* * Copyright (C) 2014 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. */ #include <androidfw/ResourceTypes.h> #include <utils/Log.h> #include <utils/String8.h> #include <gtest/gtest.h> namespace android { TEST(ResourceTypesTest, ResourceConfig_packAndUnpack2LetterLanguage) { ResTable_config config; config.packLanguage("en"); EXPECT_EQ('e', config.language[0]); EXPECT_EQ('n', config.language[1]); char out[4] = { 1, 1, 1, 1}; config.unpackLanguage(out); EXPECT_EQ('e', out[0]); EXPECT_EQ('n', out[1]); EXPECT_EQ(0, out[2]); EXPECT_EQ(0, out[3]); memset(out, 1, sizeof(out)); config.locale = 0; config.unpackLanguage(out); EXPECT_EQ(0, out[0]); EXPECT_EQ(0, out[1]); EXPECT_EQ(0, out[2]); EXPECT_EQ(0, out[3]); } TEST(ResourceTypesTest, ResourceConfig_packAndUnpack2LetterRegion) { ResTable_config config; config.packRegion("US"); EXPECT_EQ('U', config.country[0]); EXPECT_EQ('S', config.country[1]); char out[4] = { 1, 1, 1, 1}; config.unpackRegion(out); EXPECT_EQ('U', out[0]); EXPECT_EQ('S', out[1]); EXPECT_EQ(0, out[2]); EXPECT_EQ(0, out[3]); } TEST(ResourceTypesTest, ResourceConfig_packAndUnpack3LetterLanguage) { ResTable_config config; config.packLanguage("eng"); // 1-00110-01 101-00100 EXPECT_EQ(0x99, config.language[0]); EXPECT_EQ(0xa4, config.language[1]); char out[4] = { 1, 1, 1, 1}; config.unpackLanguage(out); EXPECT_EQ('e', out[0]); EXPECT_EQ('n', out[1]); EXPECT_EQ('g', out[2]); EXPECT_EQ(0, out[3]); } TEST(ResourceTypesTest, ResourceConfig_packAndUnpack3LetterRegion) { ResTable_config config; config.packRegion("419"); char out[4] = { 1, 1, 1, 1}; config.unpackRegion(out); EXPECT_EQ('4', out[0]); EXPECT_EQ('1', out[1]); EXPECT_EQ('9', out[2]); } /* static */ void fillIn(const char* lang, const char* country, const char* script, const char* variant, ResTable_config* out) { memset(out, 0, sizeof(ResTable_config)); if (lang != NULL) { out->packLanguage(lang); } if (country != NULL) { out->packRegion(country); } if (script != NULL) { memcpy(out->localeScript, script, 4); } if (variant != NULL) { memcpy(out->localeVariant, variant, strlen(variant)); } } TEST(ResourceTypesTest, IsMoreSpecificThan) { ResTable_config l; ResTable_config r; fillIn("en", NULL, NULL, NULL, &l); fillIn(NULL, NULL, NULL, NULL, &r); EXPECT_TRUE(l.isMoreSpecificThan(r)); EXPECT_FALSE(r.isMoreSpecificThan(l)); fillIn("eng", NULL, NULL, NULL, &l); EXPECT_TRUE(l.isMoreSpecificThan(r)); EXPECT_FALSE(r.isMoreSpecificThan(l)); fillIn("eng", "419", NULL, NULL, &r); EXPECT_FALSE(l.isMoreSpecificThan(r)); EXPECT_TRUE(r.isMoreSpecificThan(l)); fillIn("en", NULL, NULL, NULL, &l); fillIn("en", "US", NULL, NULL, &r); EXPECT_FALSE(l.isMoreSpecificThan(r)); EXPECT_TRUE(r.isMoreSpecificThan(l)); fillIn("en", "US", NULL, NULL, &l); fillIn("en", "US", "Latn", NULL, &r); EXPECT_FALSE(l.isMoreSpecificThan(r)); EXPECT_TRUE(r.isMoreSpecificThan(l)); fillIn("en", "US", NULL, NULL, &l); fillIn("en", "US", NULL, "POSIX", &r); EXPECT_FALSE(l.isMoreSpecificThan(r)); EXPECT_TRUE(r.isMoreSpecificThan(l)); fillIn("en", "US", "Latn", NULL, &l); fillIn("en", "US", NULL, "POSIX", &r); EXPECT_FALSE(l.isMoreSpecificThan(r)); EXPECT_TRUE(r.isMoreSpecificThan(l)); } } // namespace android. Loading
include/androidfw/ResourceTypes.h +84 −4 Original line number Diff line number Diff line Loading @@ -808,6 +808,19 @@ struct ResTable_package uint32_t lastPublicKey; }; // The most specific locale can consist of: // // - a 3 char language code // - a 3 char region code prefixed by a 'r' // - a 4 char script code prefixed by a 's' // - a 8 char variant code prefixed by a 'v' // // each separated by a single char separator, which sums up to a total of 24 // chars, (25 include the string terminator) rounded up to 28 to be 4 byte // aligned. #define RESTABLE_MAX_LOCALE_LEN 28 /** * Describes a particular resource configuration. */ Loading @@ -828,10 +841,42 @@ struct ResTable_config union { struct { // \0\0 means "any". Otherwise, en, fr, etc. // This field can take three different forms: // - \0\0 means "any". // // - Two 7 bit ascii values interpreted as ISO-639-1 language // codes ('fr', 'en' etc. etc.). The high bit for both bytes is // zero. // // - A single 16 bit little endian packed value representing an // ISO-639-2 3 letter language code. This will be of the form: // // {1, t, t, t, t, t, s, s, s, s, s, f, f, f, f, f} // // bit[0, 4] = first letter of the language code // bit[5, 9] = second letter of the language code // bit[10, 14] = third letter of the language code. // bit[15] = 1 always // // For backwards compatibility, languages that have unambiguous // two letter codes are represented in that format. // // The layout is always bigendian irrespective of the runtime // architecture. char language[2]; // \0\0 means "any". Otherwise, US, CA, etc. // This field can take three different forms: // - \0\0 means "any". // // - Two 7 bit ascii values interpreted as 2 letter region // codes ('US', 'GB' etc.). The high bit for both bytes is zero. // // - An UN M.49 3 digit region code. For simplicity, these are packed // in the same manner as the language codes, though we should need // only 10 bits to represent them, instead of the 15. // // The layout is always bigendian irrespective of the runtime // architecture. char country[2]; }; uint32_t locale; Loading Loading @@ -1006,6 +1051,15 @@ struct ResTable_config uint32_t screenSizeDp; }; // The ISO-15924 short name for the script corresponding to this // configuration. (eg. Hant, Latn, etc.). Interpreted in conjunction with // the locale field char localeScript[4]; // A single BCP-47 variant subtag. Will vary in length between 5 and 8 // chars. Interpreted in conjunction with the locale field. char localeVariant[8]; void copyFromDeviceNoSwap(const ResTable_config& o); void copyFromDtoH(const ResTable_config& o); Loading Loading @@ -1063,7 +1117,33 @@ struct ResTable_config // settings is the requested settings bool match(const ResTable_config& settings) const; void getLocale(char str[6]) 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. // // '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; // Get the 2 or 3 letter language code of this configuration. Trailing // bytes are set to '\0'. size_t unpackLanguage(char language[4]) const; // Get the 2 or 3 letter language code of this configuration. Trailing // 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]); // 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 // and 0 if they're equally specific. int isLocaleMoreSpecificThan(const ResTable_config &o) const; String8 toString() const; }; Loading
libs/androidfw/ResourceTypes.cpp +195 −44 Original line number Diff line number Diff line Loading @@ -66,11 +66,6 @@ namespace android { // size measured in sizeof(uint32_t) #define IDMAP_HEADER_SIZE (ResTable::IDMAP_HEADER_SIZE_BYTES / sizeof(uint32_t)) static void printToLogFunc(int32_t cookie, const char* txt) { ALOGV("[cookie=%d] %s", cookie, txt); } // Standard C isspace() is only required to look at the low byte of its input, so // produces incorrect results for UTF-16 characters. For safety's sake, assume that // any high-byte UTF-16 code point is not whitespace. Loading Loading @@ -1565,6 +1560,71 @@ void ResTable_config::copyFromDeviceNoSwap(const ResTable_config& o) { } } /* static */ size_t unpackLanguageOrRegion(const char in[2], const char base, char out[4]) { if (in[0] & 0x80) { // The high bit is "1", which means this is a packed three letter // language code. // The smallest 5 bits of the second char are the first alphabet. const uint8_t first = in[1] & 0x1f; // The last three bits of the second char and the first two bits // of the first char are the second alphabet. const uint8_t second = ((in[1] & 0xe0) >> 5) + ((in[0] & 0x03) << 3); // Bits 3 to 7 (inclusive) of the first char are the third alphabet. const uint8_t third = (in[0] & 0x7c) >> 2; out[0] = first + base; out[1] = second + base; out[2] = third + base; out[3] = 0; return 3; } if (in[0]) { memcpy(out, in, 2); memset(out + 2, 0, 2); return 2; } memset(out, 0, 4); return 0; } /* static */ void packLanguageOrRegion(const char in[3], const char base, char out[2]) { if (in[2] == 0) { 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; out[0] = (0x80 | (third << 2) | (second >> 3)); out[1] = ((second << 5) | first); } } void ResTable_config::packLanguage(const char language[3]) { packLanguageOrRegion(language, 'a', this->language); } void ResTable_config::packRegion(const char region[3]) { packLanguageOrRegion(region, '0', this->country); } size_t ResTable_config::unpackLanguage(char language[4]) const { return unpackLanguageOrRegion(this->language, 'a', language); } size_t ResTable_config::unpackRegion(char region[4]) const { return unpackLanguageOrRegion(this->country, '0', region); } void ResTable_config::copyFromDtoH(const ResTable_config& o) { copyFromDeviceNoSwap(o); size = sizeof(ResTable_config); Loading Loading @@ -1594,10 +1654,30 @@ void ResTable_config::swapHtoD() { screenHeightDp = htods(screenHeightDp); } /* static */ inline int compareLocales(const ResTable_config &l, const ResTable_config &r) { if (l.locale != r.locale) { // NOTE: This is the old behaviour with respect to comparison orders. // The diff value here doesn't make much sense (given our bit packing scheme) // but it's stable, and that's all we need. return l.locale - r.locale; } // The language & region are equal, so compare the scripts and variants. int script = memcmp(l.localeScript, r.localeScript, sizeof(l.localeScript)); if (script) { return script; } // The language, region and script are equal, so compare variants. // // This should happen very infrequently (if at all.) return memcmp(l.localeVariant, r.localeVariant, sizeof(l.localeVariant)); } int ResTable_config::compare(const ResTable_config& o) const { int32_t diff = (int32_t)(imsi - o.imsi); if (diff != 0) return diff; diff = (int32_t)(locale - o.locale); diff = compareLocales(*this, o); if (diff != 0) return diff; diff = (int32_t)(screenType - o.screenType); if (diff != 0) return diff; Loading @@ -1624,18 +1704,15 @@ int ResTable_config::compareLogical(const ResTable_config& o) const { if (mnc != o.mnc) { return mnc < o.mnc ? -1 : 1; } if (language[0] != o.language[0]) { return language[0] < o.language[0] ? -1 : 1; } if (language[1] != o.language[1]) { return language[1] < o.language[1] ? -1 : 1; } if (country[0] != o.country[0]) { return country[0] < o.country[0] ? -1 : 1; int diff = compareLocales(*this, o); if (diff < 0) { return -1; } if (country[1] != o.country[1]) { return country[1] < o.country[1] ? -1 : 1; if (diff > 0) { return 1; } if ((screenLayout & MASK_LAYOUTDIR) != (o.screenLayout & MASK_LAYOUTDIR)) { return (screenLayout & MASK_LAYOUTDIR) < (o.screenLayout & MASK_LAYOUTDIR) ? -1 : 1; } Loading Loading @@ -1682,7 +1759,6 @@ int ResTable_config::diff(const ResTable_config& o) const { int diffs = 0; if (mcc != o.mcc) diffs |= CONFIG_MCC; if (mnc != o.mnc) diffs |= CONFIG_MNC; if (locale != o.locale) diffs |= CONFIG_LOCALE; if (orientation != o.orientation) diffs |= CONFIG_ORIENTATION; if (density != o.density) diffs |= CONFIG_DENSITY; if (touchscreen != o.touchscreen) diffs |= CONFIG_TOUCHSCREEN; Loading @@ -1697,9 +1773,44 @@ int ResTable_config::diff(const ResTable_config& o) const { if (uiMode != o.uiMode) diffs |= CONFIG_UI_MODE; if (smallestScreenWidthDp != o.smallestScreenWidthDp) diffs |= CONFIG_SMALLEST_SCREEN_SIZE; if (screenSizeDp != o.screenSizeDp) diffs |= CONFIG_SCREEN_SIZE; const int diff = compareLocales(*this, o); if (diff) diffs |= CONFIG_LOCALE; return diffs; } int ResTable_config::isLocaleMoreSpecificThan(const ResTable_config& o) const { if (locale || o.locale) { if (language[0] != o.language[0]) { if (!language[0]) return -1; if (!o.language[0]) return 1; } if (country[0] != o.country[0]) { if (!country[0]) return -1; if (!o.country[0]) return 1; } } // There isn't a well specified "importance" order between variants and // scripts. We can't easily tell whether, say "en-Latn-US" is more or less // specific than "en-US-POSIX". // // We therefore arbitrarily decide to give priority to variants over // scripts since it seems more useful to do so. We will consider // "en-US-POSIX" to be more specific than "en-Latn-US". const int score = ((localeScript[0] != 0) ? 1 : 0) + ((localeVariant[0] != 0) ? 2 : 0); const int oScore = ((o.localeScript[0] != 0) ? 1 : 0) + ((o.localeVariant[0] != 0) ? 2 : 0); return score - oScore; } bool ResTable_config::isMoreSpecificThan(const ResTable_config& o) const { // The order of the following tests defines the importance of one // configuration parameter over another. Those tests first are more Loading @@ -1717,14 +1828,13 @@ bool ResTable_config::isMoreSpecificThan(const ResTable_config& o) const { } if (locale || o.locale) { if (language[0] != o.language[0]) { if (!language[0]) return false; if (!o.language[0]) return true; const int diff = isLocaleMoreSpecificThan(o); if (diff < 0) { return false; } if (country[0] != o.country[0]) { if (!country[0]) return false; if (!o.country[0]) return true; if (diff > 0) { return true; } } Loading Loading @@ -1860,6 +1970,18 @@ bool ResTable_config::isBetterThan(const ResTable_config& o, } } if (localeScript[0] || o.localeScript[0]) { if (localeScript[0] != o.localeScript[0] && requested->localeScript[0]) { return localeScript[0]; } } if (localeVariant[0] || o.localeVariant[0]) { if (localeVariant[0] != o.localeVariant[0] && requested->localeVariant[0]) { return localeVariant[0]; } } if (screenLayout || o.screenLayout) { if (((screenLayout^o.screenLayout) & MASK_LAYOUTDIR) != 0 && (requested->screenLayout & MASK_LAYOUTDIR)) { Loading Loading @@ -2080,17 +2202,23 @@ bool ResTable_config::match(const ResTable_config& settings) const { } } if (locale != 0) { // Don't consider the script & variants when deciding matches. // // If we two configs differ only in their script or language, they // can be weeded out in the isMoreSpecificThan test. if (language[0] != 0 && (language[0] != settings.language[0] || language[1] != settings.language[1])) { return false; } if (country[0] != 0 && (country[0] != settings.country[0] || country[1] != settings.country[1])) { return false; } } if (screenConfig != 0) { const int layoutDir = screenLayout&MASK_LAYOUTDIR; const int setLayoutDir = settings.screenLayout&MASK_LAYOUTDIR; Loading Loading @@ -2192,16 +2320,42 @@ bool ResTable_config::match(const ResTable_config& settings) const { return true; } void ResTable_config::getLocale(char str[6]) const { memset(str, 0, 6); void ResTable_config::getLocale(char str[RESTABLE_MAX_LOCALE_LEN]) const { memset(str, 0, RESTABLE_MAX_LOCALE_LEN); // This represents the "any" locale value, which has traditionally been // represented by the empty string. if (!language[0] && !country[0]) { return; } size_t charsWritten = 0; if (language[0]) { str[0] = language[0]; str[1] = language[1]; unpackLanguage(str); } if (country[0]) { str[2] = '_'; str[3] = country[0]; str[4] = country[1]; if (charsWritten) { str[charsWritten++] = '_'; str[charsWritten++] = 'r'; } charsWritten += unpackRegion(str + charsWritten); } if (localeScript[0]) { if (charsWritten) { str[charsWritten++] = '_'; str[charsWritten++] = '_s'; } memcpy(str + charsWritten, localeScript, sizeof(localeScript)); } if (localeVariant[0]) { if (charsWritten) { str[charsWritten++] = '_'; str[charsWritten++] = 'v'; } memcpy(str + charsWritten, localeVariant, sizeof(localeVariant)); } } Loading @@ -2216,14 +2370,10 @@ String8 ResTable_config::toString() const { if (res.size() > 0) res.append("-"); res.appendFormat("%dmnc", dtohs(mnc)); } if (language[0] != 0) { if (res.size() > 0) res.append("-"); res.append(language, 2); } if (country[0] != 0) { if (res.size() > 0) res.append("-"); res.append(country, 2); } char localeStr[RESTABLE_MAX_LOCALE_LEN]; getLocale(localeStr); res.append(localeStr); if ((screenLayout&MASK_LAYOUTDIR) != 0) { if (res.size() > 0) res.append("-"); switch (screenLayout&ResTable_config::MASK_LAYOUTDIR) { Loading Loading @@ -5002,8 +5152,9 @@ void ResTable::getLocales(Vector<String8>* locales) const getConfigurations(&configs); ALOGV("called getConfigurations size=%d", (int)configs.size()); const size_t I = configs.size(); char locale[RESTABLE_MAX_LOCALE_LEN]; for (size_t i=0; i<I; i++) { char locale[6]; configs[i].getLocale(locale); const size_t J = locales->size(); size_t j; Loading Loading @@ -5663,9 +5814,9 @@ void ResTable::print(bool inclValues) const printf("mError=0x%x (%s)\n", mError, strerror(mError)); } #if 0 printf("mParams=%c%c-%c%c,\n", mParams.language[0], mParams.language[1], mParams.country[0], mParams.country[1]); char localeStr[RESTABLE_MAX_LOCALE_LEN]; mParams.getLocale(localeStr); printf("mParams=%s,\n" localeStr); #endif size_t pgCount = mPackageGroups.size(); printf("Package Groups (%d)\n", (int)pgCount); Loading Loading @@ -5794,7 +5945,7 @@ void ResTable::print(bool inclValues) const continue; } uint16_t esize = dtohs(ent->size); uintptr_t esize = dtohs(ent->size); if ((esize&0x3) != 0) { printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void*)esize); continue; Loading
libs/androidfw/tests/Android.mk +2 −1 Original line number Diff line number Diff line Loading @@ -5,7 +5,8 @@ include $(CLEAR_VARS) # Build the unit tests. test_src_files := \ ObbFile_test.cpp \ ZipUtils_test.cpp ZipUtils_test.cpp \ ResourceTypes_test.cpp shared_libraries := \ libandroidfw \ Loading
libs/androidfw/tests/ResourceTypes_test.cpp 0 → 100644 +150 −0 Original line number Diff line number Diff line /* * Copyright (C) 2014 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. */ #include <androidfw/ResourceTypes.h> #include <utils/Log.h> #include <utils/String8.h> #include <gtest/gtest.h> namespace android { TEST(ResourceTypesTest, ResourceConfig_packAndUnpack2LetterLanguage) { ResTable_config config; config.packLanguage("en"); EXPECT_EQ('e', config.language[0]); EXPECT_EQ('n', config.language[1]); char out[4] = { 1, 1, 1, 1}; config.unpackLanguage(out); EXPECT_EQ('e', out[0]); EXPECT_EQ('n', out[1]); EXPECT_EQ(0, out[2]); EXPECT_EQ(0, out[3]); memset(out, 1, sizeof(out)); config.locale = 0; config.unpackLanguage(out); EXPECT_EQ(0, out[0]); EXPECT_EQ(0, out[1]); EXPECT_EQ(0, out[2]); EXPECT_EQ(0, out[3]); } TEST(ResourceTypesTest, ResourceConfig_packAndUnpack2LetterRegion) { ResTable_config config; config.packRegion("US"); EXPECT_EQ('U', config.country[0]); EXPECT_EQ('S', config.country[1]); char out[4] = { 1, 1, 1, 1}; config.unpackRegion(out); EXPECT_EQ('U', out[0]); EXPECT_EQ('S', out[1]); EXPECT_EQ(0, out[2]); EXPECT_EQ(0, out[3]); } TEST(ResourceTypesTest, ResourceConfig_packAndUnpack3LetterLanguage) { ResTable_config config; config.packLanguage("eng"); // 1-00110-01 101-00100 EXPECT_EQ(0x99, config.language[0]); EXPECT_EQ(0xa4, config.language[1]); char out[4] = { 1, 1, 1, 1}; config.unpackLanguage(out); EXPECT_EQ('e', out[0]); EXPECT_EQ('n', out[1]); EXPECT_EQ('g', out[2]); EXPECT_EQ(0, out[3]); } TEST(ResourceTypesTest, ResourceConfig_packAndUnpack3LetterRegion) { ResTable_config config; config.packRegion("419"); char out[4] = { 1, 1, 1, 1}; config.unpackRegion(out); EXPECT_EQ('4', out[0]); EXPECT_EQ('1', out[1]); EXPECT_EQ('9', out[2]); } /* static */ void fillIn(const char* lang, const char* country, const char* script, const char* variant, ResTable_config* out) { memset(out, 0, sizeof(ResTable_config)); if (lang != NULL) { out->packLanguage(lang); } if (country != NULL) { out->packRegion(country); } if (script != NULL) { memcpy(out->localeScript, script, 4); } if (variant != NULL) { memcpy(out->localeVariant, variant, strlen(variant)); } } TEST(ResourceTypesTest, IsMoreSpecificThan) { ResTable_config l; ResTable_config r; fillIn("en", NULL, NULL, NULL, &l); fillIn(NULL, NULL, NULL, NULL, &r); EXPECT_TRUE(l.isMoreSpecificThan(r)); EXPECT_FALSE(r.isMoreSpecificThan(l)); fillIn("eng", NULL, NULL, NULL, &l); EXPECT_TRUE(l.isMoreSpecificThan(r)); EXPECT_FALSE(r.isMoreSpecificThan(l)); fillIn("eng", "419", NULL, NULL, &r); EXPECT_FALSE(l.isMoreSpecificThan(r)); EXPECT_TRUE(r.isMoreSpecificThan(l)); fillIn("en", NULL, NULL, NULL, &l); fillIn("en", "US", NULL, NULL, &r); EXPECT_FALSE(l.isMoreSpecificThan(r)); EXPECT_TRUE(r.isMoreSpecificThan(l)); fillIn("en", "US", NULL, NULL, &l); fillIn("en", "US", "Latn", NULL, &r); EXPECT_FALSE(l.isMoreSpecificThan(r)); EXPECT_TRUE(r.isMoreSpecificThan(l)); fillIn("en", "US", NULL, NULL, &l); fillIn("en", "US", NULL, "POSIX", &r); EXPECT_FALSE(l.isMoreSpecificThan(r)); EXPECT_TRUE(r.isMoreSpecificThan(l)); fillIn("en", "US", "Latn", NULL, &l); fillIn("en", "US", NULL, "POSIX", &r); EXPECT_FALSE(l.isMoreSpecificThan(r)); EXPECT_TRUE(r.isMoreSpecificThan(l)); } } // namespace android.