#include <stdlib.h>
#include <string.h>
+#include <algorithm>
#include <limits>
+#include <memory>
#include <type_traits>
#include <androidfw/ByteBucketArray.h>
namespace android {
-#ifdef HAVE_WINSOCK
+#if defined(_WIN32)
#undef nhtol
#undef htonl
#define ntohl(x) ( ((x) << 24) | (((x) >> 24) & 255) | (((x) << 8) & 0xff0000) | (((x) >> 8) & 0xff00) )
if ((mHeader->flags&ResStringPool_header::UTF8_FLAG &&
((uint8_t*)mStrings)[mStringPoolSize-1] != 0) ||
- (!mHeader->flags&ResStringPool_header::UTF8_FLAG &&
+ (!(mHeader->flags&ResStringPool_header::UTF8_FLAG) &&
((uint16_t*)mStrings)[mStringPoolSize-1] != 0)) {
ALOGW("Bad string block: last string is not 0-terminated\n");
return (mError=BAD_TYPE);
AutoMutex lock(mDecodeLock);
if (mCache == NULL) {
-#ifndef HAVE_ANDROID_OS
+#ifndef __ANDROID__
if (kDebugStringPoolNoisy) {
ALOGI("CREATING STRING CACHE OF %zu bytes",
mHeader->stringCount*sizeof(char16_t**));
*outLen = decodeLength(&str);
size_t encLen = decodeLength(&str);
if ((uint32_t)(str+encLen-strings) < mStringPoolSize) {
- return (const char*)str;
+ // Reject malformed (non null-terminated) strings
+ if (str[encLen] != 0x00) {
+ ALOGW("Bad string block: string #%d is not null-terminated",
+ (int)idx);
+ return NULL;
+ }
+ return (const char*)str;
} else {
ALOGW("Bad string block: string #%d extends to %d, past end at %d\n",
(int)idx, (int)(str+encLen-strings), (int)mStringPoolSize);
}
// The language & region are equal, so compare the scripts and variants.
- int script = memcmp(l.localeScript, r.localeScript, sizeof(l.localeScript));
+ const char emptyScript[sizeof(l.localeScript)] = {'\0', '\0', '\0', '\0'};
+ const char *lScript = l.localeScriptWasComputed ? emptyScript : l.localeScript;
+ const char *rScript = r.localeScriptWasComputed ? emptyScript : r.localeScript;
+ int script = memcmp(lScript, rScript, sizeof(l.localeScript));
if (script) {
return script;
}
// 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 score = ((localeScript[0] != '\0' && !localeScriptWasComputed) ? 1 : 0) +
+ ((localeVariant[0] != '\0') ? 2 : 0);
- const int oScore = ((o.localeScript[0] != 0) ? 1 : 0) +
- ((o.localeVariant[0] != 0) ? 2 : 0);
+ const int oScore = (o.localeScript[0] != '\0' && !o.localeScriptWasComputed ? 1 : 0) +
+ ((o.localeVariant[0] != '\0') ? 2 : 0);
return score - oScore;
return false;
}
+bool ResTable_config::isLocaleBetterThan(const ResTable_config& o,
+ const ResTable_config* requested) const {
+ if (requested->locale == 0) {
+ // The request doesn't have a locale, so no resource is better
+ // than the other.
+ return false;
+ }
+
+ if (locale == 0 && o.locale == 0) {
+ // The locales parts of both resources are empty, so no one is better
+ // than the other.
+ return false;
+ }
+
+ // Non-matching locales have been filtered out, so both resources
+ // match the requested locale.
+ //
+ // Because of the locale-related checks in match() and the checks, we know
+ // that:
+ // 1) The resource languages are either empty or match the request;
+ // and
+ // 2) If the request's script is known, the resource scripts are either
+ // unknown or match the request.
+
+ if (language[0] != o.language[0]) {
+ // The languages of the two resources are not the same. We can only
+ // assume that one of the two resources matched the request because one
+ // doesn't have a language and the other has a matching language.
+ //
+ // We consider the one that has the language specified a better match.
+ //
+ // The exception is that we consider no-language resources a better match
+ // for US English and similar locales than locales that are a descendant
+ // of Internatinal English (en-001), since no-language resources are
+ // where the US English resource have traditionally lived for most apps.
+ if (requested->language[0] == 'e' && requested->language[1] == 'n') {
+ if (requested->country[0] == 'U' && requested->country[1] == 'S') {
+ // For US English itself, we consider a no-locale resource a
+ // better match if the other resource has a country other than
+ // US specified.
+ if (language[0] != '\0') {
+ return country[0] == '\0' || (country[0] == 'U' && country[1] == 'S');
+ } else {
+ return !(o.country[0] == '\0' || (o.country[0] == 'U' && o.country[1] == 'S'));
+ }
+ } else if (localeDataIsCloseToUsEnglish(requested->country)) {
+ if (language[0] != '\0') {
+ return localeDataIsCloseToUsEnglish(country);
+ } else {
+ return !localeDataIsCloseToUsEnglish(o.country);
+ }
+ }
+ }
+ return (language[0] != '\0');
+ }
+
+ // If we are here, both the resources have the same non-empty language as
+ // the request.
+ //
+ // Because the languages are the same, computeScript() always
+ // returns a non-empty script for languages it knows about, and we have passed
+ // the script checks in match(), the scripts are either all unknown or are
+ // all the same. So we can't gain anything by checking the scripts. We need
+ // to check the region and variant.
+
+ // See if any of the regions is better than the other
+ const int region_comparison = localeDataCompareRegions(
+ country, o.country,
+ language, requested->localeScript, requested->country);
+ if (region_comparison != 0) {
+ return (region_comparison > 0);
+ }
+
+ // The regions are the same. Try the variant.
+ if (requested->localeVariant[0] != '\0'
+ && strncmp(localeVariant, requested->localeVariant, sizeof(localeVariant)) == 0) {
+ return (strncmp(o.localeVariant, requested->localeVariant, sizeof(localeVariant)) != 0);
+ }
+
+ return false;
+}
+
bool ResTable_config::isBetterThan(const ResTable_config& o,
const ResTable_config* requested) const {
if (requested) {
}
}
- if (locale || o.locale) {
- if ((language[0] != o.language[0]) && requested->language[0]) {
- return (language[0]);
- }
-
- if ((country[0] != o.country[0]) && requested->country[0]) {
- return (country[0]);
- }
- }
-
- 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 (isLocaleBetterThan(o, requested)) {
+ return true;
}
if (screenLayout || o.screenLayout) {
}
}
if (locale != 0) {
- // Don't consider the script & variants when deciding matches.
+ // Don't consider country and variants when deciding matches.
+ // (Theoretically, the variant can also affect the script. For
+ // example, "ar-alalc97" probably implies the Latin script, but since
+ // CLDR doesn't support getting likely scripts for that, we'll assume
+ // the variant doesn't change the script.)
//
- // 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])) {
+ // If two configs differ only in their country and variant,
+ // they can be weeded out in the isMoreSpecificThan test.
+ if (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;
+ // For backward compatibility and supporting private-use locales, we
+ // fall back to old behavior if we couldn't determine the script for
+ // either of the desired locale or the provided locale. But if we could determine
+ // the scripts, they should be the same for the locales to match.
+ bool countriesMustMatch = false;
+ char computed_script[4];
+ const char* script;
+ if (settings.localeScript[0] == '\0') { // could not determine the request's script
+ countriesMustMatch = true;
+ } else {
+ if (localeScript[0] == '\0' && !localeScriptWasComputed) {
+ // script was not provided or computed, so we try to compute it
+ localeDataComputeScript(computed_script, language, country);
+ if (computed_script[0] == '\0') { // we could not compute the script
+ countriesMustMatch = true;
+ } else {
+ script = computed_script;
+ }
+ } else { // script was provided, so just use it
+ script = localeScript;
+ }
+ }
+
+ if (countriesMustMatch) {
+ if (country[0] != '\0'
+ && (country[0] != settings.country[0]
+ || country[1] != settings.country[1])) {
+ return false;
+ }
+ } else {
+ if (memcmp(script, settings.localeScript, sizeof(settings.localeScript)) != 0) {
+ return false;
+ }
}
}
if (!language[0]) {
return;
}
-
- if (!localeScript[0] && !localeVariant[0]) {
+ const bool scriptWasProvided = localeScript[0] != '\0' && !localeScriptWasComputed;
+ if (!scriptWasProvided && !localeVariant[0]) {
// Legacy format.
if (out.size() > 0) {
out.append("-");
return;
}
- // We are writing the modified bcp47 tag.
+ // We are writing the modified BCP 47 tag.
// It starts with 'b+' and uses '+' as a separator.
if (out.size() > 0) {
size_t len = unpackLanguage(buf);
out.append(buf, len);
- if (localeScript[0]) {
+ if (scriptWasProvided) {
out.append("+");
out.append(localeScript, sizeof(localeScript));
}
if (localeVariant[0]) {
out.append("+");
- out.append(localeVariant, sizeof(localeVariant));
+ out.append(localeVariant, strnlen(localeVariant, sizeof(localeVariant)));
}
}
charsWritten += unpackLanguage(str);
}
- if (localeScript[0]) {
+ if (localeScript[0] && !localeScriptWasComputed) {
if (charsWritten) {
str[charsWritten++] = '-';
}
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]);
+ if ('0' <= start[0] && start[0] <= '9') {
+ // this is a variant, so fall through
+ } else {
+ config->localeScript[0] = toupper(start[0]);
+ for (size_t i = 1; i < 4; ++i) {
+ config->localeScript[i] = tolower(start[i]);
+ }
+ break;
}
- break;
case 5:
case 6:
case 7:
const size_t size = in + strlen(in) - start;
assignLocaleComponent(this, start, size);
+ localeScriptWasComputed = (localeScript[0] == '\0');
+ if (localeScriptWasComputed) {
+ computeScript();
+ }
}
String8 ResTable_config::toString() const {
// table that defined the package); the ones after are skins on top of it.
struct ResTable::PackageGroup
{
- PackageGroup(ResTable* _owner, const String16& _name, uint32_t _id)
+ PackageGroup(
+ ResTable* _owner, const String16& _name, uint32_t _id,
+ bool appAsLib, bool _isSystemAsset)
: owner(_owner)
, name(_name)
, id(_id)
, largestTypeId(0)
- , bags(NULL)
- , dynamicRefTable(static_cast<uint8_t>(_id))
+ , dynamicRefTable(static_cast<uint8_t>(_id), appAsLib)
+ , isSystemAsset(_isSystemAsset)
{ }
~PackageGroup() {
}
}
+ /**
+ * Clear all cache related data that depends on parameters/configuration.
+ * This includes the bag caches and filtered types.
+ */
void clearBagCache() {
- if (bags) {
+ for (size_t i = 0; i < typeCacheEntries.size(); i++) {
if (kDebugTableNoisy) {
- printf("bags=%p\n", bags);
+ printf("type=%zu\n", i);
}
- for (size_t i = 0; i < bags->size(); i++) {
+ const TypeList& typeList = types[i];
+ if (!typeList.isEmpty()) {
+ TypeCacheEntry& cacheEntry = typeCacheEntries.editItemAt(i);
+
+ // Reset the filtered configurations.
+ cacheEntry.filteredConfigs.clear();
+
+ bag_set** typeBags = cacheEntry.cachedBags;
if (kDebugTableNoisy) {
- printf("type=%zu\n", i);
+ printf("typeBags=%p\n", typeBags);
}
- const TypeList& typeList = types[i];
- if (!typeList.isEmpty()) {
- bag_set** typeBags = bags->get(i);
+
+ if (typeBags) {
+ const size_t N = typeList[0]->entryCount;
if (kDebugTableNoisy) {
- printf("typeBags=%p\n", typeBags);
+ printf("type->entryCount=%zu\n", N);
}
- if (typeBags) {
- const size_t N = typeList[0]->entryCount;
- if (kDebugTableNoisy) {
- printf("type->entryCount=%zu\n", N);
+ for (size_t j = 0; j < N; j++) {
+ if (typeBags[j] && typeBags[j] != (bag_set*)0xFFFFFFFF) {
+ free(typeBags[j]);
}
- for (size_t j = 0; j < N; j++) {
- if (typeBags[j] && typeBags[j] != (bag_set*)0xFFFFFFFF)
- free(typeBags[j]);
- }
- free(typeBags);
}
+ free(typeBags);
+ cacheEntry.cachedBags = NULL;
}
}
- delete bags;
- bags = NULL;
}
}
uint8_t largestTypeId;
- // Computed attribute bags, first indexed by the type and second
- // by the entry in that type.
- ByteBucketArray<bag_set**>* bags;
+ // Cached objects dependent on the parameters/configuration of this ResTable.
+ // Gets cleared whenever the parameters/configuration changes.
+ // These are stored here in a parallel structure because the data in `types` may
+ // be shared by other ResTable's (framework resources are shared this way).
+ ByteBucketArray<TypeCacheEntry> typeCacheEntries;
// The table mapping dynamic references to resolved references for
// this package group.
// by having these tables in a per-package scope rather than
// per-package-group.
DynamicRefTable dynamicRefTable;
-};
-struct ResTable::bag_set
-{
- size_t numAttrs; // number in array
- size_t availAttrs; // total space in array
- uint32_t typeSpecFlags;
- // Followed by 'numAttr' bag_entry structures.
+ // If the package group comes from a system asset. Used in
+ // determining non-system locales.
+ const bool isSystemAsset;
};
ResTable::Theme::Theme(const ResTable& table)
{
memset(&mParams, 0, sizeof(mParams));
memset(mPackageMap, 0, sizeof(mPackageMap));
- addInternal(data, size, NULL, 0, cookie, copyData);
+ addInternal(data, size, NULL, 0, false, cookie, copyData);
LOG_FATAL_IF(mError != NO_ERROR, "Error parsing resource table");
if (kDebugTableSuperNoisy) {
ALOGI("Creating ResTable %p\n", this);
}
status_t ResTable::add(const void* data, size_t size, const int32_t cookie, bool copyData) {
- return addInternal(data, size, NULL, 0, cookie, copyData);
+ return addInternal(data, size, NULL, 0, false, cookie, copyData);
}
status_t ResTable::add(const void* data, size_t size, const void* idmapData, size_t idmapDataSize,
- const int32_t cookie, bool copyData) {
- return addInternal(data, size, idmapData, idmapDataSize, cookie, copyData);
+ const int32_t cookie, bool copyData, bool appAsLib) {
+ return addInternal(data, size, idmapData, idmapDataSize, appAsLib, cookie, copyData);
}
status_t ResTable::add(Asset* asset, const int32_t cookie, bool copyData) {
return UNKNOWN_ERROR;
}
- return addInternal(data, static_cast<size_t>(asset->getLength()), NULL, 0, cookie, copyData);
+ return addInternal(data, static_cast<size_t>(asset->getLength()), NULL, false, 0, cookie,
+ copyData);
}
-status_t ResTable::add(Asset* asset, Asset* idmapAsset, const int32_t cookie, bool copyData) {
+status_t ResTable::add(
+ Asset* asset, Asset* idmapAsset, const int32_t cookie, bool copyData,
+ bool appAsLib, bool isSystemAsset) {
const void* data = asset->getBuffer(true);
if (data == NULL) {
ALOGW("Unable to get buffer of resource asset file");
}
return addInternal(data, static_cast<size_t>(asset->getLength()),
- idmapData, idmapSize, cookie, copyData);
+ idmapData, idmapSize, appAsLib, cookie, copyData, isSystemAsset);
}
-status_t ResTable::add(ResTable* src)
+status_t ResTable::add(ResTable* src, bool isSystemAsset)
{
mError = src->mError;
- for (size_t i=0; i<src->mHeaders.size(); i++) {
+ for (size_t i=0; i < src->mHeaders.size(); i++) {
mHeaders.add(src->mHeaders[i]);
}
- for (size_t i=0; i<src->mPackageGroups.size(); i++) {
+ for (size_t i=0; i < src->mPackageGroups.size(); i++) {
PackageGroup* srcPg = src->mPackageGroups[i];
- PackageGroup* pg = new PackageGroup(this, srcPg->name, srcPg->id);
+ PackageGroup* pg = new PackageGroup(this, srcPg->name, srcPg->id,
+ false /* appAsLib */, isSystemAsset || srcPg->isSystemAsset);
for (size_t j=0; j<srcPg->packages.size(); j++) {
pg->packages.add(srcPg->packages[j]);
}
}
status_t ResTable::addInternal(const void* data, size_t dataSize, const void* idmapData, size_t idmapDataSize,
- const int32_t cookie, bool copyData)
+ bool appAsLib, const int32_t cookie, bool copyData, bool isSystemAsset)
{
if (!data) {
return NO_ERROR;
return (mError=BAD_TYPE);
}
- if (parsePackage((ResTable_package*)chunk, header) != NO_ERROR) {
+ if (parsePackage(
+ (ResTable_package*)chunk, header, appAsLib, isSystemAsset) != NO_ERROR) {
return mError;
}
curPackage++;
if (Res_GETPACKAGE(resID)+1 == 0) {
ALOGW("No package identifier when getting name for resource number 0x%08x", resID);
} else {
+#ifndef STATIC_ANDROIDFW_FOR_TOOLS
ALOGW("No known package when getting name for resource number 0x%08x", resID);
+#endif
}
return false;
}
}
// First see if we've already computed this bag...
- if (grp->bags) {
- bag_set** typeSet = grp->bags->get(t);
- if (typeSet) {
- bag_set* set = typeSet[e];
- if (set) {
- if (set != (bag_set*)0xFFFFFFFF) {
- if (outTypeSpecFlags != NULL) {
- *outTypeSpecFlags = set->typeSpecFlags;
- }
- *outBag = (bag_entry*)(set+1);
- if (kDebugTableSuperNoisy) {
- ALOGI("Found existing bag for: 0x%x\n", resID);
- }
- return set->numAttrs;
+ TypeCacheEntry& cacheEntry = grp->typeCacheEntries.editItemAt(t);
+ bag_set** typeSet = cacheEntry.cachedBags;
+ if (typeSet) {
+ bag_set* set = typeSet[e];
+ if (set) {
+ if (set != (bag_set*)0xFFFFFFFF) {
+ if (outTypeSpecFlags != NULL) {
+ *outTypeSpecFlags = set->typeSpecFlags;
+ }
+ *outBag = (bag_entry*)(set+1);
+ if (kDebugTableSuperNoisy) {
+ ALOGI("Found existing bag for: 0x%x\n", resID);
}
- ALOGW("Attempt to retrieve bag 0x%08x which is invalid or in a cycle.",
- resID);
- return BAD_INDEX;
+ return set->numAttrs;
}
+ ALOGW("Attempt to retrieve bag 0x%08x which is invalid or in a cycle.",
+ resID);
+ return BAD_INDEX;
}
}
// Bag not found, we need to compute it!
- if (!grp->bags) {
- grp->bags = new ByteBucketArray<bag_set**>();
- if (!grp->bags) return NO_MEMORY;
- }
-
- bag_set** typeSet = grp->bags->get(t);
if (!typeSet) {
typeSet = (bag_set**)calloc(NENTRY, sizeof(bag_set*));
if (!typeSet) return NO_MEMORY;
- grp->bags->set(t, typeSet);
+ cacheEntry.cachedBags = typeSet;
}
// Mark that we are currently working on this one.
void ResTable::setParameters(const ResTable_config* params)
{
- mLock.lock();
+ AutoMutex _lock(mLock);
+ AutoMutex _lock2(mFilteredConfigLock);
+
if (kDebugTableGetEntry) {
ALOGI("Setting parameters: %s\n", params->toString().string());
}
mParams = *params;
- for (size_t i=0; i<mPackageGroups.size(); i++) {
+ for (size_t p = 0; p < mPackageGroups.size(); p++) {
+ PackageGroup* packageGroup = mPackageGroups.editItemAt(p);
if (kDebugTableNoisy) {
- ALOGI("CLEARING BAGS FOR GROUP %zu!", i);
+ ALOGI("CLEARING BAGS FOR GROUP %zu!", p);
+ }
+ packageGroup->clearBagCache();
+
+ // Find which configurations match the set of parameters. This allows for a much
+ // faster lookup in getEntry() if the set of values is narrowed down.
+ for (size_t t = 0; t < packageGroup->types.size(); t++) {
+ if (packageGroup->types[t].isEmpty()) {
+ continue;
+ }
+
+ TypeList& typeList = packageGroup->types.editItemAt(t);
+
+ // Retrieve the cache entry for this type.
+ TypeCacheEntry& cacheEntry = packageGroup->typeCacheEntries.editItemAt(t);
+
+ for (size_t ts = 0; ts < typeList.size(); ts++) {
+ Type* type = typeList.editItemAt(ts);
+
+ std::shared_ptr<Vector<const ResTable_type*>> newFilteredConfigs =
+ std::make_shared<Vector<const ResTable_type*>>();
+
+ for (size_t ti = 0; ti < type->configs.size(); ti++) {
+ ResTable_config config;
+ config.copyFromDtoH(type->configs[ti]->config);
+
+ if (config.match(mParams)) {
+ newFilteredConfigs->add(type->configs[ti]);
+ }
+ }
+
+ if (kDebugTableNoisy) {
+ ALOGD("Updating pkg=%zu type=%zu with %zu filtered configs",
+ p, t, newFilteredConfigs->size());
+ }
+
+ cacheEntry.filteredConfigs.add(newFilteredConfigs);
+ }
}
- mPackageGroups[i]->clearBagCache();
}
- mLock.unlock();
}
void ResTable::getParameters(ResTable_config* params) const
return false;
}
}
- if (!accessor) {
- outValue->data = rid;
- return true;
+
+ if (accessor) {
+ rid = Res_MAKEID(
+ accessor->getRemappedPackage(Res_GETPACKAGE(rid)),
+ Res_GETTYPE(rid), Res_GETENTRY(rid));
+ }
+
+ uint32_t packageId = Res_GETPACKAGE(rid) + 1;
+ if (packageId != APP_PACKAGE_ID && packageId != SYS_PACKAGE_ID) {
+ outValue->dataType = Res_value::TYPE_DYNAMIC_ATTRIBUTE;
}
- rid = Res_MAKEID(
- accessor->getRemappedPackage(Res_GETPACKAGE(rid)),
- Res_GETTYPE(rid), Res_GETENTRY(rid));
- //printf("Incl %s:%s/%s: 0x%08x\n",
- // String8(package).string(), String8(type).string(),
- // String8(name).string(), rid);
outValue->data = rid;
return true;
}
if (accessor) {
uint32_t rid = accessor->getCustomResource(package, type, name);
if (rid != 0) {
- //printf("Mine %s:%s/%s: 0x%08x\n",
- // String8(package).string(), String8(type).string(),
- // String8(name).string(), rid);
- outValue->data = rid;
- return true;
+ uint32_t packageId = Res_GETPACKAGE(rid) + 1;
+ if (packageId == 0x00) {
+ outValue->data = rid;
+ outValue->dataType = Res_value::TYPE_DYNAMIC_ATTRIBUTE;
+ return true;
+ } else if (packageId == APP_PACKAGE_ID || packageId == SYS_PACKAGE_ID) {
+ // We accept packageId's generated as 0x01 in order to support
+ // building the android system resources
+ outValue->data = rid;
+ return true;
+ }
}
}
return NULL;
}
-void ResTable::getConfigurations(Vector<ResTable_config>* configs, bool ignoreMipmap) const
-{
+static bool compareResTableConfig(const ResTable_config& a, const ResTable_config& b) {
+ return a.compare(b) < 0;
+}
+
+template <typename Func>
+void ResTable::forEachConfiguration(bool ignoreMipmap, bool ignoreAndroidPackage,
+ bool includeSystemConfigs, const Func& f) const {
const size_t packageCount = mPackageGroups.size();
+ const String16 android("android");
for (size_t i = 0; i < packageCount; i++) {
const PackageGroup* packageGroup = mPackageGroups[i];
+ if (ignoreAndroidPackage && android == packageGroup->name) {
+ continue;
+ }
+ if (!includeSystemConfigs && packageGroup->isSystemAsset) {
+ continue;
+ }
const size_t typeCount = packageGroup->types.size();
for (size_t j = 0; j < typeCount; j++) {
const TypeList& typeList = packageGroup->types[j];
ResTable_config cfg;
memset(&cfg, 0, sizeof(ResTable_config));
cfg.copyFromDtoH(config->config);
- // only insert unique
- const size_t N = configs->size();
- size_t n;
- for (n = 0; n < N; n++) {
- if (0 == (*configs)[n].compare(cfg)) {
- break;
- }
- }
- // if we didn't find it
- if (n == N) {
- configs->add(cfg);
- }
+
+ f(cfg);
}
}
}
}
}
-void ResTable::getLocales(Vector<String8>* locales) const
-{
- Vector<ResTable_config> configs;
- ALOGV("calling getConfigurations");
- getConfigurations(&configs);
- ALOGV("called getConfigurations size=%d", (int)configs.size());
- const size_t I = configs.size();
+void ResTable::getConfigurations(Vector<ResTable_config>* configs, bool ignoreMipmap,
+ bool ignoreAndroidPackage, bool includeSystemConfigs) const {
+ auto func = [&](const ResTable_config& cfg) {
+ const auto beginIter = configs->begin();
+ const auto endIter = configs->end();
+ auto iter = std::lower_bound(beginIter, endIter, cfg, compareResTableConfig);
+ if (iter == endIter || iter->compare(cfg) != 0) {
+ configs->insertAt(cfg, std::distance(beginIter, iter));
+ }
+ };
+ forEachConfiguration(ignoreMipmap, ignoreAndroidPackage, includeSystemConfigs, func);
+}
+
+static bool compareString8AndCString(const String8& str, const char* cStr) {
+ return strcmp(str.string(), cStr) < 0;
+}
+
+void ResTable::getLocales(Vector<String8>* locales, bool includeSystemLocales) const {
char locale[RESTABLE_MAX_LOCALE_LEN];
- for (size_t i=0; i<I; i++) {
- configs[i].getBcp47Locale(locale);
- const size_t J = locales->size();
- size_t j;
- for (j=0; j<J; j++) {
- if (0 == strcmp(locale, (*locales)[j].string())) {
- break;
+
+ forEachConfiguration(false, false, includeSystemLocales, [&](const ResTable_config& cfg) {
+ if (cfg.locale != 0) {
+ cfg.getBcp47Locale(locale);
+
+ const auto beginIter = locales->begin();
+ const auto endIter = locales->end();
+
+ auto iter = std::lower_bound(beginIter, endIter, locale, compareString8AndCString);
+ if (iter == endIter || strcmp(iter->string(), locale) != 0) {
+ locales->insertAt(String8(locale), std::distance(beginIter, iter));
}
}
- if (j == J) {
- locales->add(String8(locale));
- }
- }
+ });
}
StringPoolRef::StringPoolRef(const ResStringPool* pool, uint32_t index)
specFlags = -1;
}
- const size_t numConfigs = typeSpec->configs.size();
+ const Vector<const ResTable_type*>* candidateConfigs = &typeSpec->configs;
+
+ std::shared_ptr<Vector<const ResTable_type*>> filteredConfigs;
+ if (config && memcmp(&mParams, config, sizeof(mParams)) == 0) {
+ // Grab the lock first so we can safely get the current filtered list.
+ AutoMutex _lock(mFilteredConfigLock);
+
+ // This configuration is equal to the one we have previously cached for,
+ // so use the filtered configs.
+
+ const TypeCacheEntry& cacheEntry = packageGroup->typeCacheEntries[typeIndex];
+ if (i < cacheEntry.filteredConfigs.size()) {
+ if (cacheEntry.filteredConfigs[i]) {
+ // Grab a reference to the shared_ptr so it doesn't get destroyed while
+ // going through this list.
+ filteredConfigs = cacheEntry.filteredConfigs[i];
+
+ // Use this filtered list.
+ candidateConfigs = filteredConfigs.get();
+ }
+ }
+ }
+
+ const size_t numConfigs = candidateConfigs->size();
for (size_t c = 0; c < numConfigs; c++) {
- const ResTable_type* const thisType = typeSpec->configs[c];
+ const ResTable_type* const thisType = candidateConfigs->itemAt(c);
if (thisType == NULL) {
continue;
}
}
status_t ResTable::parsePackage(const ResTable_package* const pkg,
- const Header* const header)
+ const Header* const header, bool appAsLib, bool isSystemAsset)
{
const uint8_t* base = (const uint8_t*)pkg;
status_t err = validate_chunk(&pkg->header, sizeof(*pkg) - sizeof(pkg->typeIdOffset),
if (id >= 256) {
LOG_ALWAYS_FATAL("Package id out of range");
return NO_ERROR;
- } else if (id == 0) {
- // This is a library so assign an ID
+ } else if (id == 0 || (id == 0x7f && appAsLib) || isSystemAsset) {
+ // This is a library or a system asset, so assign an ID
id = mNextPackageId++;
}
char16_t tmpName[sizeof(pkg->name)/sizeof(pkg->name[0])];
strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(pkg->name[0]));
- group = new PackageGroup(this, String16(tmpName), id);
+ group = new PackageGroup(this, String16(tmpName), id, appAsLib, isSystemAsset);
if (group == NULL) {
delete package;
return (mError=NO_MEMORY);
return NO_ERROR;
}
-DynamicRefTable::DynamicRefTable(uint8_t packageId)
+DynamicRefTable::DynamicRefTable(uint8_t packageId, bool appAsLib)
: mAssignedPackageId(packageId)
+ , mAppAsLib(appAsLib)
{
memset(mLookupTable, 0, sizeof(mLookupTable));
uint32_t res = *resId;
size_t packageId = Res_GETPACKAGE(res) + 1;
- if (packageId == APP_PACKAGE_ID) {
+ if (packageId == APP_PACKAGE_ID && !mAppAsLib) {
// No lookup needs to be done, app package IDs are absolute.
return NO_ERROR;
}
- if (packageId == 0) {
+ if (packageId == 0 || (packageId == APP_PACKAGE_ID && mAppAsLib)) {
// The package ID is 0x00. That means that a shared library is accessing
- // its own local resource, so we fix up the resource with the calling
- // package ID.
- *resId |= ((uint32_t) mAssignedPackageId) << 24;
+ // its own local resource.
+ // Or if app resource is loaded as shared library, the resource which has
+ // app package Id is local resources.
+ // so we fix up those resources with the calling package ID.
+ *resId = (0xFFFFFF & (*resId)) | (((uint32_t) mAssignedPackageId) << 24);
return NO_ERROR;
}
}
status_t DynamicRefTable::lookupResourceValue(Res_value* value) const {
- if (value->dataType != Res_value::TYPE_DYNAMIC_REFERENCE) {
+ uint8_t resolvedType = Res_value::TYPE_REFERENCE;
+ switch (value->dataType) {
+ case Res_value::TYPE_ATTRIBUTE:
+ resolvedType = Res_value::TYPE_ATTRIBUTE;
+ // fallthrough
+ case Res_value::TYPE_REFERENCE:
+ if (!mAppAsLib) {
+ return NO_ERROR;
+ }
+
+ // If the package is loaded as shared library, the resource reference
+ // also need to be fixed.
+ break;
+ case Res_value::TYPE_DYNAMIC_ATTRIBUTE:
+ resolvedType = Res_value::TYPE_ATTRIBUTE;
+ // fallthrough
+ case Res_value::TYPE_DYNAMIC_REFERENCE:
+ break;
+ default:
return NO_ERROR;
}
return err;
}
- value->dataType = Res_value::TYPE_REFERENCE;
+ value->dataType = resolvedType;
return NO_ERROR;
}
printf("(dynamic reference) 0x%08x\n", value.data);
} else if (value.dataType == Res_value::TYPE_ATTRIBUTE) {
printf("(attribute) 0x%08x\n", value.data);
+ } else if (value.dataType == Res_value::TYPE_DYNAMIC_ATTRIBUTE) {
+ printf("(dynamic attribute) 0x%08x\n", value.data);
} else if (value.dataType == Res_value::TYPE_STRING) {
size_t len;
const char* str8 = pkg->header->values.string8At(