summaryrefslogtreecommitdiffstats
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/androidfw/AssetManager.cpp335
-rw-r--r--libs/androidfw/ResourceTypes.cpp475
-rw-r--r--libs/androidfw/tests/Android.mk3
-rw-r--r--libs/androidfw/tests/ResourceTypes_test.cpp185
-rw-r--r--libs/hwui/AssetAtlas.cpp15
-rw-r--r--libs/hwui/AssetAtlas.h4
-rw-r--r--libs/hwui/DisplayListOp.h2
-rw-r--r--libs/hwui/PatchCache.cpp6
-rw-r--r--libs/hwui/PathCache.cpp4
-rw-r--r--libs/hwui/PixelBuffer.cpp2
-rw-r--r--libs/hwui/ResourceCache.cpp30
-rw-r--r--libs/hwui/TextureCache.cpp4
-rw-r--r--libs/hwui/font/Font.cpp6
-rw-r--r--libs/hwui/utils/TinyHashMap.h6
14 files changed, 766 insertions, 311 deletions
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 52ab361..b4d482a 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -41,10 +41,8 @@
#include <assert.h>
#include <dirent.h>
#include <errno.h>
-#include <fcntl.h>
+#include <string.h> // strerror
#include <strings.h>
-#include <sys/stat.h>
-#include <unistd.h>
#ifndef TEMP_FAILURE_RETRY
/* Used to retry syscalls that can return EINTR. */
@@ -75,7 +73,7 @@ static const char* kDefaultVendor = "default";
static const char* kAssetsRoot = "assets";
static const char* kAppZipName = NULL; //"classes.jar";
static const char* kSystemAssets = "framework/framework-res.apk";
-static const char* kIdmapCacheDir = "resource-cache";
+static const char* kResourceCache = "resource-cache";
static const char* kExcludeExtension = ".EXCLUDE";
@@ -83,14 +81,20 @@ static Asset* const kExcludedAsset = (Asset*) 0xd000000d;
static volatile int32_t gCount = 0;
+const char* AssetManager::RESOURCES_FILENAME = "resources.arsc";
+const char* AssetManager::IDMAP_BIN = "/system/bin/idmap";
+const char* AssetManager::OVERLAY_DIR = "/vendor/overlay";
+const char* AssetManager::TARGET_PACKAGE_NAME = "android";
+const char* AssetManager::TARGET_APK_PATH = "/system/framework/framework-res.apk";
+const char* AssetManager::IDMAP_DIR = "/data/resource-cache";
+
namespace {
- // Transform string /a/b/c.apk to /data/resource-cache/a@b@c.apk@idmap
String8 idmapPathForPackagePath(const String8& pkgPath)
{
const char* root = getenv("ANDROID_DATA");
LOG_ALWAYS_FATAL_IF(root == NULL, "ANDROID_DATA not set");
String8 path(root);
- path.appendPath(kIdmapCacheDir);
+ path.appendPath(kResourceCache);
char buf[256]; // 256 chars should be enough for anyone...
strncpy(buf, pkgPath.string(), 255);
@@ -208,180 +212,99 @@ bool AssetManager::addAssetPath(const String8& path, int32_t* cookie)
*cookie = static_cast<int32_t>(mAssetPaths.size());
}
- // add overlay packages for /system/framework; apps are handled by the
- // (Java) package manager
- if (strncmp(path.string(), "/system/framework/", 18) == 0) {
- // When there is an environment variable for /vendor, this
- // should be changed to something similar to how ANDROID_ROOT
- // and ANDROID_DATA are used in this file.
- String8 overlayPath("/vendor/overlay/framework/");
- overlayPath.append(path.getPathLeaf());
- if (TEMP_FAILURE_RETRY(access(overlayPath.string(), R_OK)) == 0) {
- asset_path oap;
- oap.path = overlayPath;
- oap.type = ::getFileType(overlayPath.string());
- bool addOverlay = (oap.type == kFileTypeRegular); // only .apks supported as overlay
- if (addOverlay) {
- oap.idmap = idmapPathForPackagePath(overlayPath);
-
- if (isIdmapStaleLocked(ap.path, oap.path, oap.idmap)) {
- addOverlay = createIdmapFileLocked(ap.path, oap.path, oap.idmap);
- }
- }
- if (addOverlay) {
- mAssetPaths.add(oap);
- } else {
- ALOGW("failed to add overlay package %s\n", overlayPath.string());
- }
- }
+#ifdef HAVE_ANDROID_OS
+ // Load overlays, if any
+ asset_path oap;
+ for (size_t idx = 0; mZipSet.getOverlay(ap.path, idx, &oap); idx++) {
+ mAssetPaths.add(oap);
}
+#endif
return true;
}
-bool AssetManager::isIdmapStaleLocked(const String8& originalPath, const String8& overlayPath,
- const String8& idmapPath)
+bool AssetManager::addOverlayPath(const String8& packagePath, int32_t* cookie)
{
- struct stat st;
- if (TEMP_FAILURE_RETRY(stat(idmapPath.string(), &st)) == -1) {
- if (errno == ENOENT) {
- return true; // non-existing idmap is always stale
- } else {
- ALOGW("failed to stat file %s: %s\n", idmapPath.string(), strerror(errno));
- return false;
- }
- }
- if (st.st_size < ResTable::IDMAP_HEADER_SIZE_BYTES) {
- ALOGW("file %s has unexpectedly small size=%zd\n", idmapPath.string(), (size_t)st.st_size);
- return false;
- }
- int fd = TEMP_FAILURE_RETRY(::open(idmapPath.string(), O_RDONLY));
- if (fd == -1) {
- ALOGW("failed to open file %s: %s\n", idmapPath.string(), strerror(errno));
+ const String8 idmapPath = idmapPathForPackagePath(packagePath);
+
+ AutoMutex _l(mLock);
+
+ for (size_t i = 0; i < mAssetPaths.size(); ++i) {
+ if (mAssetPaths[i].idmap == idmapPath) {
+ *cookie = static_cast<int32_t>(i + 1);
+ return true;
+ }
+ }
+
+ Asset* idmap = NULL;
+ if ((idmap = openAssetFromFileLocked(idmapPath, Asset::ACCESS_BUFFER)) == NULL) {
+ ALOGW("failed to open idmap file %s\n", idmapPath.string());
return false;
}
- char buf[ResTable::IDMAP_HEADER_SIZE_BYTES];
- ssize_t bytesLeft = ResTable::IDMAP_HEADER_SIZE_BYTES;
- for (;;) {
- ssize_t r = TEMP_FAILURE_RETRY(read(fd, buf + ResTable::IDMAP_HEADER_SIZE_BYTES - bytesLeft,
- bytesLeft));
- if (r < 0) {
- TEMP_FAILURE_RETRY(close(fd));
- return false;
- }
- bytesLeft -= r;
- if (bytesLeft == 0) {
- break;
- }
- }
- TEMP_FAILURE_RETRY(close(fd));
- uint32_t cachedOriginalCrc, cachedOverlayCrc;
- if (!ResTable::getIdmapInfo(buf, ResTable::IDMAP_HEADER_SIZE_BYTES,
- &cachedOriginalCrc, &cachedOverlayCrc)) {
+ String8 targetPath;
+ String8 overlayPath;
+ if (!ResTable::getIdmapInfo(idmap->getBuffer(false), idmap->getLength(),
+ NULL, NULL, &targetPath, &overlayPath)) {
+ ALOGW("failed to read idmap file %s\n", idmapPath.string());
+ delete idmap;
return false;
}
+ delete idmap;
- uint32_t actualOriginalCrc, actualOverlayCrc;
- if (!getZipEntryCrcLocked(originalPath, "resources.arsc", &actualOriginalCrc)) {
+ if (overlayPath != packagePath) {
+ ALOGW("idmap file %s inconcistent: expected path %s does not match actual path %s\n",
+ idmapPath.string(), packagePath.string(), overlayPath.string());
return false;
}
- if (!getZipEntryCrcLocked(overlayPath, "resources.arsc", &actualOverlayCrc)) {
+ if (access(targetPath.string(), R_OK) != 0) {
+ ALOGW("failed to access file %s: %s\n", targetPath.string(), strerror(errno));
return false;
}
- return cachedOriginalCrc != actualOriginalCrc || cachedOverlayCrc != actualOverlayCrc;
-}
-
-bool AssetManager::getZipEntryCrcLocked(const String8& zipPath, const char* entryFilename,
- uint32_t* pCrc)
-{
- asset_path ap;
- ap.path = zipPath;
- const ZipFileRO* zip = getZipFileLocked(ap);
- if (zip == NULL) {
+ if (access(idmapPath.string(), R_OK) != 0) {
+ ALOGW("failed to access file %s: %s\n", idmapPath.string(), strerror(errno));
return false;
}
- const ZipEntryRO entry = zip->findEntryByName(entryFilename);
- if (entry == NULL) {
+ if (access(overlayPath.string(), R_OK) != 0) {
+ ALOGW("failed to access file %s: %s\n", overlayPath.string(), strerror(errno));
return false;
}
- const bool gotInfo = zip->getEntryInfo(entry, NULL, NULL, NULL, NULL, NULL, (long*)pCrc);
- zip->releaseEntry(entry);
+ asset_path oap;
+ oap.path = overlayPath;
+ oap.type = ::getFileType(overlayPath.string());
+ oap.idmap = idmapPath;
+#if 0
+ ALOGD("Overlay added: targetPath=%s overlayPath=%s idmapPath=%s\n",
+ targetPath.string(), overlayPath.string(), idmapPath.string());
+#endif
+ mAssetPaths.add(oap);
+ *cookie = static_cast<int32_t>(mAssetPaths.size());
- return gotInfo;
-}
+ return true;
+ }
-bool AssetManager::createIdmapFileLocked(const String8& originalPath, const String8& overlayPath,
- const String8& idmapPath)
+bool AssetManager::createIdmap(const char* targetApkPath, const char* overlayApkPath,
+ uint32_t targetCrc, uint32_t overlayCrc, uint32_t** outData, size_t* outSize)
{
- ALOGD("%s: originalPath=%s overlayPath=%s idmapPath=%s\n",
- __FUNCTION__, originalPath.string(), overlayPath.string(), idmapPath.string());
+ AutoMutex _l(mLock);
+ const String8 paths[2] = { String8(targetApkPath), String8(overlayApkPath) };
ResTable tables[2];
- const String8* paths[2] = { &originalPath, &overlayPath };
- uint32_t originalCrc, overlayCrc;
- bool retval = false;
- ssize_t offset = 0;
- int fd = 0;
- uint32_t* data = NULL;
- size_t size;
for (int i = 0; i < 2; ++i) {
asset_path ap;
ap.type = kFileTypeRegular;
- ap.path = *paths[i];
+ ap.path = paths[i];
Asset* ass = openNonAssetInPathLocked("resources.arsc", Asset::ACCESS_BUFFER, ap);
if (ass == NULL) {
ALOGW("failed to find resources.arsc in %s\n", ap.path.string());
- goto error;
- }
- tables[i].add(ass, (void*)1, false);
- }
-
- if (!getZipEntryCrcLocked(originalPath, "resources.arsc", &originalCrc)) {
- ALOGW("failed to retrieve crc for resources.arsc in %s\n", originalPath.string());
- goto error;
- }
- if (!getZipEntryCrcLocked(overlayPath, "resources.arsc", &overlayCrc)) {
- ALOGW("failed to retrieve crc for resources.arsc in %s\n", overlayPath.string());
- goto error;
- }
-
- if (tables[0].createIdmap(tables[1], originalCrc, overlayCrc,
- (void**)&data, &size) != NO_ERROR) {
- ALOGW("failed to generate idmap data for file %s\n", idmapPath.string());
- goto error;
- }
-
- // This should be abstracted (eg replaced by a stand-alone
- // application like dexopt, triggered by something equivalent to
- // installd).
- fd = TEMP_FAILURE_RETRY(::open(idmapPath.string(), O_WRONLY | O_CREAT | O_TRUNC, 0644));
- if (fd == -1) {
- ALOGW("failed to write idmap file %s (open: %s)\n", idmapPath.string(), strerror(errno));
- goto error_free;
- }
- for (;;) {
- ssize_t written = TEMP_FAILURE_RETRY(write(fd, data + offset, size));
- if (written < 0) {
- ALOGW("failed to write idmap file %s (write: %s)\n", idmapPath.string(),
- strerror(errno));
- goto error_close;
- }
- size -= (size_t)written;
- offset += written;
- if (size == 0) {
- break;
+ return false;
}
+ tables[i].add(ass, 1, false /* copyData */, NULL /* idMap */);
}
- retval = true;
-error_close:
- TEMP_FAILURE_RETRY(close(fd));
-error_free:
- free(data);
-error:
- return retval;
+ return tables[0].createIdmap(tables[1], targetCrc, overlayCrc,
+ targetApkPath, overlayApkPath, (void**)outData, outSize) == NO_ERROR;
}
bool AssetManager::addDefaultAssets()
@@ -463,17 +386,8 @@ void AssetManager::setConfiguration(const ResTable_config& config, const char* l
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();
@@ -660,6 +574,10 @@ const ResTable* AssetManager::getResTable(bool required) const
// which we want to avoid parsing every time.
sharedRes = const_cast<AssetManager*>(this)->
mZipSet.getZipResourceTable(ap.path);
+ if (sharedRes != NULL) {
+ // skip ahead the number of system overlay packages preloaded
+ i += sharedRes->getTableCount() - 1;
+ }
}
if (sharedRes == NULL) {
ass = const_cast<AssetManager*>(this)->
@@ -682,7 +600,15 @@ const ResTable* AssetManager::getResTable(bool required) const
// can quickly copy it out for others.
ALOGV("Creating shared resources for %s", ap.path.string());
sharedRes = new ResTable();
- sharedRes->add(ass, (void*)(i+1), false, idmap);
+ sharedRes->add(ass, i + 1, false, idmap);
+#ifdef HAVE_ANDROID_OS
+ const char* data = getenv("ANDROID_DATA");
+ LOG_ALWAYS_FATAL_IF(data == NULL, "ANDROID_DATA not set");
+ String8 overlaysListPath(data);
+ overlaysListPath.appendPath(kResourceCache);
+ overlaysListPath.appendPath("overlays.list");
+ addSystemOverlays(overlaysListPath.string(), ap.path, sharedRes, i);
+#endif
sharedRes = const_cast<AssetManager*>(this)->
mZipSet.setZipResourceTable(ap.path, sharedRes);
}
@@ -706,7 +632,7 @@ const ResTable* AssetManager::getResTable(bool required) const
rt->add(sharedRes);
} else {
ALOGV("Parsing resources for %s", ap.path.string());
- rt->add(ass, (void*)(i+1), !shared, idmap);
+ rt->add(ass, i + 1, !shared, idmap);
}
if (!shared) {
@@ -733,20 +659,11 @@ void AssetManager::updateResourceParamsLocked() const
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 (llen >= 5) {
- mConfig->country[0] = mLocale[3];
- mConfig->country[1] = mLocale[4];
+ if (mLocale) {
+ mConfig->setBcp47Locale(mLocale);
+ } else {
+ mConfig->clearLocale();
}
- mConfig->size = sizeof(*mConfig);
res->setParameters(mConfig);
}
@@ -766,6 +683,46 @@ Asset* AssetManager::openIdmapLocked(const struct asset_path& ap) const
return ass;
}
+void AssetManager::addSystemOverlays(const char* pathOverlaysList,
+ const String8& targetPackagePath, ResTable* sharedRes, size_t offset) const
+{
+ FILE* fin = fopen(pathOverlaysList, "r");
+ if (fin == NULL) {
+ return;
+ }
+
+ char buf[1024];
+ while (fgets(buf, sizeof(buf), fin)) {
+ // format of each line:
+ // <path to apk><space><path to idmap><newline>
+ char* space = strchr(buf, ' ');
+ char* newline = strchr(buf, '\n');
+ asset_path oap;
+
+ if (space == NULL || newline == NULL || newline < space) {
+ continue;
+ }
+
+ oap.path = String8(buf, space - buf);
+ oap.type = kFileTypeRegular;
+ oap.idmap = String8(space + 1, newline - space - 1);
+
+ Asset* oass = const_cast<AssetManager*>(this)->
+ openNonAssetInPathLocked("resources.arsc",
+ Asset::ACCESS_BUFFER,
+ oap);
+
+ if (oass != NULL) {
+ Asset* oidmap = openIdmapLocked(oap);
+ offset++;
+ sharedRes->add(oass, offset + 1, false, oidmap);
+ const_cast<AssetManager*>(this)->mAssetPaths.add(oap);
+ const_cast<AssetManager*>(this)->mZipSet.addOverlay(targetPackagePath, oap);
+ }
+ }
+ fclose(fin);
+}
+
const ResTable& AssetManager::getResources(bool required) const
{
const ResTable* rt = getResTable(required);
@@ -1824,7 +1781,8 @@ AssetManager::SharedZip::SharedZip(const String8& path, time_t modWhen)
}
}
-sp<AssetManager::SharedZip> AssetManager::SharedZip::get(const String8& path)
+sp<AssetManager::SharedZip> AssetManager::SharedZip::get(const String8& path,
+ bool createIfNotPresent)
{
AutoMutex _l(gLock);
time_t modWhen = getFileModDate(path);
@@ -1832,6 +1790,9 @@ sp<AssetManager::SharedZip> AssetManager::SharedZip::get(const String8& path)
if (zip != NULL && zip->mModWhen == modWhen) {
return zip;
}
+ if (zip == NULL && !createIfNotPresent) {
+ return NULL;
+ }
zip = new SharedZip(path, modWhen);
gOpen.add(path, zip);
return zip;
@@ -1890,6 +1851,20 @@ bool AssetManager::SharedZip::isUpToDate()
return mModWhen == modWhen;
}
+void AssetManager::SharedZip::addOverlay(const asset_path& ap)
+{
+ mOverlays.add(ap);
+}
+
+bool AssetManager::SharedZip::getOverlay(size_t idx, asset_path* out) const
+{
+ if (idx >= mOverlays.size()) {
+ return false;
+ }
+ *out = mOverlays[idx];
+ return true;
+}
+
AssetManager::SharedZip::~SharedZip()
{
//ALOGI("Destroying SharedZip %p %s\n", this, (const char*)mPath);
@@ -2013,6 +1988,22 @@ bool AssetManager::ZipSet::isUpToDate()
return true;
}
+void AssetManager::ZipSet::addOverlay(const String8& path, const asset_path& overlay)
+{
+ int idx = getIndex(path);
+ sp<SharedZip> zip = mZipFile[idx];
+ zip->addOverlay(overlay);
+}
+
+bool AssetManager::ZipSet::getOverlay(const String8& path, size_t idx, asset_path* out) const
+{
+ sp<SharedZip> zip = SharedZip::get(path, false);
+ if (zip == NULL) {
+ return false;
+ }
+ return zip->getOverlay(idx, out);
+}
+
/*
* Compute the zip file's index.
*
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 1cc3563..51f59f6 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -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(void* cookie, const char* txt)
-{
- ALOGV("%s", 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.
@@ -106,21 +101,20 @@ static status_t validate_chunk(const ResChunk_header* chunk,
if ((ssize_t)size <= (dataEnd-((const uint8_t*)chunk))) {
return NO_ERROR;
}
- ALOGW("%s data size %p extends beyond resource end %p.",
- name, (void*)size,
- (void*)(dataEnd-((const uint8_t*)chunk)));
+ ALOGW("%s data size 0x%x extends beyond resource end %p.",
+ name, size, (void*)(dataEnd-((const uint8_t*)chunk)));
return BAD_TYPE;
}
ALOGW("%s size 0x%x or headerSize 0x%x is not on an integer boundary.",
name, (int)size, (int)headerSize);
return BAD_TYPE;
}
- ALOGW("%s size %p is smaller than header size %p.",
- name, (void*)size, (void*)(int)headerSize);
+ ALOGW("%s size 0x%x is smaller than header size 0x%x.",
+ name, size, headerSize);
return BAD_TYPE;
}
- ALOGW("%s header size %p is too small.",
- name, (void*)(int)headerSize);
+ ALOGW("%s header size 0x%x is too small.",
+ name, headerSize);
return BAD_TYPE;
}
@@ -284,11 +278,37 @@ static status_t getIdmapPackageId(const uint32_t* map, size_t mapSize, uint32_t
if (!assertIdmapHeader(map, mapSize)) {
return UNKNOWN_ERROR;
}
+ if (mapSize <= IDMAP_HEADER_SIZE + 1) {
+ ALOGW("corrupt idmap: map size %d too short\n", mapSize);
+ return UNKNOWN_ERROR;
+ }
+ uint32_t typeCount = *(map + IDMAP_HEADER_SIZE);
+ if (typeCount == 0) {
+ ALOGW("corrupt idmap: no types\n");
+ return UNKNOWN_ERROR;
+ }
+ if (IDMAP_HEADER_SIZE + 1 + typeCount > mapSize) {
+ ALOGW("corrupt idmap: number of types %d extends past idmap size %d\n", typeCount, mapSize);
+ return UNKNOWN_ERROR;
+ }
const uint32_t* p = map + IDMAP_HEADER_SIZE + 1;
+ // find first defined type
while (*p == 0) {
++p;
+ if (--typeCount == 0) {
+ ALOGW("corrupt idmap: types declared, none found\n");
+ return UNKNOWN_ERROR;
+ }
}
- *outId = (map[*p + IDMAP_HEADER_SIZE + 2] >> 24) & 0x000000ff;
+
+ // determine package id from first entry of first type
+ const uint32_t offset = *p + IDMAP_HEADER_SIZE + 2;
+ if (offset > mapSize) {
+ ALOGW("corrupt idmap: entry offset %d points outside map size %d\n", offset, mapSize);
+ return UNKNOWN_ERROR;
+ }
+ *outId = (map[offset] >> 24) & 0x000000ff;
+
return NO_ERROR;
}
@@ -1539,6 +1559,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, const char base,
+ char out[2]) {
+ if (in[2] == 0 || in[2] == '-') {
+ 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) {
+ packLanguageOrRegion(language, 'a', this->language);
+}
+
+void ResTable_config::packRegion(const char* region) {
+ 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);
@@ -1568,10 +1653,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;
@@ -1598,18 +1703,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;
}
@@ -1656,7 +1758,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;
@@ -1671,9 +1772,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
@@ -1691,14 +1827,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;
}
}
@@ -1834,6 +1969,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)) {
@@ -2054,17 +2201,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;
@@ -2166,17 +2319,92 @@ 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::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
+ // 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];
- if (country[0]) {
- str[2] = '_';
- str[3] = country[0];
- str[4] = country[1];
+ charsWritten += unpackLanguage(str);
+ }
+
+ if (localeScript[0]) {
+ if (charsWritten) {
+ 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++] = '-';
+ }
+ 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 {
@@ -2190,14 +2418,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];
+ getBcp47Locale(localeStr);
+ res.append(localeStr);
+
if ((screenLayout&MASK_LAYOUTDIR) != 0) {
if (res.size() > 0) res.append("-");
switch (screenLayout&ResTable_config::MASK_LAYOUTDIR) {
@@ -2461,7 +2685,7 @@ struct ResTable::Header
size_t size;
const uint8_t* dataEnd;
size_t index;
- void* cookie;
+ int32_t cookie;
ResStringPool values;
uint32_t* resourceIDMap;
@@ -2860,12 +3084,12 @@ ResTable::ResTable()
//ALOGI("Creating ResTable %p\n", this);
}
-ResTable::ResTable(const void* data, size_t size, void* cookie, bool copyData)
+ResTable::ResTable(const void* data, size_t size, const int32_t cookie, bool copyData)
: mError(NO_INIT)
{
memset(&mParams, 0, sizeof(mParams));
memset(mPackageMap, 0, sizeof(mPackageMap));
- add(data, size, cookie, copyData);
+ addInternal(data, size, cookie, NULL /* asset */, copyData, NULL /* idMap */);
LOG_FATAL_IF(mError != NO_ERROR, "Error parsing resource table");
//ALOGI("Creating ResTable %p\n", this);
}
@@ -2881,13 +3105,12 @@ inline ssize_t ResTable::getResourcePackageIndex(uint32_t resID) const
return ((ssize_t)mPackageMap[Res_GETPACKAGE(resID)+1])-1;
}
-status_t ResTable::add(const void* data, size_t size, void* cookie, bool copyData,
- const void* idmap)
-{
- return add(data, size, cookie, NULL, copyData, reinterpret_cast<const Asset*>(idmap));
+status_t ResTable::add(const void* data, size_t size) {
+ return addInternal(data, size, 0 /* cookie */, NULL /* asset */,
+ false /* copyData */, NULL /* idMap */);
}
-status_t ResTable::add(Asset* asset, void* cookie, bool copyData, const void* idmap)
+status_t ResTable::add(Asset* asset, const int32_t cookie, bool copyData, const void* idmap)
{
const void* data = asset->getBuffer(true);
if (data == NULL) {
@@ -2895,7 +3118,8 @@ status_t ResTable::add(Asset* asset, void* cookie, bool copyData, const void* id
return UNKNOWN_ERROR;
}
size_t size = (size_t)asset->getLength();
- return add(data, size, cookie, asset, copyData, reinterpret_cast<const Asset*>(idmap));
+ return addInternal(data, size, cookie, asset, copyData,
+ reinterpret_cast<const Asset*>(idmap));
}
status_t ResTable::add(ResTable* src)
@@ -2922,7 +3146,7 @@ status_t ResTable::add(ResTable* src)
return mError;
}
-status_t ResTable::add(const void* data, size_t size, void* cookie,
+status_t ResTable::addInternal(const void* data, size_t size, const int32_t cookie,
Asset* asset, bool copyData, const Asset* idmap)
{
if (!data) return NO_ERROR;
@@ -2945,7 +3169,7 @@ status_t ResTable::add(const void* data, size_t size, void* cookie,
const bool notDeviceEndian = htods(0xf0) != 0xf0;
LOAD_TABLE_NOISY(
- ALOGV("Adding resources to ResTable: data=%p, size=0x%x, cookie=%p, asset=%p, copy=%d "
+ ALOGV("Adding resources to ResTable: data=%p, size=0x%x, cookie=%d, asset=%p, copy=%d "
"idmap=%p\n", data, size, cookie, asset, copyData, idmap));
if (copyData || notDeviceEndian) {
@@ -3027,8 +3251,8 @@ status_t ResTable::add(const void* data, size_t size, void* cookie,
}
curPackage++;
} else {
- ALOGW("Unknown chunk type %p in table at %p.\n",
- (void*)(int)(ctype),
+ ALOGW("Unknown chunk type 0x%x in table at %p.\n",
+ ctype,
(void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header)));
}
chunk = (const ResChunk_header*)
@@ -3244,8 +3468,8 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag
if ((dtohs(entry->flags)&entry->FLAG_COMPLEX) != 0) {
if (!mayBeBag) {
- ALOGW("Requesting resource %p failed because it is complex\n",
- (void*)resID);
+ ALOGW("Requesting resource 0x%x failed because it is complex\n",
+ resID);
}
continue;
}
@@ -3520,8 +3744,8 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag,
}
if ((dtohs(entry->flags)&entry->FLAG_COMPLEX) == 0) {
- ALOGW("Skipping entry %p in package table %d because it is not complex!\n",
- (void*)resID, (int)ip);
+ ALOGW("Skipping entry 0x%x in package table %zu because it is not complex!\n",
+ resID, ip);
continue;
}
@@ -4930,7 +5154,7 @@ const ResStringPool* ResTable::getTableStringBlock(size_t index) const
return &mHeaders[index]->values;
}
-void* ResTable::getTableCookie(size_t index) const
+int32_t ResTable::getTableCookie(size_t index) const
{
return mHeaders[index]->cookie;
}
@@ -4950,18 +5174,20 @@ void ResTable::getConfigurations(Vector<ResTable_config>* configs) const
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);
}
}
}
@@ -4976,9 +5202,10 @@ 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);
+ configs[i].getBcp47Locale(locale);
const size_t J = locales->size();
size_t j;
for (j=0; j<J; j++) {
@@ -5114,26 +5341,26 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
return (mError=err);
}
- const size_t pkgSize = dtohl(pkg->header.size);
+ const uint32_t pkgSize = dtohl(pkg->header.size);
if (dtohl(pkg->typeStrings) >= pkgSize) {
- ALOGW("ResTable_package type strings at %p are past chunk size %p.",
- (void*)dtohl(pkg->typeStrings), (void*)pkgSize);
+ ALOGW("ResTable_package type strings at 0x%x are past chunk size 0x%x.",
+ dtohl(pkg->typeStrings), pkgSize);
return (mError=BAD_TYPE);
}
if ((dtohl(pkg->typeStrings)&0x3) != 0) {
- ALOGW("ResTable_package type strings at %p is not on an integer boundary.",
- (void*)dtohl(pkg->typeStrings));
+ ALOGW("ResTable_package type strings at 0x%x is not on an integer boundary.",
+ dtohl(pkg->typeStrings));
return (mError=BAD_TYPE);
}
if (dtohl(pkg->keyStrings) >= pkgSize) {
- ALOGW("ResTable_package key strings at %p are past chunk size %p.",
- (void*)dtohl(pkg->keyStrings), (void*)pkgSize);
+ ALOGW("ResTable_package key strings at 0x%x are past chunk size 0x%x.",
+ dtohl(pkg->keyStrings), pkgSize);
return (mError=BAD_TYPE);
}
if ((dtohl(pkg->keyStrings)&0x3) != 0) {
- ALOGW("ResTable_package key strings at %p is not on an integer boundary.",
- (void*)dtohl(pkg->keyStrings));
+ ALOGW("ResTable_package key strings at 0x%x is not on an integer boundary.",
+ dtohl(pkg->keyStrings));
return (mError=BAD_TYPE);
}
@@ -5271,7 +5498,7 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
return (mError=err);
}
- const size_t typeSize = dtohl(type->header.size);
+ const uint32_t typeSize = dtohl(type->header.size);
LOAD_TABLE_NOISY(printf("Type off %p: type=0x%x, headerSize=0x%x, size=%p\n",
(void*)(base-(const uint8_t*)chunk),
@@ -5280,16 +5507,16 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
(void*)typeSize));
if (dtohs(type->header.headerSize)+(sizeof(uint32_t)*dtohl(type->entryCount))
> typeSize) {
- ALOGW("ResTable_type entry index to %p extends beyond chunk end %p.",
+ ALOGW("ResTable_type entry index to %p extends beyond chunk end 0x%x.",
(void*)(dtohs(type->header.headerSize)
+(sizeof(uint32_t)*dtohl(type->entryCount))),
- (void*)typeSize);
+ typeSize);
return (mError=BAD_TYPE);
}
if (dtohl(type->entryCount) != 0
&& dtohl(type->entriesStart) > (typeSize-sizeof(ResTable_entry))) {
- ALOGW("ResTable_type entriesStart at %p extends beyond chunk end %p.",
- (void*)dtohl(type->entriesStart), (void*)typeSize);
+ ALOGW("ResTable_type entriesStart at 0x%x extends beyond chunk end 0x%x.",
+ dtohl(type->entriesStart), typeSize);
return (mError=BAD_TYPE);
}
if (type->id == 0) {
@@ -5334,23 +5561,30 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
return NO_ERROR;
}
-status_t ResTable::createIdmap(const ResTable& overlay, uint32_t originalCrc, uint32_t overlayCrc,
- void** outData, size_t* outSize) const
+status_t ResTable::createIdmap(const ResTable& overlay,
+ uint32_t targetCrc, uint32_t overlayCrc,
+ const char* targetPath, const char* overlayPath,
+ void** outData, size_t* outSize) const
{
// see README for details on the format of map
if (mPackageGroups.size() == 0) {
+ ALOGW("idmap: target package has no package groups, cannot create idmap\n");
return UNKNOWN_ERROR;
}
if (mPackageGroups[0]->packages.size() == 0) {
+ ALOGW("idmap: target package has no packages in its first package group, "
+ "cannot create idmap\n");
return UNKNOWN_ERROR;
}
Vector<Vector<uint32_t> > map;
+ // overlaid packages are assumed to contain only one package group
const PackageGroup* pg = mPackageGroups[0];
const Package* pkg = pg->packages[0];
size_t typeCount = pkg->types.size();
// starting size is header + first item (number of types in map)
*outSize = (IDMAP_HEADER_SIZE + 1) * sizeof(uint32_t);
+ // overlay packages are assumed to contain only one package group
const String16 overlayPackage(overlay.mPackageGroups[0]->packages[0]->package->name);
const uint32_t pkg_id = pkg->package->id << 24;
@@ -5368,7 +5602,7 @@ status_t ResTable::createIdmap(const ResTable& overlay, uint32_t originalCrc, ui
| (0x00ff0000 & ((typeIndex+1)<<16))
| (0x0000ffff & (entryIndex));
resource_name resName;
- if (!this->getResourceName(resID, true, &resName)) {
+ if (!this->getResourceName(resID, false, &resName)) {
ALOGW("idmap: resource 0x%08x has spec but lacks values, skipping\n", resID);
// add dummy value, or trimming leading/trailing zeroes later will fail
vector.push(0);
@@ -5426,8 +5660,22 @@ status_t ResTable::createIdmap(const ResTable& overlay, uint32_t originalCrc, ui
}
uint32_t* data = (uint32_t*)*outData;
*data++ = htodl(IDMAP_MAGIC);
- *data++ = htodl(originalCrc);
+ *data++ = htodl(targetCrc);
*data++ = htodl(overlayCrc);
+ const char* paths[] = { targetPath, overlayPath };
+ for (int j = 0; j < 2; ++j) {
+ char* p = (char*)data;
+ const char* path = paths[j];
+ const size_t I = strlen(path);
+ if (I > 255) {
+ ALOGV("path exceeds expected 255 characters: %s\n", path);
+ return UNKNOWN_ERROR;
+ }
+ for (size_t i = 0; i < 256; ++i) {
+ *p++ = i < I ? path[i] : '\0';
+ }
+ data += 256 / sizeof(uint32_t);
+ }
const size_t mapSize = map.size();
*data++ = htodl(mapSize);
size_t offset = mapSize;
@@ -5442,6 +5690,10 @@ status_t ResTable::createIdmap(const ResTable& overlay, uint32_t originalCrc, ui
offset += N;
}
}
+ if (offset == mapSize) {
+ ALOGW("idmap: no resources in overlay package present in base package\n");
+ return UNKNOWN_ERROR;
+ }
for (size_t i = 0; i < mapSize; ++i) {
const Vector<uint32_t>& vector = map.itemAt(i);
const size_t N = vector.size();
@@ -5463,14 +5715,25 @@ status_t ResTable::createIdmap(const ResTable& overlay, uint32_t originalCrc, ui
}
bool ResTable::getIdmapInfo(const void* idmap, size_t sizeBytes,
- uint32_t* pOriginalCrc, uint32_t* pOverlayCrc)
+ uint32_t* pTargetCrc, uint32_t* pOverlayCrc,
+ String8* pTargetPath, String8* pOverlayPath)
{
const uint32_t* map = (const uint32_t*)idmap;
if (!assertIdmapHeader(map, sizeBytes)) {
return false;
}
- *pOriginalCrc = map[1];
- *pOverlayCrc = map[2];
+ if (pTargetCrc) {
+ *pTargetCrc = map[1];
+ }
+ if (pOverlayCrc) {
+ *pOverlayCrc = map[2];
+ }
+ if (pTargetPath) {
+ pTargetPath->setTo(reinterpret_cast<const char*>(map + 3));
+ }
+ if (pOverlayPath) {
+ pOverlayPath->setTo(reinterpret_cast<const char*>(map + 3 + 256 / sizeof(uint32_t)));
+ }
return true;
}
@@ -5601,9 +5864,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.getBcp47Locale(localeStr);
+ printf("mParams=%s,\n" localeStr);
#endif
size_t pgCount = mPackageGroups.size();
printf("Package Groups (%d)\n", (int)pgCount);
@@ -5670,12 +5933,12 @@ void ResTable::print(bool inclValues) const
size_t entryCount = dtohl(type->entryCount);
uint32_t entriesStart = dtohl(type->entriesStart);
if ((entriesStart&0x3) != 0) {
- printf(" NON-INTEGER ResTable_type entriesStart OFFSET: %p\n", (void*)entriesStart);
+ printf(" NON-INTEGER ResTable_type entriesStart OFFSET: 0x%x\n", entriesStart);
continue;
}
uint32_t typeSize = dtohl(type->header.size);
if ((typeSize&0x3) != 0) {
- printf(" NON-INTEGER ResTable_type header.size: %p\n", (void*)typeSize);
+ printf(" NON-INTEGER ResTable_type header.size: 0x%x\n", typeSize);
continue;
}
for (size_t entryIndex=0; entryIndex<entryCount; entryIndex++) {
@@ -5714,33 +5977,31 @@ void ResTable::print(bool inclValues) const
printf(" INVALID RESOURCE 0x%08x: ", resID);
}
if ((thisOffset&0x3) != 0) {
- printf("NON-INTEGER OFFSET: %p\n", (void*)thisOffset);
+ printf("NON-INTEGER OFFSET: 0x%x\n", thisOffset);
continue;
}
if ((thisOffset+sizeof(ResTable_entry)) > typeSize) {
- printf("OFFSET OUT OF BOUNDS: %p+%p (size is %p)\n",
- (void*)entriesStart, (void*)thisOffset,
- (void*)typeSize);
+ printf("OFFSET OUT OF BOUNDS: 0x%x+0x%x (size is 0x%x)\n",
+ entriesStart, thisOffset, typeSize);
continue;
}
const ResTable_entry* ent = (const ResTable_entry*)
(((const uint8_t*)type) + entriesStart + thisOffset);
if (((entriesStart + thisOffset)&0x3) != 0) {
- printf("NON-INTEGER ResTable_entry OFFSET: %p\n",
- (void*)(entriesStart + thisOffset));
+ printf("NON-INTEGER ResTable_entry OFFSET: 0x%x\n",
+ (entriesStart + thisOffset));
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);
+ printf("NON-INTEGER ResTable_entry SIZE: 0x%x\n", esize);
continue;
}
if ((thisOffset+esize) > typeSize) {
- printf("ResTable_entry OUT OF BOUNDS: %p+%p+%p (size is %p)\n",
- (void*)entriesStart, (void*)thisOffset,
- (void*)esize, (void*)typeSize);
+ printf("ResTable_entry OUT OF BOUNDS: 0x%x+0x%x+0x%x (size is 0x%x)\n",
+ entriesStart, thisOffset, esize, typeSize);
continue;
}
diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk
index 3c55375..977ba80 100644
--- a/libs/androidfw/tests/Android.mk
+++ b/libs/androidfw/tests/Android.mk
@@ -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 \
diff --git a/libs/androidfw/tests/ResourceTypes_test.cpp b/libs/androidfw/tests/ResourceTypes_test.cpp
new file mode 100644
index 0000000..4888b4a
--- /dev/null
+++ b/libs/androidfw/tests/ResourceTypes_test.cpp
@@ -0,0 +1,185 @@
+/*
+ * 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));
+}
+
+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/libs/hwui/AssetAtlas.cpp b/libs/hwui/AssetAtlas.cpp
index eb8bb9f..e8c3d3c 100644
--- a/libs/hwui/AssetAtlas.cpp
+++ b/libs/hwui/AssetAtlas.cpp
@@ -28,7 +28,7 @@ namespace uirenderer {
// Lifecycle
///////////////////////////////////////////////////////////////////////////////
-void AssetAtlas::init(sp<GraphicBuffer> buffer, int* map, int count) {
+void AssetAtlas::init(sp<GraphicBuffer> buffer, int64_t* map, int count) {
if (mImage) {
return;
}
@@ -108,14 +108,19 @@ private:
/**
* TODO: This method does not take the rotation flag into account
*/
-void AssetAtlas::createEntries(Caches& caches, int* map, int count) {
+void AssetAtlas::createEntries(Caches& caches, int64_t* map, int count) {
const float width = float(mTexture->width);
const float height = float(mTexture->height);
for (int i = 0; i < count; ) {
- SkBitmap* bitmap = (SkBitmap*) map[i++];
- int x = map[i++];
- int y = map[i++];
+ SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(map[i++]);
+ // NOTE: We're converting from 64 bit signed values to 32 bit
+ // signed values. This is guaranteed to be safe because the "x"
+ // and "y" coordinate values are guaranteed to be representable
+ // with 32 bits. The array is 64 bits wide so that it can carry
+ // pointers on 64 bit architectures.
+ const int x = static_cast<int>(map[i++]);
+ const int y = static_cast<int>(map[i++]);
bool rotated = map[i++] > 0;
// Bitmaps should never be null, we're just extra paranoid
diff --git a/libs/hwui/AssetAtlas.h b/libs/hwui/AssetAtlas.h
index a28efc6..163bdbc 100644
--- a/libs/hwui/AssetAtlas.h
+++ b/libs/hwui/AssetAtlas.h
@@ -121,7 +121,7 @@ public:
* initialized. To re-initialize the atlas, you must
* first call terminate().
*/
- ANDROID_API void init(sp<GraphicBuffer> buffer, int* map, int count);
+ ANDROID_API void init(sp<GraphicBuffer> buffer, int64_t* map, int count);
/**
* Destroys the atlas texture. This object can be
@@ -176,7 +176,7 @@ public:
}
private:
- void createEntries(Caches& caches, int* map, int count);
+ void createEntries(Caches& caches, int64_t* map, int count);
Texture* mTexture;
Image* mImage;
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 326805a..842e028 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -1444,7 +1444,7 @@ public:
DeferredDisplayList::kOpBatch_Text :
DeferredDisplayList::kOpBatch_ColorText;
- deferInfo.mergeId = (mergeid_t)mPaint->getColor();
+ deferInfo.mergeId = reinterpret_cast<mergeid_t>(mPaint->getColor());
// don't merge decorated text - the decorations won't draw in order
bool noDecorations = !(mPaint->getFlags() & (SkPaint::kUnderlineText_Flag |
diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp
index dc0d98c..8a44604 100644
--- a/libs/hwui/PatchCache.cpp
+++ b/libs/hwui/PatchCache.cpp
@@ -129,7 +129,11 @@ void PatchCache::clearGarbage() {
Mutex::Autolock _l(mLock);
size_t count = mGarbage.size();
for (size_t i = 0; i < count; i++) {
- remove(patchesToRemove, mGarbage[i]);
+ Res_png_9patch* patch = mGarbage[i];
+ remove(patchesToRemove, patch);
+ // A Res_png_9patch is actually an array of byte that's larger
+ // than sizeof(Res_png_9patch). It must be freed as an array.
+ delete[] (int8_t*) patch;
}
mGarbage.clear();
}
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index 5df6408..cf8adf8 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -395,7 +395,9 @@ void PathCache::clearGarbage() {
Mutex::Autolock l(mLock);
size_t count = mGarbage.size();
for (size_t i = 0; i < count; i++) {
- remove(pathsToRemove, mGarbage.itemAt(i));
+ const path_pair_t& pair = mGarbage.itemAt(i);
+ remove(pathsToRemove, pair);
+ delete pair.getFirst();
}
mGarbage.clear();
}
diff --git a/libs/hwui/PixelBuffer.cpp b/libs/hwui/PixelBuffer.cpp
index 36e89c6..5b642b9 100644
--- a/libs/hwui/PixelBuffer.cpp
+++ b/libs/hwui/PixelBuffer.cpp
@@ -151,7 +151,7 @@ void GpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t hei
mCaches.bindPixelBuffer(mBuffer);
unmap();
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, mFormat,
- GL_UNSIGNED_BYTE, (void*) offset);
+ GL_UNSIGNED_BYTE, reinterpret_cast<void*>(offset));
}
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp
index 3f77021..d276a29 100644
--- a/libs/hwui/ResourceCache.cpp
+++ b/libs/hwui/ResourceCache.cpp
@@ -213,8 +213,9 @@ void ResourceCache::destructorLocked(SkPath* resource) {
// If we're not tracking this resource, just delete it
if (Caches::hasInstance()) {
Caches::getInstance().pathCache.removeDeferred(resource);
+ } else {
+ delete resource;
}
- delete resource;
return;
}
ref->destroyed = true;
@@ -235,8 +236,9 @@ void ResourceCache::destructorLocked(SkBitmap* resource) {
// If we're not tracking this resource, just delete it
if (Caches::hasInstance()) {
Caches::getInstance().textureCache.removeDeferred(resource);
+ } else {
+ delete resource;
}
- delete resource;
return;
}
ref->destroyed = true;
@@ -292,13 +294,14 @@ void ResourceCache::destructorLocked(Res_png_9patch* resource) {
ssize_t index = mCache->indexOfKey(resource);
ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL;
if (ref == NULL) {
+ // If we're not tracking this resource, just delete it
if (Caches::hasInstance()) {
Caches::getInstance().patchCache.removeDeferred(resource);
+ } else {
+ // A Res_png_9patch is actually an array of byte that's larger
+ // than sizeof(Res_png_9patch). It must be freed as an array.
+ delete[] (int8_t*) resource;
}
- // If we're not tracking this resource, just delete it
- // A Res_png_9patch is actually an array of byte that's larger
- // than sizeof(Res_png_9patch). It must be freed as an array.
- delete[] (int8_t*) resource;
return;
}
ref->destroyed = true;
@@ -355,16 +358,18 @@ void ResourceCache::deleteResourceReferenceLocked(void* resource, ResourceRefere
SkBitmap* bitmap = (SkBitmap*) resource;
if (Caches::hasInstance()) {
Caches::getInstance().textureCache.removeDeferred(bitmap);
+ } else {
+ delete bitmap;
}
- delete bitmap;
}
break;
case kPath: {
SkPath* path = (SkPath*) resource;
if (Caches::hasInstance()) {
Caches::getInstance().pathCache.removeDeferred(path);
+ } else {
+ delete path;
}
- delete path;
}
break;
case kShader: {
@@ -380,11 +385,12 @@ void ResourceCache::deleteResourceReferenceLocked(void* resource, ResourceRefere
case kNinePatch: {
if (Caches::hasInstance()) {
Caches::getInstance().patchCache.removeDeferred((Res_png_9patch*) resource);
+ } else {
+ // A Res_png_9patch is actually an array of byte that's larger
+ // than sizeof(Res_png_9patch). It must be freed as an array.
+ int8_t* patch = (int8_t*) resource;
+ delete[] patch;
}
- // A Res_png_9patch is actually an array of byte that's larger
- // than sizeof(Res_png_9patch). It must be freed as an array.
- int8_t* patch = (int8_t*) resource;
- delete[] patch;
}
break;
case kLayer: {
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index ed0a79a..54a206b 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -184,7 +184,9 @@ void TextureCache::clearGarbage() {
Mutex::Autolock _l(mLock);
size_t count = mGarbage.size();
for (size_t i = 0; i < count; i++) {
- mCache.remove(mGarbage.itemAt(i));
+ const SkBitmap* bitmap = mGarbage.itemAt(i);
+ mCache.remove(bitmap);
+ delete bitmap;
}
mGarbage.clear();
}
diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp
index 18983d8..8f5beb8 100644
--- a/libs/hwui/font/Font.cpp
+++ b/libs/hwui/font/Font.cpp
@@ -404,10 +404,10 @@ void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len
// If it's still not valid, we couldn't cache it, so we shouldn't
// draw garbage; also skip empty glyphs (spaces)
if (cachedGlyph->mIsValid && cachedGlyph->mCacheTexture) {
- float penX = x + positions[(glyphsCount << 1)];
- float penY = y + positions[(glyphsCount << 1) + 1];
+ int penX = x + (int) roundf(positions[(glyphsCount << 1)]);
+ int penY = y + (int) roundf(positions[(glyphsCount << 1) + 1]);
- (*this.*render)(cachedGlyph, roundf(penX), roundf(penY),
+ (*this.*render)(cachedGlyph, penX, penY,
bitmap, bitmapW, bitmapH, bounds, positions);
}
diff --git a/libs/hwui/utils/TinyHashMap.h b/libs/hwui/utils/TinyHashMap.h
index 8855140..4ff9a42 100644
--- a/libs/hwui/utils/TinyHashMap.h
+++ b/libs/hwui/utils/TinyHashMap.h
@@ -24,8 +24,6 @@ namespace uirenderer {
/**
* A very simple hash map that doesn't allow duplicate keys, overwriting the older entry.
- *
- * Currently, expects simple keys that are handled by hash_t()
*/
template <typename TKey, typename TValue>
class TinyHashMap {
@@ -36,7 +34,7 @@ public:
* Puts an entry in the hash, removing any existing entry with the same key
*/
void put(TKey key, TValue value) {
- hash_t hash = hash_t(key);
+ hash_t hash = android::hash_type(key);
ssize_t index = mTable.find(-1, hash, key);
if (index != -1) {
@@ -51,7 +49,7 @@ public:
* Return true if key is in the map, in which case stores the value in the output ref
*/
bool get(TKey key, TValue& outValue) {
- hash_t hash = hash_t(key);
+ hash_t hash = android::hash_type(key);
ssize_t index = mTable.find(-1, hash, key);
if (index == -1) {
return false;