diff options
Diffstat (limited to 'libs/utils/AssetManager.cpp')
-rw-r--r-- | libs/utils/AssetManager.cpp | 237 |
1 files changed, 234 insertions, 3 deletions
diff --git a/libs/utils/AssetManager.cpp b/libs/utils/AssetManager.cpp index e09e755..13004cd 100644 --- a/libs/utils/AssetManager.cpp +++ b/libs/utils/AssetManager.cpp @@ -36,6 +36,19 @@ #include <dirent.h> #include <errno.h> #include <assert.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <unistd.h> + +#ifndef TEMP_FAILURE_RETRY +/* Used to retry syscalls that can return EINTR. */ +#define TEMP_FAILURE_RETRY(exp) ({ \ + typeof (exp) _rc; \ + do { \ + _rc = (exp); \ + } while (_rc == -1 && errno == EINTR); \ + _rc; }) +#endif using namespace android; @@ -48,6 +61,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* kExcludeExtension = ".EXCLUDE"; @@ -55,6 +69,35 @@ static Asset* const kExcludedAsset = (Asset*) 0xd000000d; static volatile int32_t gCount = 0; +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); + + char buf[256]; // 256 chars should be enough for anyone... + strncpy(buf, pkgPath.string(), 255); + buf[255] = '\0'; + char* filename = buf; + while (*filename && *filename == '/') { + ++filename; + } + char* p = filename; + while (*p) { + if (*p == '/') { + *p = '@'; + } + ++p; + } + path.appendPath(filename); + path.append("@idmap"); + + return path; + } +} /* * =========================================================================== @@ -122,7 +165,7 @@ bool AssetManager::addAssetPath(const String8& path, void** cookie) return true; } } - + LOGV("In %p Asset %s path: %s", this, ap.type == kFileTypeDirectory ? "dir" : "zip", ap.path.string()); @@ -133,9 +176,181 @@ bool AssetManager::addAssetPath(const String8& path, void** cookie) *cookie = (void*)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 { + LOGW("failed to add overlay package %s\n", overlayPath.string()); + } + } + } + + return true; +} + +bool AssetManager::isIdmapStaleLocked(const String8& originalPath, const String8& overlayPath, + const String8& idmapPath) +{ + struct stat st; + if (TEMP_FAILURE_RETRY(stat(idmapPath.string(), &st)) == -1) { + if (errno == ENOENT) { + return true; // non-existing idmap is always stale + } else { + LOGW("failed to stat file %s: %s\n", idmapPath.string(), strerror(errno)); + return false; + } + } + if (st.st_size < ResTable::IDMAP_HEADER_SIZE_BYTES) { + LOGW("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) { + LOGW("failed to open file %s: %s\n", idmapPath.string(), strerror(errno)); + 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)) { + return false; + } + + uint32_t actualOriginalCrc, actualOverlayCrc; + if (!getZipEntryCrcLocked(originalPath, "resources.arsc", &actualOriginalCrc)) { + return false; + } + if (!getZipEntryCrcLocked(overlayPath, "resources.arsc", &actualOverlayCrc)) { + 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) { + return false; + } + const ZipEntryRO entry = zip->findEntryByName(entryFilename); + if (entry == NULL) { + return false; + } + if (!zip->getEntryInfo(entry, NULL, NULL, NULL, NULL, NULL, (long*)pCrc)) { + return false; + } return true; } +bool AssetManager::createIdmapFileLocked(const String8& originalPath, const String8& overlayPath, + const String8& idmapPath) +{ + LOGD("%s: originalPath=%s overlayPath=%s idmapPath=%s\n", + __FUNCTION__, originalPath.string(), overlayPath.string(), idmapPath.string()); + 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]; + Asset* ass = openNonAssetInPathLocked("resources.arsc", Asset::ACCESS_BUFFER, ap); + if (ass == NULL) { + LOGW("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)) { + LOGW("failed to retrieve crc for resources.arsc in %s\n", originalPath.string()); + goto error; + } + if (!getZipEntryCrcLocked(overlayPath, "resources.arsc", &overlayCrc)) { + LOGW("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) { + LOGW("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) { + LOGW("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) { + LOGW("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; + } + } + + retval = true; +error_close: + TEMP_FAILURE_RETRY(close(fd)); +error_free: + free(data); +error: + return retval; +} + bool AssetManager::addDefaultAssets() { const char* root = getenv("ANDROID_ROOT"); @@ -404,6 +619,7 @@ const ResTable* AssetManager::getResTable(bool required) const ResTable* sharedRes = NULL; bool shared = true; const asset_path& ap = mAssetPaths.itemAt(i); + Asset* idmap = openIdmapLocked(ap); LOGV("Looking for resource asset in '%s'\n", ap.path.string()); if (ap.type != kFileTypeDirectory) { if (i == 0) { @@ -433,7 +649,7 @@ const ResTable* AssetManager::getResTable(bool required) const // can quickly copy it out for others. LOGV("Creating shared resources for %s", ap.path.string()); sharedRes = new ResTable(); - sharedRes->add(ass, (void*)(i+1), false); + sharedRes->add(ass, (void*)(i+1), false, idmap); sharedRes = const_cast<AssetManager*>(this)-> mZipSet.setZipResourceTable(ap.path, sharedRes); } @@ -457,7 +673,7 @@ const ResTable* AssetManager::getResTable(bool required) const rt->add(sharedRes); } else { LOGV("Parsing resources for %s", ap.path.string()); - rt->add(ass, (void*)(i+1), !shared); + rt->add(ass, (void*)(i+1), !shared, idmap); } if (!shared) { @@ -498,6 +714,21 @@ void AssetManager::updateResourceParamsLocked() const res->setParameters(mConfig); } +Asset* AssetManager::openIdmapLocked(const struct asset_path& ap) const +{ + Asset* ass = NULL; + if (ap.idmap.size() != 0) { + ass = const_cast<AssetManager*>(this)-> + openAssetFromFileLocked(ap.idmap, Asset::ACCESS_BUFFER); + if (ass) { + LOGV("loading idmap %s\n", ap.idmap.string()); + } else { + LOGW("failed to load idmap %s\n", ap.idmap.string()); + } + } + return ass; +} + const ResTable& AssetManager::getResources(bool required) const { const ResTable* rt = getResTable(required); |