diff options
Diffstat (limited to 'libs')
86 files changed, 4518 insertions, 2154 deletions
diff --git a/libs/androidfw/Android.mk b/libs/androidfw/Android.mk index d21197e..957809d 100644 --- a/libs/androidfw/Android.mk +++ b/libs/androidfw/Android.mk @@ -25,6 +25,7 @@ commonSources := \ ObbFile.cpp \ ResourceTypes.cpp \ StreamingZipInflater.cpp \ + TypeWrappers.cpp \ ZipFileRO.cpp \ ZipUtils.cpp diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp index 91dda75..0340928 100644 --- a/libs/androidfw/AssetManager.cpp +++ b/libs/androidfw/AssetManager.cpp @@ -256,7 +256,7 @@ bool AssetManager::addOverlayPath(const String8& packagePath, int32_t* cookie) String8 targetPath; String8 overlayPath; if (!ResTable::getIdmapInfo(idmap->getBuffer(false), idmap->getLength(), - NULL, NULL, &targetPath, &overlayPath)) { + NULL, NULL, NULL, &targetPath, &overlayPath)) { ALOGW("failed to read idmap file %s\n", idmapPath.string()); delete idmap; return false; @@ -311,7 +311,7 @@ bool AssetManager::createIdmap(const char* targetApkPath, const char* overlayApk ALOGW("failed to find resources.arsc in %s\n", ap.path.string()); return false; } - tables[i].add(ass, 1, false /* copyData */, NULL /* idMap */); + tables[i].add(ass); } return tables[0].createIdmap(tables[1], targetCrc, overlayCrc, @@ -617,7 +617,7 @@ 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, i + 1, false, idmap); + sharedRes->add(ass, idmap, i + 1, false); #ifdef HAVE_ANDROID_OS const char* data = getenv("ANDROID_DATA"); LOG_ALWAYS_FATAL_IF(data == NULL, "ANDROID_DATA not set"); @@ -646,7 +646,7 @@ const ResTable* AssetManager::getResTable(bool required) const mResources->add(sharedRes); } else { ALOGV("Parsing resources for %s", ap.path.string()); - mResources->add(ass, i + 1, !shared, idmap); + mResources->add(ass, idmap, i + 1, !shared); } onlyEmptyResources = false; @@ -654,7 +654,7 @@ const ResTable* AssetManager::getResTable(bool required) const delete ass; } } else { - ALOGW("Installing empty resources in to table %p\n", mResources); + ALOGV("Installing empty resources in to table %p\n", mResources); mResources->addEmpty(i + 1); } @@ -736,7 +736,7 @@ void AssetManager::addSystemOverlays(const char* pathOverlaysList, if (oass != NULL) { Asset* oidmap = openIdmapLocked(oap); offset++; - sharedRes->add(oass, offset + 1, false, oidmap); + sharedRes->add(oass, oidmap, offset + 1, false); const_cast<AssetManager*>(this)->mAssetPaths.add(oap); const_cast<AssetManager*>(this)->mZipSet.addOverlay(targetPackagePath, oap); } @@ -1768,6 +1768,7 @@ bool AssetManager::fncScanAndMergeDirLocked( } mergeInfoLocked(pMergedInfo, pContents); + delete pContents; return true; } diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index a4b78a6..2e3abb5 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -17,7 +17,9 @@ #define LOG_TAG "ResourceType" //#define LOG_NDEBUG 0 +#include <androidfw/ByteBucketArray.h> #include <androidfw/ResourceTypes.h> +#include <androidfw/TypeWrappers.h> #include <utils/Atomic.h> #include <utils/ByteOrder.h> #include <utils/Debug.h> @@ -30,6 +32,7 @@ #include <memory.h> #include <ctype.h> #include <stdint.h> +#include <stddef.h> #ifndef INT32_MAX #define INT32_MAX ((int32_t)(2147483647)) @@ -42,7 +45,7 @@ #define TABLE_SUPER_NOISY(x) //x #define LOAD_TABLE_NOISY(x) //x #define TABLE_THEME(x) //x -#define LIB_NOISY(x) x +#define LIB_NOISY(x) //x namespace android { @@ -63,9 +66,8 @@ namespace android { #endif #endif -#define IDMAP_MAGIC 0x706d6469 -// size measured in sizeof(uint32_t) -#define IDMAP_HEADER_SIZE (ResTable::IDMAP_HEADER_SIZE_BYTES / sizeof(uint32_t)) +#define IDMAP_MAGIC 0x504D4449 +#define IDMAP_CURRENT_VERSION 0x00000001 #define APP_PACKAGE_ID 0x7f #define SYS_PACKAGE_ID 0x01 @@ -77,6 +79,11 @@ inline int isspace16(char16_t c) { return (c < 0x0080 && isspace(c)); } +template<typename T> +inline static T max(T a, T b) { + return a > b ? a : b; +} + // range checked; guaranteed to NUL-terminate within the stated number of available slots // NOTE: if this truncates the dst string due to running out of space, no attempt is // made to avoid splitting surrogate pairs. @@ -215,104 +222,179 @@ void Res_png_9patch::serialize(const Res_png_9patch& patch, const int32_t* xDivs fill9patchOffsets(reinterpret_cast<Res_png_9patch*>(outData)); } -static bool assertIdmapHeader(const uint32_t* map, size_t sizeBytes) -{ - if (sizeBytes < ResTable::IDMAP_HEADER_SIZE_BYTES) { - ALOGW("idmap assertion failed: size=%d bytes\n", (int)sizeBytes); +static bool assertIdmapHeader(const void* idmap, size_t size) { + if (reinterpret_cast<uintptr_t>(idmap) & 0x03) { + ALOGE("idmap: header is not word aligned"); + return false; + } + + if (size < ResTable::IDMAP_HEADER_SIZE_BYTES) { + ALOGW("idmap: header too small (%d bytes)", (uint32_t) size); return false; } - if (*map != htodl(IDMAP_MAGIC)) { // htodl: map data expected to be in correct endianess - ALOGW("idmap assertion failed: invalid magic found (is 0x%08x, expected 0x%08x)\n", - *map, htodl(IDMAP_MAGIC)); + + const uint32_t magic = htodl(*reinterpret_cast<const uint32_t*>(idmap)); + if (magic != IDMAP_MAGIC) { + ALOGW("idmap: no magic found in header (is 0x%08x, expected 0x%08x)", + magic, IDMAP_MAGIC); + return false; + } + + const uint32_t version = htodl(*(reinterpret_cast<const uint32_t*>(idmap) + 1)); + if (version != IDMAP_CURRENT_VERSION) { + // We are strict about versions because files with this format are + // auto-generated and don't need backwards compatibility. + ALOGW("idmap: version mismatch in header (is 0x%08x, expected 0x%08x)", + version, IDMAP_CURRENT_VERSION); return false; } return true; } -static status_t idmapLookup(const uint32_t* map, size_t sizeBytes, uint32_t key, uint32_t* outValue) -{ - // see README for details on the format of map - if (!assertIdmapHeader(map, sizeBytes)) { - return UNKNOWN_ERROR; - } - map = map + IDMAP_HEADER_SIZE; // skip ahead to data segment - // size of data block, in uint32_t - const size_t size = (sizeBytes - ResTable::IDMAP_HEADER_SIZE_BYTES) / sizeof(uint32_t); - const uint32_t type = Res_GETTYPE(key) + 1; // add one, idmap stores "public" type id - const uint32_t entry = Res_GETENTRY(key); - const uint32_t typeCount = *map; +class IdmapEntries { +public: + IdmapEntries() : mData(NULL) {} - if (type > typeCount) { - ALOGW("Resource ID map: type=%d exceeds number of types=%d\n", type, typeCount); - return UNKNOWN_ERROR; + bool hasEntries() const { + if (mData == NULL) { + return false; + } + + return (dtohs(*mData) > 0); } - if (typeCount > size) { - ALOGW("Resource ID map: number of types=%d exceeds size of map=%d\n", typeCount, (int)size); - return UNKNOWN_ERROR; + + size_t byteSize() const { + if (mData == NULL) { + return 0; + } + uint16_t entryCount = dtohs(mData[2]); + return (sizeof(uint16_t) * 4) + (sizeof(uint32_t) * static_cast<size_t>(entryCount)); } - const uint32_t typeOffset = map[type]; - if (typeOffset == 0) { - *outValue = 0; - return NO_ERROR; + + uint8_t targetTypeId() const { + if (mData == NULL) { + return 0; + } + return dtohs(mData[0]); } - if (typeOffset + 1 > size) { - ALOGW("Resource ID map: type offset=%d exceeds reasonable value, size of map=%d\n", - typeOffset, (int)size); - return UNKNOWN_ERROR; + + uint8_t overlayTypeId() const { + if (mData == NULL) { + return 0; + } + return dtohs(mData[1]); } - const uint32_t entryCount = map[typeOffset]; - const uint32_t entryOffset = map[typeOffset + 1]; - if (entryCount == 0 || entry < entryOffset || entry - entryOffset > entryCount - 1) { - *outValue = 0; + + status_t setTo(const void* entryHeader, size_t size) { + if (reinterpret_cast<uintptr_t>(entryHeader) & 0x03) { + ALOGE("idmap: entry header is not word aligned"); + return UNKNOWN_ERROR; + } + + if (size < sizeof(uint16_t) * 4) { + ALOGE("idmap: entry header is too small (%u bytes)", (uint32_t) size); + return UNKNOWN_ERROR; + } + + const uint16_t* header = reinterpret_cast<const uint16_t*>(entryHeader); + const uint16_t targetTypeId = dtohs(header[0]); + const uint16_t overlayTypeId = dtohs(header[1]); + if (targetTypeId == 0 || overlayTypeId == 0 || targetTypeId > 255 || overlayTypeId > 255) { + ALOGE("idmap: invalid type map (%u -> %u)", targetTypeId, overlayTypeId); + return UNKNOWN_ERROR; + } + + uint16_t entryCount = dtohs(header[2]); + if (size < sizeof(uint32_t) * (entryCount + 2)) { + ALOGE("idmap: too small (%u bytes) for the number of entries (%u)", + (uint32_t) size, (uint32_t) entryCount); + return UNKNOWN_ERROR; + } + mData = header; return NO_ERROR; } - const uint32_t index = typeOffset + 2 + entry - entryOffset; - if (index > size) { - ALOGW("Resource ID map: entry index=%u exceeds size of map=%d\n", index, (int)size); - *outValue = 0; + + status_t lookup(uint16_t entryId, uint16_t* outEntryId) const { + uint16_t entryCount = dtohs(mData[2]); + uint16_t offset = dtohs(mData[3]); + + if (entryId < offset) { + // The entry is not present in this idmap + return BAD_INDEX; + } + + entryId -= offset; + + if (entryId >= entryCount) { + // The entry is not present in this idmap + return BAD_INDEX; + } + + // It is safe to access the type here without checking the size because + // we have checked this when it was first loaded. + const uint32_t* entries = reinterpret_cast<const uint32_t*>(mData) + 2; + uint32_t mappedEntry = dtohl(entries[entryId]); + if (mappedEntry == 0xffffffff) { + // This entry is not present in this idmap + return BAD_INDEX; + } + *outEntryId = static_cast<uint16_t>(mappedEntry); return NO_ERROR; } - *outValue = map[index]; - return NO_ERROR; -} +private: + const uint16_t* mData; +}; -static status_t getIdmapPackageId(const uint32_t* map, size_t mapSize, uint32_t *outId) -{ - if (!assertIdmapHeader(map, mapSize)) { +status_t parseIdmap(const void* idmap, size_t size, uint8_t* outPackageId, KeyedVector<uint8_t, IdmapEntries>* outMap) { + if (!assertIdmapHeader(idmap, size)) { return UNKNOWN_ERROR; } - if (mapSize <= IDMAP_HEADER_SIZE + 1) { - ALOGW("corrupt idmap: map size %d too short\n", (int)mapSize); + + size -= ResTable::IDMAP_HEADER_SIZE_BYTES; + if (size < sizeof(uint16_t) * 2) { + ALOGE("idmap: too small to contain any mapping"); return UNKNOWN_ERROR; } - uint32_t typeCount = *(map + IDMAP_HEADER_SIZE); - if (typeCount == 0) { - ALOGW("corrupt idmap: no types\n"); + + const uint16_t* data = reinterpret_cast<const uint16_t*>( + reinterpret_cast<const uint8_t*>(idmap) + ResTable::IDMAP_HEADER_SIZE_BYTES); + + uint16_t targetPackageId = dtohs(*(data++)); + if (targetPackageId == 0 || targetPackageId > 255) { + ALOGE("idmap: target package ID is invalid (%02x)", targetPackageId); return UNKNOWN_ERROR; } - if (IDMAP_HEADER_SIZE + 1 + typeCount > mapSize) { - ALOGW("corrupt idmap: number of types %u extends past idmap size %d\n", typeCount, (int)mapSize); + + uint16_t mapCount = dtohs(*(data++)); + if (mapCount == 0) { + ALOGE("idmap: no mappings"); 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; - } + + if (mapCount > 255) { + ALOGW("idmap: too many mappings. Only 255 are possible but %u are present", (uint32_t) mapCount); } - // 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 %u points outside map size %d\n", offset, (int)mapSize); - return UNKNOWN_ERROR; + while (size > sizeof(uint16_t) * 4) { + IdmapEntries entries; + status_t err = entries.setTo(data, size); + if (err != NO_ERROR) { + return err; + } + + ssize_t index = outMap->add(entries.overlayTypeId(), entries); + if (index < 0) { + return NO_MEMORY; + } + + data += entries.byteSize() / sizeof(uint16_t); + size -= entries.byteSize(); } - *outId = (map[offset] >> 24) & 0x000000ff; + if (outPackageId != NULL) { + *outPackageId = static_cast<uint8_t>(targetPackageId); + } return NO_ERROR; } @@ -2726,7 +2808,7 @@ struct ResTable::Header free(resourceIDMap); } - ResTable* const owner; + const ResTable* const owner; void* ownedData; const ResTable_header* header; size_t size; @@ -2739,6 +2821,17 @@ struct ResTable::Header size_t resourceIDMapSize; }; +struct ResTable::Entry { + ResTable_config config; + const ResTable_entry* entry; + const ResTable_type* type; + uint32_t specFlags; + const Package* package; + + StringPoolRef typeStr; + StringPoolRef keyStr; +}; + struct ResTable::Type { Type(const Header* _header, const Package* _package, size_t count) @@ -2749,33 +2842,29 @@ struct ResTable::Type const size_t entryCount; const ResTable_typeSpec* typeSpec; const uint32_t* typeSpecFlags; + IdmapEntries idmapEntries; Vector<const ResTable_type*> configs; }; struct ResTable::Package { Package(ResTable* _owner, const Header* _header, const ResTable_package* _package) - : owner(_owner), header(_header), package(_package) { } - ~Package() - { - size_t i = types.size(); - while (i > 0) { - i--; - delete types[i]; + : owner(_owner), header(_header), package(_package), typeIdOffset(0) { + if (dtohs(package->header.headerSize) == sizeof(package)) { + // The package structure is the same size as the definition. + // This means it contains the typeIdOffset field. + typeIdOffset = package->typeIdOffset; } } - ResTable* const owner; + const ResTable* const owner; const Header* const header; const ResTable_package* const package; - Vector<Type*> types; ResStringPool typeStrings; ResStringPool keyStrings; - const Type* getType(size_t idx) const { - return idx < types.size() ? types[idx] : NULL; - } + size_t typeIdOffset; }; // A group of objects describing a particular resource package. @@ -2787,13 +2876,24 @@ struct ResTable::PackageGroup : owner(_owner) , name(_name) , id(_id) - , typeCount(0) + , largestTypeId(0) , bags(NULL) , dynamicRefTable(static_cast<uint8_t>(_id)) { } ~PackageGroup() { clearBagCache(); + const size_t numTypes = types.size(); + for (size_t i = 0; i < numTypes; i++) { + const TypeList& typeList = types[i]; + const size_t numInnerTypes = typeList.size(); + for (size_t j = 0; j < numInnerTypes; j++) { + if (typeList[j]->package->owner == owner) { + delete typeList[j]; + } + } + } + const size_t N = packages.size(); for (size_t i=0; i<N; i++) { Package* pkg = packages[i]; @@ -2806,17 +2906,15 @@ struct ResTable::PackageGroup void clearBagCache() { if (bags) { TABLE_NOISY(printf("bags=%p\n", bags)); - Package* pkg = packages[0]; - TABLE_NOISY(printf("typeCount=%x\n", typeCount)); - for (size_t i=0; i<typeCount; i++) { + for (size_t i = 0; i < bags->size(); i++) { TABLE_NOISY(printf("type=%d\n", i)); - const Type* type = pkg->getType(i); - if (type != NULL) { - bag_set** typeBags = bags[i]; + const TypeList& typeList = types[i]; + if (typeList.isEmpty()) { + bag_set** typeBags = bags->get(i); TABLE_NOISY(printf("typeBags=%p\n", typeBags)); if (typeBags) { - TABLE_NOISY(printf("type->entryCount=%x\n", type->entryCount)); - const size_t N = type->entryCount; + const size_t N = typeList[0]->entryCount; + TABLE_NOISY(printf("type->entryCount=%x\n", N)); for (size_t j=0; j<N; j++) { if (typeBags[j] && typeBags[j] != (bag_set*)0xFFFFFFFF) free(typeBags[j]); @@ -2825,25 +2923,38 @@ struct ResTable::PackageGroup } } } - free(bags); + delete bags; bags = NULL; } } - ResTable* const owner; + ssize_t findType16(const char16_t* type, size_t len) const { + const size_t N = packages.size(); + for (size_t i = 0; i < N; i++) { + ssize_t index = packages[i]->typeStrings.indexOfString(type, len); + if (index >= 0) { + return index + packages[i]->typeIdOffset; + } + } + return -1; + } + + const ResTable* const owner; String16 const name; uint32_t const id; + + // This is mainly used to keep track of the loaded packages + // and to clean them up properly. Accessing resources happens from + // the 'types' array. Vector<Package*> packages; - // This is for finding typeStrings and other common package stuff. - Package* basePackage; + ByteBucketArray<TypeList> types; - // For quick access. - size_t typeCount; + uint8_t largestTypeId; // Computed attribute bags, first indexed by the type and second // by the entry in that type. - bag_set*** bags; + ByteBucketArray<bag_set**>* bags; // The table mapping dynamic references to resolved references for // this package group. @@ -2879,7 +2990,7 @@ ResTable::Theme::~Theme() void ResTable::Theme::free_package(package_info* pi) { - for (size_t j=0; j<pi->numTypes; j++) { + for (size_t j = 0; j <= Res_MAXTYPE; j++) { theme_entry* te = pi->types[j].entries; if (te != NULL) { free(te); @@ -2890,10 +3001,8 @@ void ResTable::Theme::free_package(package_info* pi) ResTable::Theme::package_info* ResTable::Theme::copy_package(package_info* pi) { - package_info* newpi = (package_info*)malloc( - sizeof(package_info) + (pi->numTypes*sizeof(type_info))); - newpi->numTypes = pi->numTypes; - for (size_t j=0; j<newpi->numTypes; j++) { + package_info* newpi = (package_info*)malloc(sizeof(package_info)); + for (size_t j = 0; j <= Res_MAXTYPE; j++) { size_t cnt = pi->types[j].numEntries; newpi->types[j].numEntries = cnt; theme_entry* te = pi->types[j].entries; @@ -2946,17 +3055,14 @@ status_t ResTable::Theme::applyStyle(uint32_t resID, bool force) curPI = mPackages[pidx]; if (curPI == NULL) { PackageGroup* const grp = mTable.mPackageGroups[pidx]; - int cnt = grp->typeCount; - curPI = (package_info*)malloc( - sizeof(package_info) + (cnt*sizeof(type_info))); - curPI->numTypes = cnt; - memset(curPI->types, 0, cnt*sizeof(type_info)); + curPI = (package_info*)malloc(sizeof(package_info)); + memset(curPI, 0, sizeof(*curPI)); mPackages[pidx] = curPI; } curType = 0xffffffff; } if (curType != t) { - if (t >= curPI->numTypes) { + if (t > Res_MAXTYPE) { ALOGE("Style contains key with bad type: 0x%08x\n", attrRes); bag++; continue; @@ -2965,8 +3071,8 @@ status_t ResTable::Theme::applyStyle(uint32_t resID, bool force) curEntries = curPI->types[t].entries; if (curEntries == NULL) { PackageGroup* const grp = mTable.mPackageGroups[curPackageIndex]; - const Type* type = grp->packages[0]->getType(t); - int cnt = type != NULL ? type->entryCount : 0; + const TypeList& typeList = grp->types[t]; + int cnt = typeList.isEmpty() ? 0 : typeList[0]->entryCount; curEntries = (theme_entry*)malloc(cnt*sizeof(theme_entry)); memset(curEntries, Res_value::TYPE_NULL, cnt*sizeof(theme_entry)); curPI->types[t].numEntries = cnt; @@ -2981,8 +3087,8 @@ status_t ResTable::Theme::applyStyle(uint32_t resID, bool force) } theme_entry* curEntry = curEntries + e; TABLE_NOISY(ALOGV("Attr 0x%08x: type=0x%x, data=0x%08x; curType=0x%x", - attrRes, bag->map.value.dataType, bag->map.value.data, - curEntry->value.dataType)); + attrRes, bag->map.value.dataType, bag->map.value.data, + curEntry->value.dataType)); if (force || curEntry->value.dataType == Res_value::TYPE_NULL) { curEntry->stringBlock = bag->stringBlock; curEntry->typeSpecFlags |= bagTypeSpecFlags; @@ -3057,8 +3163,8 @@ ssize_t ResTable::Theme::getAttribute(uint32_t resID, Res_value* outValue, const package_info* const pi = mPackages[p]; TABLE_THEME(ALOGI("Found package: %p", pi)); if (pi != NULL) { - TABLE_THEME(ALOGI("Desired type index is %ld in avail %d", t, pi->numTypes)); - if (t < pi->numTypes) { + TABLE_THEME(ALOGI("Desired type index is %ld in avail %d", t, Res_MAXTYPE + 1)); + if (t <= Res_MAXTYPE) { const type_info& ti = pi->types[t]; TABLE_THEME(ALOGI("Desired entry index is %ld in avail %d", e, ti.numEntries)); if (e < ti.numEntries) { @@ -3120,14 +3226,13 @@ void ResTable::Theme::dumpToLog() const package_info* pi = mPackages[i]; if (pi == NULL) continue; - ALOGI(" Package #0x%02x:\n", (int)(i+1)); - for (size_t j=0; j<pi->numTypes; j++) { + ALOGI(" Package #0x%02x:\n", (int)(i + 1)); + for (size_t j = 0; j <= Res_MAXTYPE; j++) { type_info& ti = pi->types[j]; if (ti.numEntries == 0) continue; - - ALOGI(" Type #0x%02x:\n", (int)(j+1)); - for (size_t k=0; k<ti.numEntries; k++) { - theme_entry& te = ti.entries[k]; + ALOGI(" Type #0x%02x:\n", (int)(j + 1)); + for (size_t k = 0; k < ti.numEntries; k++) { + const theme_entry& te = ti.entries[k]; if (te.value.dataType == Res_value::TYPE_NULL) continue; ALOGI(" 0x%08x: t=0x%x, d=0x%08x (block=%d)\n", (int)Res_MAKEID(i, j, k), @@ -3150,7 +3255,7 @@ ResTable::ResTable(const void* data, size_t size, const int32_t cookie, bool cop { memset(&mParams, 0, sizeof(mParams)); memset(mPackageMap, 0, sizeof(mPackageMap)); - addInternal(data, size, cookie, copyData, NULL /* idMap */); + addInternal(data, size, NULL, 0, cookie, copyData); LOG_FATAL_IF(mError != NO_ERROR, "Error parsing resource table"); //ALOGI("Creating ResTable %p\n", this); } @@ -3166,21 +3271,45 @@ 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) { - return addInternal(data, size, 0 /* cookie */, - false /* copyData */, NULL /* idMap */); +status_t ResTable::add(const void* data, size_t size, const int32_t cookie, bool copyData) { + return addInternal(data, size, NULL, 0, cookie, copyData); } -status_t ResTable::add(Asset* asset, const int32_t cookie, bool copyData, const void* idmap) -{ +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); +} + +status_t ResTable::add(Asset* asset, const int32_t cookie, bool copyData) { const void* data = asset->getBuffer(true); if (data == NULL) { ALOGW("Unable to get buffer of resource asset file"); return UNKNOWN_ERROR; } - size_t size = (size_t)asset->getLength(); - return addInternal(data, size, cookie, copyData, - reinterpret_cast<const Asset*>(idmap)); + + return addInternal(data, static_cast<size_t>(asset->getLength()), NULL, 0, cookie, copyData); +} + +status_t ResTable::add(Asset* asset, Asset* idmapAsset, const int32_t cookie, bool copyData) { + const void* data = asset->getBuffer(true); + if (data == NULL) { + ALOGW("Unable to get buffer of resource asset file"); + return UNKNOWN_ERROR; + } + + size_t idmapSize = 0; + const void* idmapData = NULL; + if (idmapAsset != NULL) { + idmapData = idmapAsset->getBuffer(true); + if (idmapData == NULL) { + ALOGW("Unable to get buffer of idmap asset file"); + return UNKNOWN_ERROR; + } + idmapSize = static_cast<size_t>(idmapAsset->getLength()); + } + + return addInternal(data, static_cast<size_t>(asset->getLength()), + idmapData, idmapSize, cookie, copyData); } status_t ResTable::add(ResTable* src) @@ -3197,8 +3326,16 @@ status_t ResTable::add(ResTable* src) for (size_t j=0; j<srcPg->packages.size(); j++) { pg->packages.add(srcPg->packages[j]); } - pg->basePackage = srcPg->basePackage; - pg->typeCount = srcPg->typeCount; + + for (size_t j = 0; j < srcPg->types.size(); j++) { + if (srcPg->types[j].isEmpty()) { + continue; + } + + TypeList& typeList = pg->types.editItemAt(j); + typeList.appendVector(srcPg->types[j]); + } + pg->largestTypeId = max(pg->largestTypeId, srcPg->largestTypeId); mPackageGroups.add(pg); } @@ -3224,38 +3361,39 @@ status_t ResTable::addEmpty(const int32_t cookie) { return (mError=NO_ERROR); } -status_t ResTable::addInternal(const void* data, size_t size, const int32_t cookie, - bool copyData, const Asset* idmap) +status_t ResTable::addInternal(const void* data, size_t dataSize, const void* idmapData, size_t idmapDataSize, + const int32_t cookie, bool copyData) { - if (!data) return NO_ERROR; + if (!data) { + return NO_ERROR; + } + Header* header = new Header(this); header->index = mHeaders.size(); header->cookie = cookie; - if (idmap != NULL) { - const size_t idmap_size = idmap->getLength(); - const void* idmap_data = const_cast<Asset*>(idmap)->getBuffer(true); - header->resourceIDMap = (uint32_t*)malloc(idmap_size); + if (idmapData != NULL) { + header->resourceIDMap = (uint32_t*) malloc(idmapDataSize); if (header->resourceIDMap == NULL) { delete header; return (mError = NO_MEMORY); } - memcpy((void*)header->resourceIDMap, idmap_data, idmap_size); - header->resourceIDMapSize = idmap_size; + memcpy(header->resourceIDMap, idmapData, idmapDataSize); + header->resourceIDMapSize = idmapDataSize; } mHeaders.add(header); const bool notDeviceEndian = htods(0xf0) != 0xf0; LOAD_TABLE_NOISY( - 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)); + ALOGV("Adding resources to ResTable: data=%p, size=0x%x, cookie=%d, copy=%d " + "idmap=%p\n", data, dataSize, cookie, copyData, idmap)); if (copyData || notDeviceEndian) { - header->ownedData = malloc(size); + header->ownedData = malloc(dataSize); if (header->ownedData == NULL) { return (mError=NO_MEMORY); } - memcpy(header->ownedData, data, size); + memcpy(header->ownedData, data, dataSize); data = header->ownedData; } @@ -3265,10 +3403,10 @@ status_t ResTable::addInternal(const void* data, size_t size, const int32_t cook // dtohl(header->header->header.size), header->header->header.size); LOAD_TABLE_NOISY(ALOGV("Loading ResTable @%p:\n", header->header)); if (dtohs(header->header->header.headerSize) > header->size - || header->size > size) { + || header->size > dataSize) { ALOGW("Bad resource table: header size 0x%x or total size 0x%x is larger than data size 0x%x\n", (int)dtohs(header->header->header.headerSize), - (int)header->size, (int)size); + (int)header->size, (int)dataSize); return (mError=BAD_TYPE); } if (((dtohs(header->header->header.headerSize)|header->size)&0x3) != 0) { @@ -3313,16 +3451,8 @@ status_t ResTable::addInternal(const void* data, size_t size, const int32_t cook dtohl(header->header->packageCount)); return (mError=BAD_TYPE); } - uint32_t idmap_id = 0; - if (idmap != NULL) { - uint32_t tmp; - if (getIdmapPackageId(header->resourceIDMap, - header->resourceIDMapSize, - &tmp) == NO_ERROR) { - idmap_id = tmp; - } - } - if (parsePackage((ResTable_package*)chunk, header, idmap_id) != NO_ERROR) { + + if (parsePackage((ResTable_package*)chunk, header) != NO_ERROR) { return mError; } curPackage++; @@ -3405,46 +3535,38 @@ bool ResTable::getResourceName(uint32_t resID, bool allowUtf8, resource_name* ou ALOGW("Bad identifier when getting name for resource number 0x%08x", resID); return false; } - if (grp->packages.size() > 0) { - const Package* const package = grp->packages[0]; - const ResTable_type* type; - const ResTable_entry* entry; - ssize_t offset = getEntry(package, t, e, NULL, &type, &entry, NULL); - if (offset <= 0) { - return false; - } + Entry entry; + status_t err = getEntry(grp, t, e, NULL, &entry); + if (err != NO_ERROR) { + return false; + } - outName->package = grp->name.string(); - outName->packageLen = grp->name.size(); - if (allowUtf8) { - outName->type8 = grp->basePackage->typeStrings.string8At(t, &outName->typeLen); - outName->name8 = grp->basePackage->keyStrings.string8At( - dtohl(entry->key.index), &outName->nameLen); - } else { - outName->type8 = NULL; - outName->name8 = NULL; - } - if (outName->type8 == NULL) { - outName->type = grp->basePackage->typeStrings.stringAt(t, &outName->typeLen); - // If we have a bad index for some reason, we should abort. - if (outName->type == NULL) { - return false; - } + outName->package = grp->name.string(); + outName->packageLen = grp->name.size(); + if (allowUtf8) { + outName->type8 = entry.typeStr.string8(&outName->typeLen); + outName->name8 = entry.keyStr.string8(&outName->nameLen); + } else { + outName->type8 = NULL; + outName->name8 = NULL; + } + if (outName->type8 == NULL) { + outName->type = entry.typeStr.string16(&outName->typeLen); + // If we have a bad index for some reason, we should abort. + if (outName->type == NULL) { + return false; } - if (outName->name8 == NULL) { - outName->name = grp->basePackage->keyStrings.stringAt( - dtohl(entry->key.index), &outName->nameLen); - // If we have a bad index for some reason, we should abort. - if (outName->name == NULL) { - return false; - } + } + if (outName->name8 == NULL) { + outName->name = entry.keyStr.string16(&outName->nameLen); + // If we have a bad index for some reason, we should abort. + if (outName->name == NULL) { + return false; } - - return true; } - return false; + return true; } ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag, uint16_t density, @@ -3471,15 +3593,6 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag return BAD_INDEX; } - const Res_value* bestValue = NULL; - const Package* bestPackage = NULL; - ResTable_config bestItem; - memset(&bestItem, 0, sizeof(bestItem)); // make the compiler shut up - - if (outSpecFlags != NULL) *outSpecFlags = 0; - - // Look through all resource packages, starting with the most - // recently added. const PackageGroup* const grp = mPackageGroups[p]; if (grp == NULL) { ALOGW("Bad identifier when getting value for resource number 0x%08x", resID); @@ -3487,142 +3600,62 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag } // Allow overriding density - const ResTable_config* desiredConfig = &mParams; - ResTable_config* overrideConfig = NULL; + ResTable_config desiredConfig = mParams; if (density > 0) { - overrideConfig = (ResTable_config*) malloc(sizeof(ResTable_config)); - if (overrideConfig == NULL) { - ALOGE("Couldn't malloc ResTable_config for overrides: %s", strerror(errno)); - return BAD_INDEX; - } - memcpy(overrideConfig, &mParams, sizeof(ResTable_config)); - overrideConfig->density = density; - desiredConfig = overrideConfig; - } - - ssize_t rc = BAD_VALUE; - size_t ip = grp->packages.size(); - while (ip > 0) { - ip--; - int T = t; - int E = e; - - const Package* const package = grp->packages[ip]; - if (package->header->resourceIDMap) { - uint32_t overlayResID = 0x0; - status_t retval = idmapLookup(package->header->resourceIDMap, - package->header->resourceIDMapSize, - resID, &overlayResID); - if (retval == NO_ERROR && overlayResID != 0x0) { - // for this loop iteration, this is the type and entry we really want - ALOGV("resource map 0x%08x -> 0x%08x\n", resID, overlayResID); - T = Res_GETTYPE(overlayResID); - E = Res_GETENTRY(overlayResID); - } else { - // resource not present in overlay package, continue with the next package - continue; - } - } - - const ResTable_type* type; - const ResTable_entry* entry; - const Type* typeClass; - ssize_t offset = getEntry(package, T, E, desiredConfig, &type, &entry, &typeClass); - if (offset <= 0) { - // No {entry, appropriate config} pair found in package. If this - // package is an overlay package (ip != 0), this simply means the - // overlay package did not specify a default. - // Non-overlay packages are still required to provide a default. - if (offset < 0 && ip == 0) { - ALOGW("Failure getting entry for 0x%08x (t=%d e=%d) in package %zd (error %d)\n", - resID, T, E, ip, (int)offset); - rc = offset; - goto out; - } - continue; - } + desiredConfig.density = density; + } - if ((dtohs(entry->flags)&entry->FLAG_COMPLEX) != 0) { - if (!mayBeBag) { - ALOGW("Requesting resource 0x%x failed because it is complex\n", - resID); - } - continue; - } + Entry entry; + status_t err = getEntry(grp, t, e, &desiredConfig, &entry); + if (err != NO_ERROR) { + ALOGW("Failure getting entry for 0x%08x (t=%d e=%d) (error %d)\n", + resID, t, e, err); + return err; + } - if ((size_t)offset > (dtohl(type->header.size)-sizeof(Res_value))) { - ALOGW("ResTable_item at %d is beyond type chunk data %d", - (int)offset, dtohl(type->header.size)); - rc = BAD_TYPE; - goto out; + if ((dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) != 0) { + if (!mayBeBag) { + ALOGW("Requesting resource 0x%08x failed because it is complex\n", resID); } + return BAD_VALUE; + } - const Res_value* item = - (const Res_value*)(((const uint8_t*)type) + offset); - ResTable_config thisConfig; - thisConfig.copyFromDtoH(type->config); + const Res_value* value = reinterpret_cast<const Res_value*>( + reinterpret_cast<const uint8_t*>(entry.entry) + entry.entry->size); - if (outSpecFlags != NULL) { - if (typeClass->typeSpecFlags != NULL) { - *outSpecFlags |= dtohl(typeClass->typeSpecFlags[E]); - } else { - *outSpecFlags = -1; - } - } + outValue->size = dtohs(value->size); + outValue->res0 = value->res0; + outValue->dataType = value->dataType; + outValue->data = dtohl(value->data); - if (bestPackage != NULL && - (bestItem.isMoreSpecificThan(thisConfig) || bestItem.diff(thisConfig) == 0)) { - // Discard thisConfig not only if bestItem is more specific, but also if the two configs - // are identical (diff == 0), or overlay packages will not take effect. - continue; - } - - bestItem = thisConfig; - bestValue = item; - bestPackage = package; + // The reference may be pointing to a resource in a shared library. These + // references have build-time generated package IDs. These ids may not match + // the actual package IDs of the corresponding packages in this ResTable. + // We need to fix the package ID based on a mapping. + if (grp->dynamicRefTable.lookupResourceValue(outValue) != NO_ERROR) { + ALOGW("Failed to resolve referenced package: 0x%08x", outValue->data); + return BAD_VALUE; } - TABLE_NOISY(printf("Found result: package %p\n", bestPackage)); - - if (bestValue) { - outValue->size = dtohs(bestValue->size); - outValue->res0 = bestValue->res0; - outValue->dataType = bestValue->dataType; - outValue->data = dtohl(bestValue->data); - - // The reference may be pointing to a resource in a shared library. These - // references have build-time generated package IDs. These ids may not match - // the actual package IDs of the corresponding packages in this ResTable. - // We need to fix the package ID based on a mapping. - status_t err = grp->dynamicRefTable.lookupResourceValue(outValue); - if (err != NO_ERROR) { - ALOGW("Failed to resolve referenced package: 0x%08x", outValue->data); - rc = BAD_VALUE; - goto out; - } + TABLE_NOISY(size_t len; + printf("Found value: pkg=%d, type=%d, str=%s, int=%d\n", + entry.package->header->index, + outValue->dataType, + outValue->dataType == Res_value::TYPE_STRING + ? String8(entry.package->header->values.stringAt( + outValue->data, &len)).string() + : "", + outValue->data)); - if (outConfig != NULL) { - *outConfig = bestItem; - } - TABLE_NOISY(size_t len; - printf("Found value: pkg=%d, type=%d, str=%s, int=%d\n", - bestPackage->header->index, - outValue->dataType, - outValue->dataType == bestValue->TYPE_STRING - ? String8(bestPackage->header->values.stringAt( - outValue->data, &len)).string() - : "", - outValue->data)); - rc = bestPackage->header->index; - goto out; + if (outSpecFlags != NULL) { + *outSpecFlags = entry.specFlags; } -out: - if (overrideConfig != NULL) { - free(overrideConfig); + if (outConfig != NULL) { + *outConfig = entry.config; } - return rc; + return entry.package->header->index; } ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex, @@ -3721,29 +3754,25 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, PackageGroup* const grp = mPackageGroups[p]; if (grp == NULL) { ALOGW("Bad identifier when getting bag for resource number 0x%08x", resID); - return false; + return BAD_INDEX; } - if (t >= (int)grp->typeCount) { - ALOGW("Type identifier 0x%x is larger than type count 0x%x", - t+1, (int)grp->typeCount); + const TypeList& typeConfigs = grp->types[t]; + if (typeConfigs.isEmpty()) { + ALOGW("Type identifier 0x%x does not exist.", t+1); return BAD_INDEX; } - const Package* const basePackage = grp->packages[0]; - - const Type* const typeConfigs = basePackage->getType(t); - - const size_t NENTRY = typeConfigs->entryCount; + const size_t NENTRY = typeConfigs[0]->entryCount; if (e >= (int)NENTRY) { ALOGW("Entry identifier 0x%x is larger than entry count 0x%x", - e, (int)typeConfigs->entryCount); + e, (int)typeConfigs[0]->entryCount); return BAD_INDEX; } // First see if we've already computed this bag... if (grp->bags) { - bag_set** typeSet = grp->bags[t]; + bag_set** typeSet = grp->bags->get(t); if (typeSet) { bag_set* set = typeSet[e]; if (set) { @@ -3764,229 +3793,174 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, // Bag not found, we need to compute it! if (!grp->bags) { - grp->bags = (bag_set***)calloc(grp->typeCount, sizeof(bag_set*)); + grp->bags = new ByteBucketArray<bag_set**>(); if (!grp->bags) return NO_MEMORY; } - bag_set** typeSet = grp->bags[t]; + bag_set** typeSet = grp->bags->get(t); if (!typeSet) { typeSet = (bag_set**)calloc(NENTRY, sizeof(bag_set*)); if (!typeSet) return NO_MEMORY; - grp->bags[t] = typeSet; + grp->bags->set(t, typeSet); } // Mark that we are currently working on this one. typeSet[e] = (bag_set*)0xFFFFFFFF; - // This is what we are building. - bag_set* set = NULL; - TABLE_NOISY(ALOGI("Building bag: %p\n", (void*)resID)); - ResTable_config bestConfig; - memset(&bestConfig, 0, sizeof(bestConfig)); + // Now collect all bag attributes + Entry entry; + status_t err = getEntry(grp, t, e, &mParams, &entry); + if (err != NO_ERROR) { + return err; + } - // Now collect all bag attributes from all packages. - size_t ip = grp->packages.size(); - while (ip > 0) { - ip--; - int T = t; - int E = e; - - const Package* const package = grp->packages[ip]; - if (package->header->resourceIDMap) { - uint32_t overlayResID = 0x0; - status_t retval = idmapLookup(package->header->resourceIDMap, - package->header->resourceIDMapSize, - resID, &overlayResID); - if (retval == NO_ERROR && overlayResID != 0x0) { - // for this loop iteration, this is the type and entry we really want - ALOGV("resource map 0x%08x -> 0x%08x\n", resID, overlayResID); - T = Res_GETTYPE(overlayResID); - E = Res_GETENTRY(overlayResID); - } else { - // resource not present in overlay package, continue with the next package - continue; - } - } + const uint16_t entrySize = dtohs(entry.entry->size); + const uint32_t parent = entrySize >= sizeof(ResTable_map_entry) + ? dtohl(((const ResTable_map_entry*)entry.entry)->parent.ident) : 0; + const uint32_t count = entrySize >= sizeof(ResTable_map_entry) + ? dtohl(((const ResTable_map_entry*)entry.entry)->count) : 0; - const ResTable_type* type; - const ResTable_entry* entry; - const Type* typeClass; - ALOGV("Getting entry pkg=%p, t=%d, e=%d\n", package, T, E); - ssize_t offset = getEntry(package, T, E, &mParams, &type, &entry, &typeClass); - ALOGV("Resulting offset=%d\n", (int)offset); - if (offset <= 0) { - // No {entry, appropriate config} pair found in package. If this - // package is an overlay package (ip != 0), this simply means the - // overlay package did not specify a default. - // Non-overlay packages are still required to provide a default. - if (offset < 0 && ip == 0) { - if (set) free(set); - return offset; - } - continue; - } + size_t N = count; - if ((dtohs(entry->flags)&entry->FLAG_COMPLEX) == 0) { - ALOGW("Skipping entry 0x%x in package table %zu because it is not complex!\n", - resID, ip); - continue; + TABLE_NOISY(ALOGI("Found map: size=%p parent=%p count=%d\n", + entrySize, parent, count)); + + // If this map inherits from another, we need to start + // with its parent's values. Otherwise start out empty. + TABLE_NOISY(printf("Creating new bag, entrySize=0x%08x, parent=0x%08x\n", + entrySize, parent)); + + // This is what we are building. + bag_set* set = NULL; + + if (parent) { + uint32_t resolvedParent = parent; + + // Bags encode a parent reference without using the standard + // Res_value structure. That means we must always try to + // resolve a parent reference in case it is actually a + // TYPE_DYNAMIC_REFERENCE. + status_t err = grp->dynamicRefTable.lookupResourceId(&resolvedParent); + if (err != NO_ERROR) { + ALOGE("Failed resolving bag parent id 0x%08x", parent); + return UNKNOWN_ERROR; } - if (set != NULL && !type->config.isBetterThan(bestConfig, NULL)) { - continue; + const bag_entry* parentBag; + uint32_t parentTypeSpecFlags = 0; + const ssize_t NP = getBagLocked(resolvedParent, &parentBag, &parentTypeSpecFlags); + const size_t NT = ((NP >= 0) ? NP : 0) + N; + set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*NT); + if (set == NULL) { + return NO_MEMORY; } - bestConfig = type->config; - if (set) { - free(set); - set = NULL; + if (NP > 0) { + memcpy(set+1, parentBag, NP*sizeof(bag_entry)); + set->numAttrs = NP; + TABLE_NOISY(ALOGI("Initialized new bag with %d inherited attributes.\n", NP)); + } else { + TABLE_NOISY(ALOGI("Initialized new bag with no inherited attributes.\n")); + set->numAttrs = 0; } + set->availAttrs = NT; + set->typeSpecFlags = parentTypeSpecFlags; + } else { + set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*N); + if (set == NULL) { + return NO_MEMORY; + } + set->numAttrs = 0; + set->availAttrs = N; + set->typeSpecFlags = 0; + } - const uint16_t entrySize = dtohs(entry->size); - const uint32_t parent = entrySize >= sizeof(ResTable_map_entry) - ? dtohl(((const ResTable_map_entry*)entry)->parent.ident) : 0; - const uint32_t count = entrySize >= sizeof(ResTable_map_entry) - ? dtohl(((const ResTable_map_entry*)entry)->count) : 0; - - size_t N = count; + set->typeSpecFlags |= entry.specFlags; - TABLE_NOISY(ALOGI("Found map: size=%p parent=%p count=%d\n", - entrySize, parent, count)); + // Now merge in the new attributes... + size_t curOff = (reinterpret_cast<uintptr_t>(entry.entry) - reinterpret_cast<uintptr_t>(entry.type)) + + dtohs(entry.entry->size); + const ResTable_map* map; + bag_entry* entries = (bag_entry*)(set+1); + size_t curEntry = 0; + uint32_t pos = 0; + TABLE_NOISY(ALOGI("Starting with set %p, entries=%p, avail=%d\n", + set, entries, set->availAttrs)); + while (pos < count) { + TABLE_NOISY(printf("Now at %p\n", (void*)curOff)); - // If this map inherits from another, we need to start - // with its parent's values. Otherwise start out empty. - TABLE_NOISY(printf("Creating new bag, entrySize=0x%08x, parent=0x%08x\n", - entrySize, parent)); - if (parent) { - uint32_t resolvedParent = parent; + if (curOff > (dtohl(entry.type->header.size)-sizeof(ResTable_map))) { + ALOGW("ResTable_map at %d is beyond type chunk data %d", + (int)curOff, dtohl(entry.type->header.size)); + return BAD_TYPE; + } + map = (const ResTable_map*)(((const uint8_t*)entry.type) + curOff); + N++; - // Bags encode a parent reference without using the standard - // Res_value structure. That means we must always try to - // resolve a parent reference in case it is actually a - // TYPE_DYNAMIC_REFERENCE. - status_t err = grp->dynamicRefTable.lookupResourceId(&resolvedParent); - if (err != NO_ERROR) { - ALOGE("Failed resolving bag parent id 0x%08x", parent); - return UNKNOWN_ERROR; - } + const uint32_t newName = htodl(map->name.ident); + bool isInside; + uint32_t oldName = 0; + while ((isInside=(curEntry < set->numAttrs)) + && (oldName=entries[curEntry].map.name.ident) < newName) { + TABLE_NOISY(printf("#%d: Keeping existing attribute: 0x%08x\n", + curEntry, entries[curEntry].map.name.ident)); + curEntry++; + } - const bag_entry* parentBag; - uint32_t parentTypeSpecFlags = 0; - const ssize_t NP = getBagLocked(resolvedParent, &parentBag, &parentTypeSpecFlags); - const size_t NT = ((NP >= 0) ? NP : 0) + N; - set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*NT); - if (set == NULL) { - return NO_MEMORY; - } - if (NP > 0) { - memcpy(set+1, parentBag, NP*sizeof(bag_entry)); - set->numAttrs = NP; - TABLE_NOISY(ALOGI("Initialized new bag with %d inherited attributes.\n", NP)); - } else { - TABLE_NOISY(ALOGI("Initialized new bag with no inherited attributes.\n")); - set->numAttrs = 0; - } - set->availAttrs = NT; - set->typeSpecFlags = parentTypeSpecFlags; + if ((!isInside) || oldName != newName) { + // This is a new attribute... figure out what to do with it. + if (set->numAttrs >= set->availAttrs) { + // Need to alloc more memory... + const size_t newAvail = set->availAttrs+N; + set = (bag_set*)realloc(set, + sizeof(bag_set) + + sizeof(bag_entry)*newAvail); + if (set == NULL) { + return NO_MEMORY; + } + set->availAttrs = newAvail; + entries = (bag_entry*)(set+1); + TABLE_NOISY(printf("Reallocated set %p, entries=%p, avail=%d\n", + set, entries, set->availAttrs)); + } + if (isInside) { + // Going in the middle, need to make space. + memmove(entries+curEntry+1, entries+curEntry, + sizeof(bag_entry)*(set->numAttrs-curEntry)); + set->numAttrs++; + } + TABLE_NOISY(printf("#%d: Inserting new attribute: 0x%08x\n", + curEntry, newName)); } else { - set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*N); - if (set == NULL) { - return NO_MEMORY; - } - set->numAttrs = 0; - set->availAttrs = N; - set->typeSpecFlags = 0; + TABLE_NOISY(printf("#%d: Replacing existing attribute: 0x%08x\n", + curEntry, oldName)); } - if (typeClass->typeSpecFlags != NULL) { - set->typeSpecFlags |= dtohl(typeClass->typeSpecFlags[E]); - } else { - set->typeSpecFlags = -1; - } - - // Now merge in the new attributes... - ssize_t curOff = offset; - const ResTable_map* map; - bag_entry* entries = (bag_entry*)(set+1); - size_t curEntry = 0; - uint32_t pos = 0; - TABLE_NOISY(ALOGI("Starting with set %p, entries=%p, avail=%d\n", - set, entries, set->availAttrs)); - while (pos < count) { - TABLE_NOISY(printf("Now at %p\n", (void*)curOff)); - - if ((size_t)curOff > (dtohl(type->header.size)-sizeof(ResTable_map))) { - ALOGW("ResTable_map at %d is beyond type chunk data %d", - (int)curOff, dtohl(type->header.size)); - return BAD_TYPE; - } - map = (const ResTable_map*)(((const uint8_t*)type) + curOff); - N++; - - const uint32_t newName = htodl(map->name.ident); - bool isInside; - uint32_t oldName = 0; - while ((isInside=(curEntry < set->numAttrs)) - && (oldName=entries[curEntry].map.name.ident) < newName) { - TABLE_NOISY(printf("#%d: Keeping existing attribute: 0x%08x\n", - curEntry, entries[curEntry].map.name.ident)); - curEntry++; - } - - if ((!isInside) || oldName != newName) { - // This is a new attribute... figure out what to do with it. - if (set->numAttrs >= set->availAttrs) { - // Need to alloc more memory... - const size_t newAvail = set->availAttrs+N; - set = (bag_set*)realloc(set, - sizeof(bag_set) - + sizeof(bag_entry)*newAvail); - if (set == NULL) { - return NO_MEMORY; - } - set->availAttrs = newAvail; - entries = (bag_entry*)(set+1); - TABLE_NOISY(printf("Reallocated set %p, entries=%p, avail=%d\n", - set, entries, set->availAttrs)); - } - if (isInside) { - // Going in the middle, need to make space. - memmove(entries+curEntry+1, entries+curEntry, - sizeof(bag_entry)*(set->numAttrs-curEntry)); - set->numAttrs++; - } - TABLE_NOISY(printf("#%d: Inserting new attribute: 0x%08x\n", - curEntry, newName)); - } else { - TABLE_NOISY(printf("#%d: Replacing existing attribute: 0x%08x\n", - curEntry, oldName)); - } + bag_entry* cur = entries+curEntry; - bag_entry* cur = entries+curEntry; + cur->stringBlock = entry.package->header->index; + cur->map.name.ident = newName; + cur->map.value.copyFrom_dtoh(map->value); + status_t err = grp->dynamicRefTable.lookupResourceValue(&cur->map.value); + if (err != NO_ERROR) { + ALOGE("Reference item(0x%08x) in bag could not be resolved.", cur->map.value.data); + return UNKNOWN_ERROR; + } - cur->stringBlock = package->header->index; - cur->map.name.ident = newName; - cur->map.value.copyFrom_dtoh(map->value); - status_t err = grp->dynamicRefTable.lookupResourceValue(&cur->map.value); - if (err != NO_ERROR) { - ALOGE("Reference item(0x%08x) in bag could not be resolved.", cur->map.value.data); - return UNKNOWN_ERROR; - } + TABLE_NOISY(printf("Setting entry #%d %p: block=%d, name=0x%08x, type=%d, data=0x%08x\n", + curEntry, cur, cur->stringBlock, cur->map.name.ident, + cur->map.value.dataType, cur->map.value.data)); - TABLE_NOISY(printf("Setting entry #%d %p: block=%d, name=0x%08x, type=%d, data=0x%08x\n", - curEntry, cur, cur->stringBlock, cur->map.name.ident, - cur->map.value.dataType, cur->map.value.data)); + // On to the next! + curEntry++; + pos++; + const size_t size = dtohs(map->value.size); + curOff += size + sizeof(*map)-sizeof(map->value); + }; - // On to the next! - curEntry++; - pos++; - const size_t size = dtohs(map->value.size); - curOff += size + sizeof(*map)-sizeof(map->value); - }; - if (curEntry > set->numAttrs) { - set->numAttrs = curEntry; - } + if (curEntry > set->numAttrs) { + set->numAttrs = curEntry; } // And this is it... @@ -4154,80 +4128,63 @@ nope: continue; } - const ssize_t ti = group->basePackage->typeStrings.indexOfString(type, typeLen); + const ssize_t ti = group->findType16(type, typeLen); if (ti < 0) { TABLE_NOISY(printf("Type not found in package %s\n", String8(group->name).string())); continue; } - const ssize_t ei = group->basePackage->keyStrings.indexOfString(name, nameLen); - if (ei < 0) { - TABLE_NOISY(printf("Name not found in package %s\n", String8(group->name).string())); - continue; - } - - TABLE_NOISY(printf("Search indices: type=%d, name=%d\n", ti, ei)); - - const Type* const typeConfigs = group->packages[0]->getType(ti); - if (typeConfigs == NULL || typeConfigs->configs.size() <= 0) { - TABLE_NOISY(printf("Expected type structure not found in package %s for idnex %d\n", + const TypeList& typeList = group->types[ti]; + if (typeList.isEmpty()) { + TABLE_NOISY(printf("Expected type structure not found in package %s for index %d\n", String8(group->name).string(), ti)); + continue; } - size_t NTC = typeConfigs->configs.size(); - for (size_t tci=0; tci<NTC; tci++) { - const ResTable_type* const ty = typeConfigs->configs[tci]; - const uint32_t typeOffset = dtohl(ty->entriesStart); - - const uint8_t* const end = ((const uint8_t*)ty) + dtohl(ty->header.size); - const uint32_t* const eindex = (const uint32_t*) - (((const uint8_t*)ty) + dtohs(ty->header.headerSize)); - - const size_t NE = dtohl(ty->entryCount); - for (size_t i=0; i<NE; i++) { - uint32_t offset = dtohl(eindex[i]); - if (offset == ResTable_type::NO_ENTRY) { - continue; - } - - offset += typeOffset; + const size_t typeCount = typeList.size(); + for (size_t i = 0; i < typeCount; i++) { + const Type* t = typeList[i]; + const ssize_t ei = t->package->keyStrings.indexOfString(name, nameLen); + if (ei < 0) { + continue; + } - if (offset > (dtohl(ty->header.size)-sizeof(ResTable_entry))) { - ALOGW("ResTable_entry at %d is beyond type chunk data %d", - offset, dtohl(ty->header.size)); - return 0; - } - if ((offset&0x3) != 0) { - ALOGW("ResTable_entry at %d (pkg=%d type=%d ent=%d) is not on an integer boundary when looking for %s:%s/%s", - (int)offset, (int)group->id, (int)ti+1, (int)i, - String8(package, packageLen).string(), - String8(type, typeLen).string(), - String8(name, nameLen).string()); - return 0; - } + const size_t configCount = t->configs.size(); + for (size_t j = 0; j < configCount; j++) { + const TypeVariant tv(t->configs[j]); + for (TypeVariant::iterator iter = tv.beginEntries(); + iter != tv.endEntries(); + iter++) { + const ResTable_entry* entry = *iter; + if (entry == NULL) { + continue; + } - const ResTable_entry* const entry = (const ResTable_entry*) - (((const uint8_t*)ty) + offset); - if (dtohs(entry->size) < sizeof(*entry)) { - ALOGW("ResTable_entry size %d is too small", dtohs(entry->size)); - return BAD_TYPE; - } + if (dtohl(entry->key.index) == (size_t) ei) { + uint32_t resId = Res_MAKEID(group->id - 1, ti, iter.index()); + if (outTypeSpecFlags) { + Entry result; + if (getEntry(group, ti, iter.index(), NULL, &result) != NO_ERROR) { + ALOGW("Failed to find spec flags for %s:%s/%s (0x%08x)", + String8(group->name).string(), + String8(String16(type, typeLen)).string(), + String8(String16(name, nameLen)).string(), + resId); + return 0; + } + *outTypeSpecFlags = result.specFlags; - TABLE_SUPER_NOISY(printf("Looking at entry #%d: want str %d, have %d\n", - i, ei, dtohl(entry->key.index))); - if (dtohl(entry->key.index) == (size_t)ei) { - if (outTypeSpecFlags) { - *outTypeSpecFlags = typeConfigs->typeSpecFlags[i]; - if (fakePublic) { - *outTypeSpecFlags |= ResTable_typeSpec::SPEC_PUBLIC; + if (fakePublic) { + *outTypeSpecFlags |= ResTable_typeSpec::SPEC_PUBLIC; + } } + return resId; } - return Res_MAKEID(group->id-1, ti, i); } } } + break; } - return 0; } @@ -5260,6 +5217,18 @@ uint32_t ResTable::getBasePackageId(size_t idx) const return mPackageGroups[idx]->id; } +uint32_t ResTable::getLastTypeIdForPackage(size_t idx) const +{ + if (mError != NO_ERROR) { + return 0; + } + LOG_FATAL_IF(idx >= mPackageGroups.size(), + "Requested package index %d past package count %d", + (int)idx, (int)mPackageGroups.size()); + const PackageGroup* const group = mPackageGroups[idx]; + return group->largestTypeId; +} + size_t ResTable::getTableCount() const { return mHeaders.size(); @@ -5292,32 +5261,31 @@ const DynamicRefTable* ResTable::getDynamicRefTableForCookie(int32_t cookie) con void ResTable::getConfigurations(Vector<ResTable_config>* configs) const { - const size_t I = mPackageGroups.size(); - for (size_t i=0; i<I; i++) { + const size_t packageCount = mPackageGroups.size(); + for (size_t i = 0; i < packageCount; i++) { const PackageGroup* packageGroup = mPackageGroups[i]; - const size_t J = packageGroup->packages.size(); - for (size_t j=0; j<J; j++) { - const Package* package = packageGroup->packages[j]; - const size_t K = package->types.size(); - for (size_t k=0; k<K; k++) { - const Type* type = package->types[k]; - if (type == NULL) continue; - const size_t L = type->configs.size(); - for (size_t l=0; l<L; l++) { - const ResTable_type* config = type->configs[l]; + const size_t typeCount = packageGroup->types.size(); + for (size_t j = 0; j < typeCount; j++) { + const TypeList& typeList = packageGroup->types[j]; + const size_t numTypes = typeList.size(); + for (size_t k = 0; k < numTypes; k++) { + const Type* type = typeList[k]; + const size_t numConfigs = type->configs.size(); + for (size_t m = 0; m < numConfigs; m++) { + const ResTable_type* config = type->configs[m]; 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)) { + 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 (m == M) { + if (n == N) { configs->add(cfg); } } @@ -5350,122 +5318,180 @@ void ResTable::getLocales(Vector<String8>* locales) const } } -ssize_t ResTable::getEntry( - const Package* package, int typeIndex, int entryIndex, - const ResTable_config* config, - const ResTable_type** outType, const ResTable_entry** outEntry, - const Type** outTypeClass) const -{ - ALOGV("Getting entry from package %p\n", package); - const ResTable_package* const pkg = package->package; +StringPoolRef::StringPoolRef(const ResStringPool* pool, uint32_t index) + : mPool(pool), mIndex(index) {} - const Type* allTypes = package->getType(typeIndex); - ALOGV("allTypes=%p\n", allTypes); - if (allTypes == NULL) { - ALOGV("Skipping entry type index 0x%02x because type is NULL!\n", typeIndex); - return 0; +StringPoolRef::StringPoolRef() + : mPool(NULL), mIndex(0) {} + +const char* StringPoolRef::string8(size_t* outLen) const { + if (mPool != NULL) { + return mPool->string8At(mIndex, outLen); } + if (outLen != NULL) { + *outLen = 0; + } + return NULL; +} - if ((size_t)entryIndex >= allTypes->entryCount) { - ALOGW("getEntry failing because entryIndex %d is beyond type entryCount %d", - entryIndex, (int)allTypes->entryCount); +const char16_t* StringPoolRef::string16(size_t* outLen) const { + if (mPool != NULL) { + return mPool->stringAt(mIndex, outLen); + } + if (outLen != NULL) { + *outLen = 0; + } + return NULL; +} + +status_t ResTable::getEntry( + const PackageGroup* packageGroup, int typeIndex, int entryIndex, + const ResTable_config* config, + Entry* outEntry) const +{ + const TypeList& typeList = packageGroup->types[typeIndex]; + if (typeList.isEmpty()) { + ALOGV("Skipping entry type index 0x%02x because type is NULL!\n", typeIndex); return BAD_TYPE; } - const ResTable_type* type = NULL; - uint32_t offset = ResTable_type::NO_ENTRY; + const ResTable_type* bestType = NULL; + uint32_t bestOffset = ResTable_type::NO_ENTRY; + const Package* bestPackage = NULL; + uint32_t specFlags = 0; + uint8_t actualTypeIndex = typeIndex; ResTable_config bestConfig; - memset(&bestConfig, 0, sizeof(bestConfig)); // make the compiler shut up - - const size_t NT = allTypes->configs.size(); - for (size_t i=0; i<NT; i++) { - const ResTable_type* const thisType = allTypes->configs[i]; - if (thisType == NULL) continue; - - ResTable_config thisConfig; - thisConfig.copyFromDtoH(thisType->config); + memset(&bestConfig, 0, sizeof(bestConfig)); - TABLE_GETENTRY(ALOGI("Match entry 0x%x in type 0x%x (sz 0x%x): %s\n", - entryIndex, typeIndex+1, dtohl(thisType->config.size), - thisConfig.toString().string())); + // Iterate over the Types of each package. + const size_t typeCount = typeList.size(); + for (size_t i = 0; i < typeCount; i++) { + const Type* const typeSpec = typeList[i]; + + int realEntryIndex = entryIndex; + int realTypeIndex = typeIndex; + bool currentTypeIsOverlay = false; + + // Runtime overlay packages provide a mapping of app resource + // ID to package resource ID. + if (typeSpec->idmapEntries.hasEntries()) { + uint16_t overlayEntryIndex; + if (typeSpec->idmapEntries.lookup(entryIndex, &overlayEntryIndex) != NO_ERROR) { + // No such mapping exists + continue; + } + realEntryIndex = overlayEntryIndex; + realTypeIndex = typeSpec->idmapEntries.overlayTypeId() - 1; + currentTypeIsOverlay = true; + } - // Check to make sure this one is valid for the current parameters. - if (config && !thisConfig.match(*config)) { - TABLE_GETENTRY(ALOGI("Does not match config!\n")); + if (static_cast<size_t>(realEntryIndex) >= typeSpec->entryCount) { + ALOGW("For resource 0x%08x, entry index(%d) is beyond type entryCount(%d)", + Res_MAKEID(packageGroup->id - 1, typeIndex, entryIndex), + entryIndex, static_cast<int>(typeSpec->entryCount)); + // We should normally abort here, but some legacy apps declare + // resources in the 'android' package (old bug in AAPT). continue; } - // Check if there is the desired entry in this type. + // Aggregate all the flags for each package that defines this entry. + if (typeSpec->typeSpecFlags != NULL) { + specFlags |= dtohl(typeSpec->typeSpecFlags[realEntryIndex]); + } else { + specFlags = -1; + } - const uint8_t* const end = ((const uint8_t*)thisType) - + dtohl(thisType->header.size); - const uint32_t* const eindex = (const uint32_t*) - (((const uint8_t*)thisType) + dtohs(thisType->header.headerSize)); + const size_t numConfigs = typeSpec->configs.size(); + for (size_t c = 0; c < numConfigs; c++) { + const ResTable_type* const thisType = typeSpec->configs[c]; + if (thisType == NULL) { + continue; + } - uint32_t thisOffset = dtohl(eindex[entryIndex]); - if (thisOffset == ResTable_type::NO_ENTRY) { - TABLE_GETENTRY(ALOGI("Skipping because it is not defined!\n")); - continue; - } + ResTable_config thisConfig; + thisConfig.copyFromDtoH(thisType->config); - if (type != NULL) { - // Check if this one is less specific than the last found. If so, - // we will skip it. We check starting with things we most care - // about to those we least care about. - if (!thisConfig.isBetterThan(bestConfig, config)) { - TABLE_GETENTRY(ALOGI("This config is worse than last!\n")); + // Check to make sure this one is valid for the current parameters. + if (config != NULL && !thisConfig.match(*config)) { + continue; + } + + // Check if there is the desired entry in this type. + const uint8_t* const end = reinterpret_cast<const uint8_t*>(thisType) + + dtohl(thisType->header.size); + const uint32_t* const eindex = reinterpret_cast<const uint32_t*>( + reinterpret_cast<const uint8_t*>(thisType) + dtohs(thisType->header.headerSize)); + + uint32_t thisOffset = dtohl(eindex[realEntryIndex]); + if (thisOffset == ResTable_type::NO_ENTRY) { + // There is no entry for this index and configuration. continue; } - } - type = thisType; - offset = thisOffset; - bestConfig = thisConfig; - TABLE_GETENTRY(ALOGI("Best entry so far -- using it!\n")); - if (!config) break; + if (bestType != NULL) { + // Check if this one is less specific than the last found. If so, + // we will skip it. We check starting with things we most care + // about to those we least care about. + if (!thisConfig.isBetterThan(bestConfig, config)) { + if (!currentTypeIsOverlay || thisConfig.compare(bestConfig) != 0) { + continue; + } + } + } + + bestType = thisType; + bestOffset = thisOffset; + bestConfig = thisConfig; + bestPackage = typeSpec->package; + actualTypeIndex = realTypeIndex; + + // If no config was specified, any type will do, so skip + if (config == NULL) { + break; + } + } } - if (type == NULL) { - TABLE_GETENTRY(ALOGI("No value found for requested entry!\n")); + if (bestType == NULL) { return BAD_INDEX; } - offset += dtohl(type->entriesStart); - TABLE_NOISY(ALOGD("Looking in resource table %p, typeOff=%p, offset=%p", - package->header->header, (void*)(((const char*)type)-((const char*)package->header->header)), - (void*)offset)); + bestOffset += dtohl(bestType->entriesStart); - if (offset > (dtohl(type->header.size)-sizeof(ResTable_entry))) { + if (bestOffset > (dtohl(bestType->header.size)-sizeof(ResTable_entry))) { ALOGW("ResTable_entry at 0x%x is beyond type chunk data 0x%x", - offset, dtohl(type->header.size)); + bestOffset, dtohl(bestType->header.size)); return BAD_TYPE; } - if ((offset&0x3) != 0) { - ALOGW("ResTable_entry at 0x%x is not on an integer boundary", - offset); + if ((bestOffset & 0x3) != 0) { + ALOGW("ResTable_entry at 0x%x is not on an integer boundary", bestOffset); return BAD_TYPE; } - const ResTable_entry* const entry = (const ResTable_entry*) - (((const uint8_t*)type) + offset); + const ResTable_entry* const entry = reinterpret_cast<const ResTable_entry*>( + reinterpret_cast<const uint8_t*>(bestType) + bestOffset); if (dtohs(entry->size) < sizeof(*entry)) { ALOGW("ResTable_entry size 0x%x is too small", dtohs(entry->size)); return BAD_TYPE; } - *outType = type; - *outEntry = entry; - if (outTypeClass != NULL) { - *outTypeClass = allTypes; + if (outEntry != NULL) { + outEntry->entry = entry; + outEntry->config = bestConfig; + outEntry->type = bestType; + outEntry->specFlags = specFlags; + outEntry->package = bestPackage; + outEntry->typeStr = StringPoolRef(&bestPackage->typeStrings, actualTypeIndex - bestPackage->typeIdOffset); + outEntry->keyStr = StringPoolRef(&bestPackage->keyStrings, dtohl(entry->key.index)); } - return offset + dtohs(entry->size); + return NO_ERROR; } status_t ResTable::parsePackage(const ResTable_package* const pkg, - const Header* const header, uint32_t idmap_id) + const Header* const header) { const uint8_t* base = (const uint8_t*)pkg; - status_t err = validate_chunk(&pkg->header, sizeof(*pkg), + status_t err = validate_chunk(&pkg->header, sizeof(*pkg) - sizeof(pkg->typeIdOffset), header->dataEnd, "ResTable_package"); if (err != NO_ERROR) { return (mError=err); @@ -5494,89 +5520,88 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, return (mError=BAD_TYPE); } - Package* package = NULL; - PackageGroup* group = NULL; - uint32_t id = idmap_id != 0 ? idmap_id : dtohl(pkg->id); - // If at this point id == 0, pkg is an overlay package without a - // corresponding idmap. During regular usage, overlay packages are - // always loaded alongside their idmaps, but during idmap creation - // the package is temporarily loaded by itself. - if (id < 256) { - - package = new Package(this, header, pkg); - if (package == NULL) { - return (mError=NO_MEMORY); - } - - if (idmap_id == 0) { - err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings), - header->dataEnd-(base+dtohl(pkg->typeStrings))); - if (err != NO_ERROR) { - delete group; - delete package; - return (mError=err); - } + uint32_t id = dtohl(pkg->id); + KeyedVector<uint8_t, IdmapEntries> idmapEntries; - err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings), - header->dataEnd-(base+dtohl(pkg->keyStrings))); - if (err != NO_ERROR) { - delete group; - delete package; - return (mError=err); - } + if (header->resourceIDMap != NULL) { + uint8_t targetPackageId = 0; + status_t err = parseIdmap(header->resourceIDMap, header->resourceIDMapSize, &targetPackageId, &idmapEntries); + if (err != NO_ERROR) { + ALOGW("Overlay is broken"); + return (mError=err); } + id = targetPackageId; + } - if (id == 0) { - // This is a library so assign an ID - id = mNextPackageId++; - } + 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 + id = mNextPackageId++; + } - size_t idx = mPackageMap[id]; - if (idx == 0) { - idx = mPackageGroups.size()+1; + PackageGroup* group = NULL; + Package* package = new Package(this, header, pkg); + if (package == NULL) { + return (mError=NO_MEMORY); + } - char16_t tmpName[sizeof(pkg->name)/sizeof(char16_t)]; - strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(char16_t)); - group = new PackageGroup(this, String16(tmpName), id); - if (group == NULL) { - delete package; - return (mError=NO_MEMORY); - } + err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings), + header->dataEnd-(base+dtohl(pkg->typeStrings))); + if (err != NO_ERROR) { + delete group; + delete package; + return (mError=err); + } - //printf("Adding new package id %d at index %d\n", id, idx); - err = mPackageGroups.add(group); - if (err < NO_ERROR) { - return (mError=err); - } - group->basePackage = package; + err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings), + header->dataEnd-(base+dtohl(pkg->keyStrings))); + if (err != NO_ERROR) { + delete group; + delete package; + return (mError=err); + } - mPackageMap[id] = (uint8_t)idx; + size_t idx = mPackageMap[id]; + if (idx == 0) { + idx = mPackageGroups.size() + 1; - // Find all packages that reference this package - size_t N = mPackageGroups.size(); - for (size_t i = 0; i < N; i++) { - mPackageGroups[i]->dynamicRefTable.addMapping( - group->name, static_cast<uint8_t>(group->id)); - } - } else { - group = mPackageGroups.itemAt(idx-1); - if (group == NULL) { - return (mError=UNKNOWN_ERROR); - } + char16_t tmpName[sizeof(pkg->name)/sizeof(char16_t)]; + strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(char16_t)); + group = new PackageGroup(this, String16(tmpName), id); + if (group == NULL) { + delete package; + return (mError=NO_MEMORY); } - err = group->packages.add(package); + + //printf("Adding new package id %d at index %d\n", id, idx); + err = mPackageGroups.add(group); if (err < NO_ERROR) { return (mError=err); } + + mPackageMap[id] = static_cast<uint8_t>(idx); + + // Find all packages that reference this package + size_t N = mPackageGroups.size(); + for (size_t i = 0; i < N; i++) { + mPackageGroups[i]->dynamicRefTable.addMapping( + group->name, static_cast<uint8_t>(group->id)); + } } else { - LOG_ALWAYS_FATAL("Package id out of range"); - return NO_ERROR; + group = mPackageGroups.itemAt(idx - 1); + if (group == NULL) { + return (mError=UNKNOWN_ERROR); + } } + err = group->packages.add(package); + if (err < NO_ERROR) { + return (mError=err); + } // Iterate through all chunks. - size_t curPackage = 0; - const ResChunk_header* chunk = (const ResChunk_header*)(((const uint8_t*)pkg) + dtohs(pkg->header.headerSize)); @@ -5597,6 +5622,7 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, } const size_t typeSpecSize = dtohl(typeSpec->header.size); + const size_t newEntryCount = dtohl(typeSpec->entryCount); LOAD_TABLE_NOISY(printf("TypeSpec off %p: type=0x%x, headerSize=0x%x, size=%p\n", (void*)(base-(const uint8_t*)chunk), @@ -5605,12 +5631,11 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, (void*)typeSpecSize)); // look for block overrun or int overflow when multiplying by 4 if ((dtohl(typeSpec->entryCount) > (INT32_MAX/sizeof(uint32_t)) - || dtohs(typeSpec->header.headerSize)+(sizeof(uint32_t)*dtohl(typeSpec->entryCount)) + || dtohs(typeSpec->header.headerSize)+(sizeof(uint32_t)*newEntryCount) > typeSpecSize)) { ALOGW("ResTable_typeSpec entry index to %p extends beyond chunk end %p.", - (void*)(dtohs(typeSpec->header.headerSize) - +(sizeof(uint32_t)*dtohl(typeSpec->entryCount))), - (void*)typeSpecSize); + (void*)(dtohs(typeSpec->header.headerSize) + (sizeof(uint32_t)*newEntryCount)), + (void*)typeSpecSize); return (mError=BAD_TYPE); } @@ -5619,21 +5644,36 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, return (mError=BAD_TYPE); } - while (package->types.size() < typeSpec->id) { - package->types.add(NULL); - } - Type* t = package->types[typeSpec->id-1]; - if (t == NULL) { - t = new Type(header, package, dtohl(typeSpec->entryCount)); - package->types.editItemAt(typeSpec->id-1) = t; - } else if (dtohl(typeSpec->entryCount) != t->entryCount) { - ALOGW("ResTable_typeSpec entry count inconsistent: given %d, previously %d", - (int)dtohl(typeSpec->entryCount), (int)t->entryCount); - return (mError=BAD_TYPE); + if (newEntryCount > 0) { + uint8_t typeIndex = typeSpec->id - 1; + ssize_t idmapIndex = idmapEntries.indexOfKey(typeSpec->id); + if (idmapIndex >= 0) { + typeIndex = idmapEntries[idmapIndex].targetTypeId() - 1; + } + + TypeList& typeList = group->types.editItemAt(typeIndex); + if (!typeList.isEmpty()) { + const Type* existingType = typeList[0]; + if (existingType->entryCount != newEntryCount && idmapIndex < 0) { + ALOGW("ResTable_typeSpec entry count inconsistent: given %d, previously %d", + (int) newEntryCount, (int) existingType->entryCount); + // We should normally abort here, but some legacy apps declare + // resources in the 'android' package (old bug in AAPT). + } + } + + Type* t = new Type(header, package, newEntryCount); + t->typeSpec = typeSpec; + t->typeSpecFlags = (const uint32_t*)( + ((const uint8_t*)typeSpec) + dtohs(typeSpec->header.headerSize)); + if (idmapIndex >= 0) { + t->idmapEntries = idmapEntries[idmapIndex]; + } + typeList.add(t); + group->largestTypeId = max(group->largestTypeId, typeSpec->id); + } else { + ALOGV("Skipping empty ResTable_typeSpec for type %d", typeSpec->id); } - t->typeSpecFlags = (const uint32_t*)( - ((const uint8_t*)typeSpec) + dtohs(typeSpec->header.headerSize)); - t->typeSpec = typeSpec; } else if (ctype == RES_TABLE_TYPE_TYPE) { const ResTable_type* type = (const ResTable_type*)(chunk); @@ -5644,50 +5684,69 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, } const uint32_t typeSize = dtohl(type->header.size); + const size_t newEntryCount = dtohl(type->entryCount); LOAD_TABLE_NOISY(printf("Type off %p: type=0x%x, headerSize=0x%x, size=%p\n", (void*)(base-(const uint8_t*)chunk), dtohs(type->header.type), dtohs(type->header.headerSize), (void*)typeSize)); - if (dtohs(type->header.headerSize)+(sizeof(uint32_t)*dtohl(type->entryCount)) - > typeSize) { + if (dtohs(type->header.headerSize)+(sizeof(uint32_t)*newEntryCount) + > typeSize) { ALOGW("ResTable_type entry index to %p extends beyond chunk end 0x%x.", - (void*)(dtohs(type->header.headerSize) - +(sizeof(uint32_t)*dtohl(type->entryCount))), - typeSize); + (void*)(dtohs(type->header.headerSize) + (sizeof(uint32_t)*newEntryCount)), + typeSize); return (mError=BAD_TYPE); } - if (dtohl(type->entryCount) != 0 + + if (newEntryCount != 0 && dtohl(type->entriesStart) > (typeSize-sizeof(ResTable_entry))) { 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) { ALOGW("ResTable_type has an id of 0."); return (mError=BAD_TYPE); } - while (package->types.size() < type->id) { - package->types.add(NULL); - } - Type* t = package->types[type->id-1]; - if (t == NULL) { - t = new Type(header, package, dtohl(type->entryCount)); - package->types.editItemAt(type->id-1) = t; - } else if (dtohl(type->entryCount) != t->entryCount) { - ALOGW("ResTable_type entry count inconsistent: given %d, previously %d", - (int)dtohl(type->entryCount), (int)t->entryCount); - return (mError=BAD_TYPE); + if (newEntryCount > 0) { + uint8_t typeIndex = type->id - 1; + ssize_t idmapIndex = idmapEntries.indexOfKey(type->id); + if (idmapIndex >= 0) { + typeIndex = idmapEntries[idmapIndex].targetTypeId() - 1; + } + + TypeList& typeList = group->types.editItemAt(typeIndex); + if (typeList.isEmpty()) { + ALOGE("No TypeSpec for type %d", type->id); + return (mError=BAD_TYPE); + } + + Type* t = typeList.editItemAt(typeList.size() - 1); + if (newEntryCount != t->entryCount) { + ALOGE("ResTable_type entry count inconsistent: given %d, previously %d", + (int)newEntryCount, (int)t->entryCount); + return (mError=BAD_TYPE); + } + + if (t->package != package) { + ALOGE("No TypeSpec for type %d", type->id); + return (mError=BAD_TYPE); + } + + t->configs.add(type); + + TABLE_GETENTRY( + ResTable_config thisConfig; + thisConfig.copyFromDtoH(type->config); + ALOGI("Adding config to type %d: %s\n", + type->id, thisConfig.toString().string())); + } else { + ALOGV("Skipping empty ResTable_type for type %d", type->id); } - TABLE_GETENTRY( - ResTable_config thisConfig; - thisConfig.copyFromDtoH(type->config); - ALOGI("Adding config to type %d: %s\n", - type->id, thisConfig.toString().string())); - t->configs.add(type); } else if (ctype == RES_TABLE_LIBRARY_TYPE) { if (group->dynamicRefTable.entries().size() == 0) { status_t err = group->dynamicRefTable.load((const ResTable_lib_header*) chunk); @@ -5714,10 +5773,6 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, (((const uint8_t*)chunk) + csize); } - if (group->typeCount == 0) { - group->typeCount = package->types.size(); - } - return NO_ERROR; } @@ -5818,6 +5873,12 @@ status_t DynamicRefTable::lookupResourceValue(Res_value* value) const { return NO_ERROR; } +struct IdmapTypeMap { + ssize_t overlayTypeId; + size_t entryOffset; + Vector<uint32_t> entryMap; +}; + status_t ResTable::createIdmap(const ResTable& overlay, uint32_t targetCrc, uint32_t overlayCrc, const char* targetPath, const char* overlayPath, @@ -5828,41 +5889,46 @@ status_t ResTable::createIdmap(const ResTable& overlay, 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; + KeyedVector<uint8_t, IdmapTypeMap> 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); + + // starting size is header + *outSize = ResTable::IDMAP_HEADER_SIZE_BYTES; + + // target package id and number of types in map + *outSize += 2 * sizeof(uint16_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; - - for (size_t typeIndex = 0; typeIndex < typeCount; ++typeIndex) { - ssize_t first = -1; - ssize_t last = -1; - const Type* typeConfigs = pkg->getType(typeIndex); - ssize_t mapIndex = map.add(); - if (mapIndex < 0) { - return NO_MEMORY; + + for (size_t typeIndex = 0; typeIndex < pg->types.size(); ++typeIndex) { + const TypeList& typeList = pg->types[typeIndex]; + if (typeList.isEmpty()) { + continue; } - Vector<uint32_t>& vector = map.editItemAt(mapIndex); + + const Type* typeConfigs = typeList[0]; + + IdmapTypeMap typeMap; + typeMap.overlayTypeId = -1; + typeMap.entryOffset = 0; + for (size_t entryIndex = 0; entryIndex < typeConfigs->entryCount; ++entryIndex) { - uint32_t resID = pkg_id - | (0x00ff0000 & ((typeIndex+1)<<16)) - | (0x0000ffff & (entryIndex)); + uint32_t resID = Res_MAKEID(pg->id - 1, typeIndex, entryIndex); resource_name 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); + if (typeMap.entryMap.isEmpty()) { + typeMap.entryOffset++; + } continue; } @@ -5874,49 +5940,55 @@ status_t ResTable::createIdmap(const ResTable& overlay, overlayType.size(), overlayPackage.string(), overlayPackage.size()); - if (overlayResID != 0) { - overlayResID = pkg_id | (0x00ffffff & overlayResID); - last = Res_GETENTRY(resID); - if (first == -1) { - first = Res_GETENTRY(resID); + if (overlayResID == 0) { + if (typeMap.entryMap.isEmpty()) { + typeMap.entryOffset++; } + continue; } - vector.push(overlayResID); -#if 0 - if (overlayResID != 0) { - ALOGD("%s/%s 0x%08x -> 0x%08x\n", - String8(String16(resName.type)).string(), - String8(String16(resName.name)).string(), - resID, overlayResID); + + if (typeMap.overlayTypeId == -1) { + typeMap.overlayTypeId = Res_GETTYPE(overlayResID) + 1; + } + + if (Res_GETTYPE(overlayResID) + 1 != static_cast<size_t>(typeMap.overlayTypeId)) { + ALOGE("idmap: can't mix type ids in entry map. Resource 0x%08x maps to 0x%08x" + " but entries should map to resources of type %02x", + resID, overlayResID, typeMap.overlayTypeId); + return BAD_TYPE; + } + + if (typeMap.entryOffset + typeMap.entryMap.size() < entryIndex) { + // Resize to accomodate this entry and the 0's in between. + if (typeMap.entryMap.resize((entryIndex - typeMap.entryOffset) + 1) < 0) { + return NO_MEMORY; + } + typeMap.entryMap.editTop() = Res_GETENTRY(overlayResID); + } else { + typeMap.entryMap.add(Res_GETENTRY(overlayResID)); } -#endif } - if (first != -1) { - // shave off trailing entries which lack overlay values - const size_t last_past_one = last + 1; - if (last_past_one < vector.size()) { - vector.removeItemsAt(last_past_one, vector.size() - last_past_one); + if (!typeMap.entryMap.isEmpty()) { + if (map.add(static_cast<uint8_t>(typeIndex), typeMap) < 0) { + return NO_MEMORY; } - // shave off leading entries which lack overlay values - vector.removeItemsAt(0, first); - // store offset to first overlaid resource ID of this type - vector.insertAt((uint32_t)first, 0, 1); - // reserve space for number and offset of entries, and the actual entries - *outSize += (2 + vector.size()) * sizeof(uint32_t); - } else { - // no entries of current type defined in overlay package - vector.clear(); - // reserve space for type offset - *outSize += 1 * sizeof(uint32_t); + *outSize += (4 * sizeof(uint16_t)) + (typeMap.entryMap.size() * sizeof(uint32_t)); } } + if (map.isEmpty()) { + ALOGW("idmap: no resources in overlay package present in base package"); + return UNKNOWN_ERROR; + } + if ((*outData = malloc(*outSize)) == NULL) { return NO_MEMORY; } + uint32_t* data = (uint32_t*)*outData; *data++ = htodl(IDMAP_MAGIC); + *data++ = htodl(IDMAP_CURRENT_VERSION); *data++ = htodl(targetCrc); *data++ = htodl(overlayCrc); const char* paths[] = { targetPath, overlayPath }; @@ -5934,44 +6006,30 @@ status_t ResTable::createIdmap(const ResTable& overlay, data += 256 / sizeof(uint32_t); } const size_t mapSize = map.size(); - *data++ = htodl(mapSize); - size_t offset = mapSize; + uint16_t* typeData = reinterpret_cast<uint16_t*>(data); + *typeData++ = htods(pg->id); + *typeData++ = htods(mapSize); for (size_t i = 0; i < mapSize; ++i) { - const Vector<uint32_t>& vector = map.itemAt(i); - const size_t N = vector.size(); - if (N == 0) { - *data++ = htodl(0); - } else { - offset++; - *data++ = htodl(offset); - 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(); - if (N == 0) { - continue; - } - if (N == 1) { // vector expected to hold (offset) + (N > 0 entries) - ALOGW("idmap: type %u supposedly has entries, but no entries found\n", (uint32_t)i); - return UNKNOWN_ERROR; - } - *data++ = htodl(N - 1); // do not count the offset (which is vector's first element) - for (size_t j = 0; j < N; ++j) { - const uint32_t& overlayResID = vector.itemAt(j); - *data++ = htodl(overlayResID); + uint8_t targetTypeId = map.keyAt(i); + const IdmapTypeMap& typeMap = map[i]; + *typeData++ = htods(targetTypeId + 1); + *typeData++ = htods(typeMap.overlayTypeId); + *typeData++ = htods(typeMap.entryMap.size()); + *typeData++ = htods(typeMap.entryOffset); + + const size_t entryCount = typeMap.entryMap.size(); + uint32_t* entries = reinterpret_cast<uint32_t*>(typeData); + for (size_t j = 0; j < entryCount; j++) { + entries[j] = htodl(typeMap.entryMap[j]); } + typeData += entryCount * 2; } return NO_ERROR; } bool ResTable::getIdmapInfo(const void* idmap, size_t sizeBytes, + uint32_t* pVersion, uint32_t* pTargetCrc, uint32_t* pOverlayCrc, String8* pTargetPath, String8* pOverlayPath) { @@ -5979,17 +6037,20 @@ bool ResTable::getIdmapInfo(const void* idmap, size_t sizeBytes, if (!assertIdmapHeader(map, sizeBytes)) { return false; } + if (pVersion) { + *pVersion = dtohl(map[1]); + } if (pTargetCrc) { - *pTargetCrc = map[1]; + *pTargetCrc = dtohl(map[2]); } if (pOverlayCrc) { - *pOverlayCrc = map[2]; + *pOverlayCrc = dtohl(map[3]); } if (pTargetPath) { - pTargetPath->setTo(reinterpret_cast<const char*>(map + 3)); + pTargetPath->setTo(reinterpret_cast<const char*>(map + 4)); } if (pOverlayPath) { - pOverlayPath->setTo(reinterpret_cast<const char*>(map + 3 + 256 / sizeof(uint32_t))); + pOverlayPath->setTo(reinterpret_cast<const char*>(map + 4 + 256 / sizeof(uint32_t))); } return true; } @@ -6138,184 +6199,184 @@ void ResTable::print(bool inclValues) const size_t pkgCount = pg->packages.size(); for (size_t pkgIndex=0; pkgIndex<pkgCount; pkgIndex++) { const Package* pkg = pg->packages[pkgIndex]; - size_t typeCount = pkg->types.size(); - printf(" Package %d id=%d name=%s typeCount=%d\n", (int)pkgIndex, - pkg->package->id, String8(String16(pkg->package->name)).string(), - (int)typeCount); - for (size_t typeIndex=0; typeIndex<typeCount; typeIndex++) { - const Type* typeConfigs = pkg->getType(typeIndex); - if (typeConfigs == NULL) { - printf(" type %d NULL\n", (int)typeIndex); + printf(" Package %d id=%d name=%s\n", (int)pkgIndex, + pkg->package->id, String8(String16(pkg->package->name)).string()); + } + + for (size_t typeIndex=0; typeIndex < pg->types.size(); typeIndex++) { + const TypeList& typeList = pg->types[typeIndex]; + if (typeList.isEmpty()) { + //printf(" type %d NULL\n", (int)typeIndex); + continue; + } + const Type* typeConfigs = typeList[0]; + const size_t NTC = typeConfigs->configs.size(); + printf(" type %d configCount=%d entryCount=%d\n", + (int)typeIndex, (int)NTC, (int)typeConfigs->entryCount); + if (typeConfigs->typeSpecFlags != NULL) { + for (size_t entryIndex=0; entryIndex<typeConfigs->entryCount; entryIndex++) { + uint32_t resID = (0xff000000 & ((pg->id)<<24)) + | (0x00ff0000 & ((typeIndex+1)<<16)) + | (0x0000ffff & (entryIndex)); + // Since we are creating resID without actually + // iterating over them, we have no idea which is a + // dynamic reference. We must check. + pg->dynamicRefTable.lookupResourceId(&resID); + + resource_name resName; + if (this->getResourceName(resID, true, &resName)) { + String8 type8; + String8 name8; + if (resName.type8 != NULL) { + type8 = String8(resName.type8, resName.typeLen); + } else { + type8 = String8(resName.type, resName.typeLen); + } + if (resName.name8 != NULL) { + name8 = String8(resName.name8, resName.nameLen); + } else { + name8 = String8(resName.name, resName.nameLen); + } + printf(" spec resource 0x%08x %s:%s/%s: flags=0x%08x\n", + resID, + CHAR16_TO_CSTR(resName.package, resName.packageLen), + type8.string(), name8.string(), + dtohl(typeConfigs->typeSpecFlags[entryIndex])); + } else { + printf(" INVALID TYPE CONFIG FOR RESOURCE 0x%08x\n", resID); + } + } + } + for (size_t configIndex=0; configIndex<NTC; configIndex++) { + const ResTable_type* type = typeConfigs->configs[configIndex]; + if ((((uint64_t)type)&0x3) != 0) { + printf(" NON-INTEGER ResTable_type ADDRESS: %p\n", type); continue; } - const size_t NTC = typeConfigs->configs.size(); - printf(" type %d configCount=%d entryCount=%d\n", - (int)typeIndex, (int)NTC, (int)typeConfigs->entryCount); - if (typeConfigs->typeSpecFlags != NULL) { - for (size_t entryIndex=0; entryIndex<typeConfigs->entryCount; entryIndex++) { - uint32_t resID = (0xff000000 & ((pkg->package->id)<<24)) - | (0x00ff0000 & ((typeIndex+1)<<16)) - | (0x0000ffff & (entryIndex)); - // Since we are creating resID without actually - // iterating over them, we have no idea which is a - // dynamic reference. We must check. - pg->dynamicRefTable.lookupResourceId(&resID); - - resource_name resName; - if (this->getResourceName(resID, true, &resName)) { - String8 type8; - String8 name8; - if (resName.type8 != NULL) { - type8 = String8(resName.type8, resName.typeLen); - } else { - type8 = String8(resName.type, resName.typeLen); - } - if (resName.name8 != NULL) { - name8 = String8(resName.name8, resName.nameLen); - } else { - name8 = String8(resName.name, resName.nameLen); - } - printf(" spec resource 0x%08x %s:%s/%s: flags=0x%08x\n", - resID, - CHAR16_TO_CSTR(resName.package, resName.packageLen), - type8.string(), name8.string(), - dtohl(typeConfigs->typeSpecFlags[entryIndex])); + String8 configStr = type->config.toString(); + printf(" config %s:\n", configStr.size() > 0 + ? configStr.string() : "(default)"); + size_t entryCount = dtohl(type->entryCount); + uint32_t entriesStart = dtohl(type->entriesStart); + if ((entriesStart&0x3) != 0) { + 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: 0x%x\n", typeSize); + continue; + } + for (size_t entryIndex=0; entryIndex<entryCount; entryIndex++) { + + const uint8_t* const end = ((const uint8_t*)type) + + dtohl(type->header.size); + const uint32_t* const eindex = (const uint32_t*) + (((const uint8_t*)type) + dtohs(type->header.headerSize)); + + uint32_t thisOffset = dtohl(eindex[entryIndex]); + if (thisOffset == ResTable_type::NO_ENTRY) { + continue; + } + + uint32_t resID = (0xff000000 & ((pg->id)<<24)) + | (0x00ff0000 & ((typeIndex+1)<<16)) + | (0x0000ffff & (entryIndex)); + pg->dynamicRefTable.lookupResourceId(&resID); + resource_name resName; + if (this->getResourceName(resID, true, &resName)) { + String8 type8; + String8 name8; + if (resName.type8 != NULL) { + type8 = String8(resName.type8, resName.typeLen); + } else { + type8 = String8(resName.type, resName.typeLen); + } + if (resName.name8 != NULL) { + name8 = String8(resName.name8, resName.nameLen); } else { - printf(" INVALID TYPE CONFIG FOR RESOURCE 0x%08x\n", resID); + name8 = String8(resName.name, resName.nameLen); } + printf(" resource 0x%08x %s:%s/%s: ", resID, + CHAR16_TO_CSTR(resName.package, resName.packageLen), + type8.string(), name8.string()); + } else { + printf(" INVALID RESOURCE 0x%08x: ", resID); } - } - for (size_t configIndex=0; configIndex<NTC; configIndex++) { - const ResTable_type* type = typeConfigs->configs[configIndex]; - if ((((uint64_t)type)&0x3) != 0) { - printf(" NON-INTEGER ResTable_type ADDRESS: %p\n", type); + if ((thisOffset&0x3) != 0) { + printf("NON-INTEGER OFFSET: 0x%x\n", thisOffset); continue; } - String8 configStr = type->config.toString(); - printf(" config %s:\n", configStr.size() > 0 - ? configStr.string() : "(default)"); - size_t entryCount = dtohl(type->entryCount); - uint32_t entriesStart = dtohl(type->entriesStart); - if ((entriesStart&0x3) != 0) { - printf(" NON-INTEGER ResTable_type entriesStart OFFSET: 0x%x\n", entriesStart); + if ((thisOffset+sizeof(ResTable_entry)) > typeSize) { + printf("OFFSET OUT OF BOUNDS: 0x%x+0x%x (size is 0x%x)\n", + entriesStart, thisOffset, typeSize); continue; } - uint32_t typeSize = dtohl(type->header.size); - if ((typeSize&0x3) != 0) { - printf(" NON-INTEGER ResTable_type header.size: 0x%x\n", typeSize); + + const ResTable_entry* ent = (const ResTable_entry*) + (((const uint8_t*)type) + entriesStart + thisOffset); + if (((entriesStart + thisOffset)&0x3) != 0) { + printf("NON-INTEGER ResTable_entry OFFSET: 0x%x\n", + (entriesStart + thisOffset)); continue; } - for (size_t entryIndex=0; entryIndex<entryCount; entryIndex++) { - const uint8_t* const end = ((const uint8_t*)type) - + dtohl(type->header.size); - const uint32_t* const eindex = (const uint32_t*) - (((const uint8_t*)type) + dtohs(type->header.headerSize)); + uintptr_t esize = dtohs(ent->size); + if ((esize&0x3) != 0) { + printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void *)esize); + continue; + } + if ((thisOffset+esize) > typeSize) { + printf("ResTable_entry OUT OF BOUNDS: 0x%x+0x%x+%p (size is 0x%x)\n", + entriesStart, thisOffset, (void *)esize, typeSize); + continue; + } - uint32_t thisOffset = dtohl(eindex[entryIndex]); - if (thisOffset == ResTable_type::NO_ENTRY) { - continue; - } + const Res_value* valuePtr = NULL; + const ResTable_map_entry* bagPtr = NULL; + Res_value value; + if ((dtohs(ent->flags)&ResTable_entry::FLAG_COMPLEX) != 0) { + printf("<bag>"); + bagPtr = (const ResTable_map_entry*)ent; + } else { + valuePtr = (const Res_value*) + (((const uint8_t*)ent) + esize); + value.copyFrom_dtoh(*valuePtr); + printf("t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)", + (int)value.dataType, (int)value.data, + (int)value.size, (int)value.res0); + } - uint32_t resID = (0xff000000 & ((pkg->package->id)<<24)) - | (0x00ff0000 & ((typeIndex+1)<<16)) - | (0x0000ffff & (entryIndex)); - pg->dynamicRefTable.lookupResourceId(&resID); - resource_name resName; - if (this->getResourceName(resID, true, &resName)) { - String8 type8; - String8 name8; - if (resName.type8 != NULL) { - type8 = String8(resName.type8, resName.typeLen); - } else { - type8 = String8(resName.type, resName.typeLen); - } - if (resName.name8 != NULL) { - name8 = String8(resName.name8, resName.nameLen); - } else { - name8 = String8(resName.name, resName.nameLen); + if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) { + printf(" (PUBLIC)"); + } + printf("\n"); + + if (inclValues) { + if (valuePtr != NULL) { + printf(" "); + print_value(typeConfigs->package, value); + } else if (bagPtr != NULL) { + const int N = dtohl(bagPtr->count); + const uint8_t* baseMapPtr = (const uint8_t*)ent; + size_t mapOffset = esize; + const ResTable_map* mapPtr = (ResTable_map*)(baseMapPtr+mapOffset); + const uint32_t parent = dtohl(bagPtr->parent.ident); + uint32_t resolvedParent = parent; + status_t err = pg->dynamicRefTable.lookupResourceId(&resolvedParent); + if (err != NO_ERROR) { + resolvedParent = 0; } - printf(" resource 0x%08x %s:%s/%s: ", resID, - CHAR16_TO_CSTR(resName.package, resName.packageLen), - type8.string(), name8.string()); - } else { - printf(" INVALID RESOURCE 0x%08x: ", resID); - } - if ((thisOffset&0x3) != 0) { - printf("NON-INTEGER OFFSET: 0x%x\n", thisOffset); - continue; - } - if ((thisOffset+sizeof(ResTable_entry)) > 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: 0x%x\n", - (entriesStart + thisOffset)); - continue; - } - - uintptr_t esize = dtohs(ent->size); - if ((esize&0x3) != 0) { - printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void *)esize); - continue; - } - if ((thisOffset+esize) > typeSize) { - printf("ResTable_entry OUT OF BOUNDS: 0x%x+0x%x+%p (size is 0x%x)\n", - entriesStart, thisOffset, (void *)esize, typeSize); - continue; - } - - const Res_value* valuePtr = NULL; - const ResTable_map_entry* bagPtr = NULL; - Res_value value; - if ((dtohs(ent->flags)&ResTable_entry::FLAG_COMPLEX) != 0) { - printf("<bag>"); - bagPtr = (const ResTable_map_entry*)ent; - } else { - valuePtr = (const Res_value*) - (((const uint8_t*)ent) + esize); - value.copyFrom_dtoh(*valuePtr); - printf("t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)", - (int)value.dataType, (int)value.data, - (int)value.size, (int)value.res0); - } - - if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) { - printf(" (PUBLIC)"); - } - printf("\n"); - - if (inclValues) { - if (valuePtr != NULL) { - printf(" "); - print_value(pkg, value); - } else if (bagPtr != NULL) { - const int N = dtohl(bagPtr->count); - const uint8_t* baseMapPtr = (const uint8_t*)ent; - size_t mapOffset = esize; - const ResTable_map* mapPtr = (ResTable_map*)(baseMapPtr+mapOffset); - const uint32_t parent = dtohl(bagPtr->parent.ident); - uint32_t resolvedParent = parent; - status_t err = pg->dynamicRefTable.lookupResourceId(&resolvedParent); - if (err != NO_ERROR) { - resolvedParent = 0; - } - printf(" Parent=0x%08x(Resolved=0x%08x), Count=%d\n", - parent, resolvedParent, N); - for (int i=0; i<N && mapOffset < (typeSize-sizeof(ResTable_map)); i++) { - printf(" #%i (Key=0x%08x): ", - i, dtohl(mapPtr->name.ident)); - value.copyFrom_dtoh(mapPtr->value); - print_value(pkg, value); - const size_t size = dtohs(mapPtr->value.size); - mapOffset += size + sizeof(*mapPtr)-sizeof(mapPtr->value); - mapPtr = (ResTable_map*)(baseMapPtr+mapOffset); - } + printf(" Parent=0x%08x(Resolved=0x%08x), Count=%d\n", + parent, resolvedParent, N); + for (int i=0; i<N && mapOffset < (typeSize-sizeof(ResTable_map)); i++) { + printf(" #%i (Key=0x%08x): ", + i, dtohl(mapPtr->name.ident)); + value.copyFrom_dtoh(mapPtr->value); + print_value(typeConfigs->package, value); + const size_t size = dtohs(mapPtr->value.size); + mapOffset += size + sizeof(*mapPtr)-sizeof(mapPtr->value); + mapPtr = (ResTable_map*)(baseMapPtr+mapOffset); } } } diff --git a/libs/androidfw/TypeWrappers.cpp b/libs/androidfw/TypeWrappers.cpp new file mode 100644 index 0000000..06b4040 --- /dev/null +++ b/libs/androidfw/TypeWrappers.cpp @@ -0,0 +1,70 @@ +/* + * 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/TypeWrappers.h> + +namespace android { + +TypeVariant::iterator& TypeVariant::iterator::operator++() { + mIndex++; + if (mIndex > dtohl(mTypeVariant->data->entryCount)) { + mIndex = dtohl(mTypeVariant->data->entryCount); + } + return *this; +} + +const ResTable_entry* TypeVariant::iterator::operator*() const { + const ResTable_type* type = mTypeVariant->data; + const uint32_t entryCount = dtohl(type->entryCount); + if (mIndex >= entryCount) { + return NULL; + } + + const uintptr_t containerEnd = reinterpret_cast<uintptr_t>(type) + + dtohl(type->header.size); + const uint32_t* const entryIndices = reinterpret_cast<const uint32_t*>( + reinterpret_cast<uintptr_t>(type) + dtohs(type->header.headerSize)); + if (reinterpret_cast<uintptr_t>(entryIndices) + (sizeof(uint32_t) * entryCount) > containerEnd) { + ALOGE("Type's entry indices extend beyond its boundaries"); + return NULL; + } + + const uint32_t entryOffset = dtohl(entryIndices[mIndex]); + if (entryOffset == ResTable_type::NO_ENTRY) { + return NULL; + } + + if ((entryOffset & 0x3) != 0) { + ALOGE("Index %u points to entry with unaligned offset 0x%08x", mIndex, entryOffset); + return NULL; + } + + const ResTable_entry* entry = reinterpret_cast<const ResTable_entry*>( + reinterpret_cast<uintptr_t>(type) + dtohl(type->entriesStart) + entryOffset); + if (reinterpret_cast<uintptr_t>(entry) > containerEnd - sizeof(*entry)) { + ALOGE("Entry offset at index %u points outside the Type's boundaries", mIndex); + return NULL; + } else if (reinterpret_cast<uintptr_t>(entry) + dtohs(entry->size) > containerEnd) { + ALOGE("Entry at index %u extends beyond Type's boundaries", mIndex); + return NULL; + } else if (dtohs(entry->size) < sizeof(*entry)) { + ALOGE("Entry at index %u is too small (%u)", mIndex, dtohs(entry->size)); + return NULL; + } + return entry; +} + +} // namespace android diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk index 9e9649c..4ff6eec 100644 --- a/libs/androidfw/tests/Android.mk +++ b/libs/androidfw/tests/Android.mk @@ -1,33 +1,66 @@ -# Build the unit tests. +# +# 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. +# + +# ========================================================== +# Setup some common variables for the different build +# targets here. +# ========================================================== LOCAL_PATH:= $(call my-dir) +testFiles := \ + ByteBucketArray_test.cpp \ + Idmap_test.cpp \ + ResourceTypes_test.cpp \ + ResTable_test.cpp \ + Split_test.cpp \ + TypeWrappers_test.cpp \ + ZipUtils_test.cpp + +# ========================================================== +# Build the host tests: libandroidfw_tests +# ========================================================== +include $(CLEAR_VARS) + +LOCAL_MODULE := libandroidfw_tests + +LOCAL_SRC_FILES := $(testFiles) +LOCAL_STATIC_LIBRARIES := \ + libandroidfw \ + libutils \ + libcutils \ + liblog + +include $(BUILD_HOST_NATIVE_TEST) + + +# ========================================================== +# Build the device tests: libandroidfw_tests +# ========================================================== include $(CLEAR_VARS) -# Build the unit tests. -test_src_files := \ +LOCAL_MODULE := libandroidfw_tests + +LOCAL_SRC_FILES := $(testFiles) \ BackupData_test.cpp \ - ObbFile_test.cpp \ - ZipUtils_test.cpp \ - ResourceTypes_test.cpp + ObbFile_test.cpp -shared_libraries := \ +LOCAL_SHARED_LIBRARIES := \ libandroidfw \ libcutils \ libutils \ libui \ libstlport -static_libraries := \ - libgtest \ - libgtest_main - -$(foreach file,$(test_src_files), \ - $(eval include $(CLEAR_VARS)) \ - $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \ - $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \ - $(eval LOCAL_SRC_FILES := $(file)) \ - $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \ - $(eval include $(BUILD_NATIVE_TEST)) \ -) - -# Build the manual test programs. -include $(call all-makefiles-under, $(LOCAL_PATH)) +include $(BUILD_NATIVE_TEST) diff --git a/libs/androidfw/tests/ByteBucketArray_test.cpp b/libs/androidfw/tests/ByteBucketArray_test.cpp new file mode 100644 index 0000000..376e79c --- /dev/null +++ b/libs/androidfw/tests/ByteBucketArray_test.cpp @@ -0,0 +1,41 @@ +/* + * 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/ByteBucketArray.h> + +#include <gtest/gtest.h> + +using android::ByteBucketArray; + +TEST(ByteBucketArrayTest, TestSparseInsertion) { + ByteBucketArray<int> bba; + ASSERT_TRUE(bba.set(0, 1)); + ASSERT_TRUE(bba.set(10, 2)); + ASSERT_TRUE(bba.set(26, 3)); + ASSERT_TRUE(bba.set(129, 4)); + ASSERT_TRUE(bba.set(234, 5)); + + for (size_t i = 0; i < bba.size(); i++) { + switch (i) { + case 0: EXPECT_EQ(1, bba[i]); break; + case 10: EXPECT_EQ(2, bba[i]); break; + case 26: EXPECT_EQ(3, bba[i]); break; + case 129: EXPECT_EQ(4, bba[i]); break; + case 234: EXPECT_EQ(5, bba[i]); break; + default: EXPECT_EQ(0, bba[i]); break; + } + } +} diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp new file mode 100644 index 0000000..d829b76 --- /dev/null +++ b/libs/androidfw/tests/Idmap_test.cpp @@ -0,0 +1,124 @@ +/* + * 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/String8.h> +#include <utils/String16.h> +#include "TestHelpers.h" + +#include <gtest/gtest.h> + +using namespace android; + +namespace { + +/** + * Include a binary resource table. + * + * Package: com.android.test.basic + */ +#include "data/basic/basic_arsc.h" + +/** + * Include a binary resource table. + * This table is an overlay. + * + * Package: com.android.test.basic + */ +#include "data/overlay/overlay_arsc.h" + +enum { MAY_NOT_BE_BAG = false }; + +static const uint32_t attr_attr1 = 0x7f010000; +static const uint32_t attr_attr2 = 0x7f010001; +static const uint32_t string_test1 = 0x7f020000; +static const uint32_t string_test2 = 0x7f020001; +static const uint32_t integer_number1 = 0x7f030000; +static const uint32_t integer_number2 = 0x7f030001; +static const uint32_t style_Theme1 = 0x7f040000; +static const uint32_t style_Theme2 = 0x7f040001; +static const uint32_t array_integerArray1 = 0x7f050000; + +class IdmapTest : public ::testing::Test { +protected: + virtual void SetUp() { + ASSERT_EQ(NO_ERROR, mTargetTable.add(basic_arsc, basic_arsc_len)); + ASSERT_EQ(NO_ERROR, mOverlayTable.add(overlay_arsc, overlay_arsc_len)); + char targetName[256] = "com.android.test.basic"; + ASSERT_EQ(NO_ERROR, mTargetTable.createIdmap(mOverlayTable, 0, 0, + targetName, targetName, &mData, &mDataSize)); + } + + virtual void TearDown() { + free(mData); + } + + ResTable mTargetTable; + ResTable mOverlayTable; + void* mData; + size_t mDataSize; +}; + +TEST_F(IdmapTest, canLoadIdmap) { + ASSERT_EQ(NO_ERROR, mTargetTable.add(overlay_arsc, overlay_arsc_len, mData, mDataSize)); +} + +TEST_F(IdmapTest, overlayOverridesResourceValue) { + Res_value val; + ssize_t block = mTargetTable.getResource(string_test2, &val, false); + ASSERT_GE(block, 0); + ASSERT_EQ(Res_value::TYPE_STRING, val.dataType); + const ResStringPool* pool = mTargetTable.getTableStringBlock(block); + ASSERT_TRUE(pool != NULL); + ASSERT_LT(val.data, pool->size()); + + size_t strLen; + const char16_t* targetStr16 = pool->stringAt(val.data, &strLen); + ASSERT_TRUE(targetStr16 != NULL); + ASSERT_EQ(String16("test2"), String16(targetStr16, strLen)); + + ASSERT_EQ(NO_ERROR, mTargetTable.add(overlay_arsc, overlay_arsc_len, mData, mDataSize)); + + ssize_t newBlock = mTargetTable.getResource(string_test2, &val, false); + ASSERT_GE(newBlock, 0); + ASSERT_NE(block, newBlock); + ASSERT_EQ(Res_value::TYPE_STRING, val.dataType); + pool = mTargetTable.getTableStringBlock(newBlock); + ASSERT_TRUE(pool != NULL); + ASSERT_LT(val.data, pool->size()); + + targetStr16 = pool->stringAt(val.data, &strLen); + ASSERT_TRUE(targetStr16 != NULL); + ASSERT_EQ(String16("test2-overlay"), String16(targetStr16, strLen)); +} + +TEST_F(IdmapTest, overlaidResourceHasSameName) { + ASSERT_EQ(NO_ERROR, mTargetTable.add(overlay_arsc, overlay_arsc_len, mData, mDataSize)); + + ResTable::resource_name resName; + ASSERT_TRUE(mTargetTable.getResourceName(array_integerArray1, false, &resName)); + + ASSERT_TRUE(resName.package != NULL); + ASSERT_TRUE(resName.type != NULL); + ASSERT_TRUE(resName.name != NULL); + + EXPECT_EQ(String16("com.android.test.basic"), String16(resName.package, resName.packageLen)); + EXPECT_EQ(String16("array"), String16(resName.type, resName.typeLen)); + EXPECT_EQ(String16("integerArray1"), String16(resName.name, resName.nameLen)); +} + +} // namespace diff --git a/libs/androidfw/tests/ResTable_test.cpp b/libs/androidfw/tests/ResTable_test.cpp new file mode 100644 index 0000000..54d42c3 --- /dev/null +++ b/libs/androidfw/tests/ResTable_test.cpp @@ -0,0 +1,189 @@ +/* + * 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/String8.h> +#include <utils/String16.h> +#include "TestHelpers.h" + +#include <gtest/gtest.h> + +using namespace android; + +namespace { + +/** + * Include a binary resource table. + * + * Package: com.android.test.basic + */ +#include "data/basic/basic_arsc.h" + +enum { MAY_NOT_BE_BAG = false }; + +static const uint32_t attr_attr1 = 0x7f010000; +static const uint32_t attr_attr2 = 0x7f010001; +static const uint32_t string_test1 = 0x7f020000; +static const uint32_t string_test2 = 0x7f020001; +static const uint32_t integer_number1 = 0x7f030000; +static const uint32_t integer_number2 = 0x7f030001; +static const uint32_t style_Theme1 = 0x7f040000; +static const uint32_t style_Theme2 = 0x7f040001; +static const uint32_t array_integerArray1 = 0x7f050000; + +TEST(ResTableTest, shouldLoadSuccessfully) { + ResTable table; + ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len)); +} + +TEST(ResTableTest, simpleTypeIsRetrievedCorrectly) { + ResTable table; + ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len)); + + Res_value val; + ssize_t block = table.getResource(string_test1, &val, MAY_NOT_BE_BAG); + + ASSERT_GE(block, 0); + ASSERT_EQ(Res_value::TYPE_STRING, val.dataType); + + const ResStringPool* pool = table.getTableStringBlock(block); + ASSERT_TRUE(NULL != pool); + ASSERT_EQ(String8("test1"), pool->string8ObjectAt(val.data)); +} + +TEST(ResTableTest, resourceNameIsResolved) { + ResTable table; + ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len)); + + String16 defPackage("com.android.test.basic"); + String16 testName("@string/test1"); + uint32_t resID = table.identifierForName(testName.string(), testName.size(), + 0, 0, + defPackage.string(), defPackage.size()); + ASSERT_NE(uint32_t(0x00000000), resID); + ASSERT_EQ(string_test1, resID); +} + +TEST(ResTableTest, noParentThemeIsAppliedCorrectly) { + ResTable table; + ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len)); + + ResTable::Theme theme(table); + ASSERT_EQ(NO_ERROR, theme.applyStyle(style_Theme1)); + + Res_value val; + uint32_t specFlags = 0; + ssize_t index = theme.getAttribute(attr_attr1, &val, &specFlags); + ASSERT_GE(index, 0); + ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); + ASSERT_EQ(uint32_t(100), val.data); + + index = theme.getAttribute(attr_attr2, &val, &specFlags); + ASSERT_GE(index, 0); + ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType); + ASSERT_EQ(integer_number1, val.data); +} + +TEST(ResTableTest, parentThemeIsAppliedCorrectly) { + ResTable table; + ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len)); + + ResTable::Theme theme(table); + ASSERT_EQ(NO_ERROR, theme.applyStyle(style_Theme2)); + + Res_value val; + uint32_t specFlags = 0; + ssize_t index = theme.getAttribute(attr_attr1, &val, &specFlags); + ASSERT_GE(index, 0); + ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); + ASSERT_EQ(uint32_t(300), val.data); + + index = theme.getAttribute(attr_attr2, &val, &specFlags); + ASSERT_GE(index, 0); + ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType); + ASSERT_EQ(integer_number1, val.data); +} + +TEST(ResTableTest, referenceToBagIsNotResolved) { + ResTable table; + ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len)); + + Res_value val; + ssize_t block = table.getResource(integer_number2, &val, MAY_NOT_BE_BAG); + ASSERT_GE(block, 0); + ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType); + ASSERT_EQ(array_integerArray1, val.data); + + ssize_t newBlock = table.resolveReference(&val, block); + EXPECT_EQ(block, newBlock); + EXPECT_EQ(Res_value::TYPE_REFERENCE, val.dataType); + EXPECT_EQ(array_integerArray1, val.data); +} + +TEST(ResTableTest, resourcesStillAccessibleAfterParameterChange) { + ResTable table; + ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len)); + + Res_value val; + ssize_t block = table.getResource(integer_number1, &val, MAY_NOT_BE_BAG); + ASSERT_GE(block, 0); + ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); + + const ResTable::bag_entry* entry; + ssize_t count = table.lockBag(array_integerArray1, &entry); + ASSERT_GE(count, 0); + table.unlockBag(entry); + + ResTable_config param; + memset(¶m, 0, sizeof(param)); + param.density = 320; + table.setParameters(¶m); + + block = table.getResource(integer_number1, &val, MAY_NOT_BE_BAG); + ASSERT_GE(block, 0); + ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); + + count = table.lockBag(array_integerArray1, &entry); + ASSERT_GE(count, 0); + table.unlockBag(entry); +} + +TEST(ResTableTest, resourceIsOverridenWithBetterConfig) { + ResTable table; + ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len)); + + Res_value val; + ssize_t block = table.getResource(integer_number1, &val, MAY_NOT_BE_BAG); + ASSERT_GE(block, 0); + ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); + ASSERT_EQ(uint32_t(200), val.data); + + ResTable_config param; + memset(¶m, 0, sizeof(param)); + param.language[0] = 's'; + param.language[1] = 'v'; + param.country[0] = 'S'; + param.country[1] = 'E'; + table.setParameters(¶m); + + block = table.getResource(integer_number1, &val, MAY_NOT_BE_BAG); + ASSERT_GE(block, 0); + ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); + ASSERT_EQ(uint32_t(400), val.data); +} + +} diff --git a/libs/androidfw/tests/ResourceTypes_test.cpp b/libs/androidfw/tests/ResourceTypes_test.cpp index 4888b4a..6041e08 100644 --- a/libs/androidfw/tests/ResourceTypes_test.cpp +++ b/libs/androidfw/tests/ResourceTypes_test.cpp @@ -64,8 +64,8 @@ TEST(ResourceTypesTest, ResourceConfig_packAndUnpack3LetterLanguage) { config.packLanguage("eng"); // 1-00110-01 101-00100 - EXPECT_EQ(0x99, config.language[0]); - EXPECT_EQ(0xa4, config.language[1]); + EXPECT_EQ('\x99', config.language[0]); + EXPECT_EQ('\xA4', config.language[1]); char out[4] = { 1, 1, 1, 1}; config.unpackLanguage(out); diff --git a/libs/androidfw/tests/Split_test.cpp b/libs/androidfw/tests/Split_test.cpp new file mode 100644 index 0000000..dbfdeae --- /dev/null +++ b/libs/androidfw/tests/Split_test.cpp @@ -0,0 +1,173 @@ +/* + * 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/String8.h> +#include <utils/String16.h> +#include "TestHelpers.h" + +#include <gtest/gtest.h> + +/** + * Include a binary resource table. This table + * is a base table for an APK split. + * + * Package: com.android.example.split + * + * layout/main 0x7f020000 {default, fr-sw600dp-v13} + * + * string/app_title 0x7f030000 {default} + * string/test 0x7f030001 {default} + * string/boom 0x7f030002 {default} + * string/blah 0x7f030003 {default} + * + * array/lotsofstrings 0x7f040000 {default} + * array/numList 0x7f040001 {default} + * array/ary 0x7f040002 {default} + * + */ +#include "data/split_base_arsc.h" + +/** + * Include a binary resource table. This table + * is a configuration split table for an APK split. + * + * Package: com.android.example.split + * + * string/app_title 0x7f030000 {fr} + * string/test 0x7f030001 {de,fr} + * string/blah 0x7f030003 {fr} + * + * array/lotsofstrings 0x7f040000 {fr} + * + */ +#include "data/split_de_fr_arsc.h" + + +using namespace android; + +enum { MAY_NOT_BE_BAG = false }; + +void makeConfigFrench(ResTable_config* config) { + memset(config, 0, sizeof(*config)); + config->language[0] = 'f'; + config->language[1] = 'r'; +} + +TEST(SplitTest, TestLoadBase) { + ResTable table; + ASSERT_EQ(NO_ERROR, table.add(split_base_arsc, split_base_arsc_len)); +} + +TEST(SplitTest, TestGetResourceFromBase) { + ResTable_config frenchConfig; + makeConfigFrench(&frenchConfig); + + ResTable table; + table.setParameters(&frenchConfig); + + ASSERT_EQ(NO_ERROR, table.add(split_base_arsc, split_base_arsc_len)); + + ResTable_config expectedConfig; + memset(&expectedConfig, 0, sizeof(expectedConfig)); + + Res_value val; + ResTable_config config; + ssize_t block = table.getResource(0x7f030000, &val, MAY_NOT_BE_BAG, 0, NULL, &config); + + // The returned block should tell us which string pool to get the value, if it is a string. + EXPECT_GE(block, 0); + + // We expect the default resource to be selected since it is the only resource configuration. + EXPECT_EQ(0, expectedConfig.compare(config)); + + EXPECT_EQ(Res_value::TYPE_STRING, val.dataType); +} + +TEST(SplitTest, TestGetResourceFromSplit) { + ResTable_config expectedConfig; + makeConfigFrench(&expectedConfig); + + ResTable table; + table.setParameters(&expectedConfig); + + ASSERT_EQ(NO_ERROR, table.add(split_base_arsc, split_base_arsc_len)); + ASSERT_EQ(NO_ERROR, table.add(split_de_fr_arsc, split_de_fr_arsc_len)); + + Res_value val; + ResTable_config config; + ssize_t block = table.getResource(0x7f030000, &val, MAY_NOT_BE_BAG, 0, NULL, &config); + + EXPECT_GE(block, 0); + + EXPECT_EQ(0, expectedConfig.compare(config)); + + EXPECT_EQ(Res_value::TYPE_STRING, val.dataType); +} + +TEST(SplitTest, ResourcesFromBaseAndSplitHaveSameNames) { + ResTable_config expectedConfig; + makeConfigFrench(&expectedConfig); + + ResTable table; + table.setParameters(&expectedConfig); + + ASSERT_EQ(NO_ERROR, table.add(split_base_arsc, split_base_arsc_len)); + + ResTable::resource_name baseName; + EXPECT_TRUE(table.getResourceName(0x7f030003, false, &baseName)); + + ASSERT_EQ(NO_ERROR, table.add(split_de_fr_arsc, split_de_fr_arsc_len)); + + ResTable::resource_name frName; + EXPECT_TRUE(table.getResourceName(0x7f030003, false, &frName)); + + EXPECT_EQ( + String16(baseName.package, baseName.packageLen), + String16(frName.package, frName.packageLen)); + + EXPECT_EQ( + String16(baseName.type, baseName.typeLen), + String16(frName.type, frName.typeLen)); + + EXPECT_EQ( + String16(baseName.name, baseName.nameLen), + String16(frName.name, frName.nameLen)); +} + +TEST(SplitTest, TypeEntrySpecFlagsAreUpdated) { + ResTable_config defaultConfig; + memset(&defaultConfig, 0, sizeof(defaultConfig)); + + ResTable table; + ASSERT_EQ(NO_ERROR, table.add(split_base_arsc, split_base_arsc_len)); + + Res_value val; + uint32_t specFlags = 0; + ssize_t block = table.getResource(0x7f030000, &val, MAY_NOT_BE_BAG, 0, &specFlags, NULL); + EXPECT_GE(block, 0); + + EXPECT_EQ(static_cast<uint32_t>(0), specFlags); + + ASSERT_EQ(NO_ERROR, table.add(split_de_fr_arsc, split_de_fr_arsc_len)); + + uint32_t frSpecFlags = 0; + block = table.getResource(0x7f030000, &val, MAY_NOT_BE_BAG, 0, &frSpecFlags, NULL); + EXPECT_GE(block, 0); + + EXPECT_EQ(ResTable_config::CONFIG_LOCALE, frSpecFlags); +} diff --git a/libs/androidfw/tests/TestHelpers.h b/libs/androidfw/tests/TestHelpers.h new file mode 100644 index 0000000..75a233a --- /dev/null +++ b/libs/androidfw/tests/TestHelpers.h @@ -0,0 +1,17 @@ +#ifndef __TEST_HELPERS_H +#define __TEST_HELPERS_H + +#include <ostream> + +#include <utils/String8.h> +#include <utils/String16.h> + +static inline ::std::ostream& operator<<(::std::ostream& out, const android::String8& str) { + return out << str.string(); +} + +static inline ::std::ostream& operator<<(::std::ostream& out, const android::String16& str) { + return out << android::String8(str).string(); +} + +#endif // __TEST_HELPERS_H diff --git a/libs/androidfw/tests/TypeWrappers_test.cpp b/libs/androidfw/tests/TypeWrappers_test.cpp new file mode 100644 index 0000000..d69abe5 --- /dev/null +++ b/libs/androidfw/tests/TypeWrappers_test.cpp @@ -0,0 +1,109 @@ +/* + * 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 <androidfw/TypeWrappers.h> +#include <utils/String8.h> + +#include <gtest/gtest.h> + +namespace android { + +void* createTypeData() { + ResTable_type t; + memset(&t, 0, sizeof(t)); + t.header.type = RES_TABLE_TYPE_TYPE; + t.header.headerSize = sizeof(t); + t.id = 1; + t.entryCount = 3; + + uint32_t offsets[3]; + t.entriesStart = t.header.headerSize + sizeof(offsets); + t.header.size = t.entriesStart; + + offsets[0] = 0; + ResTable_entry e1; + memset(&e1, 0, sizeof(e1)); + e1.size = sizeof(e1); + e1.key.index = 0; + t.header.size += sizeof(e1); + + Res_value v1; + memset(&v1, 0, sizeof(v1)); + t.header.size += sizeof(v1); + + offsets[1] = ResTable_type::NO_ENTRY; + + offsets[2] = sizeof(e1) + sizeof(v1); + ResTable_entry e2; + memset(&e2, 0, sizeof(e2)); + e2.size = sizeof(e2); + e2.key.index = 1; + t.header.size += sizeof(e2); + + Res_value v2; + memset(&v2, 0, sizeof(v2)); + t.header.size += sizeof(v2); + + uint8_t* data = (uint8_t*)malloc(t.header.size); + uint8_t* p = data; + memcpy(p, &t, sizeof(t)); + p += sizeof(t); + memcpy(p, offsets, sizeof(offsets)); + p += sizeof(offsets); + memcpy(p, &e1, sizeof(e1)); + p += sizeof(e1); + memcpy(p, &v1, sizeof(v1)); + p += sizeof(v1); + memcpy(p, &e2, sizeof(e2)); + p += sizeof(e2); + memcpy(p, &v2, sizeof(v2)); + p += sizeof(v2); + return data; +} + +TEST(TypeVariantIteratorTest, shouldIterateOverTypeWithoutErrors) { + ResTable_type* data = (ResTable_type*) createTypeData(); + + TypeVariant v(data); + + TypeVariant::iterator iter = v.beginEntries(); + ASSERT_EQ(uint32_t(0), iter.index()); + ASSERT_TRUE(NULL != *iter); + ASSERT_EQ(uint32_t(0), iter->key.index); + ASSERT_NE(v.endEntries(), iter); + + iter++; + + ASSERT_EQ(uint32_t(1), iter.index()); + ASSERT_TRUE(NULL == *iter); + ASSERT_NE(v.endEntries(), iter); + + iter++; + + ASSERT_EQ(uint32_t(2), iter.index()); + ASSERT_TRUE(NULL != *iter); + ASSERT_EQ(uint32_t(1), iter->key.index); + ASSERT_NE(v.endEntries(), iter); + + iter++; + + ASSERT_EQ(v.endEntries(), iter); + + free(data); +} + +} // namespace android diff --git a/libs/androidfw/tests/data/.gitignore b/libs/androidfw/tests/data/.gitignore new file mode 100644 index 0000000..c05cfb0 --- /dev/null +++ b/libs/androidfw/tests/data/.gitignore @@ -0,0 +1,2 @@ +*.apk +*.arsc diff --git a/libs/androidfw/tests/data/basic/AndroidManifest.xml b/libs/androidfw/tests/data/basic/AndroidManifest.xml new file mode 100644 index 0000000..a56ac18 --- /dev/null +++ b/libs/androidfw/tests/data/basic/AndroidManifest.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.test.basic"> + <application> + </application> +</manifest> diff --git a/libs/androidfw/tests/data/basic/basic_arsc.h b/libs/androidfw/tests/data/basic/basic_arsc.h new file mode 100644 index 0000000..6532076 --- /dev/null +++ b/libs/androidfw/tests/data/basic/basic_arsc.h @@ -0,0 +1,131 @@ +unsigned char basic_arsc[] = { + 0x02, 0x00, 0x0c, 0x00, 0xfc, 0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x1c, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x31, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, + 0x32, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xb0, 0x05, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, + 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00, + 0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, + 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, 0x69, 0x00, 0x63, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x7c, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, + 0x72, 0x00, 0x00, 0x00, 0x06, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, + 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x00, 0x00, 0x07, 0x00, 0x69, 0x00, + 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x73, 0x00, 0x74, 0x00, 0x79, 0x00, 0x6c, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, 0x72, 0x00, 0x72, 0x00, + 0x61, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, + 0xdc, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x2a, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, + 0x5c, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x31, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, + 0x32, 0x00, 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, + 0x74, 0x00, 0x31, 0x00, 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, + 0x73, 0x00, 0x74, 0x00, 0x32, 0x00, 0x00, 0x00, 0x07, 0x00, 0x6e, 0x00, + 0x75, 0x00, 0x6d, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x31, 0x00, + 0x00, 0x00, 0x07, 0x00, 0x6e, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x62, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x32, 0x00, 0x00, 0x00, 0x06, 0x00, 0x54, 0x00, + 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x31, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x54, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00, + 0x32, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, + 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x41, 0x00, 0x72, 0x00, + 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x44, 0x00, 0x84, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x08, 0x00, 0x00, 0x10, 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x10, 0x05, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x44, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x44, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x10, 0xc8, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0x7f, + 0x01, 0x02, 0x44, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x73, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x10, 0x90, 0x01, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, + 0x90, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7f, 0x08, 0x00, 0x00, 0x10, + 0x64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x7f, 0x08, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x03, 0x7f, 0x10, 0x00, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x7f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7f, + 0x08, 0x00, 0x00, 0x10, 0x2c, 0x01, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x7c, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x08, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, + 0x08, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, + 0x08, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00, 0x00 +}; +unsigned int basic_arsc_len = 1532; diff --git a/libs/androidfw/tests/data/basic/build b/libs/androidfw/tests/data/basic/build new file mode 100755 index 0000000..237342c --- /dev/null +++ b/libs/androidfw/tests/data/basic/build @@ -0,0 +1,6 @@ +#!/bin/bash + +aapt package -M AndroidManifest.xml -S res -F bundle.apk -f && \ +unzip bundle.apk resources.arsc && \ +mv resources.arsc basic.arsc && \ +xxd -i basic.arsc > basic_arsc.h diff --git a/libs/androidfw/tests/data/basic/res/values-sv/values.xml b/libs/androidfw/tests/data/basic/res/values-sv/values.xml new file mode 100644 index 0000000..9d52307 --- /dev/null +++ b/libs/androidfw/tests/data/basic/res/values-sv/values.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <integer name="number1">400</integer> +</resources> diff --git a/libs/androidfw/tests/data/basic/res/values/values.xml b/libs/androidfw/tests/data/basic/res/values/values.xml new file mode 100644 index 0000000..662eda6 --- /dev/null +++ b/libs/androidfw/tests/data/basic/res/values/values.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <attr name="attr1" format="reference|integer" /> + <attr name="attr2" format="reference|integer" /> + + <string name="test1">test1</string> + <string name="test2">test2</string> + + <integer name="number1">200</integer> + <integer name="number2">@array/integerArray1</integer> + + <style name="Theme1"> + <item name="com.android.test.basic:attr1">100</item> + <item name="com.android.test.basic:attr2">@integer/number1</item> + </style> + + <style name="Theme2" parent="@com.android.test.basic:style/Theme1"> + <item name="com.android.test.basic:attr1">300</item> + </style> + + <integer-array name="integerArray1"> + <item>1</item> + <item>2</item> + <item>3</item> + </integer-array> +</resources> diff --git a/libs/androidfw/tests/data/overlay/AndroidManifest.xml b/libs/androidfw/tests/data/overlay/AndroidManifest.xml new file mode 100644 index 0000000..a56ac18 --- /dev/null +++ b/libs/androidfw/tests/data/overlay/AndroidManifest.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.test.basic"> + <application> + </application> +</manifest> diff --git a/libs/androidfw/tests/data/overlay/build b/libs/androidfw/tests/data/overlay/build new file mode 100755 index 0000000..87cf6de --- /dev/null +++ b/libs/androidfw/tests/data/overlay/build @@ -0,0 +1,6 @@ +#!/bin/bash + +aapt package -M AndroidManifest.xml -S res -F bundle.apk -f && \ +unzip bundle.apk resources.arsc && \ +mv resources.arsc overlay.arsc && \ +xxd -i overlay.arsc > overlay_arsc.h diff --git a/libs/androidfw/tests/data/overlay/overlay_arsc.h b/libs/androidfw/tests/data/overlay/overlay_arsc.h new file mode 100644 index 0000000..5bd98b2 --- /dev/null +++ b/libs/androidfw/tests/data/overlay/overlay_arsc.h @@ -0,0 +1,69 @@ +unsigned char overlay_arsc[] = { + 0x02, 0x00, 0x0c, 0x00, 0x10, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x1c, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x74, 0x00, + 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x6f, 0x00, + 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xc4, 0x02, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, + 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00, + 0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, + 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, 0x69, 0x00, 0x63, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x74, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x54, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, + 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00, 0x06, 0x00, 0x73, 0x00, + 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x61, 0x00, 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, + 0x74, 0x00, 0x32, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x69, 0x00, 0x6e, 0x00, + 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x41, 0x00, + 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x31, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x44, 0x00, 0x58, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x44, 0x00, 0x70, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10, + 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10, + 0x0b, 0x00, 0x00, 0x00 +}; +unsigned int overlay_arsc_len = 784; diff --git a/libs/androidfw/tests/data/overlay/res/values/values.xml b/libs/androidfw/tests/data/overlay/res/values/values.xml new file mode 100644 index 0000000..227e889 --- /dev/null +++ b/libs/androidfw/tests/data/overlay/res/values/values.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="test2">test2-overlay</string> + <integer-array name="integerArray1"> + <item>10</item> + <item>11</item> + </integer-array> +</resources> diff --git a/libs/androidfw/tests/data/split_base_arsc.h b/libs/androidfw/tests/data/split_base_arsc.h new file mode 100644 index 0000000..e0321e9 --- /dev/null +++ b/libs/androidfw/tests/data/split_base_arsc.h @@ -0,0 +1,221 @@ +unsigned char split_base_arsc[] = { + 0x02, 0x00, 0x0c, 0x00, 0x30, 0x0a, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x1c, 0x00, 0x94, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, + 0x72, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00, + 0xb4, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, + 0xf4, 0x00, 0x00, 0x00, 0x0c, 0x01, 0x00, 0x00, 0x2a, 0x01, 0x00, 0x00, + 0x44, 0x01, 0x00, 0x00, 0x13, 0x00, 0x72, 0x00, 0x65, 0x00, 0x73, 0x00, + 0x2f, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, 0x6f, 0x00, 0x75, 0x00, + 0x74, 0x00, 0x2f, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, + 0x2e, 0x00, 0x78, 0x00, 0x6d, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x22, 0x00, + 0x72, 0x00, 0x65, 0x00, 0x73, 0x00, 0x2f, 0x00, 0x6c, 0x00, 0x61, 0x00, + 0x79, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x74, 0x00, 0x2d, 0x00, 0x66, 0x00, + 0x72, 0x00, 0x2d, 0x00, 0x73, 0x00, 0x77, 0x00, 0x36, 0x00, 0x30, 0x00, + 0x30, 0x00, 0x64, 0x00, 0x70, 0x00, 0x2d, 0x00, 0x76, 0x00, 0x31, 0x00, + 0x33, 0x00, 0x2f, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, + 0x2e, 0x00, 0x78, 0x00, 0x6d, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x09, 0x00, + 0x53, 0x00, 0x70, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x74, 0x00, 0x20, 0x00, + 0x41, 0x00, 0x50, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x48, 0x00, + 0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x2c, 0x00, 0x20, 0x00, + 0x57, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x6c, 0x00, 0x64, 0x00, 0x21, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x42, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x6d, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x07, 0x00, 0x42, 0x00, 0x6c, 0x00, 0x61, 0x00, + 0x68, 0x00, 0x2e, 0x00, 0x2e, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x0b, 0x00, + 0x48, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x20, 0x00, + 0x74, 0x00, 0x68, 0x00, 0x65, 0x00, 0x72, 0x00, 0x65, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x47, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x64, 0x00, 0x20, 0x00, + 0x62, 0x00, 0x79, 0x00, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x49, 0x00, + 0x20, 0x00, 0x68, 0x00, 0x61, 0x00, 0x7a, 0x00, 0x20, 0x00, 0x41, 0x00, + 0x4c, 0x00, 0x4c, 0x00, 0x21, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x49, 0x00, + 0x20, 0x00, 0x68, 0x00, 0x61, 0x00, 0x7a, 0x00, 0x20, 0x00, 0x31, 0x00, + 0x21, 0x00, 0x31, 0x00, 0x21, 0x00, 0x20, 0x00, 0x3a, 0x00, 0x29, 0x00, + 0x00, 0x00, 0x0b, 0x00, 0x49, 0x00, 0x20, 0x00, 0x6e, 0x00, 0x6f, 0x00, + 0x20, 0x00, 0x68, 0x00, 0x61, 0x00, 0x7a, 0x00, 0x20, 0x00, 0x3a, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1c, 0x01, + 0x90, 0x08, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, + 0x6d, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, + 0x6f, 0x00, 0x69, 0x00, 0x64, 0x00, 0x2e, 0x00, 0x65, 0x00, 0x78, 0x00, + 0x61, 0x00, 0x6d, 0x00, 0x70, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x2e, 0x00, + 0x73, 0x00, 0x70, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x74, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1c, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0xf0, 0x01, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0xd4, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, + 0x3a, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x66, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, 0x6f, 0x00, 0x75, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, + 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, + 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x00, 0x00, 0x07, 0x00, + 0x70, 0x00, 0x6c, 0x00, 0x75, 0x00, 0x72, 0x00, 0x61, 0x00, 0x6c, 0x00, + 0x73, 0x00, 0x00, 0x00, 0x04, 0x00, 0x62, 0x00, 0x6f, 0x00, 0x6f, 0x00, + 0x6c, 0x00, 0x00, 0x00, 0x05, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6c, 0x00, + 0x6f, 0x00, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x64, 0x00, 0x69, 0x00, + 0x6d, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x69, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x07, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, + 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x1c, 0x00, 0x30, 0x01, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, + 0x46, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, + 0xa4, 0x00, 0x00, 0x00, 0xb2, 0x00, 0x00, 0x00, 0xca, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x61, 0x00, 0x70, 0x00, 0x70, 0x00, 0x5f, 0x00, 0x74, 0x00, + 0x69, 0x00, 0x74, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x62, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x62, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x68, 0x00, 0x00, 0x00, 0x0d, 0x00, + 0x6c, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x66, 0x00, + 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, + 0x73, 0x00, 0x00, 0x00, 0x07, 0x00, 0x6e, 0x00, 0x75, 0x00, 0x6d, 0x00, + 0x4c, 0x00, 0x69, 0x00, 0x73, 0x00, 0x74, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x61, 0x00, 0x72, 0x00, 0x79, 0x00, 0x00, 0x00, 0x04, 0x00, 0x70, 0x00, + 0x6c, 0x00, 0x75, 0x00, 0x72, 0x00, 0x00, 0x00, 0x03, 0x00, 0x71, 0x00, + 0x75, 0x00, 0x65, 0x00, 0x00, 0x00, 0x05, 0x00, 0x67, 0x00, 0x72, 0x00, + 0x65, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x05, 0x00, 0x77, 0x00, + 0x69, 0x00, 0x64, 0x00, 0x74, 0x00, 0x68, 0x00, 0x00, 0x00, 0x0a, 0x00, + 0x69, 0x00, 0x64, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x69, 0x00, + 0x66, 0x00, 0x69, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x6e, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x24, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x66, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x10, 0x00, 0x20, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, + 0x94, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x54, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, + 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x03, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x05, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x10, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0xc8, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x03, + 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x03, + 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x08, 0x00, 0x00, 0x10, 0xd2, 0x04, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x01, 0x01, 0x00, 0x03, 0x7f, + 0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x01, 0x02, 0x00, 0x03, 0x7f, + 0x02, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x05, 0x01, 0x19, 0x00, 0x00, + 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, + 0x7c, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x03, 0x0a, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x03, 0x09, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x12, 0xff, 0xff, 0xff, 0xff, + 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x1d, 0x00, 0xff, 0x00, 0xff, + 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x05, 0x01, 0x17, 0x00, 0x00, + 0x01, 0x02, 0x44, 0x00, 0x58, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x58, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x05, + 0x01, 0xe6, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x44, 0x00, 0x58, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, + 0x0b, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x44, 0x00, 0x58, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, + 0x7b, 0x00, 0x00, 0x00 +}; +unsigned int split_base_arsc_len = 2608; diff --git a/libs/androidfw/tests/data/split_de_fr_arsc.h b/libs/androidfw/tests/data/split_de_fr_arsc.h new file mode 100644 index 0000000..6f6a416 --- /dev/null +++ b/libs/androidfw/tests/data/split_de_fr_arsc.h @@ -0,0 +1,118 @@ +unsigned char split_de_fr_arsc[] = { + 0x02, 0x00, 0x0c, 0x00, 0x64, 0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x1c, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x00, + 0x6c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x41, 0x00, 0x63, 0x00, 0x68, 0x00, + 0x74, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x41, 0x00, 0x50, 0x00, 0x4b, 0x00, 0x20, 0x00, 0x44, 0x00, + 0x69, 0x00, 0x76, 0x00, 0x69, 0x00, 0x73, 0x00, 0xe9, 0x00, 0x00, 0x00, + 0x0f, 0x00, 0x42, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x6a, 0x00, 0x6f, 0x00, + 0x75, 0x00, 0x72, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x4d, 0x00, 0x6f, 0x00, + 0x6e, 0x00, 0x64, 0x00, 0x65, 0x00, 0x21, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x42, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x68, 0x00, 0x2e, 0x00, 0x2e, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x48, 0x00, 0xe9, 0x00, 0x20, 0x00, 0x6c, 0x00, + 0xe0, 0x00, 0x00, 0x00, 0x09, 0x00, 0x41, 0x00, 0x75, 0x00, 0x20, 0x00, + 0x72, 0x00, 0x65, 0x00, 0x76, 0x00, 0x6f, 0x00, 0x69, 0x00, 0x72, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1c, 0x01, 0xa0, 0x04, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, + 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00, + 0x64, 0x00, 0x2e, 0x00, 0x65, 0x00, 0x78, 0x00, 0x61, 0x00, 0x6d, 0x00, + 0x70, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x2e, 0x00, 0x73, 0x00, 0x70, 0x00, + 0x6c, 0x00, 0x69, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x01, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0xf0, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x1c, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, + 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00, 0x06, 0x00, 0x6c, 0x00, + 0x61, 0x00, 0x79, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x74, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, + 0x67, 0x00, 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, 0x72, 0x00, 0x72, 0x00, + 0x61, 0x00, 0x79, 0x00, 0x00, 0x00, 0x07, 0x00, 0x70, 0x00, 0x6c, 0x00, + 0x75, 0x00, 0x72, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x62, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x72, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x65, 0x00, + 0x6e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x69, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x2e, 0x00, 0x00, 0x00, 0x09, 0x00, 0x61, 0x00, 0x70, 0x00, 0x70, 0x00, + 0x5f, 0x00, 0x74, 0x00, 0x69, 0x00, 0x74, 0x00, 0x6c, 0x00, 0x65, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x62, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x68, 0x00, + 0x00, 0x00, 0x0d, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x73, 0x00, + 0x6f, 0x00, 0x66, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, + 0x6e, 0x00, 0x67, 0x00, 0x73, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x65, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, + 0x84, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x54, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x66, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, + 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x44, 0x00, 0x78, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x66, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x01, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x03, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x03, 0x05, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +unsigned int split_de_fr_arsc_len = 1380; diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index 900a72e..a704e19 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -14,6 +14,7 @@ ifeq ($(USE_OPENGL_RENDERER),true) AmbientShadow.cpp \ Animator.cpp \ AssetAtlas.cpp \ + DamageAccumulator.cpp \ FontRenderer.cpp \ GammaFontRenderer.cpp \ Caches.cpp \ @@ -44,6 +45,7 @@ ifeq ($(USE_OPENGL_RENDERER),true) RenderBufferCache.cpp \ RenderNode.cpp \ RenderProperties.cpp \ + RenderState.cpp \ ResourceCache.cpp \ ShadowTessellator.cpp \ SkiaShader.cpp \ @@ -60,6 +62,7 @@ ifeq ($(USE_OPENGL_RENDERER),true) LOCAL_SRC_FILES += \ renderthread/CanvasContext.cpp \ renderthread/DrawFrameTask.cpp \ + renderthread/EglManager.cpp \ renderthread/RenderProxy.cpp \ renderthread/RenderTask.cpp \ renderthread/RenderThread.cpp \ @@ -68,12 +71,8 @@ ifeq ($(USE_OPENGL_RENDERER),true) intermediates := $(call intermediates-dir-for,STATIC_LIBRARIES,libRS,TARGET,) LOCAL_C_INCLUDES += \ - $(JNI_H_INCLUDE) \ - $(LOCAL_PATH)/../../include/utils \ external/skia/src/core - include external/stlport/libstlport.mk - LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES LOCAL_CFLAGS += -Wno-unused-parameter LOCAL_MODULE_CLASS := SHARED_LIBRARIES @@ -81,16 +80,15 @@ ifeq ($(USE_OPENGL_RENDERER),true) LOCAL_MODULE := libhwui LOCAL_MODULE_TAGS := optional + include external/stlport/libstlport.mk + ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT)) LOCAL_CFLAGS += -DANDROID_ENABLE_RENDERSCRIPT - LOCAL_SHARED_LIBRARIES += libRS libRScpp libstlport + LOCAL_SHARED_LIBRARIES += libRS libRScpp LOCAL_C_INCLUDES += \ $(intermediates) \ frameworks/rs/cpp \ - frameworks/rs \ - external/stlport/stlport \ - bionic/ \ - bionic/libstdc++/include + frameworks/rs endif ifndef HWUI_COMPILE_SYMBOLS diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp index eff3011..dc6d852 100644 --- a/libs/hwui/Animator.cpp +++ b/libs/hwui/Animator.cpp @@ -160,6 +160,10 @@ void RenderPropertyAnimator::onAttached(RenderNode* target) { (target->mutateStagingProperties().*mPropertyAccess->setter)(finalValue()); } +uint32_t RenderPropertyAnimator::dirtyMask() { + return mPropertyAccess->dirtyMask; +} + float RenderPropertyAnimator::getValue(RenderNode* target) const { return (target->properties().*mPropertyAccess->getter)(); } diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h index a0c7c55..6cb72c4c 100644 --- a/libs/hwui/Animator.h +++ b/libs/hwui/Animator.h @@ -61,6 +61,8 @@ public: bool isFinished() { return mPlayState == FINISHED; } float finalValue() { return mFinalValue; } + ANDROID_API virtual uint32_t dirtyMask() { return 0; } + protected: BaseRenderNodeAnimator(float finalValue); virtual ~BaseRenderNodeAnimator(); @@ -112,12 +114,14 @@ public: ANDROID_API virtual void onAttached(RenderNode* target); + ANDROID_API virtual uint32_t dirtyMask(); + protected: virtual float getValue(RenderNode* target) const; virtual void setValue(RenderNode* target, float value); private: - typedef void (RenderProperties::*SetFloatProperty)(float value); + typedef bool (RenderProperties::*SetFloatProperty)(float value); typedef float (RenderProperties::*GetFloatProperty)() const; struct PropertyAccessors; diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp index 6fd9999..402f28b 100644 --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -565,11 +565,8 @@ void Caches::deleteTexture(GLuint texture) { // call, any texture operation will be performed on the default // texture (name=0) - for (int i = 0; i < REQUIRED_TEXTURE_UNITS_COUNT; i++) { - if (mBoundTextures[i] == texture) { - mBoundTextures[i] = 0; - } - } + unbindTexture(texture); + glDeleteTextures(1, &texture); } @@ -577,6 +574,14 @@ void Caches::resetBoundTextures() { memset(mBoundTextures, 0, REQUIRED_TEXTURE_UNITS_COUNT * sizeof(GLuint)); } +void Caches::unbindTexture(GLuint texture) { + for (int i = 0; i < REQUIRED_TEXTURE_UNITS_COUNT; i++) { + if (mBoundTextures[i] == texture) { + mBoundTextures[i] = 0; + } + } +} + /////////////////////////////////////////////////////////////////////////////// // Scissor /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h index b4b5927..83a5d9a 100644 --- a/libs/hwui/Caches.h +++ b/libs/hwui/Caches.h @@ -261,6 +261,11 @@ public: void resetBoundTextures(); /** + * Clear the cache of bound textures. + */ + void unbindTexture(GLuint texture); + + /** * Sets the scissor for the current surface. */ bool setScissor(GLint x, GLint y, GLint width, GLint height); diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp new file mode 100644 index 0000000..8b32c40 --- /dev/null +++ b/libs/hwui/DamageAccumulator.cpp @@ -0,0 +1,219 @@ +/* + * 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. + */ + +#define LOG_TAG "DamageAccumulator" + +#include "DamageAccumulator.h" + +#include <cutils/log.h> + +#include "RenderNode.h" +#include "utils/MathUtils.h" + +namespace android { +namespace uirenderer { + +NullDamageAccumulator NullDamageAccumulator::sInstance; + +NullDamageAccumulator* NullDamageAccumulator::instance() { + return &sInstance; +} + +enum TransformType { + TransformInvalid = 0, + TransformRenderNode, + TransformMatrix4, + TransformNone, +}; + +struct DirtyStack { + TransformType type; + union { + const RenderNode* renderNode; + const Matrix4* matrix4; + }; + // When this frame is pop'd, this rect is mapped through the above transform + // and applied to the previous (aka parent) frame + SkRect pendingDirty; + DirtyStack* prev; + DirtyStack* next; +}; + +DamageAccumulator::DamageAccumulator() { + mHead = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack)); + memset(mHead, 0, sizeof(DirtyStack)); + // Create a root that we will not pop off + mHead->prev = mHead; + mHead->type = TransformNone; +} + +void DamageAccumulator::pushCommon() { + if (!mHead->next) { + DirtyStack* nextFrame = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack)); + nextFrame->next = 0; + nextFrame->prev = mHead; + mHead->next = nextFrame; + } + mHead = mHead->next; + mHead->pendingDirty.setEmpty(); +} + +void DamageAccumulator::pushTransform(const RenderNode* transform) { + pushCommon(); + mHead->type = TransformRenderNode; + mHead->renderNode = transform; +} + +void DamageAccumulator::pushTransform(const Matrix4* transform) { + pushCommon(); + mHead->type = TransformMatrix4; + mHead->matrix4 = transform; +} + +void DamageAccumulator::pushNullTransform() { + pushCommon(); + mHead->type = TransformNone; +} + +void DamageAccumulator::popTransform() { + LOG_ALWAYS_FATAL_IF(mHead->prev == mHead, "Cannot pop the root frame!"); + DirtyStack* dirtyFrame = mHead; + mHead = mHead->prev; + switch (dirtyFrame->type) { + case TransformRenderNode: + applyRenderNodeTransform(dirtyFrame); + break; + case TransformMatrix4: + applyMatrix4Transform(dirtyFrame); + break; + case TransformNone: + mHead->pendingDirty.join(dirtyFrame->pendingDirty); + break; + default: + LOG_ALWAYS_FATAL("Tried to pop an invalid type: %d", dirtyFrame->type); + } +} + +static inline void mapRect(const Matrix4* matrix, const SkRect& in, SkRect* out) { + if (in.isEmpty()) return; + Rect temp(in); + matrix->mapRect(temp); + out->join(RECT_ARGS(temp)); +} + +void DamageAccumulator::applyMatrix4Transform(DirtyStack* frame) { + mapRect(frame->matrix4, frame->pendingDirty, &mHead->pendingDirty); +} + +static inline void mapRect(const RenderProperties& props, const SkRect& in, SkRect* out) { + if (in.isEmpty()) return; + const SkMatrix* transform = props.getTransformMatrix(); + SkRect temp(in); + if (transform && !transform->isIdentity()) { + transform->mapRect(&temp); + } + temp.offset(props.getLeft(), props.getTop()); + out->join(temp); +} + +static DirtyStack* findParentRenderNode(DirtyStack* frame) { + while (frame->prev != frame) { + frame = frame->prev; + if (frame->type == TransformRenderNode) { + return frame; + } + } + return NULL; +} + +static DirtyStack* findProjectionReceiver(DirtyStack* frame) { + if (frame) { + while (frame->prev != frame) { + frame = frame->prev; + if (frame->type == TransformRenderNode + && frame->renderNode->hasProjectionReceiver()) { + return frame; + } + } + } + return NULL; +} + +static void applyTransforms(DirtyStack* frame, DirtyStack* end) { + SkRect* rect = &frame->pendingDirty; + while (frame != end) { + if (frame->type == TransformRenderNode) { + mapRect(frame->renderNode->properties(), *rect, rect); + } else { + mapRect(frame->matrix4, *rect, rect); + } + frame = frame->prev; + } +} + +void DamageAccumulator::applyRenderNodeTransform(DirtyStack* frame) { + if (frame->pendingDirty.isEmpty()) { + return; + } + + const RenderProperties& props = frame->renderNode->properties(); + if (props.getAlpha() <= 0) { + return; + } + + // Perform clipping + if (props.getClipDamageToBounds() && !frame->pendingDirty.isEmpty()) { + if (!frame->pendingDirty.intersect(0, 0, props.getWidth(), props.getHeight())) { + frame->pendingDirty.setEmpty(); + } + } + + // apply all transforms + mapRect(props, frame->pendingDirty, &mHead->pendingDirty); + + // project backwards if necessary + if (props.getProjectBackwards() && !frame->pendingDirty.isEmpty()) { + // First, find our parent RenderNode: + DirtyStack* parentNode = findParentRenderNode(frame); + // Find our parent's projection receiver, which is what we project onto + DirtyStack* projectionReceiver = findProjectionReceiver(parentNode); + if (projectionReceiver) { + applyTransforms(frame, projectionReceiver); + projectionReceiver->pendingDirty.join(frame->pendingDirty); + } + + frame->pendingDirty.setEmpty(); + } +} + +void DamageAccumulator::dirty(float left, float top, float right, float bottom) { + mHead->pendingDirty.join(left, top, right, bottom); +} + +void DamageAccumulator::peekAtDirty(SkRect* dest) { + *dest = mHead->pendingDirty; +} + +void DamageAccumulator::finish(SkRect* totalDirty) { + LOG_ALWAYS_FATAL_IF(mHead->prev != mHead, "Cannot finish, mismatched push/pop calls! %p vs. %p", mHead->prev, mHead); + // Root node never has a transform, so this is the fully mapped dirty rect + *totalDirty = mHead->pendingDirty; + totalDirty->roundOut(); + mHead->pendingDirty.setEmpty(); +} + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/DamageAccumulator.h b/libs/hwui/DamageAccumulator.h new file mode 100644 index 0000000..fc9b41b --- /dev/null +++ b/libs/hwui/DamageAccumulator.h @@ -0,0 +1,103 @@ +/* + * 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. + */ +#ifndef DAMAGEACCUMULATOR_H +#define DAMAGEACCUMULATOR_H + +#include <cutils/compiler.h> +#include <utils/LinearAllocator.h> + +#include <SkMatrix.h> +#include <SkRect.h> + +#include "utils/Macros.h" + +namespace android { +namespace uirenderer { + +struct DirtyStack; +class RenderNode; +class Matrix4; + +class IDamageAccumulator { +public: + virtual void pushTransform(const RenderNode* transform) = 0; + virtual void pushTransform(const Matrix4* transform) = 0; + virtual void pushNullTransform() = 0; + virtual void popTransform() = 0; + virtual void dirty(float left, float top, float right, float bottom) = 0; + virtual void peekAtDirty(SkRect* dest) = 0; +protected: + virtual ~IDamageAccumulator() {} +}; + +class DamageAccumulator : public IDamageAccumulator { + PREVENT_COPY_AND_ASSIGN(DamageAccumulator); +public: + DamageAccumulator(); + // mAllocator will clean everything up for us, no need for a dtor + + // Push a transform node onto the stack. This should be called prior + // to any dirty() calls. Subsequent calls to dirty() + // will be affected by the transform when popTransform() is called. + virtual void pushTransform(const RenderNode* transform); + virtual void pushTransform(const Matrix4* transform); + // This is used in combination with peekAtDirty to inspect the damage + // area of a subtree + virtual void pushNullTransform(); + + // Pops a transform node from the stack, propagating the dirty rect + // up to the parent node. Returns the IDamageTransform that was just applied + virtual void popTransform(); + + virtual void dirty(float left, float top, float right, float bottom); + + // Returns the current dirty area, *NOT* transformed by pushed transforms + virtual void peekAtDirty(SkRect* dest); + + void finish(SkRect* totalDirty); + +private: + void pushCommon(); + void applyMatrix4Transform(DirtyStack* frame); + void applyRenderNodeTransform(DirtyStack* frame); + + LinearAllocator mAllocator; + DirtyStack* mHead; +}; + +class NullDamageAccumulator : public IDamageAccumulator { + PREVENT_COPY_AND_ASSIGN(NullDamageAccumulator); +public: + virtual void pushTransform(const RenderNode* transform) { } + virtual void pushTransform(const Matrix4* transform) { } + virtual void pushNullTransform() { } + virtual void popTransform() { } + virtual void dirty(float left, float top, float right, float bottom) { } + virtual void peekAtDirty(SkRect* dest) { dest->setEmpty(); } + + ANDROID_API static NullDamageAccumulator* instance(); + +private: + NullDamageAccumulator() {} + ~NullDamageAccumulator() {} + + static NullDamageAccumulator sInstance; +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* DAMAGEACCUMULATOR_H */ diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp index 937bf8d..a998594 100644 --- a/libs/hwui/DeferredDisplayList.cpp +++ b/libs/hwui/DeferredDisplayList.cpp @@ -500,7 +500,7 @@ void DeferredDisplayList::addRestoreToCount(OpenGLRenderer& renderer, StateOp* o void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) { /* 1: op calculates local bounds */ DeferredDisplayState* const state = createState(); - if (op->getLocalBounds(renderer.getDrawModifiers(), state->mBounds)) { + if (op->getLocalBounds(state->mBounds)) { if (state->mBounds.isEmpty()) { // valid empty bounds, don't bother deferring tryRecycleState(state); diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h index 48489c2..8a015b2 100644 --- a/libs/hwui/DeferredDisplayList.h +++ b/libs/hwui/DeferredDisplayList.h @@ -38,7 +38,6 @@ class SaveLayerOp; class StateOp; class DeferredDisplayState; -class OpenGLRenderer; class Batch; class DrawBatch; diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp index 97e9bf6..8e99b9a 100644 --- a/libs/hwui/DeferredLayerUpdater.cpp +++ b/libs/hwui/DeferredLayerUpdater.cpp @@ -27,8 +27,7 @@ static void defaultLayerDestroyer(Layer* layer) { } DeferredLayerUpdater::DeferredLayerUpdater(Layer* layer, LayerDestroyer destroyer) - : mDisplayList(0) - , mSurfaceTexture(0) + : mSurfaceTexture(0) , mTransform(0) , mNeedsGLContextAttach(false) , mUpdateTexImage(false) @@ -41,7 +40,6 @@ DeferredLayerUpdater::DeferredLayerUpdater(Layer* layer, LayerDestroyer destroye mColorFilter = SkSafeRef(mLayer->getColorFilter()); mAlpha = mLayer->getAlpha(); mMode = mLayer->getMode(); - mDirtyRect.setEmpty(); if (!mDestroyer) { mDestroyer = defaultLayerDestroyer; @@ -60,33 +58,13 @@ void DeferredLayerUpdater::setPaint(const SkPaint* paint) { SkRefCnt_SafeAssign(mColorFilter, colorFilter); } -void DeferredLayerUpdater::setDisplayList(RenderNode* displayList, - int left, int top, int right, int bottom) { - mDisplayList = displayList; - if (mDirtyRect.isEmpty()) { - mDirtyRect.set(left, top, right, bottom); - } else { - mDirtyRect.unionWith(Rect(left, top, right, bottom)); - } -} - bool DeferredLayerUpdater::apply(TreeInfo& info) { bool success = true; // These properties are applied the same to both layer types mLayer->setColorFilter(mColorFilter); mLayer->setAlpha(mAlpha, mMode); - if (mDisplayList.get()) { - if (mWidth != mLayer->layer.getWidth() || mHeight != mLayer->layer.getHeight()) { - success = LayerRenderer::resizeLayer(mLayer, mWidth, mHeight); - } - mLayer->setBlend(mBlend); - mDisplayList->prepareTree(info); - mLayer->updateDeferred(mDisplayList.get(), - mDirtyRect.left, mDirtyRect.top, mDirtyRect.right, mDirtyRect.bottom); - mDirtyRect.setEmpty(); - mDisplayList = 0; - } else if (mSurfaceTexture.get()) { + if (mSurfaceTexture.get()) { if (mNeedsGLContextAttach) { mNeedsGLContextAttach = false; mSurfaceTexture->attachToContext(mLayer->getTexture()); diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h index b7cfe80..c76bd5e 100644 --- a/libs/hwui/DeferredLayerUpdater.h +++ b/libs/hwui/DeferredLayerUpdater.h @@ -23,7 +23,6 @@ #include <utils/StrongPointer.h> #include "Layer.h" -#include "OpenGLRenderer.h" #include "Rect.h" #include "RenderNode.h" @@ -74,9 +73,6 @@ public: mTransform = matrix ? new SkMatrix(*matrix) : 0; } - ANDROID_API void setDisplayList(RenderNode* displayList, - int left, int top, int right, int bottom); - ANDROID_API void setPaint(const SkPaint* paint); ANDROID_API bool apply(TreeInfo& info); @@ -94,11 +90,6 @@ private: int mAlpha; SkXfermode::Mode mMode; - // Layer type specific properties - // displayList and surfaceTexture are mutually exclusive, only 1 may be set - // dirtyRect is only valid if displayList is set - sp<RenderNode> mDisplayList; - Rect mDirtyRect; sp<GLConsumer> mSurfaceTexture; SkMatrix* mTransform; bool mNeedsGLContextAttach; diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp index 96c6292..e38b532 100644 --- a/libs/hwui/DisplayList.cpp +++ b/libs/hwui/DisplayList.cpp @@ -80,10 +80,6 @@ void DisplayListData::cleanupResources() { delete paths.itemAt(i); } - for (size_t i = 0; i < matrices.size(); i++) { - delete matrices.itemAt(i); - } - bitmapResources.clear(); ownedBitmapResources.clear(); patchResources.clear(); @@ -91,12 +87,11 @@ void DisplayListData::cleanupResources() { paints.clear(); regions.clear(); paths.clear(); - matrices.clear(); layers.clear(); } -void DisplayListData::addChild(DrawDisplayListOp* op) { - LOG_ALWAYS_FATAL_IF(!op->renderNode(), "DrawDisplayListOp with no render node!"); +void DisplayListData::addChild(DrawRenderNodeOp* op) { + LOG_ALWAYS_FATAL_IF(!op->renderNode(), "DrawRenderNodeOp with no render node!"); mChildren.push(op); mReferenceHolders.push(op->renderNode()); diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h index 11e78b0..bfffbb4 100644 --- a/libs/hwui/DisplayList.h +++ b/libs/hwui/DisplayList.h @@ -61,7 +61,7 @@ class ClipRectOp; class SaveLayerOp; class SaveOp; class RestoreToCountOp; -class DrawDisplayListOp; +class DrawRenderNodeOp; /** * Holds data used in the playback a tree of DisplayLists. @@ -125,7 +125,6 @@ public: Vector<const SkPath*> paths; SortedVector<const SkPath*> sourcePaths; Vector<const SkRegion*> regions; - Vector<const SkMatrix*> matrices; Vector<Layer*> layers; uint32_t functorCount; bool hasDrawOps; @@ -134,8 +133,8 @@ public: return !displayListOps.size(); } - void addChild(DrawDisplayListOp* childOp); - const Vector<DrawDisplayListOp*>& children() { return mChildren; } + void addChild(DrawRenderNodeOp* childOp); + const Vector<DrawRenderNodeOp*>& children() { return mChildren; } void refProperty(CanvasPropertyPrimitive* prop) { mReferenceHolders.push(prop); @@ -149,7 +148,7 @@ private: Vector< sp<VirtualLightRefBase> > mReferenceHolders; // list of children display lists for quick, non-drawing traversal - Vector<DrawDisplayListOp*> mChildren; + Vector<DrawRenderNodeOp*> mChildren; void cleanupResources(); }; diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h index 3281116..5ed04a0 100644 --- a/libs/hwui/DisplayListOp.h +++ b/libs/hwui/DisplayListOp.h @@ -171,7 +171,7 @@ public: * * returns true if bounds exist */ - virtual bool getLocalBounds(const DrawModifiers& drawModifiers, Rect& localBounds) { + virtual bool getLocalBounds(Rect& localBounds) { return false; } @@ -253,7 +253,7 @@ public: // default empty constructor for bounds, to be overridden in child constructor body DrawBoundedOp(const SkPaint* paint): DrawOp(paint) { } - bool getLocalBounds(const DrawModifiers& drawModifiers, Rect& localBounds) { + bool getLocalBounds(Rect& localBounds) { localBounds.set(mLocalBounds); OpenGLRenderer::TextShadow textShadow; if (OpenGLRenderer::getTextShadow(mPaint, &textShadow)) { @@ -472,7 +472,7 @@ private: class SetMatrixOp : public StateOp { public: - SetMatrixOp(const SkMatrix* matrix) + SetMatrixOp(const SkMatrix& matrix) : mMatrix(matrix) {} virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { @@ -480,22 +480,22 @@ public: } virtual void output(int level, uint32_t logFlags) const { - if (mMatrix) { - OP_LOG("SetMatrix " SK_MATRIX_STRING, SK_MATRIX_ARGS(mMatrix)); - } else { + if (mMatrix.isIdentity()) { OP_LOGS("SetMatrix (reset)"); + } else { + OP_LOG("SetMatrix " SK_MATRIX_STRING, SK_MATRIX_ARGS(&mMatrix)); } } virtual const char* name() { return "SetMatrix"; } private: - const SkMatrix* mMatrix; + const SkMatrix mMatrix; }; class ConcatMatrixOp : public StateOp { public: - ConcatMatrixOp(const SkMatrix* matrix) + ConcatMatrixOp(const SkMatrix& matrix) : mMatrix(matrix) {} virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { @@ -503,13 +503,13 @@ public: } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("ConcatMatrix " SK_MATRIX_STRING, SK_MATRIX_ARGS(mMatrix)); + OP_LOG("ConcatMatrix " SK_MATRIX_STRING, SK_MATRIX_ARGS(&mMatrix)); } virtual const char* name() { return "ConcatMatrix"; } private: - const SkMatrix* mMatrix; + const SkMatrix mMatrix; }; class ClipOp : public StateOp { @@ -746,10 +746,10 @@ protected: class DrawBitmapMatrixOp : public DrawBoundedOp { public: - DrawBitmapMatrixOp(const SkBitmap* bitmap, const SkMatrix* matrix, const SkPaint* paint) + DrawBitmapMatrixOp(const SkBitmap* bitmap, const SkMatrix& matrix, const SkPaint* paint) : DrawBoundedOp(paint), mBitmap(bitmap), mMatrix(matrix) { mLocalBounds.set(0, 0, bitmap->width(), bitmap->height()); - const mat4 transform(*matrix); + const mat4 transform(matrix); transform.mapRect(mLocalBounds); } @@ -758,7 +758,7 @@ public: } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw bitmap %p matrix " SK_MATRIX_STRING, mBitmap, SK_MATRIX_ARGS(mMatrix)); + OP_LOG("Draw bitmap %p matrix " SK_MATRIX_STRING, mBitmap, SK_MATRIX_ARGS(&mMatrix)); } virtual const char* name() { return "DrawBitmapMatrix"; } @@ -770,7 +770,7 @@ public: private: const SkBitmap* mBitmap; - const SkMatrix* mMatrix; + const SkMatrix mMatrix; }; class DrawBitmapRectOp : public DrawBoundedOp { @@ -788,7 +788,7 @@ public: } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw bitmap %p src="RECT_STRING", dst="RECT_STRING, + OP_LOG("Draw bitmap %p src=" RECT_STRING ", dst=" RECT_STRING, mBitmap, RECT_ARGS(mSrc), RECT_ARGS(mLocalBounds)); } @@ -978,7 +978,7 @@ public: } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw patch "RECT_STRING, RECT_ARGS(mLocalBounds)); + OP_LOG("Draw patch " RECT_STRING, RECT_ARGS(mLocalBounds)); } virtual const char* name() { return "DrawPatch"; } @@ -1029,7 +1029,7 @@ public: DrawStrokableOp(float left, float top, float right, float bottom, const SkPaint* paint) : DrawBoundedOp(left, top, right, bottom, paint) {}; - bool getLocalBounds(const DrawModifiers& drawModifiers, Rect& localBounds) { + bool getLocalBounds(Rect& localBounds) { localBounds.set(mLocalBounds); if (mPaint && mPaint->getStyle() != SkPaint::kFill_Style) { localBounds.outset(strokeWidthOutset()); @@ -1060,7 +1060,7 @@ public: } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw Rect "RECT_STRING, RECT_ARGS(mLocalBounds)); + OP_LOG("Draw Rect " RECT_STRING, RECT_ARGS(mLocalBounds)); } virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, @@ -1111,15 +1111,15 @@ public: } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw RoundRect "RECT_STRING", rx %f, ry %f", RECT_ARGS(mLocalBounds), mRx, mRy); + OP_LOG("Draw RoundRect " RECT_STRING ", rx %f, ry %f", RECT_ARGS(mLocalBounds), mRx, mRy); } virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, const DeferredDisplayState& state) { DrawStrokableOp::onDefer(renderer, deferInfo, state); if (!mPaint->getPathEffect()) { - renderer.getCaches().tessellationCache.precacheRoundRect(state.mMatrix, - mLocalBounds.getWidth(), mLocalBounds.getHeight(), mRx, mRy, mPaint); + renderer.getCaches().tessellationCache.precacheRoundRect(state.mMatrix, *mPaint, + mLocalBounds.getWidth(), mLocalBounds.getHeight(), mRx, mRy); } } @@ -1184,7 +1184,7 @@ public: } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw Oval "RECT_STRING, RECT_ARGS(mLocalBounds)); + OP_LOG("Draw Oval " RECT_STRING, RECT_ARGS(mLocalBounds)); } virtual const char* name() { return "DrawOval"; } @@ -1204,7 +1204,7 @@ public: } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw Arc "RECT_STRING", start %f, sweep %f, useCenter %d", + OP_LOG("Draw Arc " RECT_STRING ", start %f, sweep %f, useCenter %d", RECT_ARGS(mLocalBounds), mStartAngle, mSweepAngle, mUseCenter); } @@ -1241,7 +1241,7 @@ public: } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw Path %p in "RECT_STRING, mPath, RECT_ARGS(mLocalBounds)); + OP_LOG("Draw Path %p in " RECT_STRING, mPath, RECT_ARGS(mLocalBounds)); } virtual const char* name() { return "DrawPath"; } @@ -1399,7 +1399,7 @@ public: virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { Rect bounds; - getLocalBounds(renderer.getDrawModifiers(), bounds); + getLocalBounds(bounds); return renderer.drawText(mText, mBytesCount, mCount, mX, mY, mPositions, getPaint(renderer), mTotalAdvance, bounds); } @@ -1465,48 +1465,48 @@ private: Functor* mFunctor; }; -class DrawDisplayListOp : public DrawBoundedOp { - friend class RenderNode; // grant DisplayList access to info of child +class DrawRenderNodeOp : public DrawBoundedOp { + friend class RenderNode; // grant RenderNode access to info of child public: - DrawDisplayListOp(RenderNode* displayList, int flags, const mat4& transformFromParent) - : DrawBoundedOp(0, 0, displayList->getWidth(), displayList->getHeight(), 0), - mDisplayList(displayList), mFlags(flags), mTransformFromParent(transformFromParent) {} + DrawRenderNodeOp(RenderNode* renderNode, int flags, const mat4& transformFromParent) + : DrawBoundedOp(0, 0, renderNode->getWidth(), renderNode->getHeight(), 0), + mRenderNode(renderNode), mFlags(flags), mTransformFromParent(transformFromParent) {} virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level, bool useQuickReject) { - if (mDisplayList && mDisplayList->isRenderable() && !mSkipInOrderDraw) { - mDisplayList->deferNodeInParent(deferStruct, level + 1); + if (mRenderNode && mRenderNode->isRenderable() && !mSkipInOrderDraw) { + mRenderNode->defer(deferStruct, level + 1); } } virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level, bool useQuickReject) { - if (mDisplayList && mDisplayList->isRenderable() && !mSkipInOrderDraw) { - mDisplayList->replayNodeInParent(replayStruct, level + 1); + if (mRenderNode && mRenderNode->isRenderable() && !mSkipInOrderDraw) { + mRenderNode->replay(replayStruct, level + 1); } } - // NOT USED since replay() is overridden virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { - return DrawGlInfo::kStatusDone; + LOG_ALWAYS_FATAL("should not be called, because replay() is overridden"); + return 0; } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw Display List %p, flags %#x", mDisplayList, mFlags); - if (mDisplayList && (logFlags & kOpLogFlag_Recurse)) { - mDisplayList->output(level + 1); + OP_LOG("Draw Display List %p, flags %#x", mRenderNode, mFlags); + if (mRenderNode && (logFlags & kOpLogFlag_Recurse)) { + mRenderNode->output(level + 1); } } - virtual const char* name() { return "DrawDisplayList"; } + virtual const char* name() { return "DrawRenderNode"; } - RenderNode* renderNode() { return mDisplayList; } + RenderNode* renderNode() { return mRenderNode; } private: - RenderNode* mDisplayList; + RenderNode* mRenderNode; const int mFlags; /////////////////////////// - // Properties below are used by DisplayList::computeOrderingImpl() and iterate() + // Properties below are used by RenderNode::computeOrderingImpl() and issueOperations() /////////////////////////// /** * Records transform vs parent, used for computing total transform without rerunning DL contents @@ -1514,12 +1514,12 @@ private: const mat4 mTransformFromParent; /** - * Holds the transformation between the projection surface ViewGroup and this DisplayList + * Holds the transformation between the projection surface ViewGroup and this RenderNode * drawing instance. Represents any translations / transformations done within the drawing of * the compositing ancestor ViewGroup's draw, before the draw of the View represented by this * DisplayList draw instance. * - * Note: doesn't include any transformation recorded within the DisplayList and its properties. + * Note: doesn't include transformation within the RenderNode, or its properties. */ mat4 mTransformFromCompositingAncestor; bool mSkipInOrderDraw; diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp index 229afdf..7789358 100644 --- a/libs/hwui/DisplayListRenderer.cpp +++ b/libs/hwui/DisplayListRenderer.cpp @@ -141,14 +141,12 @@ void DisplayListRenderer::skew(float sx, float sy) { StatefulBaseRenderer::skew(sx, sy); } -void DisplayListRenderer::setMatrix(const SkMatrix* matrix) { - matrix = refMatrix(matrix); +void DisplayListRenderer::setMatrix(const SkMatrix& matrix) { addStateOp(new (alloc()) SetMatrixOp(matrix)); StatefulBaseRenderer::setMatrix(matrix); } -void DisplayListRenderer::concatMatrix(const SkMatrix* matrix) { - matrix = refMatrix(matrix); +void DisplayListRenderer::concatMatrix(const SkMatrix& matrix) { addStateOp(new (alloc()) ConcatMatrixOp(matrix)); StatefulBaseRenderer::concatMatrix(matrix); } @@ -171,18 +169,16 @@ bool DisplayListRenderer::clipRegion(const SkRegion* region, SkRegion::Op op) { return StatefulBaseRenderer::clipRegion(region, op); } -status_t DisplayListRenderer::drawDisplayList(RenderNode* displayList, - Rect& dirty, int32_t flags) { +status_t DisplayListRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t flags) { // dirty is an out parameter and should not be recorded, // it matters only when replaying the display list - if (displayList->stagingProperties().isProjectionReceiver()) { + if (renderNode->stagingProperties().isProjectionReceiver()) { // use staging property, since recording on UI thread mDisplayListData->projectionReceiveIndex = mDisplayListData->displayListOps.size(); } - DrawDisplayListOp* op = new (alloc()) DrawDisplayListOp(displayList, - flags, *currentTransform()); + DrawRenderNodeOp* op = new (alloc()) DrawRenderNodeOp(renderNode, flags, *currentTransform()); addDrawOp(op); mDisplayListData->addChild(op); return DrawGlInfo::kStatusDone; @@ -203,10 +199,9 @@ status_t DisplayListRenderer::drawBitmap(const SkBitmap* bitmap, float left, flo return DrawGlInfo::kStatusDone; } -status_t DisplayListRenderer::drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix, +status_t DisplayListRenderer::drawBitmap(const SkBitmap* bitmap, const SkMatrix& matrix, const SkPaint* paint) { bitmap = refBitmap(bitmap); - matrix = refMatrix(matrix); paint = refPaint(paint); addDrawOp(new (alloc()) DrawBitmapMatrixOp(bitmap, matrix, paint)); @@ -311,6 +306,10 @@ status_t DisplayListRenderer::drawOval(float left, float top, float right, float status_t DisplayListRenderer::drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint) { + if (fabs(sweepAngle) > 360.0f) { + return drawOval(left, top, right, bottom, paint); + } + paint = refPaint(paint); addDrawOp(new (alloc()) DrawArcOp(left, top, right, bottom, startAngle, sweepAngle, useCenter, paint)); @@ -426,7 +425,7 @@ void DisplayListRenderer::addStateOp(StateOp* op) { void DisplayListRenderer::addDrawOp(DrawOp* op) { Rect localBounds; - if (op->getLocalBounds(mDrawModifiers, localBounds)) { + if (op->getLocalBounds(localBounds)) { bool rejected = quickRejectConservative(localBounds.left, localBounds.top, localBounds.right, localBounds.bottom); op->setQuickRejected(rejected); diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h index dff4f6c..8ca9af1 100644 --- a/libs/hwui/DisplayListRenderer.h +++ b/libs/hwui/DisplayListRenderer.h @@ -23,7 +23,6 @@ #include <cutils/compiler.h> #include "DisplayListLogBuffer.h" -#include "OpenGLRenderer.h" #include "RenderNode.h" namespace android { @@ -53,12 +52,12 @@ class StateOp; /** * Records drawing commands in a display list for later playback into an OpenGLRenderer. */ -class DisplayListRenderer: public OpenGLRenderer { +class ANDROID_API DisplayListRenderer: public StatefulBaseRenderer { public: - ANDROID_API DisplayListRenderer(); + DisplayListRenderer(); virtual ~DisplayListRenderer(); - ANDROID_API DisplayListData* finishRecording(); + DisplayListData* finishRecording(); virtual bool isRecording() const { return true; } @@ -81,13 +80,13 @@ public: const SkPaint* paint, int flags); // Matrix - virtual void translate(float dx, float dy, float dz); + virtual void translate(float dx, float dy, float dz = 0.0f); virtual void rotate(float degrees); virtual void scale(float sx, float sy); virtual void skew(float sx, float sy); - virtual void setMatrix(const SkMatrix* matrix); - virtual void concatMatrix(const SkMatrix* matrix); + virtual void setMatrix(const SkMatrix& matrix); + virtual void concatMatrix(const SkMatrix& matrix); // Clip virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op); @@ -98,6 +97,10 @@ public: virtual void resetPaintFilter(); virtual void setupPaintFilter(int clearBits, int setBits); + bool isCurrentTransformSimple() { + return currentTransform()->isSimple(); + } + // ---------------------------------------------------------------------------- // Canvas draw operations // ---------------------------------------------------------------------------- @@ -106,7 +109,7 @@ public: // Bitmap-based virtual status_t drawBitmap(const SkBitmap* bitmap, float left, float top, const SkPaint* paint); - virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix, + virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix& matrix, const SkPaint* paint); virtual status_t drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, @@ -148,16 +151,10 @@ public: // Canvas draw operations - special // ---------------------------------------------------------------------------- virtual status_t drawLayer(Layer* layer, float x, float y); - virtual status_t drawDisplayList(RenderNode* displayList, Rect& dirty, - int32_t replayFlags); + virtual status_t drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t replayFlags); // TODO: rename for consistency virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty); -protected: - // NOTE: must override these to avoid calling into super class, which calls GL. These may be - // removed once DisplayListRenderer no longer inherits from OpenGLRenderer - virtual void onViewportInitialized() {}; - virtual void onSnapshotRestored() {}; private: void insertRestoreToCount(); @@ -238,17 +235,6 @@ private: return regionCopy; } - inline const SkMatrix* refMatrix(const SkMatrix* matrix) { - if (matrix) { - // Copying the matrix is cheap and prevents against the user changing - // the original matrix before the operation that uses it - const SkMatrix* copy = new SkMatrix(*matrix); - mDisplayListData->matrices.add(copy); - return copy; - } - return matrix; - } - inline Layer* refLayer(Layer* layer) { mDisplayListData->layers.add(layer); mCaches.resourceCache.incrementRefcount(layer); diff --git a/libs/hwui/DrawProfiler.cpp b/libs/hwui/DrawProfiler.cpp index 971a66e..2409554 100644 --- a/libs/hwui/DrawProfiler.cpp +++ b/libs/hwui/DrawProfiler.cpp @@ -109,7 +109,7 @@ void DrawProfiler::finishFrame() { mCurrentFrame = (mCurrentFrame + 1) % mDataSize; } -void DrawProfiler::unionDirty(Rect* dirty) { +void DrawProfiler::unionDirty(SkRect* dirty) { RETURN_IF_DISABLED(); // Not worth worrying about minimizing the dirty region for debugging, so just // dirty the entire viewport. diff --git a/libs/hwui/DrawProfiler.h b/libs/hwui/DrawProfiler.h index c1aa1c6..7c06e5d 100644 --- a/libs/hwui/DrawProfiler.h +++ b/libs/hwui/DrawProfiler.h @@ -37,7 +37,7 @@ public: void markPlaybackEnd(); void finishFrame(); - void unionDirty(Rect* dirty); + void unionDirty(SkRect* dirty); void draw(OpenGLRenderer* canvas); void dumpData(int fd); diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp index 4407ab0..bf0ab5c 100644 --- a/libs/hwui/FontRenderer.cpp +++ b/libs/hwui/FontRenderer.cpp @@ -592,7 +592,7 @@ void FontRenderer::setFont(const SkPaint* paint, const mat4& matrix) { } FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, const char *text, - uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) { + uint32_t startIndex, uint32_t len, int numGlyphs, float radius, const float* positions) { checkInit(); DropShadow image; @@ -613,8 +613,9 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, co Rect bounds; mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions); - uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius; - uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius; + uint32_t intRadius = Blur::convertRadiusToInt(radius); + uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * intRadius; + uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * intRadius; uint32_t maxSize = Caches::getInstance().maxTextureSize; if (paddedWidth > maxSize || paddedHeight > maxSize) { @@ -635,8 +636,8 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, co memset(dataBuffer, 0, size); - int penX = radius - bounds.left; - int penY = radius - bounds.bottom; + int penX = intRadius - bounds.left; + int penY = intRadius - bounds.bottom; if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) { // text has non-whitespace, so draw and blur to create the shadow @@ -727,9 +728,10 @@ void FontRenderer::removeFont(const Font* font) { } } -void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius) { +void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, float radius) { + uint32_t intRadius = Blur::convertRadiusToInt(radius); #ifdef ANDROID_ENABLE_RENDERSCRIPT - if (width * height * radius >= RS_MIN_INPUT_CUTOFF) { + if (width * height * intRadius >= RS_MIN_INPUT_CUTOFF) { uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height); if (mRs == 0) { @@ -768,12 +770,12 @@ void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int } #endif - float *gaussian = new float[2 * radius + 1]; - Blur::generateGaussianWeights(gaussian, radius); + float *gaussian = new float[2 * intRadius + 1]; + Blur::generateGaussianWeights(gaussian, intRadius); uint8_t* scratch = new uint8_t[width * height]; - Blur::horizontal(gaussian, radius, *image, scratch, width, height); - Blur::vertical(gaussian, radius, scratch, *image, width, height); + Blur::horizontal(gaussian, intRadius, *image, scratch, width, height); + Blur::vertical(gaussian, intRadius, scratch, *image, width, height); delete[] gaussian; delete[] scratch; diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h index 9259028..8ce22b0 100644 --- a/libs/hwui/FontRenderer.h +++ b/libs/hwui/FontRenderer.h @@ -129,7 +129,7 @@ public: // After renderDropShadow returns, the called owns the memory in DropShadow.image // and is responsible for releasing it when it's done with it DropShadow renderDropShadow(const SkPaint* paint, const char *text, uint32_t startIndex, - uint32_t len, int numGlyphs, uint32_t radius, const float* positions); + uint32_t len, int numGlyphs, float radius, const float* positions); void setTextureFiltering(bool linearFiltering) { mLinearFiltering = linearFiltering; @@ -218,7 +218,7 @@ private: int32_t width, int32_t height); // the input image handle may have its pointer replaced (to avoid copies) - void blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius); + void blurImage(uint8_t** image, int32_t width, int32_t height, float radius); }; }; // namespace uirenderer diff --git a/libs/hwui/Interpolator.cpp b/libs/hwui/Interpolator.cpp index 1f84b86..fc0e8a0 100644 --- a/libs/hwui/Interpolator.cpp +++ b/libs/hwui/Interpolator.cpp @@ -112,6 +112,10 @@ float LUTInterpolator::interpolate(float input) { int i1 = (int) ipart; int i2 = MathUtils::min(i1 + 1, mSize - 1); + LOG_ALWAYS_FATAL_IF(i1 < 0 || i2 < 0, "negatives in interpolation!" + " i1=%d, i2=%d, input=%f, lutpos=%f, size=%zu, values=%p, ipart=%f, weight=%f", + i1, i2, input, lutpos, mSize, mValues, ipart, weight); + float v1 = mValues[i1]; float v2 = mValues[i2]; diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp index 6a2ef2a..73a4da5 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -20,6 +20,7 @@ #include "Caches.h" #include "DeferredDisplayList.h" +#include "RenderState.h" #include "Layer.h" #include "LayerRenderer.h" #include "OpenGLRenderer.h" @@ -28,8 +29,10 @@ namespace android { namespace uirenderer { -Layer::Layer(const uint32_t layerWidth, const uint32_t layerHeight): - caches(Caches::getInstance()), texture(caches) { +Layer::Layer(RenderState& renderState, const uint32_t layerWidth, const uint32_t layerHeight) + : caches(Caches::getInstance()) + , renderState(renderState) + , texture(caches) { mesh = NULL; meshElementCount = 0; cacheable = true; @@ -41,7 +44,7 @@ Layer::Layer(const uint32_t layerWidth, const uint32_t layerHeight): colorFilter = NULL; deferredUpdateScheduled = false; renderer = NULL; - displayList = NULL; + renderNode = NULL; fbo = 0; stencil = NULL; debugDrawUpdate = false; @@ -72,7 +75,7 @@ uint32_t Layer::computeIdealHeight(uint32_t layerHeight) { void Layer::requireRenderer() { if (!renderer) { - renderer = new LayerRenderer(this); + renderer = new LayerRenderer(renderState, this); renderer->initProperties(); } } @@ -123,28 +126,26 @@ bool Layer::resize(const uint32_t width, const uint32_t height) { void Layer::removeFbo(bool flush) { if (stencil) { - GLuint previousFbo; - glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo); - if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, fbo); + GLuint previousFbo = renderState.getFramebuffer(); + renderState.bindFramebuffer(fbo); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0); - if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); + renderState.bindFramebuffer(previousFbo); caches.renderBufferCache.put(stencil); stencil = NULL; } if (fbo) { - if (flush) LayerRenderer::flushLayer(this); + if (flush) LayerRenderer::flushLayer(renderState, this); // If put fails the cache will delete the FBO caches.fboCache.put(fbo); fbo = 0; } } -void Layer::updateDeferred(RenderNode* displayList, - int left, int top, int right, int bottom) { +void Layer::updateDeferred(RenderNode* renderNode, int left, int top, int right, int bottom) { requireRenderer(); - this->displayList = displayList; + this->renderNode = renderNode; const Rect r(left, top, right, bottom); dirtyRect.unionWith(r); deferredUpdateScheduled = true; @@ -185,6 +186,7 @@ void Layer::deleteTexture() { } void Layer::clearTexture() { + caches.unbindTexture(texture.id); texture.id = 0; } @@ -218,14 +220,14 @@ void Layer::defer() { renderer->setupFrameState(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom, !isBlend()); - displayList->computeOrdering(); - displayList->deferNodeTree(deferredState); + renderNode->computeOrdering(); + renderNode->defer(deferredState, 0); deferredUpdateScheduled = false; } void Layer::cancelDefer() { - displayList = NULL; + renderNode = NULL; deferredUpdateScheduled = false; if (deferredList) { delete deferredList; @@ -245,7 +247,7 @@ void Layer::flush() { renderer->finish(); dirtyRect.setEmpty(); - displayList = NULL; + renderNode = NULL; } } @@ -254,14 +256,14 @@ void Layer::render() { renderer->prepareDirty(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom, !isBlend()); - renderer->drawDisplayList(displayList.get(), dirtyRect, RenderNode::kReplayFlag_ClipChildren); + renderer->drawRenderNode(renderNode.get(), dirtyRect, RenderNode::kReplayFlag_ClipChildren); renderer->finish(); dirtyRect.setEmpty(); deferredUpdateScheduled = false; - displayList = NULL; + renderNode = NULL; } }; // namespace uirenderer diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index 49610d5..0bf05d0 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -43,6 +43,7 @@ namespace uirenderer { // Forward declarations class Caches; +class RenderState; class OpenGLRenderer; class RenderNode; class DeferredDisplayList; @@ -53,7 +54,7 @@ class DeferStateStruct; */ class Layer { public: - Layer(const uint32_t layerWidth, const uint32_t layerHeight); + Layer(RenderState& renderState, const uint32_t layerWidth, const uint32_t layerHeight); ~Layer(); static uint32_t computeIdealWidth(uint32_t layerWidth); @@ -85,8 +86,7 @@ public: regionRect.translate(layer.left, layer.top); } - void updateDeferred(RenderNode* displayList, - int left, int top, int right, int bottom); + void updateDeferred(RenderNode* renderNode, int left, int top, int right, int bottom); inline uint32_t getWidth() const { return texture.width; @@ -297,7 +297,7 @@ public: */ bool deferredUpdateScheduled; OpenGLRenderer* renderer; - sp<RenderNode> displayList; + sp<RenderNode> renderNode; Rect dirtyRect; bool debugDrawUpdate; bool hasDrawnSinceUpdate; @@ -307,6 +307,8 @@ private: Caches& caches; + RenderState& renderState; + /** * Name of the FBO used to render the layer. If the name is 0 * this layer is not backed by an FBO, but a simple texture. diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp index 6be0146..13869aa 100644 --- a/libs/hwui/LayerCache.cpp +++ b/libs/hwui/LayerCache.cpp @@ -91,7 +91,7 @@ void LayerCache::clear() { mCache.clear(); } -Layer* LayerCache::get(const uint32_t width, const uint32_t height) { +Layer* LayerCache::get(RenderState& renderState, const uint32_t width, const uint32_t height) { Layer* layer = NULL; LayerEntry entry(width, height); @@ -108,7 +108,7 @@ Layer* LayerCache::get(const uint32_t width, const uint32_t height) { } else { LAYER_LOGD("Creating new layer %dx%d", entry.mWidth, entry.mHeight); - layer = new Layer(entry.mWidth, entry.mHeight); + layer = new Layer(renderState, entry.mWidth, entry.mHeight); layer->setBlend(true); layer->setEmpty(true); layer->setFbo(0); diff --git a/libs/hwui/LayerCache.h b/libs/hwui/LayerCache.h index 221bfe0..1b0fc2c 100644 --- a/libs/hwui/LayerCache.h +++ b/libs/hwui/LayerCache.h @@ -24,6 +24,8 @@ namespace android { namespace uirenderer { +class RenderState; + /////////////////////////////////////////////////////////////////////////////// // Defines /////////////////////////////////////////////////////////////////////////////// @@ -55,7 +57,7 @@ public: * @param width The desired width of the layer * @param height The desired height of the layer */ - Layer* get(const uint32_t width, const uint32_t height); + Layer* get(RenderState& renderState, const uint32_t width, const uint32_t height); /** * Adds the layer to the cache. The layer will not be added if there is diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp index df9aee5..873baf5 100644 --- a/libs/hwui/LayerRenderer.cpp +++ b/libs/hwui/LayerRenderer.cpp @@ -20,6 +20,7 @@ #include <private/hwui/DrawGlInfo.h> +#include "RenderState.h" #include "LayerCache.h" #include "LayerRenderer.h" #include "Matrix.h" @@ -33,7 +34,9 @@ namespace uirenderer { // Rendering /////////////////////////////////////////////////////////////////////////////// -LayerRenderer::LayerRenderer(Layer* layer): mLayer(layer) { +LayerRenderer::LayerRenderer(RenderState& renderState, Layer* layer) + : OpenGLRenderer(renderState) + , mLayer(layer) { } LayerRenderer::~LayerRenderer() { @@ -43,7 +46,7 @@ status_t LayerRenderer::prepareDirty(float left, float top, float right, float b bool opaque) { LAYER_RENDERER_LOGD("Rendering into layer, fbo = %d", mLayer->getFbo()); - glBindFramebuffer(GL_FRAMEBUFFER, mLayer->getFbo()); + renderState().bindFramebuffer(mLayer->getFbo()); const float width = mLayer->layer.getWidth(); const float height = mLayer->layer.getHeight(); @@ -180,7 +183,7 @@ void LayerRenderer::generateMesh() { // Layers management /////////////////////////////////////////////////////////////////////////////// -Layer* LayerRenderer::createRenderLayer(uint32_t width, uint32_t height) { +Layer* LayerRenderer::createRenderLayer(RenderState& renderState, uint32_t width, uint32_t height) { LAYER_RENDERER_LOGD("Requesting new render layer %dx%d", width, height); Caches& caches = Caches::getInstance(); @@ -191,7 +194,7 @@ Layer* LayerRenderer::createRenderLayer(uint32_t width, uint32_t height) { } caches.activeTexture(0); - Layer* layer = caches.layerCache.get(width, height); + Layer* layer = caches.layerCache.get(renderState, width, height); if (!layer) { ALOGW("Could not obtain a layer"); return NULL; @@ -221,10 +224,9 @@ Layer* LayerRenderer::createRenderLayer(uint32_t width, uint32_t height) { layer->setDirty(true); layer->region.clear(); - GLuint previousFbo; - glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo); + GLuint previousFbo = renderState.getFramebuffer(); - glBindFramebuffer(GL_FRAMEBUFFER, layer->getFbo()); + renderState.bindFramebuffer(layer->getFbo()); layer->bindTexture(); // Initialize the texture if needed @@ -235,7 +237,7 @@ Layer* LayerRenderer::createRenderLayer(uint32_t width, uint32_t height) { // This should only happen if we run out of memory if (glGetError() != GL_NO_ERROR) { ALOGE("Could not allocate texture for layer (fbo=%d %dx%d)", fbo, width, height); - glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); + renderState.bindFramebuffer(previousFbo); caches.resourceCache.decrementRefcount(layer); return NULL; } @@ -244,7 +246,7 @@ Layer* LayerRenderer::createRenderLayer(uint32_t width, uint32_t height) { glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, layer->getTexture(), 0); - glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); + renderState.bindFramebuffer(previousFbo); return layer; } @@ -265,10 +267,10 @@ bool LayerRenderer::resizeLayer(Layer* layer, uint32_t width, uint32_t height) { return true; } -Layer* LayerRenderer::createTextureLayer() { +Layer* LayerRenderer::createTextureLayer(RenderState& renderState) { LAYER_RENDERER_LOGD("Creating new texture layer"); - Layer* layer = new Layer(0, 0); + Layer* layer = new Layer(renderState, 0, 0); layer->setCacheable(false); layer->setTextureLayer(true); layer->setEmpty(true); @@ -332,27 +334,32 @@ void LayerRenderer::destroyLayerDeferred(Layer* layer) { } } -void LayerRenderer::flushLayer(Layer* layer) { +void LayerRenderer::flushLayer(RenderState& renderState, Layer* layer) { #ifdef GL_EXT_discard_framebuffer + if (!layer) return; + GLuint fbo = layer->getFbo(); - if (layer && fbo) { + if (fbo) { // If possible, discard any enqueud operations on deferred // rendering architectures if (Extensions::getInstance().hasDiscardFramebuffer()) { - GLuint previousFbo; - glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo); - if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, fbo); + GLuint previousFbo = renderState.getFramebuffer(); + if (fbo != previousFbo) { + renderState.bindFramebuffer(fbo); + } const GLenum attachments[] = { GL_COLOR_ATTACHMENT0 }; glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, attachments); - if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); + if (fbo != previousFbo) { + renderState.bindFramebuffer(previousFbo); + } } } #endif } -bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) { +bool LayerRenderer::copyLayer(RenderState& renderState, Layer* layer, SkBitmap* bitmap) { Caches& caches = Caches::getInstance(); if (layer && bitmap->width() <= caches.maxTextureSize && bitmap->height() <= caches.maxTextureSize) { @@ -367,7 +374,8 @@ bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) { GLuint texture; GLuint previousFbo; - GLuint previousViewport[4]; + GLsizei previousViewportWidth; + GLsizei previousViewportHeight; GLenum format; GLenum type; @@ -402,9 +410,9 @@ bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) { layer->setAlpha(255, SkXfermode::kSrc_Mode); layer->setFbo(fbo); - glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo); - glGetIntegerv(GL_VIEWPORT, (GLint*) &previousViewport); - glBindFramebuffer(GL_FRAMEBUFFER, fbo); + previousFbo = renderState.getFramebuffer(); + renderState.getViewport(&previousViewportWidth, &previousViewportHeight); + renderState.bindFramebuffer(fbo); glGenTextures(1, &texture); if ((error = glGetError()) != GL_NO_ERROR) goto error; @@ -429,7 +437,7 @@ bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) { if ((error = glGetError()) != GL_NO_ERROR) goto error; { - LayerRenderer renderer(layer); + LayerRenderer renderer(renderState, layer); renderer.setViewport(bitmap->width(), bitmap->height()); renderer.OpenGLRenderer::prepareDirty(0.0f, 0.0f, bitmap->width(), bitmap->height(), !layer->isBlend()); @@ -469,13 +477,12 @@ error: } #endif - glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); + renderState.bindFramebuffer(previousFbo); layer->setAlpha(alpha, mode); layer->setFbo(previousLayerFbo); caches.deleteTexture(texture); caches.fboCache.put(fbo); - glViewport(previousViewport[0], previousViewport[1], - previousViewport[2], previousViewport[3]); + renderState.setViewport(previousViewportWidth, previousViewportHeight); return status; } diff --git a/libs/hwui/LayerRenderer.h b/libs/hwui/LayerRenderer.h index e79e7b8..9202e49 100644 --- a/libs/hwui/LayerRenderer.h +++ b/libs/hwui/LayerRenderer.h @@ -27,6 +27,8 @@ namespace android { namespace uirenderer { +class RenderState; + /////////////////////////////////////////////////////////////////////////////// // Defines /////////////////////////////////////////////////////////////////////////////// @@ -44,7 +46,7 @@ namespace uirenderer { class LayerRenderer: public OpenGLRenderer { public: - ANDROID_API LayerRenderer(Layer* layer); + LayerRenderer(RenderState& renderState, Layer* layer); virtual ~LayerRenderer(); virtual void onViewportInitialized(int width, int height) { /* do nothing */ } @@ -52,16 +54,16 @@ public: virtual status_t clear(float left, float top, float right, float bottom, bool opaque); virtual void finish(); - ANDROID_API static Layer* createTextureLayer(); - ANDROID_API static Layer* createRenderLayer(uint32_t width, uint32_t height); - ANDROID_API static bool resizeLayer(Layer* layer, uint32_t width, uint32_t height); - ANDROID_API static void updateTextureLayer(Layer* layer, uint32_t width, uint32_t height, + static Layer* createTextureLayer(RenderState& renderState); + static Layer* createRenderLayer(RenderState& renderState, uint32_t width, uint32_t height); + static bool resizeLayer(Layer* layer, uint32_t width, uint32_t height); + static void updateTextureLayer(Layer* layer, uint32_t width, uint32_t height, bool isOpaque, bool forceFilter, GLenum renderTarget, float* textureTransform); - ANDROID_API static void destroyLayer(Layer* layer); + static void destroyLayer(Layer* layer); ANDROID_API static void destroyLayerDeferred(Layer* layer); - ANDROID_API static bool copyLayer(Layer* layer, SkBitmap* bitmap); + static bool copyLayer(RenderState& renderState, Layer* layer, SkBitmap* bitmap); - static void flushLayer(Layer* layer); + static void flushLayer(RenderState& renderState, Layer* layer); protected: virtual void ensureStencilBuffer(); diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp index 2268386..9f2014f 100644 --- a/libs/hwui/Matrix.cpp +++ b/libs/hwui/Matrix.cpp @@ -417,6 +417,8 @@ void Matrix4::mapPoint(float& x, float& y) const { } void Matrix4::mapRect(Rect& r) const { + if (isIdentity()) return; + if (isSimple()) { MUL_ADD_STORE(r.left, data[kScaleX], data[kTranslateX]); MUL_ADD_STORE(r.right, data[kScaleX], data[kTranslateX]); diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h index e33a001..1c5c578 100644 --- a/libs/hwui/Matrix.h +++ b/libs/hwui/Matrix.h @@ -147,6 +147,7 @@ public: data[kTranslateX] += x; data[kTranslateY] += y; data[kTranslateZ] += z; + mType |= kTypeUnknown; } else { // Doing a translation will only affect the translate bit of the type // Save the type diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 31f399a..ede89d7 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -35,6 +35,7 @@ #include "DeferredDisplayList.h" #include "DisplayListRenderer.h" #include "Fence.h" +#include "RenderState.h" #include "PathTessellator.h" #include "Properties.h" #include "ShadowTessellator.h" @@ -129,8 +130,10 @@ static inline T min(T a, T b) { // Constructors/destructor /////////////////////////////////////////////////////////////////////////////// -OpenGLRenderer::OpenGLRenderer(): - mCaches(Caches::getInstance()), mExtensions(Extensions::getInstance()) { +OpenGLRenderer::OpenGLRenderer(RenderState& renderState) + : mCaches(Caches::getInstance()) + , mExtensions(Extensions::getInstance()) + , mRenderState(renderState) { // *set* draw modifiers to be 0 memset(&mDrawModifiers, 0, sizeof(mDrawModifiers)); mDrawModifiers.mOverrideLayerAlpha = 1.0f; @@ -187,7 +190,7 @@ status_t OpenGLRenderer::startFrame() { discardFramebuffer(mTilingClip.left, mTilingClip.top, mTilingClip.right, mTilingClip.bottom); - glViewport(0, 0, getWidth(), getHeight()); + mRenderState.setViewport(getWidth(), getHeight()); // Functors break the tiling extension in pretty spectacular ways // This ensures we don't use tiling when a functor is going to be @@ -311,46 +314,9 @@ void OpenGLRenderer::finish() { mFrameStarted = false; } -void OpenGLRenderer::interrupt() { - if (mCaches.currentProgram) { - if (mCaches.currentProgram->isInUse()) { - mCaches.currentProgram->remove(); - mCaches.currentProgram = NULL; - } - } - mCaches.resetActiveTexture(); - mCaches.unbindMeshBuffer(); - mCaches.unbindIndicesBuffer(); - mCaches.resetVertexPointers(); - mCaches.disableTexCoordsVertexArray(); - debugOverdraw(false, false); -} - -void OpenGLRenderer::resume() { - const Snapshot* snapshot = currentSnapshot(); - glViewport(0, 0, getViewportWidth(), getViewportHeight()); - glBindFramebuffer(GL_FRAMEBUFFER, snapshot->fbo); - debugOverdraw(true, false); - - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - - mCaches.scissorEnabled = glIsEnabled(GL_SCISSOR_TEST); - mCaches.enableScissor(); - mCaches.resetScissor(); - dirtyClip(); - - mCaches.activeTexture(0); - mCaches.resetBoundTextures(); - - mCaches.blend = true; - glEnable(GL_BLEND); - glBlendFunc(mCaches.lastSrcMode, mCaches.lastDstMode); - glBlendEquation(GL_FUNC_ADD); -} - void OpenGLRenderer::resumeAfterLayer() { - glViewport(0, 0, getViewportWidth(), getViewportHeight()); - glBindFramebuffer(GL_FRAMEBUFFER, currentSnapshot()->fbo); + mRenderState.setViewport(getViewportWidth(), getViewportHeight()); + mRenderState.bindFramebuffer(currentSnapshot()->fbo); debugOverdraw(true, false); mCaches.resetScissor(); @@ -379,20 +345,19 @@ status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) { info.height = getViewportHeight(); currentTransform()->copyTo(&info.transform[0]); - bool dirtyClip = mDirtyClip; + bool prevDirtyClip = mDirtyClip; // setup GL state for functor if (mDirtyClip) { setStencilFromClip(); // can issue draws, so must precede enableScissor()/interrupt() } - if (mCaches.enableScissor() || dirtyClip) { + if (mCaches.enableScissor() || prevDirtyClip) { setScissorFromClip(); } - interrupt(); - // call functor immediately after GL state setup - (*functor)(DrawGlInfo::kModeDraw, &info); + mRenderState.invokeFunctor(functor, DrawGlInfo::kModeDraw, &info); + // Scissor may have been modified, reset dirty clip + dirtyClip(); - resume(); return DrawGlInfo::kStatusDrew; } @@ -413,17 +378,7 @@ void OpenGLRenderer::endMark() const { } void OpenGLRenderer::debugOverdraw(bool enable, bool clear) { - if (mCaches.debugOverdraw && getTargetFbo() == 0) { - if (clear) { - mCaches.disableScissor(); - mCaches.stencil.clear(); - } - if (enable) { - mCaches.stencil.enableDebugWrite(); - } else { - mCaches.stencil.disable(); - } - } + mRenderState.debugOverdraw(enable, clear); } void OpenGLRenderer::renderOverdraw() { @@ -474,8 +429,8 @@ void OpenGLRenderer::countOverdraw() { /////////////////////////////////////////////////////////////////////////////// bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) { - if (layer->deferredUpdateScheduled && layer->renderer && - layer->displayList.get() && layer->displayList->isRenderable()) { + if (layer->deferredUpdateScheduled && layer->renderer + && layer->renderNode.get() && layer->renderNode->isRenderable()) { ATRACE_CALL(); Rect& dirty = layer->dirtyRect; @@ -528,7 +483,7 @@ void OpenGLRenderer::updateLayers() { if (CC_UNLIKELY(mCaches.drawDeferDisabled)) { mLayerUpdates.clear(); - glBindFramebuffer(GL_FRAMEBUFFER, getTargetFbo()); + mRenderState.bindFramebuffer(getTargetFbo()); } endMark(); } @@ -556,7 +511,7 @@ void OpenGLRenderer::flushLayers() { } mLayerUpdates.clear(); - glBindFramebuffer(GL_FRAMEBUFFER, getTargetFbo()); + mRenderState.bindFramebuffer(getTargetFbo()); endMark(); } @@ -620,7 +575,7 @@ void OpenGLRenderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& bool restoreLayer = removed.flags & Snapshot::kFlagIsLayer; if (restoreViewport) { - glViewport(0, 0, getViewportWidth(), getViewportHeight()); + mRenderState.setViewport(getViewportWidth(), getViewportHeight()); } if (restoreClip) { @@ -791,7 +746,7 @@ bool OpenGLRenderer::createLayer(float left, float top, float right, float botto } mCaches.activeTexture(0); - Layer* layer = mCaches.layerCache.get(bounds.getWidth(), bounds.getHeight()); + Layer* layer = mCaches.layerCache.get(mRenderState, bounds.getWidth(), bounds.getHeight()); if (!layer) { return false; } @@ -853,7 +808,7 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) { endTiling(); debugOverdraw(false, false); // Bind texture to FBO - glBindFramebuffer(GL_FRAMEBUFFER, layer->getFbo()); + mRenderState.bindFramebuffer(layer->getFbo()); layer->bindTexture(); // Initialize the texture if needed @@ -876,7 +831,7 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) { dirtyClip(); // Change the ortho projection - glViewport(0, 0, bounds.getWidth(), bounds.getHeight()); + mRenderState.setViewport(bounds.getWidth(), bounds.getHeight()); return true; } @@ -907,7 +862,7 @@ void OpenGLRenderer::composeLayer(const Snapshot& removed, const Snapshot& resto layer->removeFbo(false); // Unbind current FBO and restore previous one - glBindFramebuffer(GL_FRAMEBUFFER, restored.fbo); + mRenderState.bindFramebuffer(restored.fbo); debugOverdraw(true, false); startTilingCurrentClip(); @@ -1931,25 +1886,24 @@ void OpenGLRenderer::setupDrawIndexedVertices(GLvoid* vertices) { // Drawing /////////////////////////////////////////////////////////////////////////////// -status_t OpenGLRenderer::drawDisplayList(RenderNode* displayList, Rect& dirty, - int32_t replayFlags) { +status_t OpenGLRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t replayFlags) { status_t status; // All the usual checks and setup operations (quickReject, setupDraw, etc.) // will be performed by the display list itself - if (displayList && displayList->isRenderable()) { + if (renderNode && renderNode->isRenderable()) { // compute 3d ordering - displayList->computeOrdering(); + renderNode->computeOrdering(); if (CC_UNLIKELY(mCaches.drawDeferDisabled)) { status = startFrame(); ReplayStateStruct replayStruct(*this, dirty, replayFlags); - displayList->replayNodeTree(replayStruct); + renderNode->replay(replayStruct, 0); return status | replayStruct.mDrawGlStatus; } bool avoidOverdraw = !mCaches.debugOverdraw && !mCountOverdraw; // shh, don't tell devs! DeferredDisplayList deferredList(*currentClipRect(), avoidOverdraw); DeferStateStruct deferStruct(deferredList, *this, replayFlags); - displayList->deferNodeTree(deferStruct); + renderNode->defer(deferStruct, 0); flushLayers(); status = startFrame(); @@ -2043,10 +1997,10 @@ status_t OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, float left, float to return DrawGlInfo::kStatusDrew; } -status_t OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix, +status_t OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, const SkMatrix& matrix, const SkPaint* paint) { Rect r(0.0f, 0.0f, bitmap->width(), bitmap->height()); - const mat4 transform(*matrix); + const mat4 transform(matrix); transform.mapRect(r); if (quickRejectSetupScissor(r.left, r.top, r.right, r.bottom)) { @@ -2556,8 +2510,8 @@ status_t OpenGLRenderer::drawRoundRect(float left, float top, float right, float return drawShape(left, top, texture, p); } - const VertexBuffer* vertexBuffer = mCaches.tessellationCache.getRoundRect(*currentTransform(), - right - left, bottom - top, rx, ry, p); + const VertexBuffer* vertexBuffer = mCaches.tessellationCache.getRoundRect( + *currentTransform(), *p, right - left, bottom - top, rx, ry); return drawVertexBuffer(left, top, *vertexBuffer, p); } @@ -2611,10 +2565,6 @@ status_t OpenGLRenderer::drawArc(float left, float top, float right, float botto return DrawGlInfo::kStatusDone; } - if (fabs(sweepAngle) >= 360.0f) { - return drawOval(left, top, right, bottom, p); - } - // TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180) if (p->getStyle() != SkPaint::kStroke_Style || p->getPathEffect() != 0 || useCenter) { mCaches.activeTexture(0); diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index b9b369f..4ff5780 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -57,6 +57,7 @@ namespace android { namespace uirenderer { class DeferredDisplayState; +class RenderState; class RenderNode; class TextSetupFunctor; class VertexBuffer; @@ -119,33 +120,31 @@ enum ModelViewMode { */ class OpenGLRenderer : public StatefulBaseRenderer { public: - ANDROID_API OpenGLRenderer(); + OpenGLRenderer(RenderState& renderState); virtual ~OpenGLRenderer(); - ANDROID_API void initProperties(); + void initProperties(); virtual void onViewportInitialized(); virtual status_t prepareDirty(float left, float top, float right, float bottom, bool opaque); virtual void finish(); - virtual void interrupt(); - virtual void resume(); - ANDROID_API void setCountOverdrawEnabled(bool enabled) { + void setCountOverdrawEnabled(bool enabled) { mCountOverdraw = enabled; } - ANDROID_API float getOverdraw() { + float getOverdraw() { return mCountOverdraw ? mOverdraw : 0.0f; } virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty); - ANDROID_API void pushLayerUpdate(Layer* layer); - ANDROID_API void cancelLayerUpdate(Layer* layer); - ANDROID_API void clearLayerUpdates(); - ANDROID_API void flushLayerUpdates(); + void pushLayerUpdate(Layer* layer); + void cancelLayerUpdate(Layer* layer); + void clearLayerUpdates(); + void flushLayerUpdates(); - ANDROID_API virtual int saveLayer(float left, float top, float right, float bottom, + virtual int saveLayer(float left, float top, float right, float bottom, const SkPaint* paint, int flags) { return saveLayer(left, top, right, bottom, paint, flags, NULL); } @@ -158,13 +157,13 @@ public: int saveLayerDeferred(float left, float top, float right, float bottom, const SkPaint* paint, int flags); - virtual status_t drawDisplayList(RenderNode* displayList, Rect& dirty, int32_t replayFlags = 1); + virtual status_t drawRenderNode(RenderNode* displayList, Rect& dirty, int32_t replayFlags = 1); virtual status_t drawLayer(Layer* layer, float x, float y); virtual status_t drawBitmap(const SkBitmap* bitmap, float left, float top, const SkPaint* paint); status_t drawBitmaps(const SkBitmap* bitmap, AssetAtlas::Entry* entry, int bitmapCount, TextureVertex* vertices, bool pureTranslate, const Rect& bounds, const SkPaint* paint); - virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix, + virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix& matrix, const SkPaint* paint); virtual status_t drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, @@ -231,7 +230,7 @@ public: const DrawModifiers& getDrawModifiers() { return mDrawModifiers; } void setDrawModifiers(const DrawModifiers& drawModifiers) { mDrawModifiers = drawModifiers; } - ANDROID_API bool isCurrentTransformSimple() { + bool isCurrentTransformSimple() { return currentTransform()->isSimple(); } @@ -474,6 +473,8 @@ protected: return false; } + inline RenderState& renderState() { return mRenderState; } + private: /** * Discards the content of the framebuffer if supported by the driver. @@ -977,6 +978,7 @@ private: // Various caches Caches& mCaches; Extensions& mExtensions; + RenderState& mRenderState; // List of rectangles to clear after saveLayer() is invoked Vector<Rect*> mLayers; @@ -1012,7 +1014,6 @@ private: bool mSkipOutlineClip; - friend class DisplayListRenderer; friend class Layer; friend class TextSetupFunctor; friend class DrawBitmapOp; diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp index ab6b742..97eb583 100644 --- a/libs/hwui/PathCache.cpp +++ b/libs/hwui/PathCache.cpp @@ -104,8 +104,7 @@ void PathCache::computeBounds(const SkRect& bounds, const SkPaint* paint, } static void initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height) { - bitmap.setConfig(SkBitmap::kA8_Config, width, height); - bitmap.allocPixels(); + bitmap.allocPixels(SkImageInfo::MakeA8(width, height)); bitmap.eraseColor(0); } @@ -339,7 +338,7 @@ void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) { float left, top, offset; uint32_t width, height; - PathCache::computePathBounds(t->path, t->paint, left, top, offset, width, height); + PathCache::computePathBounds(t->path, &t->paint, left, top, offset, width, height); PathTexture* texture = t->texture; texture->left = left; @@ -350,7 +349,7 @@ void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) { if (width <= mMaxTextureSize && height <= mMaxTextureSize) { SkBitmap* bitmap = new SkBitmap(); - drawPath(t->path, t->paint, *bitmap, left, top, offset, width, height); + drawPath(t->path, &t->paint, *bitmap, left, top, offset, width, height); t->setResult(bitmap); } else { texture->width = 0; diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h index 6177ff1..eee138b 100644 --- a/libs/hwui/PathCache.h +++ b/libs/hwui/PathCache.h @@ -272,7 +272,7 @@ private: class PathTask: public Task<SkBitmap*> { public: PathTask(const SkPath* path, const SkPaint* paint, PathTexture* texture): - path(path), paint(paint), texture(texture) { + path(path), paint(*paint), texture(texture) { } ~PathTask() { @@ -280,7 +280,8 @@ private: } const SkPath* path; - const SkPaint* paint; + //copied, since input paint may not be immutable + const SkPaint paint; PathTexture* texture; }; diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp index a58e3e7..310b107 100644 --- a/libs/hwui/PathTessellator.cpp +++ b/libs/hwui/PathTessellator.cpp @@ -57,17 +57,26 @@ namespace uirenderer { #define ROUND_CAP_THRESH 0.25f #define PI 3.1415926535897932f +// temporary error thresholds +#define ERROR_DEPTH 20 +#define ERROR_SCALE 1e10 +#define ERROR_SQR_INV_THRESH 1e-20 + void PathTessellator::extractTessellationScales(const Matrix4& transform, float* scaleX, float* scaleY) { - *scaleX = 1.0f; - *scaleY = 1.0f; - if (CC_UNLIKELY(!transform.isPureTranslate())) { + if (CC_LIKELY(transform.isPureTranslate())) { + *scaleX = 1.0f; + *scaleY = 1.0f; + } else { float m00 = transform.data[Matrix4::kScaleX]; float m01 = transform.data[Matrix4::kSkewY]; float m10 = transform.data[Matrix4::kSkewX]; float m11 = transform.data[Matrix4::kScaleY]; *scaleX = sqrt(m00 * m00 + m01 * m01); *scaleY = sqrt(m10 * m10 + m11 * m11); + + LOG_ALWAYS_FATAL_IF(*scaleX > ERROR_SCALE || *scaleY > ERROR_SCALE, + "scales %e x %e too large for tessellation", *scaleX, *scaleY); } } @@ -92,10 +101,12 @@ struct PaintInfo { public: PaintInfo(const SkPaint* paint, const mat4& transform) : style(paint->getStyle()), cap(paint->getStrokeCap()), isAA(paint->isAntiAlias()), - inverseScaleX(1.0f), inverseScaleY(1.0f), halfStrokeWidth(paint->getStrokeWidth() * 0.5f), maxAlpha(1.0f) { // compute inverse scales - if (CC_UNLIKELY(!transform.isPureTranslate())) { + if (CC_LIKELY(transform.isPureTranslate())) { + inverseScaleX = 1.0f; + inverseScaleY = 1.0f; + } else { float scaleX, scaleY; PathTessellator::extractTessellationScales(transform, &scaleX, &scaleY); inverseScaleX = (scaleX != 0) ? (1.0f / scaleX) : 1.0f; @@ -922,6 +933,9 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo Vector<Vertex>& outputVertices) { ATRACE_CALL(); + LOG_ALWAYS_FATAL_IF(sqrInvScaleX < ERROR_SQR_INV_THRESH || sqrInvScaleY < ERROR_SQR_INV_THRESH, + "Invalid scale factors used for approx %e, %e", sqrInvScaleX, sqrInvScaleY); + // TODO: to support joins other than sharp miter, join vertices should be labelled in the // perimeter, or resolved into more vertices. Reconsider forceClose-ing in that case. SkPath::Iter iter(path, forceClose); @@ -975,14 +989,14 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo // Bezier approximation /////////////////////////////////////////////////////////////////////////////// -// Depth at which recursion is aborted -#define ABORT_DEPTH 10 - void PathTessellator::recursiveCubicBezierVertices( float p1x, float p1y, float c1x, float c1y, float p2x, float p2y, float c2x, float c2y, float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared, Vector<Vertex>& outputVertices, int depth) { + LOG_ALWAYS_FATAL_IF(depth >= ERROR_DEPTH, "ERROR DEPTH exceeded: cubic approx, invscale %e x %e, vertcount %d", + sqrInvScaleX, sqrInvScaleY, outputVertices.size()); + float dx = p2x - p1x; float dy = p2y - p1y; float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx); @@ -990,8 +1004,7 @@ void PathTessellator::recursiveCubicBezierVertices( float d = d1 + d2; // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors - - if (depth >= ABORT_DEPTH || d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) { + if (d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) { // below thresh, draw line by adding endpoint pushToVector(outputVertices, p2x, p2y); } else { @@ -1029,11 +1042,14 @@ void PathTessellator::recursiveQuadraticBezierVertices( float cx, float cy, float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared, Vector<Vertex>& outputVertices, int depth) { + LOG_ALWAYS_FATAL_IF(depth >= ERROR_DEPTH, "ERROR_DEPTH exceeded: quadratic approx, invscale %e x %e, vertcount %d", + sqrInvScaleX, sqrInvScaleY, outputVertices.size()); + float dx = bx - ax; float dy = by - ay; float d = (cx - bx) * dy - (cy - by) * dx; - if (depth >= ABORT_DEPTH || d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) { + if (d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) { // below thresh, draw line by adding endpoint pushToVector(outputVertices, bx, by); } else { diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index d964efc..131384a 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -15,19 +15,24 @@ */ #define ATRACE_TAG ATRACE_TAG_VIEW +#define LOG_TAG "OpenGLRenderer" #include "RenderNode.h" #include <algorithm> +#include <string> #include <SkCanvas.h> #include <algorithm> #include <utils/Trace.h> +#include "DamageAccumulator.h" #include "Debug.h" #include "DisplayListOp.h" #include "DisplayListLogBuffer.h" +#include "LayerRenderer.h" +#include "OpenGLRenderer.h" #include "utils/MathUtils.h" namespace android { @@ -57,12 +62,14 @@ RenderNode::RenderNode() , mNeedsDisplayListDataSync(false) , mDisplayListData(0) , mStagingDisplayListData(0) - , mNeedsAnimatorsSync(false) { + , mNeedsAnimatorsSync(false) + , mLayer(0) { } RenderNode::~RenderNode() { delete mDisplayListData; delete mStagingDisplayListData; + LayerRenderer::destroyLayerDeferred(mLayer); } void RenderNode::setStagingDisplayList(DisplayListData* data) { @@ -110,14 +117,97 @@ void RenderNode::prepareTree(TreeInfo& info) { prepareTreeImpl(info); } -void RenderNode::prepareTreeImpl(TreeInfo& info) { - if (info.performStagingPush) { - pushStagingChanges(info); +void RenderNode::damageSelf(TreeInfo& info) { + if (isRenderable()) { + if (properties().getClipDamageToBounds()) { + info.damageAccumulator->dirty(0, 0, properties().getWidth(), properties().getHeight()); + } else { + // Hope this is big enough? + // TODO: Get this from the display list ops or something + info.damageAccumulator->dirty(INT_MIN, INT_MIN, INT_MAX, INT_MAX); + } + } +} + +void RenderNode::prepareLayer(TreeInfo& info) { + LayerType layerType = properties().layerProperties().type(); + if (CC_UNLIKELY(layerType == kLayerTypeRenderLayer)) { + // We push a null transform here as we don't care what the existing dirty + // area is, only what our display list dirty is as well as our children's + // dirty area + info.damageAccumulator->pushNullTransform(); + } +} + +void RenderNode::pushLayerUpdate(TreeInfo& info) { + LayerType layerType = properties().layerProperties().type(); + // If we are not a layer OR we cannot be rendered (eg, view was detached) + // we need to destroy any Layers we may have had previously + if (CC_LIKELY(layerType != kLayerTypeRenderLayer) || CC_UNLIKELY(!isRenderable())) { + if (layerType == kLayerTypeRenderLayer) { + info.damageAccumulator->popTransform(); + } + if (CC_UNLIKELY(mLayer)) { + LayerRenderer::destroyLayer(mLayer); + mLayer = NULL; + } + return; + } + + if (!mLayer) { + mLayer = LayerRenderer::createRenderLayer(info.renderState, getWidth(), getHeight()); + applyLayerPropertiesToLayer(info); + damageSelf(info); + } else if (mLayer->layer.getWidth() != getWidth() || mLayer->layer.getHeight() != getHeight()) { + if (!LayerRenderer::resizeLayer(mLayer, getWidth(), getHeight())) { + LayerRenderer::destroyLayer(mLayer); + mLayer = 0; + } + damageSelf(info); + } + + SkRect dirty; + info.damageAccumulator->peekAtDirty(&dirty); + info.damageAccumulator->popTransform(); + + if (!mLayer) { + if (info.errorHandler) { + std::string msg = "Unable to create layer for "; + msg += getName(); + info.errorHandler->onError(msg); + } + return; } - if (info.evaluateAnimations) { + + if (!dirty.isEmpty()) { + mLayer->updateDeferred(this, dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom); + } + // This is not inside the above if because we may have called + // updateDeferred on a previous prepare pass that didn't have a renderer + if (info.renderer && mLayer->deferredUpdateScheduled) { + info.renderer->pushLayerUpdate(mLayer); + } +} + +void RenderNode::prepareTreeImpl(TreeInfo& info) { + info.damageAccumulator->pushTransform(this); + if (info.mode == TreeInfo::MODE_FULL) { + pushStagingPropertiesChanges(info); evaluateAnimations(info); + } else if (info.mode == TreeInfo::MODE_MAYBE_DETACHING) { + pushStagingPropertiesChanges(info); + } else if (info.mode == TreeInfo::MODE_RT_ONLY) { + evaluateAnimations(info); + } + + prepareLayer(info); + if (info.mode == TreeInfo::MODE_FULL) { + pushStagingDisplayListChanges(info); } prepareSubTree(info, mDisplayListData); + pushLayerUpdate(info); + + info.damageAccumulator->popTransform(); } class PushAnimatorsFunctor { @@ -134,7 +224,7 @@ private: TreeInfo& mInfo; }; -void RenderNode::pushStagingChanges(TreeInfo& info) { +void RenderNode::pushStagingPropertiesChanges(TreeInfo& info) { // Push the animators first so that setupStartValueIfNecessary() is called // before properties() is trampled by stagingProperties(), as they are // required by some animators. @@ -149,18 +239,41 @@ void RenderNode::pushStagingChanges(TreeInfo& info) { } if (mDirtyPropertyFields) { mDirtyPropertyFields = 0; + damageSelf(info); + info.damageAccumulator->popTransform(); mProperties = mStagingProperties; + applyLayerPropertiesToLayer(info); + // We could try to be clever and only re-damage if the matrix changed. + // However, we don't need to worry about that. The cost of over-damaging + // here is only going to be a single additional map rect of this node + // plus a rect join(). The parent's transform (and up) will only be + // performed once. + info.damageAccumulator->pushTransform(this); + damageSelf(info); } +} + +void RenderNode::applyLayerPropertiesToLayer(TreeInfo& info) { + if (CC_LIKELY(!mLayer)) return; + + const LayerProperties& props = properties().layerProperties(); + mLayer->setAlpha(props.alpha(), props.xferMode()); + mLayer->setColorFilter(props.colorFilter()); + mLayer->setBlend(props.needsBlending()); +} + +void RenderNode::pushStagingDisplayListChanges(TreeInfo& info) { if (mNeedsDisplayListDataSync) { mNeedsDisplayListDataSync = false; // Do a push pass on the old tree to handle freeing DisplayListData // that are no longer used - TreeInfo oldTreeInfo; + TreeInfo oldTreeInfo(TreeInfo::MODE_MAYBE_DETACHING, info.renderState); + oldTreeInfo.damageAccumulator = info.damageAccumulator; prepareSubTree(oldTreeInfo, mDisplayListData); - // TODO: The damage for the old tree should be accounted for delete mDisplayListData; mDisplayListData = mStagingDisplayListData; mStagingDisplayListData = 0; + damageSelf(info); } } @@ -180,12 +293,21 @@ private: void RenderNode::evaluateAnimations(TreeInfo& info) { if (!mAnimators.size()) return; + // TODO: Can we target this better? For now treat it like any other staging + // property push and just damage self before and after animators are run + + damageSelf(info); + info.damageAccumulator->popTransform(); + AnimateFunctor functor(this, info); std::vector< sp<BaseRenderNodeAnimator> >::iterator newEnd; newEnd = std::remove_if(mAnimators.begin(), mAnimators.end(), functor); mAnimators.erase(newEnd, mAnimators.end()); mProperties.updateMatrix(); info.out.hasAnimations |= mAnimators.size(); + + info.damageAccumulator->pushTransform(this); + damageSelf(info); } void RenderNode::prepareSubTree(TreeInfo& info, DisplayListData* subtree) { @@ -201,8 +323,11 @@ void RenderNode::prepareSubTree(TreeInfo& info, DisplayListData* subtree) { info.prepareTextures = cache.prefetchAndMarkInUse(subtree->bitmapResources[i]); } for (size_t i = 0; i < subtree->children().size(); i++) { - RenderNode* childNode = subtree->children()[i]->mDisplayList; + DrawRenderNodeOp* op = subtree->children()[i]; + RenderNode* childNode = op->mRenderNode; + info.damageAccumulator->pushTransform(&op->mTransformFromParent); childNode->prepareTreeImpl(info); + info.damageAccumulator->popTransform(); } } } @@ -223,9 +348,9 @@ void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler) { renderer.translate(properties().getLeft(), properties().getTop()); } if (properties().getStaticMatrix()) { - renderer.concatMatrix(properties().getStaticMatrix()); + renderer.concatMatrix(*properties().getStaticMatrix()); } else if (properties().getAnimationMatrix()) { - renderer.concatMatrix(properties().getAnimationMatrix()); + renderer.concatMatrix(*properties().getAnimationMatrix()); } if (properties().hasTransformMatrix()) { if (properties().isTransformTranslateOnly()) { @@ -234,9 +359,10 @@ void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler) { renderer.concatMatrix(*properties().getTransformMatrix()); } } - bool clipToBoundsNeeded = properties().getCaching() ? false : properties().getClipToBounds(); + const bool isLayer = properties().layerProperties().type() != kLayerTypeNone; + bool clipToBoundsNeeded = isLayer ? false : properties().getClipToBounds(); if (properties().getAlpha() < 1) { - if (properties().getCaching()) { + if (isLayer) { renderer.setOverrideLayerAlpha(properties().getAlpha()); } else if (!properties().getHasOverlappingRendering()) { renderer.scaleAlpha(properties().getAlpha()); @@ -329,16 +455,16 @@ void RenderNode::computeOrdering() { // transform properties are applied correctly to top level children if (mDisplayListData == NULL) return; for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) { - DrawDisplayListOp* childOp = mDisplayListData->children()[i]; - childOp->mDisplayList->computeOrderingImpl(childOp, + DrawRenderNodeOp* childOp = mDisplayListData->children()[i]; + childOp->mRenderNode->computeOrderingImpl(childOp, properties().getOutline().getPath(), &mProjectedNodes, &mat4::identity()); } } void RenderNode::computeOrderingImpl( - DrawDisplayListOp* opState, + DrawRenderNodeOp* opState, const SkPath* outlineOfProjectionSurface, - Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface, + Vector<DrawRenderNodeOp*>* compositedChildrenOfProjectionSurface, const mat4* transformFromProjectionSurface) { mProjectedNodes.clear(); if (mDisplayListData == NULL || mDisplayListData->isEmpty()) return; @@ -362,11 +488,11 @@ void RenderNode::computeOrderingImpl( const bool isProjectionReceiver = mDisplayListData->projectionReceiveIndex >= 0; bool haveAppliedPropertiesToProjection = false; for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) { - DrawDisplayListOp* childOp = mDisplayListData->children()[i]; - RenderNode* child = childOp->mDisplayList; + DrawRenderNodeOp* childOp = mDisplayListData->children()[i]; + RenderNode* child = childOp->mRenderNode; const SkPath* projectionOutline = NULL; - Vector<DrawDisplayListOp*>* projectionChildren = NULL; + Vector<DrawRenderNodeOp*>* projectionChildren = NULL; const mat4* projectionTransform = NULL; if (isProjectionReceiver && !child->properties().getProjectBackwards()) { // if receiving projections, collect projecting descendent @@ -410,15 +536,7 @@ private: const int mLevel; }; -void RenderNode::deferNodeTree(DeferStateStruct& deferStruct) { - DeferOperationHandler handler(deferStruct, 0); - if (MathUtils::isPositive(properties().getZ())) { - issueDrawShadowOperation(Matrix4::identity(), handler); - } - issueOperations<DeferOperationHandler>(deferStruct.mRenderer, handler); -} - -void RenderNode::deferNodeInParent(DeferStateStruct& deferStruct, const int level) { +void RenderNode::defer(DeferStateStruct& deferStruct, const int level) { DeferOperationHandler handler(deferStruct, level); issueOperations<DeferOperationHandler>(deferStruct.mRenderer, handler); } @@ -448,29 +566,21 @@ private: const int mLevel; }; -void RenderNode::replayNodeTree(ReplayStateStruct& replayStruct) { - ReplayOperationHandler handler(replayStruct, 0); - if (MathUtils::isPositive(properties().getZ())) { - issueDrawShadowOperation(Matrix4::identity(), handler); - } - issueOperations<ReplayOperationHandler>(replayStruct.mRenderer, handler); -} - -void RenderNode::replayNodeInParent(ReplayStateStruct& replayStruct, const int level) { +void RenderNode::replay(ReplayStateStruct& replayStruct, const int level) { ReplayOperationHandler handler(replayStruct, level); issueOperations<ReplayOperationHandler>(replayStruct.mRenderer, handler); } -void RenderNode::buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes) { +void RenderNode::buildZSortedChildList(Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes) { if (mDisplayListData == NULL || mDisplayListData->children().size() == 0) return; for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) { - DrawDisplayListOp* childOp = mDisplayListData->children()[i]; - RenderNode* child = childOp->mDisplayList; + DrawRenderNodeOp* childOp = mDisplayListData->children()[i]; + RenderNode* child = childOp->mRenderNode; float childZ = child->properties().getZ(); if (!MathUtils::isZero(childZ)) { - zTranslatedNodes.add(ZDrawDisplayListOpPair(childZ, childOp)); + zTranslatedNodes.add(ZDrawRenderNodeOpPair(childZ, childOp)); childOp->mSkipInOrderDraw = true; } else if (!child->properties().getProjectBackwards()) { // regular, in order drawing DisplayList @@ -515,10 +625,40 @@ void RenderNode::issueDrawShadowOperation(const Matrix4& transformFromParent, T& handler(shadowOp, PROPERTY_SAVECOUNT, properties().getClipToBounds()); } +template <class T> +int RenderNode::issueOperationsOfNegZChildren( + const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes, + OpenGLRenderer& renderer, T& handler) { + if (zTranslatedNodes.isEmpty()) return -1; + + // create a save around the body of the ViewGroup's draw method, so that + // matrix/clip methods don't affect composited children + int shadowSaveCount = renderer.getSaveCount(); + handler(new (handler.allocator()) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag), + PROPERTY_SAVECOUNT, properties().getClipToBounds()); + + issueOperationsOf3dChildren(zTranslatedNodes, kNegativeZChildren, renderer, handler); + return shadowSaveCount; +} + +template <class T> +void RenderNode::issueOperationsOfPosZChildren(int shadowRestoreTo, + const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes, + OpenGLRenderer& renderer, T& handler) { + if (zTranslatedNodes.isEmpty()) return; + + LOG_ALWAYS_FATAL_IF(shadowRestoreTo < 0, "invalid save to restore to"); + handler(new (handler.allocator()) RestoreToCountOp(shadowRestoreTo), + PROPERTY_SAVECOUNT, properties().getClipToBounds()); + renderer.setOverrideLayerAlpha(1.0f); + + issueOperationsOf3dChildren(zTranslatedNodes, kPositiveZChildren, renderer, handler); +} + #define SHADOW_DELTA 0.1f template <class T> -void RenderNode::issueOperationsOf3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes, +void RenderNode::issueOperationsOf3dChildren(const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes, ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler) { const int size = zTranslatedNodes.size(); if (size == 0 @@ -553,8 +693,8 @@ void RenderNode::issueOperationsOf3dChildren(const Vector<ZDrawDisplayListOpPair float lastCasterZ = 0.0f; while (shadowIndex < endIndex || drawIndex < endIndex) { if (shadowIndex < endIndex) { - DrawDisplayListOp* casterOp = zTranslatedNodes[shadowIndex].value; - RenderNode* caster = casterOp->mDisplayList; + DrawRenderNodeOp* casterOp = zTranslatedNodes[shadowIndex].value; + RenderNode* caster = casterOp->mRenderNode; const float casterZ = zTranslatedNodes[shadowIndex].key; // attempt to render the shadow if the caster about to be drawn is its caster, // OR if its caster's Z value is similar to the previous potential caster @@ -571,8 +711,8 @@ void RenderNode::issueOperationsOf3dChildren(const Vector<ZDrawDisplayListOpPair // since it modifies the renderer's matrix int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag); - DrawDisplayListOp* childOp = zTranslatedNodes[drawIndex].value; - RenderNode* child = childOp->mDisplayList; + DrawRenderNodeOp* childOp = zTranslatedNodes[drawIndex].value; + RenderNode* child = childOp->mRenderNode; renderer.concatMatrix(childOp->mTransformFromParent); childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone @@ -588,8 +728,6 @@ template <class T> void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& handler) { DISPLAY_LIST_LOGD("%*s%d projected children:", (handler.level() + 1) * 2, "", mProjectedNodes.size()); const SkPath* projectionReceiverOutline = properties().getOutline().getPath(); - bool maskProjecteesWithPath = projectionReceiverOutline != NULL - && !projectionReceiverOutline->isRect(NULL); int restoreTo = renderer.getSaveCount(); // If the projection reciever has an outline, we mask each of the projected rendernodes to it @@ -610,7 +748,7 @@ void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& SaveLayerOp* op = new (alloc) SaveLayerOp( outlineBounds.left(), outlineBounds.top(), outlineBounds.right(), outlineBounds.bottom(), - 255, SkCanvas::kARGB_ClipLayer_SaveFlag); + 255, SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag | SkCanvas::kARGB_ClipLayer_SaveFlag); op->setMask(projectionReceiverOutline); handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds()); @@ -623,7 +761,7 @@ void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& // draw projected nodes for (size_t i = 0; i < mProjectedNodes.size(); i++) { - DrawDisplayListOp* childOp = mProjectedNodes[i]; + DrawRenderNodeOp* childOp = mProjectedNodes[i]; // matrix save, concat, and restore can be done safely without allocating operations int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag); @@ -651,8 +789,14 @@ void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& */ template <class T> void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) { + const bool drawLayer = (mLayer && (&renderer != mLayer->renderer)); + // If we are updating the contents of mLayer, we don't want to apply any of + // the RenderNode's properties to this issueOperations pass. Those will all + // be applied when the layer is drawn, aka when this is true. + const bool useViewProperties = (!mLayer || drawLayer); + const int level = handler.level(); - if (mDisplayListData->isEmpty() || properties().getAlpha() <= 0) { + if (mDisplayListData->isEmpty() || (useViewProperties && properties().getAlpha() <= 0)) { DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, getName()); return; } @@ -674,7 +818,9 @@ void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) { DISPLAY_LIST_LOGD("%*sSave %d %d", (level + 1) * 2, "", SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo); - setViewProperties<T>(renderer, handler); + if (useViewProperties) { + setViewProperties<T>(renderer, handler); + } bool quickRejected = properties().getClipToBounds() && renderer.quickRejectConservative(0, 0, properties().getWidth(), properties().getHeight()); @@ -683,31 +829,36 @@ void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) { renderer.setClippingOutline(alloc, &(mProperties.getOutline())); } - Vector<ZDrawDisplayListOpPair> zTranslatedNodes; - buildZSortedChildList(zTranslatedNodes); + if (drawLayer) { + handler(new (alloc) DrawLayerOp(mLayer, 0, 0), + renderer.getSaveCount() - 1, properties().getClipToBounds()); + } else { + Vector<ZDrawRenderNodeOpPair> zTranslatedNodes; + buildZSortedChildList(zTranslatedNodes); - // for 3d root, draw children with negative z values - issueOperationsOf3dChildren(zTranslatedNodes, kNegativeZChildren, renderer, handler); + // for 3d root, draw children with negative z values + int shadowRestoreTo = issueOperationsOfNegZChildren(zTranslatedNodes, renderer, handler); - DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance(); - const int saveCountOffset = renderer.getSaveCount() - 1; - const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex; - for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) { - DisplayListOp *op = mDisplayListData->displayListOps[i]; + DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance(); + const int saveCountOffset = renderer.getSaveCount() - 1; + const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex; + for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) { + DisplayListOp *op = mDisplayListData->displayListOps[i]; #if DEBUG_DISPLAY_LIST - op->output(level + 1); + op->output(level + 1); #endif - logBuffer.writeCommand(level, op->name()); - handler(op, saveCountOffset, properties().getClipToBounds()); + logBuffer.writeCommand(level, op->name()); + handler(op, saveCountOffset, properties().getClipToBounds()); - if (CC_UNLIKELY(i == projectionReceiveIndex && mProjectedNodes.size() > 0)) { - issueOperationsOfProjectedChildren(renderer, handler); + if (CC_UNLIKELY(i == projectionReceiveIndex && mProjectedNodes.size() > 0)) { + issueOperationsOfProjectedChildren(renderer, handler); + } } - } - // for 3d root, draw children with positive z values - issueOperationsOf3dChildren(zTranslatedNodes, kPositiveZChildren, renderer, handler); + // for 3d root, draw children with positive z values + issueOperationsOfPosZChildren(shadowRestoreTo, zTranslatedNodes, renderer, handler); + } } DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo); diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index 1a5377b..3980dad 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -39,6 +39,7 @@ #include <androidfw/ResourceTypes.h> +#include "DamageAccumulator.h" #include "Debug.h" #include "Matrix.h" #include "DeferredDisplayList.h" @@ -54,7 +55,6 @@ class SkRegion; namespace android { namespace uirenderer { -class DeferredDisplayList; class DisplayListOp; class DisplayListRenderer; class OpenGLRenderer; @@ -66,7 +66,7 @@ class ClipRectOp; class SaveLayerOp; class SaveOp; class RestoreToCountOp; -class DrawDisplayListOp; +class DrawRenderNodeOp; /** * Primary class for storing recorded canvas commands, as well as per-View/ViewGroup display properties. @@ -112,11 +112,8 @@ public: void computeOrdering(); - void deferNodeTree(DeferStateStruct& deferStruct); - void deferNodeInParent(DeferStateStruct& deferStruct, const int level); - - void replayNodeTree(ReplayStateStruct& replayStruct); - void replayNodeInParent(ReplayStateStruct& replayStruct, const int level); + void defer(DeferStateStruct& deferStruct, const int level); + void replay(ReplayStateStruct& replayStruct, const int level); ANDROID_API void output(uint32_t level = 1); ANDROID_API int getDebugSize(); @@ -125,6 +122,10 @@ public: return mDisplayListData && mDisplayListData->hasDrawOps; } + bool hasProjectionReceiver() const { + return mDisplayListData && mDisplayListData->projectionReceiveIndex >= 0; + } + const char* getName() const { return mName.string(); } @@ -148,7 +149,7 @@ public: mDirtyPropertyFields |= fields; } - const RenderProperties& properties() { + const RenderProperties& properties() const { return mProperties; } @@ -184,13 +185,18 @@ public: // UI thread only! ANDROID_API void removeAnimator(const sp<BaseRenderNodeAnimator>& animator) { mStagingAnimators.erase(animator); + // Force a sync of the staging property value + mDirtyPropertyFields |= animator->dirtyMask(); mNeedsAnimatorsSync = true; } +protected: + virtual void damageSelf(TreeInfo& info); + private: - typedef key_value_pair_t<float, DrawDisplayListOp*> ZDrawDisplayListOpPair; + typedef key_value_pair_t<float, DrawRenderNodeOp*> ZDrawRenderNodeOpPair; - static size_t findNonNegativeIndex(const Vector<ZDrawDisplayListOpPair>& nodes) { + static size_t findNonNegativeIndex(const Vector<ZDrawRenderNodeOpPair>& nodes) { for (size_t i = 0; i < nodes.size(); i++) { if (nodes[i].key >= 0.0f) return i; } @@ -204,21 +210,29 @@ private: void applyViewPropertyTransforms(mat4& matrix, bool true3dTransform = false); - void computeOrderingImpl(DrawDisplayListOp* opState, + void computeOrderingImpl(DrawRenderNodeOp* opState, const SkPath* outlineOfProjectionSurface, - Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface, + Vector<DrawRenderNodeOp*>* compositedChildrenOfProjectionSurface, const mat4* transformFromProjectionSurface); template <class T> inline void setViewProperties(OpenGLRenderer& renderer, T& handler); - void buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes); + void buildZSortedChildList(Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes); template<class T> inline void issueDrawShadowOperation(const Matrix4& transformFromParent, T& handler); template <class T> - inline void issueOperationsOf3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes, + inline int issueOperationsOfNegZChildren( + const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes, + OpenGLRenderer& renderer, T& handler); + template <class T> + inline void issueOperationsOfPosZChildren(int shadowRestoreTo, + const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes, + OpenGLRenderer& renderer, T& handler); + template <class T> + inline void issueOperationsOf3dChildren(const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes, ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler); template <class T> @@ -226,7 +240,7 @@ private: /** * Issue the RenderNode's operations into a handler, recursing for subtrees through - * DrawDisplayListOp's defer() or replay() methods + * DrawRenderNodeOp's defer() or replay() methods */ template <class T> inline void issueOperations(OpenGLRenderer& renderer, T& handler); @@ -246,9 +260,13 @@ private: }; void prepareTreeImpl(TreeInfo& info); - void pushStagingChanges(TreeInfo& info); + void pushStagingPropertiesChanges(TreeInfo& info); + void pushStagingDisplayListChanges(TreeInfo& info); void evaluateAnimations(TreeInfo& info); void prepareSubTree(TreeInfo& info, DisplayListData* subtree); + void applyLayerPropertiesToLayer(TreeInfo& info); + void prepareLayer(TreeInfo& info); + void pushLayerUpdate(TreeInfo& info); String8 mName; @@ -264,12 +282,16 @@ private: std::set< sp<BaseRenderNodeAnimator> > mStagingAnimators; std::vector< sp<BaseRenderNodeAnimator> > mAnimators; + // Owned by RT. Lifecycle is managed by prepareTree(), with the exception + // being in ~RenderNode() which may happen on any thread. + Layer* mLayer; + /** * Draw time state - these properties are only set and used during rendering */ // for projection surfaces, contains a list of all children items - Vector<DrawDisplayListOp*> mProjectedNodes; + Vector<DrawRenderNodeOp*> mProjectedNodes; }; // class RenderNode } /* namespace uirenderer */ diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp index 5f7d4e3..8848b2f 100644 --- a/libs/hwui/RenderProperties.cpp +++ b/libs/hwui/RenderProperties.cpp @@ -21,16 +21,59 @@ #include <utils/Trace.h> #include <SkCanvas.h> +#include <SkColorFilter.h> #include <SkMatrix.h> #include <SkPath.h> #include <SkPathOps.h> #include "Matrix.h" +#include "OpenGLRenderer.h" #include "utils/MathUtils.h" namespace android { namespace uirenderer { +LayerProperties::LayerProperties() + : mType(kLayerTypeNone) + , mColorFilter(NULL) { + reset(); +} + +LayerProperties::~LayerProperties() { + setType(kLayerTypeNone); +} + +void LayerProperties::reset() { + mOpaque = false; + setFromPaint(NULL); +} + +bool LayerProperties::setColorFilter(SkColorFilter* filter) { + if (mColorFilter == filter) return false; + SkRefCnt_SafeAssign(mColorFilter, filter); + return true; +} + +bool LayerProperties::setFromPaint(const SkPaint* paint) { + bool changed = false; + SkXfermode::Mode mode; + int alpha; + OpenGLRenderer::getAlphaAndModeDirect(paint, &alpha, &mode); + changed |= setAlpha(static_cast<uint8_t>(alpha)); + changed |= setXferMode(mode); + changed |= setColorFilter(paint ? paint->getColorFilter() : NULL); + return changed; +} + +LayerProperties& LayerProperties::operator=(const LayerProperties& other) { + setType(other.type()); + setOpaque(other.opaque()); + setAlpha(other.alpha()); + setXferMode(other.xferMode()); + setColorFilter(other.colorFilter()); + return *this; +} + RenderProperties::PrimitiveFields::PrimitiveFields() : mClipToBounds(true) , mProjectBackwards(false) @@ -45,8 +88,7 @@ RenderProperties::PrimitiveFields::PrimitiveFields() , mLeft(0), mTop(0), mRight(0), mBottom(0) , mWidth(0), mHeight(0) , mPivotExplicitlySet(false) - , mMatrixOrPivotDirty(false) - , mCaching(false) { + , mMatrixOrPivotDirty(false) { } RenderProperties::ComputedFields::ComputedFields() @@ -73,6 +115,7 @@ RenderProperties& RenderProperties::operator=(const RenderProperties& other) { setStaticMatrix(other.getStaticMatrix()); setAnimationMatrix(other.getAnimationMatrix()); setCameraDistance(other.getCameraDistance()); + mLayerProperties = other.layerProperties(); // Force recalculation of the matrix, since other's dirty bit may be clear mPrimitiveFields.mMatrixOrPivotDirty = true; @@ -103,9 +146,9 @@ void RenderProperties::debugOutputProperties(const int level) const { } } - bool clipToBoundsNeeded = mPrimitiveFields.mCaching ? false : mPrimitiveFields.mClipToBounds; + bool clipToBoundsNeeded = layerProperties().type() != kLayerTypeNone ? false : mPrimitiveFields.mClipToBounds; if (mPrimitiveFields.mAlpha < 1) { - if (mPrimitiveFields.mCaching) { + if (layerProperties().type() != kLayerTypeNone) { ALOGD("%*sSetOverrideLayerAlpha %.2f", level * 2, "", mPrimitiveFields.mAlpha); } else if (!mPrimitiveFields.mHasOverlappingRendering) { ALOGD("%*sScaleAlpha %.2f", level * 2, "", mPrimitiveFields.mAlpha); diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h index c0e3ce7..227d56e 100644 --- a/libs/hwui/RenderProperties.h +++ b/libs/hwui/RenderProperties.h @@ -21,6 +21,7 @@ #include <vector> #include <cutils/compiler.h> #include <androidfw/ResourceTypes.h> +#include <utils/Log.h> #include <SkCamera.h> #include <SkMatrix.h> @@ -30,8 +31,10 @@ #include "Rect.h" #include "RevealClip.h" #include "Outline.h" +#include "utils/MathUtils.h" class SkBitmap; +class SkColorFilter; class SkPaint; namespace android { @@ -39,40 +42,125 @@ namespace uirenderer { class Matrix4; class RenderNode; +class RenderProperties; + +// The __VA_ARGS__ will be executed if a & b are not equal +#define RP_SET(a, b, ...) (a != b ? (a = b, ##__VA_ARGS__, true) : false) +#define RP_SET_AND_DIRTY(a, b) RP_SET(a, b, mPrimitiveFields.mMatrixOrPivotDirty = true) + +// Keep in sync with View.java:LAYER_TYPE_* +enum LayerType { + kLayerTypeNone = 0, + // Although we cannot build the software layer directly (must be done at + // record time), this information is used when applying alpha. + kLayerTypeSoftware = 1, + kLayerTypeRenderLayer = 2, + // TODO: LayerTypeSurfaceTexture? Maybe? +}; + +class ANDROID_API LayerProperties { +public: + bool setType(LayerType type) { + if (RP_SET(mType, type)) { + reset(); + return true; + } + return false; + } + + LayerType type() const { + return mType; + } + + bool setOpaque(bool opaque) { + return RP_SET(mOpaque, opaque); + } + + bool opaque() const { + return mOpaque; + } + + bool setAlpha(uint8_t alpha) { + return RP_SET(mAlpha, alpha); + } + + uint8_t alpha() const { + return mAlpha; + } + + bool setXferMode(SkXfermode::Mode mode) { + return RP_SET(mMode, mode); + } + + SkXfermode::Mode xferMode() const { + return mMode; + } + + bool setColorFilter(SkColorFilter* filter); + + SkColorFilter* colorFilter() const { + return mColorFilter; + } + + // Sets alpha, xfermode, and colorfilter from an SkPaint + // paint may be NULL, in which case defaults will be set + bool setFromPaint(const SkPaint* paint); + + bool needsBlending() const { + return !opaque() || alpha() < 255; + } + + LayerProperties& operator=(const LayerProperties& other); + +private: + LayerProperties(); + ~LayerProperties(); + void reset(); + + friend class RenderProperties; + + LayerType mType; + // Whether or not that Layer's content is opaque, doesn't include alpha + bool mOpaque; + uint8_t mAlpha; + SkXfermode::Mode mMode; + SkColorFilter* mColorFilter; +}; /* * Data structure that holds the properties for a RenderNode */ -class RenderProperties { +class ANDROID_API RenderProperties { public: RenderProperties(); virtual ~RenderProperties(); RenderProperties& operator=(const RenderProperties& other); - void setClipToBounds(bool clipToBounds) { - mPrimitiveFields.mClipToBounds = clipToBounds; + bool setClipToBounds(bool clipToBounds) { + return RP_SET(mPrimitiveFields.mClipToBounds, clipToBounds); } - void setProjectBackwards(bool shouldProject) { - mPrimitiveFields.mProjectBackwards = shouldProject; + bool setProjectBackwards(bool shouldProject) { + return RP_SET(mPrimitiveFields.mProjectBackwards, shouldProject); } - void setProjectionReceiver(bool shouldRecieve) { - mPrimitiveFields.mProjectionReceiver = shouldRecieve; + bool setProjectionReceiver(bool shouldRecieve) { + return RP_SET(mPrimitiveFields.mProjectionReceiver, shouldRecieve); } bool isProjectionReceiver() const { return mPrimitiveFields.mProjectionReceiver; } - void setStaticMatrix(const SkMatrix* matrix) { + bool setStaticMatrix(const SkMatrix* matrix) { delete mStaticMatrix; if (matrix) { mStaticMatrix = new SkMatrix(*matrix); } else { mStaticMatrix = NULL; } + return true; } // Can return NULL @@ -80,72 +168,61 @@ public: return mStaticMatrix; } - void setAnimationMatrix(const SkMatrix* matrix) { + bool setAnimationMatrix(const SkMatrix* matrix) { delete mAnimationMatrix; if (matrix) { mAnimationMatrix = new SkMatrix(*matrix); } else { mAnimationMatrix = NULL; } + return true; } - void setAlpha(float alpha) { + bool setAlpha(float alpha) { alpha = fminf(1.0f, fmaxf(0.0f, alpha)); - if (alpha != mPrimitiveFields.mAlpha) { - mPrimitiveFields.mAlpha = alpha; - } + return RP_SET(mPrimitiveFields.mAlpha, alpha); } float getAlpha() const { return mPrimitiveFields.mAlpha; } - void setHasOverlappingRendering(bool hasOverlappingRendering) { - mPrimitiveFields.mHasOverlappingRendering = hasOverlappingRendering; + bool setHasOverlappingRendering(bool hasOverlappingRendering) { + return RP_SET(mPrimitiveFields.mHasOverlappingRendering, hasOverlappingRendering); } bool hasOverlappingRendering() const { return mPrimitiveFields.mHasOverlappingRendering; } - void setElevation(float elevation) { - if (elevation != mPrimitiveFields.mElevation) { - mPrimitiveFields.mElevation = elevation; - // mMatrixOrPivotDirty not set, since matrix doesn't respect Z - } + bool setElevation(float elevation) { + return RP_SET(mPrimitiveFields.mElevation, elevation); + // Don't dirty matrix/pivot, since they don't respect Z } float getElevation() const { return mPrimitiveFields.mElevation; } - void setTranslationX(float translationX) { - if (translationX != mPrimitiveFields.mTranslationX) { - mPrimitiveFields.mTranslationX = translationX; - mPrimitiveFields.mMatrixOrPivotDirty = true; - } + bool setTranslationX(float translationX) { + return RP_SET_AND_DIRTY(mPrimitiveFields.mTranslationX, translationX); } float getTranslationX() const { return mPrimitiveFields.mTranslationX; } - void setTranslationY(float translationY) { - if (translationY != mPrimitiveFields.mTranslationY) { - mPrimitiveFields.mTranslationY = translationY; - mPrimitiveFields.mMatrixOrPivotDirty = true; - } + bool setTranslationY(float translationY) { + return RP_SET_AND_DIRTY(mPrimitiveFields.mTranslationY, translationY); } float getTranslationY() const { return mPrimitiveFields.mTranslationY; } - void setTranslationZ(float translationZ) { - if (translationZ != mPrimitiveFields.mTranslationZ) { - mPrimitiveFields.mTranslationZ = translationZ; - // mMatrixOrPivotDirty not set, since matrix doesn't respect Z - } + bool setTranslationZ(float translationZ) { + return RP_SET(mPrimitiveFields.mTranslationZ, translationZ); + // mMatrixOrPivotDirty not set, since matrix doesn't respect Z } float getTranslationZ() const { @@ -153,8 +230,8 @@ public: } // Animation helper - void setX(float value) { - setTranslationX(value - getLeft()); + bool setX(float value) { + return setTranslationX(value - getLeft()); } // Animation helper @@ -163,8 +240,8 @@ public: } // Animation helper - void setY(float value) { - setTranslationY(value - getTop()); + bool setY(float value) { + return setTranslationY(value - getTop()); } // Animation helper @@ -173,87 +250,82 @@ public: } // Animation helper - void setZ(float value) { - setTranslationZ(value - getElevation()); + bool setZ(float value) { + return setTranslationZ(value - getElevation()); } float getZ() const { return getElevation() + getTranslationZ(); } - void setRotation(float rotation) { - if (rotation != mPrimitiveFields.mRotation) { - mPrimitiveFields.mRotation = rotation; - mPrimitiveFields.mMatrixOrPivotDirty = true; - } + bool setRotation(float rotation) { + return RP_SET_AND_DIRTY(mPrimitiveFields.mRotation, rotation); } float getRotation() const { return mPrimitiveFields.mRotation; } - void setRotationX(float rotationX) { - if (rotationX != mPrimitiveFields.mRotationX) { - mPrimitiveFields.mRotationX = rotationX; - mPrimitiveFields.mMatrixOrPivotDirty = true; - } + bool setRotationX(float rotationX) { + return RP_SET_AND_DIRTY(mPrimitiveFields.mRotationX, rotationX); } float getRotationX() const { return mPrimitiveFields.mRotationX; } - void setRotationY(float rotationY) { - if (rotationY != mPrimitiveFields.mRotationY) { - mPrimitiveFields.mRotationY = rotationY; - mPrimitiveFields.mMatrixOrPivotDirty = true; - } + bool setRotationY(float rotationY) { + return RP_SET_AND_DIRTY(mPrimitiveFields.mRotationY, rotationY); } float getRotationY() const { return mPrimitiveFields.mRotationY; } - void setScaleX(float scaleX) { - if (scaleX != mPrimitiveFields.mScaleX) { - mPrimitiveFields.mScaleX = scaleX; - mPrimitiveFields.mMatrixOrPivotDirty = true; - } + bool setScaleX(float scaleX) { + LOG_ALWAYS_FATAL_IF(scaleX > 1000000, "invalid scaleX %e", scaleX); + return RP_SET_AND_DIRTY(mPrimitiveFields.mScaleX, scaleX); } float getScaleX() const { return mPrimitiveFields.mScaleX; } - void setScaleY(float scaleY) { - if (scaleY != mPrimitiveFields.mScaleY) { - mPrimitiveFields.mScaleY = scaleY; - mPrimitiveFields.mMatrixOrPivotDirty = true; - } + bool setScaleY(float scaleY) { + LOG_ALWAYS_FATAL_IF(scaleY > 1000000, "invalid scaleY %e", scaleY); + return RP_SET_AND_DIRTY(mPrimitiveFields.mScaleY, scaleY); } float getScaleY() const { return mPrimitiveFields.mScaleY; } - void setPivotX(float pivotX) { - mPrimitiveFields.mPivotX = pivotX; - mPrimitiveFields.mMatrixOrPivotDirty = true; - mPrimitiveFields.mPivotExplicitlySet = true; + bool setPivotX(float pivotX) { + if (RP_SET(mPrimitiveFields.mPivotX, pivotX) + || !mPrimitiveFields.mPivotExplicitlySet) { + mPrimitiveFields.mMatrixOrPivotDirty = true; + mPrimitiveFields.mPivotExplicitlySet = true; + return true; + } + return false; } /* Note that getPivotX and getPivotY are adjusted by updateMatrix(), - * so the value returned mPrimitiveFields.may be stale if the RenderProperties has been - * mPrimitiveFields.modified since the last call to updateMatrix() + * so the value returned may be stale if the RenderProperties has been + * modified since the last call to updateMatrix() */ float getPivotX() const { return mPrimitiveFields.mPivotX; } - void setPivotY(float pivotY) { - mPrimitiveFields.mPivotY = pivotY; - mPrimitiveFields.mMatrixOrPivotDirty = true; - mPrimitiveFields.mPivotExplicitlySet = true; + bool setPivotY(float pivotY) { + if (RP_SET(mPrimitiveFields.mPivotY, pivotY) + || !mPrimitiveFields.mPivotExplicitlySet) { + mPrimitiveFields.mMatrixOrPivotDirty = true; + mPrimitiveFields.mPivotExplicitlySet = true; + return true; + } + return false; } float getPivotY() const { @@ -264,11 +336,13 @@ public: return mPrimitiveFields.mPivotExplicitlySet; } - void setCameraDistance(float distance) { + bool setCameraDistance(float distance) { if (distance != getCameraDistance()) { mPrimitiveFields.mMatrixOrPivotDirty = true; mComputedFields.mTransformCamera.setCameraLocation(0, 0, distance); + return true; } + return false; } float getCameraDistance() const { @@ -276,75 +350,73 @@ public: return const_cast<Sk3DView*>(&mComputedFields.mTransformCamera)->getCameraLocationZ(); } - void setLeft(int left) { - if (left != mPrimitiveFields.mLeft) { - mPrimitiveFields.mLeft = left; + bool setLeft(int left) { + if (RP_SET(mPrimitiveFields.mLeft, left)) { mPrimitiveFields.mWidth = mPrimitiveFields.mRight - mPrimitiveFields.mLeft; if (!mPrimitiveFields.mPivotExplicitlySet) { mPrimitiveFields.mMatrixOrPivotDirty = true; } + return true; } + return false; } float getLeft() const { return mPrimitiveFields.mLeft; } - void setTop(int top) { - if (top != mPrimitiveFields.mTop) { - mPrimitiveFields.mTop = top; + bool setTop(int top) { + if (RP_SET(mPrimitiveFields.mTop, top)) { mPrimitiveFields.mHeight = mPrimitiveFields.mBottom - mPrimitiveFields.mTop; if (!mPrimitiveFields.mPivotExplicitlySet) { mPrimitiveFields.mMatrixOrPivotDirty = true; } + return true; } + return false; } float getTop() const { return mPrimitiveFields.mTop; } - void setRight(int right) { - if (right != mPrimitiveFields.mRight) { - mPrimitiveFields.mRight = right; + bool setRight(int right) { + if (RP_SET(mPrimitiveFields.mRight, right)) { mPrimitiveFields.mWidth = mPrimitiveFields.mRight - mPrimitiveFields.mLeft; if (!mPrimitiveFields.mPivotExplicitlySet) { mPrimitiveFields.mMatrixOrPivotDirty = true; } + return true; } + return false; } float getRight() const { return mPrimitiveFields.mRight; } - void setBottom(int bottom) { - if (bottom != mPrimitiveFields.mBottom) { - mPrimitiveFields.mBottom = bottom; + bool setBottom(int bottom) { + if (RP_SET(mPrimitiveFields.mBottom, bottom)) { mPrimitiveFields.mHeight = mPrimitiveFields.mBottom - mPrimitiveFields.mTop; if (!mPrimitiveFields.mPivotExplicitlySet) { mPrimitiveFields.mMatrixOrPivotDirty = true; } + return true; } + return false; } float getBottom() const { return mPrimitiveFields.mBottom; } - void setLeftTop(int left, int top) { - if (left != mPrimitiveFields.mLeft || top != mPrimitiveFields.mTop) { - mPrimitiveFields.mLeft = left; - mPrimitiveFields.mTop = top; - mPrimitiveFields.mWidth = mPrimitiveFields.mRight - mPrimitiveFields.mLeft; - mPrimitiveFields.mHeight = mPrimitiveFields.mBottom - mPrimitiveFields.mTop; - if (!mPrimitiveFields.mPivotExplicitlySet) { - mPrimitiveFields.mMatrixOrPivotDirty = true; - } - } + bool setLeftTop(int left, int top) { + bool leftResult = setLeft(left); + bool topResult = setTop(top); + return leftResult || topResult; } - void setLeftTopRightBottom(int left, int top, int right, int bottom) { + bool setLeftTopRightBottom(int left, int top, int right, int bottom) { if (left != mPrimitiveFields.mLeft || top != mPrimitiveFields.mTop || right != mPrimitiveFields.mRight || bottom != mPrimitiveFields.mBottom) { mPrimitiveFields.mLeft = left; @@ -356,31 +428,27 @@ public: if (!mPrimitiveFields.mPivotExplicitlySet) { mPrimitiveFields.mMatrixOrPivotDirty = true; } + return true; } + return false; } - void offsetLeftRight(float offset) { + bool offsetLeftRight(float offset) { if (offset != 0) { mPrimitiveFields.mLeft += offset; mPrimitiveFields.mRight += offset; - if (!mPrimitiveFields.mPivotExplicitlySet) { - mPrimitiveFields.mMatrixOrPivotDirty = true; - } + return true; } + return false; } - void offsetTopBottom(float offset) { + bool offsetTopBottom(float offset) { if (offset != 0) { mPrimitiveFields.mTop += offset; mPrimitiveFields.mBottom += offset; - if (!mPrimitiveFields.mPivotExplicitlySet) { - mPrimitiveFields.mMatrixOrPivotDirty = true; - } + return true; } - } - - void setCaching(bool caching) { - mPrimitiveFields.mCaching = caching; + return false; } int getWidth() const { @@ -409,10 +477,6 @@ public: return mComputedFields.mTransformMatrix; } - bool getCaching() const { - return mPrimitiveFields.mCaching; - } - bool getClipToBounds() const { return mPrimitiveFields.mClipToBounds; } @@ -435,7 +499,7 @@ public: void debugOutputProperties(const int level) const; - ANDROID_API void updateMatrix(); + void updateMatrix(); bool hasClippingPath() const { return mPrimitiveFields.mRevealClip.willClip(); @@ -458,6 +522,23 @@ public: return mPrimitiveFields.mRevealClip; } + const LayerProperties& layerProperties() const { + return mLayerProperties; + } + + LayerProperties& mutateLayerProperties() { + return mLayerProperties; + } + + // Returns true if damage calculations should be clipped to bounds + // TODO: Figure out something better for getZ(), as children should still be + // clipped to this RP's bounds. But as we will damage -INT_MAX to INT_MAX + // for this RP's getZ() anyway, this can be optimized when we have a + // Z damage estimate instead of INT_MAX + bool getClipDamageToBounds() const { + return getClipToBounds() && (getZ() <= 0 || getOutline().isEmpty()); + } + private: // Rendering properties @@ -480,11 +561,11 @@ private: int mWidth, mHeight; bool mPivotExplicitlySet; bool mMatrixOrPivotDirty; - bool mCaching; } mPrimitiveFields; SkMatrix* mStaticMatrix; SkMatrix* mAnimationMatrix; + LayerProperties mLayerProperties; /** * These fields are all generated from other properties and are not set directly. diff --git a/libs/hwui/RenderState.cpp b/libs/hwui/RenderState.cpp new file mode 100644 index 0000000..97f379d --- /dev/null +++ b/libs/hwui/RenderState.cpp @@ -0,0 +1,112 @@ +/* + * 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 "RenderState.h" + +namespace android { +namespace uirenderer { + +RenderState::RenderState() + : mCaches(NULL) + , mViewportWidth(0) + , mViewportHeight(0) + , mFramebuffer(0) { +} + +RenderState::~RenderState() { +} + +void RenderState::onGLContextCreated() { + // This is delayed because the first access of Caches makes GL calls + mCaches = &Caches::getInstance(); + mCaches->init(); +} + +void RenderState::setViewport(GLsizei width, GLsizei height) { + mViewportWidth = width; + mViewportHeight = height; + glViewport(0, 0, mViewportWidth, mViewportHeight); +} + + +void RenderState::getViewport(GLsizei* outWidth, GLsizei* outHeight) { + *outWidth = mViewportWidth; + *outHeight = mViewportHeight; +} + +void RenderState::bindFramebuffer(GLuint fbo) { + if (mFramebuffer != fbo) { + mFramebuffer = fbo; + glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); + } +} + +void RenderState::invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info) { + interruptForFunctorInvoke(); + (*functor)(mode, info); + resumeFromFunctorInvoke(); +} + +void RenderState::interruptForFunctorInvoke() { + if (mCaches->currentProgram) { + if (mCaches->currentProgram->isInUse()) { + mCaches->currentProgram->remove(); + mCaches->currentProgram = NULL; + } + } + mCaches->resetActiveTexture(); + mCaches->unbindMeshBuffer(); + mCaches->unbindIndicesBuffer(); + mCaches->resetVertexPointers(); + mCaches->disableTexCoordsVertexArray(); + debugOverdraw(false, false); +} + +void RenderState::resumeFromFunctorInvoke() { + glViewport(0, 0, mViewportWidth, mViewportHeight); + glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); + debugOverdraw(false, false); + + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + + mCaches->scissorEnabled = glIsEnabled(GL_SCISSOR_TEST); + mCaches->enableScissor(); + mCaches->resetScissor(); + + mCaches->activeTexture(0); + mCaches->resetBoundTextures(); + + mCaches->blend = true; + glEnable(GL_BLEND); + glBlendFunc(mCaches->lastSrcMode, mCaches->lastDstMode); + glBlendEquation(GL_FUNC_ADD); +} + +void RenderState::debugOverdraw(bool enable, bool clear) { + if (mCaches->debugOverdraw && mFramebuffer == 0) { + if (clear) { + mCaches->disableScissor(); + mCaches->stencil.clear(); + } + if (enable) { + mCaches->stencil.enableDebugWrite(); + } else { + mCaches->stencil.disable(); + } + } +} + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/RenderState.h b/libs/hwui/RenderState.h new file mode 100644 index 0000000..f7116e2 --- /dev/null +++ b/libs/hwui/RenderState.h @@ -0,0 +1,70 @@ +/* + * 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. + */ +#ifndef RENDERSTATE_H +#define RENDERSTATE_H + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +#include <private/hwui/DrawGlInfo.h> + +#include "Caches.h" +#include "utils/Macros.h" + +namespace android { +namespace uirenderer { + +namespace renderthread { +class RenderThread; +} + +// TODO: Replace Cache's GL state tracking with this. For now it's more a thin +// wrapper of Caches for users to migrate to. +class RenderState { + PREVENT_COPY_AND_ASSIGN(RenderState); +public: + void onGLContextCreated(); + + void setViewport(GLsizei width, GLsizei height); + void getViewport(GLsizei* outWidth, GLsizei* outHeight); + + void bindFramebuffer(GLuint fbo); + GLint getFramebuffer() { return mFramebuffer; } + + void invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info); + + void debugOverdraw(bool enable, bool clear); + +private: + friend class renderthread::RenderThread; + + void interruptForFunctorInvoke(); + void resumeFromFunctorInvoke(); + + RenderState(); + ~RenderState(); + + Caches* mCaches; + + GLsizei mViewportWidth; + GLsizei mViewportHeight; + GLuint mFramebuffer; +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* RENDERSTATE_H */ diff --git a/libs/hwui/Renderer.h b/libs/hwui/Renderer.h index 23cab0e..2ec99c9 100644 --- a/libs/hwui/Renderer.h +++ b/libs/hwui/Renderer.h @@ -129,21 +129,6 @@ public: */ virtual void finish() = 0; - /** - * This method must be invoked before handing control over to a draw functor. - * See callDrawGLFunction() for instance. - * - * This command must not be recorded inside display lists. - */ - virtual void interrupt() = 0; - - /** - * This method must be invoked after getting control back from a draw functor. - * - * This command must not be recorded inside display lists. - */ - virtual void resume() = 0; - // ---------------------------------------------------------------------------- // Canvas state operations // ---------------------------------------------------------------------------- @@ -170,8 +155,8 @@ public: virtual void scale(float sx, float sy) = 0; virtual void skew(float sx, float sy) = 0; - virtual void setMatrix(const SkMatrix* matrix) = 0; - virtual void concatMatrix(const SkMatrix* matrix) = 0; + virtual void setMatrix(const SkMatrix& matrix) = 0; + virtual void concatMatrix(const SkMatrix& matrix) = 0; // clip virtual const Rect& getLocalClipBounds() const = 0; @@ -193,7 +178,7 @@ public: // Bitmap-based virtual status_t drawBitmap(const SkBitmap* bitmap, float left, float top, const SkPaint* paint) = 0; - virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix, + virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix& matrix, const SkPaint* paint) = 0; virtual status_t drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, @@ -233,7 +218,7 @@ public: // Canvas draw operations - special // ---------------------------------------------------------------------------- virtual status_t drawLayer(Layer* layer, float x, float y) = 0; - virtual status_t drawDisplayList(RenderNode* displayList, Rect& dirty, + virtual status_t drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t replayFlags) = 0; // TODO: rename for consistency diff --git a/libs/hwui/StatefulBaseRenderer.cpp b/libs/hwui/StatefulBaseRenderer.cpp index f2e28e1..95c0ee5 100644 --- a/libs/hwui/StatefulBaseRenderer.cpp +++ b/libs/hwui/StatefulBaseRenderer.cpp @@ -125,20 +125,16 @@ void StatefulBaseRenderer::skew(float sx, float sy) { mSnapshot->transform->skew(sx, sy); } -void StatefulBaseRenderer::setMatrix(const SkMatrix* matrix) { - if (matrix) { - mSnapshot->transform->load(*matrix); - } else { - mSnapshot->transform->loadIdentity(); - } +void StatefulBaseRenderer::setMatrix(const SkMatrix& matrix) { + mSnapshot->transform->load(matrix); } void StatefulBaseRenderer::setMatrix(const Matrix4& matrix) { mSnapshot->transform->load(matrix); } -void StatefulBaseRenderer::concatMatrix(const SkMatrix* matrix) { - mat4 transform(*matrix); +void StatefulBaseRenderer::concatMatrix(const SkMatrix& matrix) { + mat4 transform(matrix); mSnapshot->transform->multiply(transform); } diff --git a/libs/hwui/StatefulBaseRenderer.h b/libs/hwui/StatefulBaseRenderer.h index dbb1d85..e8e024f 100644 --- a/libs/hwui/StatefulBaseRenderer.h +++ b/libs/hwui/StatefulBaseRenderer.h @@ -76,9 +76,9 @@ public: virtual void scale(float sx, float sy); virtual void skew(float sx, float sy); - virtual void setMatrix(const SkMatrix* matrix); + virtual void setMatrix(const SkMatrix& matrix); void setMatrix(const Matrix4& matrix); // internal only convenience method - virtual void concatMatrix(const SkMatrix* matrix); + virtual void concatMatrix(const SkMatrix& matrix); void concatMatrix(const Matrix4& matrix); // internal only convenience method // Clip diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp index 37d5347..08b54ff 100644 --- a/libs/hwui/TessellationCache.cpp +++ b/libs/hwui/TessellationCache.cpp @@ -39,6 +39,8 @@ namespace uirenderer { TessellationCache::Description::Description() : type(kNone) + , scaleX(1.0f) + , scaleY(1.0f) , aa(false) , cap(SkPaint::kDefault_Cap) , style(SkPaint::kFill_Style) @@ -46,21 +48,13 @@ TessellationCache::Description::Description() memset(&shape, 0, sizeof(Shape)); } -TessellationCache::Description::Description(Type type) +TessellationCache::Description::Description(Type type, const Matrix4& transform, const SkPaint& paint) : type(type) - , aa(false) - , cap(SkPaint::kDefault_Cap) - , style(SkPaint::kFill_Style) - , strokeWidth(1.0f) { - memset(&shape, 0, sizeof(Shape)); -} - -TessellationCache::Description::Description(Type type, const SkPaint* paint) - : type(type) - , aa(paint->isAntiAlias()) - , cap(paint->getStrokeCap()) - , style(paint->getStyle()) - , strokeWidth(paint->getStrokeWidth()) { + , aa(paint.isAntiAlias()) + , cap(paint.getStrokeCap()) + , style(paint.getStyle()) + , strokeWidth(paint.getStrokeWidth()) { + PathTessellator::extractTessellationScales(transform, &scaleX, &scaleY); memset(&shape, 0, sizeof(Shape)); } @@ -70,10 +64,20 @@ hash_t TessellationCache::Description::hash() const { hash = JenkinsHashMix(hash, cap); hash = JenkinsHashMix(hash, style); hash = JenkinsHashMix(hash, android::hash_type(strokeWidth)); + hash = JenkinsHashMix(hash, android::hash_type(scaleX)); + hash = JenkinsHashMix(hash, android::hash_type(scaleY)); hash = JenkinsHashMixBytes(hash, (uint8_t*) &shape, sizeof(Shape)); return JenkinsHashWhiten(hash); } +void TessellationCache::Description::setupMatrixAndPaint(Matrix4* matrix, SkPaint* paint) const { + matrix->loadScale(scaleX, scaleY, 1.0f); + paint->setAntiAlias(aa); + paint->setStrokeCap(cap); + paint->setStyle(style); + paint->setStrokeWidth(strokeWidth); +} + TessellationCache::ShadowDescription::ShadowDescription() : nodeKey(NULL) { memset(&matrixData, 0, 16 * sizeof(float)); @@ -96,20 +100,15 @@ hash_t TessellationCache::ShadowDescription::hash() const { class TessellationCache::TessellationTask : public Task<VertexBuffer*> { public: - TessellationTask(Tessellator tessellator, const Description& description, - const SkPaint* paint) + TessellationTask(Tessellator tessellator, const Description& description) : tessellator(tessellator) - , description(description) - , paint(*paint) { + , description(description) { } ~TessellationTask() {} Tessellator tessellator; Description description; - - //copied, since input paint may not be immutable - const SkPaint paint; }; class TessellationCache::TessellationProcessor : public TaskProcessor<VertexBuffer*> { @@ -121,7 +120,7 @@ public: virtual void onProcess(const sp<Task<VertexBuffer*> >& task) { TessellationTask* t = static_cast<TessellationTask*>(task.get()); ATRACE_NAME("shape tessellation"); - VertexBuffer* buffer = t->tessellator(t->description, t->paint); + VertexBuffer* buffer = t->tessellator(t->description); t->setResult(buffer); } }; @@ -354,12 +353,11 @@ void TessellationCache::setMaxSize(uint32_t maxSize) { void TessellationCache::trim() { - // uint32_t size = getSize(); - // while (size > mMaxSize) { - // size -= mCache.peekOldestValue()->getSize(); - // mCache.removeOldest(); - // } - mCache.clear(); // Workaround caching tessellation corruption + uint32_t size = getSize(); + while (size > mMaxSize) { + size -= mCache.peekOldestValue()->getSize(); + mCache.removeOldest(); + } mShadowCache.clear(); } @@ -417,21 +415,12 @@ void TessellationCache::getShadowBuffers(const Matrix4* drawTransform, const Rec // Tessellation precaching /////////////////////////////////////////////////////////////////////////////// -static VertexBuffer* tessellatePath(const SkPath& path, const SkPaint* paint, - float scaleX, float scaleY) { - VertexBuffer* buffer = new VertexBuffer(); - Matrix4 matrix; - matrix.loadScale(scaleX, scaleY, 1); - PathTessellator::tessellatePath(path, paint, matrix, *buffer); - return buffer; -} - TessellationCache::Buffer* TessellationCache::getOrCreateBuffer( - const Description& entry, Tessellator tessellator, const SkPaint* paint) { + const Description& entry, Tessellator tessellator) { Buffer* buffer = mCache.get(entry); if (!buffer) { // not cached, enqueue a task to fill the buffer - sp<TessellationTask> task = new TessellationTask(tessellator, entry, paint); + sp<TessellationTask> task = new TessellationTask(tessellator, entry); buffer = new Buffer(task); if (mProcessor == NULL) { @@ -443,43 +432,49 @@ TessellationCache::Buffer* TessellationCache::getOrCreateBuffer( return buffer; } +static VertexBuffer* tessellatePath(const TessellationCache::Description& description, + const SkPath& path) { + Matrix4 matrix; + SkPaint paint; + description.setupMatrixAndPaint(&matrix, &paint); + VertexBuffer* buffer = new VertexBuffer(); + PathTessellator::tessellatePath(path, &paint, matrix, *buffer); + return buffer; +} + /////////////////////////////////////////////////////////////////////////////// -// Rounded rects +// RoundRect /////////////////////////////////////////////////////////////////////////////// -static VertexBuffer* tessellateRoundRect(const TessellationCache::Description& description, - const SkPaint& paint) { - SkRect rect = SkRect::MakeWH(description.shape.roundRect.mWidth, - description.shape.roundRect.mHeight); - float rx = description.shape.roundRect.mRx; - float ry = description.shape.roundRect.mRy; - if (paint.getStyle() == SkPaint::kStrokeAndFill_Style) { - float outset = paint.getStrokeWidth() / 2; +static VertexBuffer* tessellateRoundRect(const TessellationCache::Description& description) { + SkRect rect = SkRect::MakeWH(description.shape.roundRect.width, + description.shape.roundRect.height); + float rx = description.shape.roundRect.rx; + float ry = description.shape.roundRect.ry; + if (description.style == SkPaint::kStrokeAndFill_Style) { + float outset = description.strokeWidth / 2; rect.outset(outset, outset); rx += outset; ry += outset; } SkPath path; path.addRoundRect(rect, rx, ry); - return tessellatePath(path, &paint, - description.shape.roundRect.mScaleX, description.shape.roundRect.mScaleY); + return tessellatePath(description, path); } -TessellationCache::Buffer* TessellationCache::getRoundRectBuffer(const Matrix4& transform, - float width, float height, float rx, float ry, const SkPaint* paint) { - Description entry(Description::kRoundRect, paint); - entry.shape.roundRect.mWidth = width; - entry.shape.roundRect.mHeight = height; - entry.shape.roundRect.mRx = rx; - entry.shape.roundRect.mRy = ry; - PathTessellator::extractTessellationScales(transform, - &entry.shape.roundRect.mScaleX, &entry.shape.roundRect.mScaleY); - - return getOrCreateBuffer(entry, &tessellateRoundRect, paint); +TessellationCache::Buffer* TessellationCache::getRoundRectBuffer( + const Matrix4& transform, const SkPaint& paint, + float width, float height, float rx, float ry) { + Description entry(Description::kRoundRect, transform, paint); + entry.shape.roundRect.width = width; + entry.shape.roundRect.height = height; + entry.shape.roundRect.rx = rx; + entry.shape.roundRect.ry = ry; + return getOrCreateBuffer(entry, &tessellateRoundRect); } -const VertexBuffer* TessellationCache::getRoundRect(const Matrix4& transform, - float width, float height, float rx, float ry, const SkPaint* paint) { - return getRoundRectBuffer(transform, width, height, rx, ry, paint)->getVertexBuffer(); +const VertexBuffer* TessellationCache::getRoundRect(const Matrix4& transform, const SkPaint& paint, + float width, float height, float rx, float ry) { + return getRoundRectBuffer(transform, paint, width, height, rx, ry)->getVertexBuffer(); } }; // namespace uirenderer diff --git a/libs/hwui/TessellationCache.h b/libs/hwui/TessellationCache.h index d4ff943..688a699 100644 --- a/libs/hwui/TessellationCache.h +++ b/libs/hwui/TessellationCache.h @@ -50,30 +50,28 @@ public: enum Type { kNone, kRoundRect, - kAmbientShadow, - kSpotShadow }; Type type; + float scaleX; + float scaleY; bool aa; SkPaint::Cap cap; SkPaint::Style style; float strokeWidth; union Shape { struct RoundRect { - float mScaleX; - float mScaleY; - float mWidth; - float mHeight; - float mRx; - float mRy; + float width; + float height; + float rx; + float ry; } roundRect; } shape; Description(); - Description(Type type); - Description(Type type, const SkPaint* paint); + Description(Type type, const Matrix4& transform, const SkPaint& paint); hash_t hash() const; + void setupMatrixAndPaint(Matrix4* matrix, SkPaint* paint) const; }; struct ShadowDescription { @@ -123,12 +121,12 @@ public: // TODO: precache/get for Oval, Lines, Points, etc. - void precacheRoundRect(const Matrix4& transform, - float width, float height, float rx, float ry, const SkPaint* paint) { - getRoundRectBuffer(transform, width, height, rx, ry, paint); + void precacheRoundRect(const Matrix4& transform, const SkPaint& paint, + float width, float height, float rx, float ry) { + getRoundRectBuffer(transform, paint, width, height, rx, ry); } - const VertexBuffer* getRoundRect(const Matrix4& transform, - float width, float height, float rx, float ry, const SkPaint* paint); + const VertexBuffer* getRoundRect(const Matrix4& transform, const SkPaint& paint, + float width, float height, float rx, float ry); void precacheShadows(const Matrix4* drawTransform, const Rect& localClip, bool opaque, const SkPath* casterPerimeter, @@ -146,14 +144,14 @@ private: class TessellationTask; class TessellationProcessor; + typedef VertexBuffer* (*Tessellator)(const Description&); - typedef VertexBuffer* (*Tessellator)(const Description&, const SkPaint&); + Buffer* getRectBuffer(const Matrix4& transform, const SkPaint& paint, + float width, float height); + Buffer* getRoundRectBuffer(const Matrix4& transform, const SkPaint& paint, + float width, float height, float rx, float ry); - Buffer* getRoundRectBuffer(const Matrix4& transform, - float width, float height, float rx, float ry, const SkPaint* paint); - - Buffer* getOrCreateBuffer(const Description& entry, - Tessellator tessellator, const SkPaint* paint); + Buffer* getOrCreateBuffer(const Description& entry, Tessellator tessellator); uint32_t mSize; uint32_t mMaxSize; diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp index 60b4b96..9212d0a 100644 --- a/libs/hwui/TextureCache.cpp +++ b/libs/hwui/TextureCache.cpp @@ -288,19 +288,19 @@ void TextureCache::generateTexture(const SkBitmap* bitmap, Texture* texture, boo switch (bitmap->config()) { case SkBitmap::kA8_Config: glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(), + uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(), texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels()); texture->blend = true; break; case SkBitmap::kRGB_565_Config: glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel()); - uploadToTexture(resize, GL_RGB, bitmap->rowBytesAsPixels(), + uploadToTexture(resize, GL_RGB, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(), texture->width, texture->height, GL_UNSIGNED_SHORT_5_6_5, bitmap->getPixels()); texture->blend = false; break; case SkBitmap::kARGB_8888_Config: glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel()); - uploadToTexture(resize, GL_RGBA, bitmap->rowBytesAsPixels(), + uploadToTexture(resize, GL_RGBA, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(), texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels()); // Do this after calling getPixels() to make sure Skia's deferred // decoding happened @@ -333,34 +333,55 @@ void TextureCache::generateTexture(const SkBitmap* bitmap, Texture* texture, boo void TextureCache::uploadLoFiTexture(bool resize, const SkBitmap* bitmap, uint32_t width, uint32_t height) { SkBitmap rgbaBitmap; - rgbaBitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height, 0, bitmap->alphaType()); - rgbaBitmap.allocPixels(); + rgbaBitmap.allocPixels(SkImageInfo::MakeN32(width, height, bitmap->alphaType())); rgbaBitmap.eraseColor(0); SkCanvas canvas(rgbaBitmap); canvas.drawBitmap(*bitmap, 0.0f, 0.0f, NULL); - uploadToTexture(resize, GL_RGBA, rgbaBitmap.rowBytesAsPixels(), width, height, - GL_UNSIGNED_BYTE, rgbaBitmap.getPixels()); + uploadToTexture(resize, GL_RGBA, rgbaBitmap.rowBytesAsPixels(), rgbaBitmap.bytesPerPixel(), + width, height, GL_UNSIGNED_BYTE, rgbaBitmap.getPixels()); } -void TextureCache::uploadToTexture(bool resize, GLenum format, GLsizei stride, +void TextureCache::uploadToTexture(bool resize, GLenum format, GLsizei stride, GLsizei bpp, GLsizei width, GLsizei height, GLenum type, const GLvoid * data) { - // TODO: With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer - // if the stride doesn't match the width const bool useStride = stride != width && Extensions::getInstance().hasUnpackRowLength(); - if (useStride) { - glPixelStorei(GL_UNPACK_ROW_LENGTH, stride); - } + if ((stride == width) || useStride) { + if (useStride) { + glPixelStorei(GL_UNPACK_ROW_LENGTH, stride); + } - if (resize) { - glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data); + if (resize) { + glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data); + } else { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data); + } + + if (useStride) { + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + } } else { - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data); - } + // With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer + // if the stride doesn't match the width + + GLvoid * temp = (GLvoid *) malloc(width * height * bpp); + if (!temp) return; + + uint8_t * pDst = (uint8_t *)temp; + uint8_t * pSrc = (uint8_t *)data; + for (GLsizei i = 0; i < height; i++) { + memcpy(pDst, pSrc, width * bpp); + pDst += width * bpp; + pSrc += stride * bpp; + } + + if (resize) { + glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, temp); + } else { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, temp); + } - if (useStride) { - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + free(temp); } } diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h index e5b5c1a..61db5b0 100644 --- a/libs/hwui/TextureCache.h +++ b/libs/hwui/TextureCache.h @@ -142,7 +142,7 @@ private: void generateTexture(const SkBitmap* bitmap, Texture* texture, bool regenerate = false); void uploadLoFiTexture(bool resize, const SkBitmap* bitmap, uint32_t width, uint32_t height); - void uploadToTexture(bool resize, GLenum format, GLsizei stride, + void uploadToTexture(bool resize, GLenum format, GLsizei stride, GLsizei bpp, GLsizei width, GLsizei height, GLenum type, const GLvoid * data); void init(); diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h index 8355f83..249e525 100644 --- a/libs/hwui/TreeInfo.h +++ b/libs/hwui/TreeInfo.h @@ -16,13 +16,20 @@ #ifndef TREEINFO_H #define TREEINFO_H +#include <string> + #include <utils/Timers.h> +#include "DamageAccumulator.h" +#include "utils/Macros.h" + namespace android { namespace uirenderer { class BaseRenderNodeAnimator; class AnimationListener; +class OpenGLRenderer; +class RenderState; class AnimationHook { public: @@ -31,21 +38,60 @@ protected: ~AnimationHook() {} }; -struct TreeInfo { - // The defaults here should be safe for everyone but DrawFrameTask to use as-is. - TreeInfo() - : frameTimeMs(0) +class ErrorHandler { +public: + virtual void onError(const std::string& message) = 0; +protected: + ~ErrorHandler() {} +}; + +// This would be a struct, but we want to PREVENT_COPY_AND_ASSIGN +class TreeInfo { + PREVENT_COPY_AND_ASSIGN(TreeInfo); +public: + enum TraversalMode { + // The full monty - sync, push, run animators, etc... Used by DrawFrameTask + // May only be used if both the UI thread and RT thread are blocked on the + // prepare + MODE_FULL, + // Run only what can be done safely on RT thread. Currently this only means + // animators, but potentially things like SurfaceTexture updates + // could be handled by this as well if there are no listeners + MODE_RT_ONLY, + // The subtree is being detached. Maybe. If the RenderNode is present + // in both the old and new display list's children then it will get a + // MODE_MAYBE_DETACHING followed shortly by a MODE_FULL. + // Push any pending display list changes in case it is detached, + // but don't evaluate animators and such as if it isn't detached as a + // MODE_FULL will follow shortly. + MODE_MAYBE_DETACHING, + // TODO: TRIM_MEMORY? + }; + + explicit TreeInfo(TraversalMode mode, RenderState& renderState) + : mode(mode) + , frameTimeMs(0) , animationHook(NULL) - , prepareTextures(false) - , performStagingPush(true) - , evaluateAnimations(false) + , prepareTextures(mode == MODE_FULL) + , damageAccumulator(NullDamageAccumulator::instance()) + , renderState(renderState) + , renderer(NULL) + , errorHandler(NULL) {} + const TraversalMode mode; nsecs_t frameTimeMs; AnimationHook* animationHook; + // TODO: Remove this? Currently this is used to signal to stop preparing + // textures if we run out of cache space. bool prepareTextures; - bool performStagingPush; - bool evaluateAnimations; + // Must not be null + IDamageAccumulator* damageAccumulator; + RenderState& renderState; + // The renderer that will be drawing the next frame. Use this to push any + // layer updates or similar. May be NULL. + OpenGLRenderer* renderer; + ErrorHandler* errorHandler; struct Out { Out() diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 9ebee1d..281a8e1 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -18,332 +18,31 @@ #include "CanvasContext.h" -#include <cutils/properties.h> #include <private/hwui/DrawGlInfo.h> #include <strings.h> +#include "EglManager.h" #include "RenderThread.h" #include "../Caches.h" #include "../DeferredLayerUpdater.h" +#include "../RenderState.h" #include "../LayerRenderer.h" #include "../OpenGLRenderer.h" #include "../Stencil.h" -#define PROPERTY_RENDER_DIRTY_REGIONS "debug.hwui.render_dirty_regions" -#define GLES_VERSION 2 - -// Android-specific addition that is used to show when frames began in systrace -EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface); - namespace android { namespace uirenderer { namespace renderthread { -#define ERROR_CASE(x) case x: return #x; -static const char* egl_error_str(EGLint error) { - switch (error) { - ERROR_CASE(EGL_SUCCESS) - ERROR_CASE(EGL_NOT_INITIALIZED) - ERROR_CASE(EGL_BAD_ACCESS) - ERROR_CASE(EGL_BAD_ALLOC) - ERROR_CASE(EGL_BAD_ATTRIBUTE) - ERROR_CASE(EGL_BAD_CONFIG) - ERROR_CASE(EGL_BAD_CONTEXT) - ERROR_CASE(EGL_BAD_CURRENT_SURFACE) - ERROR_CASE(EGL_BAD_DISPLAY) - ERROR_CASE(EGL_BAD_MATCH) - ERROR_CASE(EGL_BAD_NATIVE_PIXMAP) - ERROR_CASE(EGL_BAD_NATIVE_WINDOW) - ERROR_CASE(EGL_BAD_PARAMETER) - ERROR_CASE(EGL_BAD_SURFACE) - ERROR_CASE(EGL_CONTEXT_LOST) - default: - return "Unknown error"; - } -} -static const char* egl_error_str() { - return egl_error_str(eglGetError()); -} - -static bool load_dirty_regions_property() { - char buf[PROPERTY_VALUE_MAX]; - int len = property_get(PROPERTY_RENDER_DIRTY_REGIONS, buf, "true"); - return !strncasecmp("true", buf, len); -} - -// This class contains the shared global EGL objects, such as EGLDisplay -// and EGLConfig, which are re-used by CanvasContext -class GlobalContext { -public: - static GlobalContext* get(); - - // Returns true on success, false on failure - void initialize(); - - bool hasContext(); - - void usePBufferSurface(); - EGLSurface createSurface(EGLNativeWindowType window); - void destroySurface(EGLSurface surface); - - void destroy(); - - bool isCurrent(EGLSurface surface) { return mCurrentSurface == surface; } - // Returns true if the current surface changed, false if it was already current - bool makeCurrent(EGLSurface surface); - void beginFrame(EGLSurface surface, EGLint* width, EGLint* height); - void swapBuffers(EGLSurface surface); - - bool enableDirtyRegions(EGLSurface surface); - - void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize); - -private: - GlobalContext(); - // GlobalContext is never destroyed, method is purposely not implemented - ~GlobalContext(); - - void loadConfig(); - void createContext(); - void initAtlas(); - - static GlobalContext* sContext; - - EGLDisplay mEglDisplay; - EGLConfig mEglConfig; - EGLContext mEglContext; - EGLSurface mPBufferSurface; - - const bool mRequestDirtyRegions; - bool mCanSetDirtyRegions; - - EGLSurface mCurrentSurface; - - sp<GraphicBuffer> mAtlasBuffer; - int64_t* mAtlasMap; - size_t mAtlasMapSize; -}; - -GlobalContext* GlobalContext::sContext = 0; - -GlobalContext* GlobalContext::get() { - if (!sContext) { - sContext = new GlobalContext(); - } - return sContext; -} - -GlobalContext::GlobalContext() - : mEglDisplay(EGL_NO_DISPLAY) - , mEglConfig(0) - , mEglContext(EGL_NO_CONTEXT) - , mPBufferSurface(EGL_NO_SURFACE) - , mRequestDirtyRegions(load_dirty_regions_property()) - , mCurrentSurface(EGL_NO_SURFACE) - , mAtlasMap(NULL) - , mAtlasMapSize(0) { - mCanSetDirtyRegions = mRequestDirtyRegions; - ALOGD("Render dirty regions requested: %s", mRequestDirtyRegions ? "true" : "false"); -} - -void GlobalContext::initialize() { - if (hasContext()) return; - - mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); - LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY, - "Failed to get EGL_DEFAULT_DISPLAY! err=%s", egl_error_str()); - - EGLint major, minor; - LOG_ALWAYS_FATAL_IF(eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE, - "Failed to initialize display %p! err=%s", mEglDisplay, egl_error_str()); - - ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor); - - loadConfig(); - createContext(); - usePBufferSurface(); - Caches::getInstance().init(); - initAtlas(); -} - -bool GlobalContext::hasContext() { - return mEglDisplay != EGL_NO_DISPLAY; -} - -void GlobalContext::loadConfig() { - EGLint swapBehavior = mCanSetDirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0; - EGLint attribs[] = { - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL_RED_SIZE, 8, - EGL_GREEN_SIZE, 8, - EGL_BLUE_SIZE, 8, - EGL_ALPHA_SIZE, 8, - EGL_DEPTH_SIZE, 0, - EGL_CONFIG_CAVEAT, EGL_NONE, - EGL_STENCIL_SIZE, Stencil::getStencilSize(), - EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior, - EGL_NONE - }; - - EGLint num_configs = 1; - if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, num_configs, &num_configs) - || num_configs != 1) { - // Failed to get a valid config - if (mCanSetDirtyRegions) { - ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without..."); - // Try again without dirty regions enabled - mCanSetDirtyRegions = false; - loadConfig(); - } else { - LOG_ALWAYS_FATAL("Failed to choose config, error = %s", egl_error_str()); - } - } -} - -void GlobalContext::createContext() { - EGLint attribs[] = { EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION, EGL_NONE }; - mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attribs); - LOG_ALWAYS_FATAL_IF(mEglContext == EGL_NO_CONTEXT, - "Failed to create context, error = %s", egl_error_str()); -} - -void GlobalContext::setTextureAtlas(const sp<GraphicBuffer>& buffer, - int64_t* map, size_t mapSize) { - - // Already initialized - if (mAtlasBuffer.get()) { - ALOGW("Multiple calls to setTextureAtlas!"); - delete map; - return; - } - - mAtlasBuffer = buffer; - mAtlasMap = map; - mAtlasMapSize = mapSize; - - if (hasContext()) { - usePBufferSurface(); - initAtlas(); - } -} - -void GlobalContext::initAtlas() { - if (mAtlasBuffer.get()) { - Caches::getInstance().assetAtlas.init(mAtlasBuffer, mAtlasMap, mAtlasMapSize); - } -} - -void GlobalContext::usePBufferSurface() { - LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY, - "usePBufferSurface() called on uninitialized GlobalContext!"); - - if (mPBufferSurface == EGL_NO_SURFACE) { - EGLint attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE }; - mPBufferSurface = eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs); - } - makeCurrent(mPBufferSurface); -} - -EGLSurface GlobalContext::createSurface(EGLNativeWindowType window) { - initialize(); - return eglCreateWindowSurface(mEglDisplay, mEglConfig, window, NULL); -} - -void GlobalContext::destroySurface(EGLSurface surface) { - if (isCurrent(surface)) { - makeCurrent(EGL_NO_SURFACE); - } - if (!eglDestroySurface(mEglDisplay, surface)) { - ALOGW("Failed to destroy surface %p, error=%s", (void*)surface, egl_error_str()); - } -} - -void GlobalContext::destroy() { - if (mEglDisplay == EGL_NO_DISPLAY) return; - - usePBufferSurface(); - if (Caches::hasInstance()) { - Caches::getInstance().terminate(); - } - - eglDestroyContext(mEglDisplay, mEglContext); - eglDestroySurface(mEglDisplay, mPBufferSurface); - eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - eglTerminate(mEglDisplay); - eglReleaseThread(); - - mEglDisplay = EGL_NO_DISPLAY; - mEglContext = EGL_NO_CONTEXT; - mPBufferSurface = EGL_NO_SURFACE; - mCurrentSurface = EGL_NO_SURFACE; -} - -bool GlobalContext::makeCurrent(EGLSurface surface) { - if (isCurrent(surface)) return false; - - if (surface == EGL_NO_SURFACE) { - // If we are setting EGL_NO_SURFACE we don't care about any of the potential - // return errors, which would only happen if mEglDisplay had already been - // destroyed in which case the current context is already NO_CONTEXT - eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - } else if (!eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)) { - LOG_ALWAYS_FATAL("Failed to make current on surface %p, error=%s", - (void*)surface, egl_error_str()); - } - mCurrentSurface = surface; - return true; -} - -void GlobalContext::beginFrame(EGLSurface surface, EGLint* width, EGLint* height) { - LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE, - "Tried to beginFrame on EGL_NO_SURFACE!"); - makeCurrent(surface); - if (width) { - eglQuerySurface(mEglDisplay, surface, EGL_WIDTH, width); - } - if (height) { - eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, height); - } - eglBeginFrame(mEglDisplay, surface); -} - -void GlobalContext::swapBuffers(EGLSurface surface) { - eglSwapBuffers(mEglDisplay, surface); - EGLint err = eglGetError(); - LOG_ALWAYS_FATAL_IF(err != EGL_SUCCESS, - "Encountered EGL error %d %s during rendering", err, egl_error_str(err)); -} - -bool GlobalContext::enableDirtyRegions(EGLSurface surface) { - if (!mRequestDirtyRegions) return false; - - if (mCanSetDirtyRegions) { - if (!eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED)) { - ALOGW("Failed to set EGL_SWAP_BEHAVIOR on surface %p, error=%s", - (void*) surface, egl_error_str()); - return false; - } - return true; - } - // Perhaps it is already enabled? - EGLint value; - if (!eglQuerySurface(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, &value)) { - ALOGW("Failed to query EGL_SWAP_BEHAVIOR on surface %p, error=%p", - (void*) surface, egl_error_str()); - return false; - } - return value == EGL_BUFFER_PRESERVED; -} - -CanvasContext::CanvasContext(bool translucent, RenderNode* rootRenderNode) - : mRenderThread(RenderThread::getInstance()) +CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode) + : mRenderThread(thread) + , mEglManager(thread.eglManager()) , mEglSurface(EGL_NO_SURFACE) , mDirtyRegionsEnabled(false) , mOpaque(!translucent) - , mCanvas(0) + , mCanvas(NULL) , mHaveNewSurface(false) , mRootRenderNode(rootRenderNode) { - mGlobalContext = GlobalContext::get(); } CanvasContext::~CanvasContext() { @@ -363,19 +62,16 @@ void CanvasContext::setSurface(ANativeWindow* window) { mNativeWindow = window; if (mEglSurface != EGL_NO_SURFACE) { - mGlobalContext->destroySurface(mEglSurface); + mEglManager.destroySurface(mEglSurface); mEglSurface = EGL_NO_SURFACE; } if (window) { - mEglSurface = mGlobalContext->createSurface(window); - LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE, - "Failed to create EGLSurface for window %p, eglErr = %s", - (void*) window, egl_error_str()); + mEglSurface = mEglManager.createSurface(window); } if (mEglSurface != EGL_NO_SURFACE) { - mDirtyRegionsEnabled = mGlobalContext->enableDirtyRegions(mEglSurface); + mDirtyRegionsEnabled = mEglManager.enableDirtyRegions(mEglSurface); mHaveNewSurface = true; makeCurrent(); } else { @@ -384,7 +80,7 @@ void CanvasContext::setSurface(ANativeWindow* window) { } void CanvasContext::swapBuffers() { - mGlobalContext->swapBuffers(mEglSurface); + mEglManager.swapBuffers(mEglSurface); mHaveNewSurface = false; } @@ -397,7 +93,7 @@ void CanvasContext::requireSurface() { bool CanvasContext::initialize(ANativeWindow* window) { if (mCanvas) return false; setSurface(window); - mCanvas = new OpenGLRenderer(); + mCanvas = new OpenGLRenderer(mRenderThread.renderState()); mCanvas->initProperties(); return true; } @@ -424,7 +120,7 @@ void CanvasContext::setOpaque(bool opaque) { void CanvasContext::makeCurrent() { // TODO: Figure out why this workaround is needed, see b/13913604 // In the meantime this matches the behavior of GLRenderer, so it is not a regression - mHaveNewSurface |= mGlobalContext->makeCurrent(mEglSurface); + mHaveNewSurface |= mEglManager.makeCurrent(mEglSurface); } void CanvasContext::processLayerUpdate(DeferredLayerUpdater* layerUpdater, TreeInfo& info) { @@ -439,6 +135,8 @@ void CanvasContext::prepareTree(TreeInfo& info) { mRenderThread.removeFrameCallback(this); info.frameTimeMs = mRenderThread.timeLord().frameTimeMs(); + info.damageAccumulator = &mDamageAccumulator; + info.renderer = mCanvas; mRootRenderNode->prepareTree(info); int runningBehind = 0; @@ -450,9 +148,7 @@ void CanvasContext::prepareTree(TreeInfo& info) { info.out.canDrawThisFrame = !runningBehind; if (info.out.hasAnimations || !info.out.canDrawThisFrame) { - if (info.out.hasFunctors) { - info.out.requiresUiRedraw = true; - } else if (!info.out.requiresUiRedraw) { + if (!info.out.requiresUiRedraw) { // If animationsNeedsRedraw is set don't bother posting for an RT anim // as we will just end up fighting the UI thread. mRenderThread.postFrameCallback(this); @@ -465,33 +161,36 @@ void CanvasContext::notifyFramePending() { mRenderThread.pushBackFrameCallback(this); } -void CanvasContext::draw(Rect* dirty) { +void CanvasContext::draw() { LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE, - "drawDisplayList called on a context with no canvas or surface!"); + "drawRenderNode called on a context with no canvas or surface!"); profiler().markPlaybackStart(); + SkRect dirty; + mDamageAccumulator.finish(&dirty); + EGLint width, height; - mGlobalContext->beginFrame(mEglSurface, &width, &height); + mEglManager.beginFrame(mEglSurface, &width, &height); if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) { mCanvas->setViewport(width, height); - dirty = NULL; + dirty.setEmpty(); } else if (!mDirtyRegionsEnabled || mHaveNewSurface) { - dirty = NULL; + dirty.setEmpty(); } else { - profiler().unionDirty(dirty); + profiler().unionDirty(&dirty); } status_t status; - if (dirty && !dirty->isEmpty()) { - status = mCanvas->prepareDirty(dirty->left, dirty->top, - dirty->right, dirty->bottom, mOpaque); + if (!dirty.isEmpty()) { + status = mCanvas->prepareDirty(dirty.fLeft, dirty.fTop, + dirty.fRight, dirty.fBottom, mOpaque); } else { status = mCanvas->prepare(mOpaque); } Rect outBounds; - status |= mCanvas->drawDisplayList(mRootRenderNode.get(), outBounds); + status |= mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds); profiler().draw(mCanvas); @@ -516,40 +215,35 @@ void CanvasContext::doFrame() { profiler().startFrame(); - TreeInfo info; - info.evaluateAnimations = true; - info.performStagingPush = false; + TreeInfo info(TreeInfo::MODE_RT_ONLY, mRenderThread.renderState()); info.prepareTextures = false; prepareTree(info); if (info.out.canDrawThisFrame) { - draw(NULL); + draw(); } } -void CanvasContext::invokeFunctor(Functor* functor) { +void CanvasContext::invokeFunctor(RenderThread& thread, Functor* functor) { ATRACE_CALL(); DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext; - if (mGlobalContext->hasContext()) { - requireGlContext(); + if (thread.eglManager().hasEglContext()) { + thread.eglManager().requireGlContext(); mode = DrawGlInfo::kModeProcess; } - (*functor)(mode, NULL); - if (mCanvas) { - mCanvas->resume(); - } + thread.renderState().invokeFunctor(functor, mode, NULL); } bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) { requireGlContext(); - TreeInfo info; + TreeInfo info(TreeInfo::MODE_FULL, mRenderThread.renderState()); layer->apply(info); - return LayerRenderer::copyLayer(layer->backingLayer(), bitmap); + return LayerRenderer::copyLayer(mRenderThread.renderState(), layer->backingLayer(), bitmap); } void CanvasContext::flushCaches(Caches::FlushMode flushMode) { - if (mGlobalContext->hasContext()) { + if (mEglManager.hasEglContext()) { requireGlContext(); Caches::getInstance().flush(flushMode); } @@ -562,25 +256,25 @@ void CanvasContext::runWithGlContext(RenderTask* task) { Layer* CanvasContext::createRenderLayer(int width, int height) { requireSurface(); - return LayerRenderer::createRenderLayer(width, height); + return LayerRenderer::createRenderLayer(mRenderThread.renderState(), width, height); } Layer* CanvasContext::createTextureLayer() { requireSurface(); - return LayerRenderer::createTextureLayer(); + return LayerRenderer::createTextureLayer(mRenderThread.renderState()); } void CanvasContext::requireGlContext() { if (mEglSurface != EGL_NO_SURFACE) { makeCurrent(); } else { - mGlobalContext->usePBufferSurface(); + mEglManager.usePBufferSurface(); } } -void CanvasContext::setTextureAtlas(const sp<GraphicBuffer>& buffer, - int64_t* map, size_t mapSize) { - GlobalContext::get()->setTextureAtlas(buffer, map, mapSize); +void CanvasContext::setTextureAtlas(RenderThread& thread, + const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize) { + thread.eglManager().setTextureAtlas(buffer, map, mapSize); } } /* namespace renderthread */ diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 00c5bf0..d2ce1a6 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -23,6 +23,7 @@ #include <utils/Functor.h> #include <utils/Vector.h> +#include "../DamageAccumulator.h" #include "../DrawProfiler.h" #include "../RenderNode.h" #include "RenderTask.h" @@ -40,13 +41,13 @@ class Layer; namespace renderthread { -class GlobalContext; +class EglManager; // This per-renderer class manages the bridge between the global EGL context // and the render surface. class CanvasContext : public IFrameCallback { public: - CanvasContext(bool translucent, RenderNode* rootRenderNode); + CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode); virtual ~CanvasContext(); bool initialize(ANativeWindow* window); @@ -57,7 +58,7 @@ public: void makeCurrent(); void processLayerUpdate(DeferredLayerUpdater* layerUpdater, TreeInfo& info); void prepareTree(TreeInfo& info); - void draw(Rect* dirty); + void draw(); void destroyCanvasAndSurface(); // IFrameCallback, Chroreographer-driven frame callback entry point @@ -67,15 +68,15 @@ public: void flushCaches(Caches::FlushMode flushMode); - void invokeFunctor(Functor* functor); + static void invokeFunctor(RenderThread& thread, Functor* functor); void runWithGlContext(RenderTask* task); Layer* createRenderLayer(int width, int height); Layer* createTextureLayer(); - ANDROID_API static void setTextureAtlas(const sp<GraphicBuffer>& buffer, - int64_t* map, size_t mapSize); + ANDROID_API static void setTextureAtlas(RenderThread& thread, + const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize); void notifyFramePending(); @@ -90,8 +91,8 @@ private: void requireGlContext(); - GlobalContext* mGlobalContext; RenderThread& mRenderThread; + EglManager& mEglManager; sp<ANativeWindow> mNativeWindow; EGLSurface mEglSurface; bool mDirtyRegionsEnabled; @@ -99,6 +100,7 @@ private: bool mOpaque; OpenGLRenderer* mCanvas; bool mHaveNewSurface; + DamageAccumulator mDamageAccumulator; const sp<RenderNode> mRootRenderNode; diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp index 61d67ca..fddffd5 100644 --- a/libs/hwui/renderthread/DrawFrameTask.cpp +++ b/libs/hwui/renderthread/DrawFrameTask.cpp @@ -68,10 +68,6 @@ void DrawFrameTask::removeLayerUpdate(DeferredLayerUpdater* layer) { } } -void DrawFrameTask::setDirty(int left, int top, int right, int bottom) { - mDirty.set(left, top, right, bottom); -} - int DrawFrameTask::drawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos) { LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!"); @@ -83,7 +79,6 @@ int DrawFrameTask::drawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos // Reset the single-frame data mFrameTimeNanos = 0; mRecordDurationNanos = 0; - mDirty.setEmpty(); return mSyncResult; } @@ -103,13 +98,12 @@ void DrawFrameTask::run() { bool canUnblockUiThread; bool canDrawThisFrame; { - TreeInfo info; + TreeInfo info(TreeInfo::MODE_FULL, mRenderThread->renderState()); canUnblockUiThread = syncFrameState(info); canDrawThisFrame = info.out.canDrawThisFrame; } // Grab a copy of everything we need - Rect dirty(mDirty); CanvasContext* context = mContext; // From this point on anything in "this" is *UNSAFE TO ACCESS* @@ -118,7 +112,7 @@ void DrawFrameTask::run() { } if (CC_LIKELY(canDrawThisFrame)) { - context->draw(&dirty); + context->draw(); } if (!canUnblockUiThread) { @@ -126,37 +120,25 @@ void DrawFrameTask::run() { } } -static void initTreeInfo(TreeInfo& info) { - info.prepareTextures = true; - info.performStagingPush = true; - info.evaluateAnimations = true; -} - bool DrawFrameTask::syncFrameState(TreeInfo& info) { ATRACE_CALL(); mRenderThread->timeLord().vsyncReceived(mFrameTimeNanos); mContext->makeCurrent(); Caches::getInstance().textureCache.resetMarkInUse(); - initTreeInfo(info); for (size_t i = 0; i < mLayers.size(); i++) { mContext->processLayerUpdate(mLayers[i].get(), info); } mLayers.clear(); - if (info.out.hasAnimations) { - // TODO: Uh... crap? - } mContext->prepareTree(info); if (info.out.hasAnimations) { - // TODO: dirty calculations, for now just do a full-screen inval - mDirty.setEmpty(); if (info.out.requiresUiRedraw) { mSyncResult |= kSync_UIRedrawRequired; } } // If prepareTextures is false, we ran out of texture cache space - return !info.out.hasFunctors && info.prepareTextures; + return info.prepareTextures; } void DrawFrameTask::unblockUiThread() { diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h index d4129b6..243cc5d 100644 --- a/libs/hwui/renderthread/DrawFrameTask.h +++ b/libs/hwui/renderthread/DrawFrameTask.h @@ -41,7 +41,7 @@ class RenderThread; enum SyncResult { kSync_OK = 0, - kSync_UIRedrawRequired = 1 << 1, + kSync_UIRedrawRequired = 1 << 0, }; /* @@ -60,7 +60,6 @@ public: void pushLayerUpdate(DeferredLayerUpdater* layer); void removeLayerUpdate(DeferredLayerUpdater* layer); - void setDirty(int left, int top, int right, int bottom); void setDensity(float density) { mDensity = density; } int drawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos); @@ -80,7 +79,6 @@ private: /********************************************* * Single frame data *********************************************/ - Rect mDirty; nsecs_t mFrameTimeNanos; nsecs_t mRecordDurationNanos; float mDensity; diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp new file mode 100644 index 0000000..05ca34d --- /dev/null +++ b/libs/hwui/renderthread/EglManager.cpp @@ -0,0 +1,288 @@ +/* + * 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. + */ + +#define LOG_TAG "EglContext" + +#include "EglManager.h" + +#include <cutils/log.h> +#include <cutils/properties.h> + +#include "../RenderState.h" +#include "RenderThread.h" + +#define PROPERTY_RENDER_DIRTY_REGIONS "debug.hwui.render_dirty_regions" +#define GLES_VERSION 2 + +// Android-specific addition that is used to show when frames began in systrace +EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface); + +namespace android { +namespace uirenderer { +namespace renderthread { + +#define ERROR_CASE(x) case x: return #x; +static const char* egl_error_str(EGLint error) { + switch (error) { + ERROR_CASE(EGL_SUCCESS) + ERROR_CASE(EGL_NOT_INITIALIZED) + ERROR_CASE(EGL_BAD_ACCESS) + ERROR_CASE(EGL_BAD_ALLOC) + ERROR_CASE(EGL_BAD_ATTRIBUTE) + ERROR_CASE(EGL_BAD_CONFIG) + ERROR_CASE(EGL_BAD_CONTEXT) + ERROR_CASE(EGL_BAD_CURRENT_SURFACE) + ERROR_CASE(EGL_BAD_DISPLAY) + ERROR_CASE(EGL_BAD_MATCH) + ERROR_CASE(EGL_BAD_NATIVE_PIXMAP) + ERROR_CASE(EGL_BAD_NATIVE_WINDOW) + ERROR_CASE(EGL_BAD_PARAMETER) + ERROR_CASE(EGL_BAD_SURFACE) + ERROR_CASE(EGL_CONTEXT_LOST) + default: + return "Unknown error"; + } +} +static const char* egl_error_str() { + return egl_error_str(eglGetError()); +} + +static bool load_dirty_regions_property() { + char buf[PROPERTY_VALUE_MAX]; + int len = property_get(PROPERTY_RENDER_DIRTY_REGIONS, buf, "true"); + return !strncasecmp("true", buf, len); +} + +EglManager::EglManager(RenderThread& thread) + : mRenderThread(thread) + , mEglDisplay(EGL_NO_DISPLAY) + , mEglConfig(0) + , mEglContext(EGL_NO_CONTEXT) + , mPBufferSurface(EGL_NO_SURFACE) + , mRequestDirtyRegions(load_dirty_regions_property()) + , mCurrentSurface(EGL_NO_SURFACE) + , mAtlasMap(NULL) + , mAtlasMapSize(0) { + mCanSetDirtyRegions = mRequestDirtyRegions; + ALOGD("Render dirty regions requested: %s", mRequestDirtyRegions ? "true" : "false"); +} + +void EglManager::initialize() { + if (hasEglContext()) return; + + mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY, + "Failed to get EGL_DEFAULT_DISPLAY! err=%s", egl_error_str()); + + EGLint major, minor; + LOG_ALWAYS_FATAL_IF(eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE, + "Failed to initialize display %p! err=%s", mEglDisplay, egl_error_str()); + + ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor); + + loadConfig(); + createContext(); + usePBufferSurface(); + mRenderThread.renderState().onGLContextCreated(); + initAtlas(); +} + +bool EglManager::hasEglContext() { + return mEglDisplay != EGL_NO_DISPLAY; +} + +void EglManager::requireGlContext() { + LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY, "No EGL context"); + + // We don't care *WHAT* surface is active, just that one is active to give + // us access to the GL context + if (mCurrentSurface == EGL_NO_SURFACE) { + usePBufferSurface(); + } +} + +void EglManager::loadConfig() { + EGLint swapBehavior = mCanSetDirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0; + EGLint attribs[] = { + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 0, + EGL_CONFIG_CAVEAT, EGL_NONE, + EGL_STENCIL_SIZE, Stencil::getStencilSize(), + EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior, + EGL_NONE + }; + + EGLint num_configs = 1; + if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, num_configs, &num_configs) + || num_configs != 1) { + // Failed to get a valid config + if (mCanSetDirtyRegions) { + ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without..."); + // Try again without dirty regions enabled + mCanSetDirtyRegions = false; + loadConfig(); + } else { + LOG_ALWAYS_FATAL("Failed to choose config, error = %s", egl_error_str()); + } + } +} + +void EglManager::createContext() { + EGLint attribs[] = { EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION, EGL_NONE }; + mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attribs); + LOG_ALWAYS_FATAL_IF(mEglContext == EGL_NO_CONTEXT, + "Failed to create context, error = %s", egl_error_str()); +} + +void EglManager::setTextureAtlas(const sp<GraphicBuffer>& buffer, + int64_t* map, size_t mapSize) { + + // Already initialized + if (mAtlasBuffer.get()) { + ALOGW("Multiple calls to setTextureAtlas!"); + delete map; + return; + } + + mAtlasBuffer = buffer; + mAtlasMap = map; + mAtlasMapSize = mapSize; + + if (hasEglContext()) { + usePBufferSurface(); + initAtlas(); + } +} + +void EglManager::initAtlas() { + if (mAtlasBuffer.get()) { + Caches::getInstance().assetAtlas.init(mAtlasBuffer, mAtlasMap, mAtlasMapSize); + } +} + +void EglManager::usePBufferSurface() { + LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY, + "usePBufferSurface() called on uninitialized GlobalContext!"); + + if (mPBufferSurface == EGL_NO_SURFACE) { + EGLint attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE }; + mPBufferSurface = eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs); + } + makeCurrent(mPBufferSurface); +} + +EGLSurface EglManager::createSurface(EGLNativeWindowType window) { + initialize(); + EGLSurface surface = eglCreateWindowSurface(mEglDisplay, mEglConfig, window, NULL); + LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE, + "Failed to create EGLSurface for window %p, eglErr = %s", + (void*) window, egl_error_str()); + return surface; +} + +void EglManager::destroySurface(EGLSurface surface) { + if (isCurrent(surface)) { + makeCurrent(EGL_NO_SURFACE); + } + if (!eglDestroySurface(mEglDisplay, surface)) { + ALOGW("Failed to destroy surface %p, error=%s", (void*)surface, egl_error_str()); + } +} + +void EglManager::destroy() { + if (mEglDisplay == EGL_NO_DISPLAY) return; + + usePBufferSurface(); + if (Caches::hasInstance()) { + Caches::getInstance().terminate(); + } + + eglDestroyContext(mEglDisplay, mEglContext); + eglDestroySurface(mEglDisplay, mPBufferSurface); + eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglTerminate(mEglDisplay); + eglReleaseThread(); + + mEglDisplay = EGL_NO_DISPLAY; + mEglContext = EGL_NO_CONTEXT; + mPBufferSurface = EGL_NO_SURFACE; + mCurrentSurface = EGL_NO_SURFACE; +} + +bool EglManager::makeCurrent(EGLSurface surface) { + if (isCurrent(surface)) return false; + + if (surface == EGL_NO_SURFACE) { + // If we are setting EGL_NO_SURFACE we don't care about any of the potential + // return errors, which would only happen if mEglDisplay had already been + // destroyed in which case the current context is already NO_CONTEXT + eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + } else if (!eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)) { + LOG_ALWAYS_FATAL("Failed to make current on surface %p, error=%s", + (void*)surface, egl_error_str()); + } + mCurrentSurface = surface; + return true; +} + +void EglManager::beginFrame(EGLSurface surface, EGLint* width, EGLint* height) { + LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE, + "Tried to beginFrame on EGL_NO_SURFACE!"); + makeCurrent(surface); + if (width) { + eglQuerySurface(mEglDisplay, surface, EGL_WIDTH, width); + } + if (height) { + eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, height); + } + eglBeginFrame(mEglDisplay, surface); +} + +void EglManager::swapBuffers(EGLSurface surface) { + eglSwapBuffers(mEglDisplay, surface); + EGLint err = eglGetError(); + LOG_ALWAYS_FATAL_IF(err != EGL_SUCCESS, + "Encountered EGL error %d %s during rendering", err, egl_error_str(err)); +} + +bool EglManager::enableDirtyRegions(EGLSurface surface) { + if (!mRequestDirtyRegions) return false; + + if (mCanSetDirtyRegions) { + if (!eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED)) { + ALOGW("Failed to set EGL_SWAP_BEHAVIOR on surface %p, error=%s", + (void*) surface, egl_error_str()); + return false; + } + return true; + } + // Perhaps it is already enabled? + EGLint value; + if (!eglQuerySurface(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, &value)) { + ALOGW("Failed to query EGL_SWAP_BEHAVIOR on surface %p, error=%p", + (void*) surface, egl_error_str()); + return false; + } + return value == EGL_BUFFER_PRESERVED; +} + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h new file mode 100644 index 0000000..a844cfc --- /dev/null +++ b/libs/hwui/renderthread/EglManager.h @@ -0,0 +1,88 @@ +/* + * 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. + */ +#ifndef EGLMANAGER_H +#define EGLMANAGER_H + +#include <cutils/compiler.h> +#include <EGL/egl.h> +#include <ui/GraphicBuffer.h> +#include <utils/StrongPointer.h> + +namespace android { +namespace uirenderer { +namespace renderthread { + +class RenderThread; + +// This class contains the shared global EGL objects, such as EGLDisplay +// and EGLConfig, which are re-used by CanvasContext +class EglManager { +public: + // Returns true on success, false on failure + void initialize(); + + bool hasEglContext(); + void requireGlContext(); + + void usePBufferSurface(); + EGLSurface createSurface(EGLNativeWindowType window); + void destroySurface(EGLSurface surface); + + void destroy(); + + bool isCurrent(EGLSurface surface) { return mCurrentSurface == surface; } + // Returns true if the current surface changed, false if it was already current + bool makeCurrent(EGLSurface surface); + void beginFrame(EGLSurface surface, EGLint* width, EGLint* height); + void swapBuffers(EGLSurface surface); + + bool enableDirtyRegions(EGLSurface surface); + + void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize); + +private: + friend class RenderThread; + + EglManager(RenderThread& thread); + // EglContext is never destroyed, method is purposely not implemented + ~EglManager(); + + void loadConfig(); + void createContext(); + void initAtlas(); + + RenderThread& mRenderThread; + + EGLDisplay mEglDisplay; + EGLConfig mEglConfig; + EGLContext mEglContext; + EGLSurface mPBufferSurface; + + const bool mRequestDirtyRegions; + bool mCanSetDirtyRegions; + + EGLSurface mCurrentSurface; + + sp<GraphicBuffer> mAtlasBuffer; + int64_t* mAtlasMap; + size_t mAtlasMapSize; +}; + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* EGLMANAGER_H */ diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 0901963..f90a26a 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -47,13 +47,13 @@ namespace renderthread { #define SETUP_TASK(method) \ LOG_ALWAYS_FATAL_IF( METHOD_INVOKE_PAYLOAD_SIZE < sizeof(ARGS(method)), \ - "METHOD_INVOKE_PAYLOAD_SIZE %d is smaller than sizeof(" #method "Args) %d", \ + "METHOD_INVOKE_PAYLOAD_SIZE %zu is smaller than sizeof(" #method "Args) %zu", \ METHOD_INVOKE_PAYLOAD_SIZE, sizeof(ARGS(method))); \ MethodInvokeRenderTask* task = new MethodInvokeRenderTask((RunnableMethod) Bridge_ ## method); \ ARGS(method) *args = (ARGS(method) *) task->payload() -CREATE_BRIDGE2(createContext, bool translucent, RenderNode* rootRenderNode) { - return new CanvasContext(args->translucent, args->rootRenderNode); +CREATE_BRIDGE3(createContext, RenderThread* thread, bool translucent, RenderNode* rootRenderNode) { + return new CanvasContext(*args->thread, args->translucent, args->rootRenderNode); } RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode) @@ -62,6 +62,7 @@ RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode) SETUP_TASK(createContext); args->translucent = translucent; args->rootRenderNode = rootRenderNode; + args->thread = &mRenderThread; mContext = (CanvasContext*) postAndWait(task); mDrawFrameTask.setContext(&mRenderThread, mContext); } @@ -180,8 +181,7 @@ void RenderProxy::setOpaque(bool opaque) { } int RenderProxy::syncAndDrawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos, - float density, int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom) { - mDrawFrameTask.setDirty(dirtyLeft, dirtyTop, dirtyRight, dirtyBottom); + float density) { mDrawFrameTask.setDensity(density); return mDrawFrameTask.drawFrame(frameTimeNanos, recordDurationNanos); } @@ -200,20 +200,29 @@ void RenderProxy::destroyCanvasAndSurface() { postAndWait(task); } -CREATE_BRIDGE2(invokeFunctor, CanvasContext* context, Functor* functor) { - args->context->invokeFunctor(args->functor); +CREATE_BRIDGE2(invokeFunctor, RenderThread* thread, Functor* functor) { + CanvasContext::invokeFunctor(*args->thread, args->functor); return NULL; } void RenderProxy::invokeFunctor(Functor* functor, bool waitForCompletion) { ATRACE_CALL(); + RenderThread& thread = RenderThread::getInstance(); SETUP_TASK(invokeFunctor); - args->context = mContext; + args->thread = &thread; args->functor = functor; if (waitForCompletion) { - postAndWait(task); + // waitForCompletion = true is expected to be fairly rare and only + // happen in destruction. Thus it should be fine to temporarily + // create a Mutex + Mutex mutex; + Condition condition; + SignalingRenderTask syncTask(task, &mutex, &condition); + AutoMutex _lock(mutex); + thread.queue(&syncTask); + condition.wait(mutex); } else { - post(task); + thread.queue(task); } } @@ -234,7 +243,7 @@ CREATE_BRIDGE1(destroyLayer, Layer* layer) { return NULL; } -static void enqueueDestroyLayer(Layer* layer) { +void RenderProxy::enqueueDestroyLayer(Layer* layer) { SETUP_TASK(destroyLayer); args->layer = layer; RenderThread::getInstance().queue(task); @@ -243,7 +252,7 @@ static void enqueueDestroyLayer(Layer* layer) { CREATE_BRIDGE3(createDisplayListLayer, CanvasContext* context, int width, int height) { Layer* layer = args->context->createRenderLayer(args->width, args->height); if (!layer) return 0; - return new DeferredLayerUpdater(layer, enqueueDestroyLayer); + return new DeferredLayerUpdater(layer, RenderProxy::enqueueDestroyLayer); } DeferredLayerUpdater* RenderProxy::createDisplayListLayer(int width, int height) { @@ -259,7 +268,7 @@ DeferredLayerUpdater* RenderProxy::createDisplayListLayer(int width, int height) CREATE_BRIDGE1(createTextureLayer, CanvasContext* context) { Layer* layer = args->context->createTextureLayer(); if (!layer) return 0; - return new DeferredLayerUpdater(layer, enqueueDestroyLayer); + return new DeferredLayerUpdater(layer, RenderProxy::enqueueDestroyLayer); } DeferredLayerUpdater* RenderProxy::createTextureLayer() { @@ -337,6 +346,22 @@ void RenderProxy::dumpProfileInfo(int fd) { postAndWait(task); } +CREATE_BRIDGE4(setTextureAtlas, RenderThread* thread, GraphicBuffer* buffer, int64_t* map, size_t size) { + CanvasContext::setTextureAtlas(*args->thread, args->buffer, args->map, args->size); + args->buffer->decStrong(0); + return NULL; +} + +void RenderProxy::setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size) { + SETUP_TASK(setTextureAtlas); + args->thread = &mRenderThread; + args->buffer = buffer.get(); + args->buffer->incStrong(0); + args->map = map; + args->size = size; + post(task); +} + void RenderProxy::post(RenderTask* task) { mRenderThread.queue(task); } diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index 944ff9c..df0aff0 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -70,13 +70,14 @@ public: ANDROID_API void setup(int width, int height, const Vector3& lightCenter, float lightRadius); ANDROID_API void setOpaque(bool opaque); ANDROID_API int syncAndDrawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos, - float density, int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom); + float density); ANDROID_API void destroyCanvasAndSurface(); - ANDROID_API void invokeFunctor(Functor* functor, bool waitForCompletion); + ANDROID_API static void invokeFunctor(Functor* functor, bool waitForCompletion); ANDROID_API void runWithGlContext(RenderTask* task); + static void enqueueDestroyLayer(Layer* layer); ANDROID_API DeferredLayerUpdater* createDisplayListLayer(int width, int height); ANDROID_API DeferredLayerUpdater* createTextureLayer(); ANDROID_API bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap); @@ -90,6 +91,8 @@ public: ANDROID_API void dumpProfileInfo(int fd); + ANDROID_API void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size); + private: RenderThread& mRenderThread; CanvasContext* mContext; diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 4a4e254..03e98d5 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -21,7 +21,9 @@ #include <gui/DisplayEventReceiver.h> #include <utils/Log.h> +#include "../RenderState.h" #include "CanvasContext.h" +#include "EglManager.h" #include "RenderProxy.h" namespace android { @@ -138,13 +140,16 @@ RenderThread::RenderThread() : Thread(true), Singleton<RenderThread>() , mDisplayEventReceiver(0) , mVsyncRequested(false) , mFrameCallbackTaskPending(false) - , mFrameCallbackTask(0) { + , mFrameCallbackTask(0) + , mRenderState(NULL) + , mEglManager(NULL) { mFrameCallbackTask = new DispatchFrameCallbacks(this); mLooper = new Looper(false); run("RenderThread"); } RenderThread::~RenderThread() { + LOG_ALWAYS_FATAL("Can't destroy the render thread"); } void RenderThread::initializeDisplayEventReceiver() { @@ -159,6 +164,12 @@ void RenderThread::initializeDisplayEventReceiver() { Looper::EVENT_INPUT, RenderThread::displayEventReceiverCallback, this); } +void RenderThread::initThreadLocals() { + initializeDisplayEventReceiver(); + mEglManager = new EglManager(*this); + mRenderState = new RenderState(); +} + int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) { if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) { ALOGE("Display event receiver pipe was closed or an error occurred. " @@ -233,7 +244,7 @@ void RenderThread::requestVsync() { } bool RenderThread::threadLoop() { - initializeDisplayEventReceiver(); + initThreadLocals(); int timeoutMillis = -1; for (;;) { diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index 4412584..0b91e9d 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -31,12 +31,18 @@ #include "TimeLord.h" namespace android { + class DisplayEventReceiver; namespace uirenderer { + +class RenderState; + namespace renderthread { class DispatchFrameCallbacks; +class EglManager; +class RenderProxy; class TaskQueue { public: @@ -62,7 +68,7 @@ protected: ~IFrameCallback() {} }; -class ANDROID_API RenderThread : public Thread, public Singleton<RenderThread> { +class ANDROID_API RenderThread : public Thread, protected Singleton<RenderThread> { public: // RenderThread takes complete ownership of tasks that are queued // and will delete them after they are run @@ -79,6 +85,8 @@ public: void pushBackFrameCallback(IFrameCallback* callback); TimeLord& timeLord() { return mTimeLord; } + RenderState& renderState() { return *mRenderState; } + EglManager& eglManager() { return *mEglManager; } protected: virtual bool threadLoop(); @@ -86,10 +94,12 @@ protected: private: friend class Singleton<RenderThread>; friend class DispatchFrameCallbacks; + friend class RenderProxy; RenderThread(); virtual ~RenderThread(); + void initThreadLocals(); void initializeDisplayEventReceiver(); static int displayEventReceiverCallback(int fd, int events, void* data); void drainDisplayEventQueue(bool skipCallbacks = false); @@ -119,6 +129,8 @@ private: DispatchFrameCallbacks* mFrameCallbackTask; TimeLord mTimeLord; + RenderState* mRenderState; + EglManager* mEglManager; }; } /* namespace renderthread */ diff --git a/libs/hwui/thread/Task.h b/libs/hwui/thread/Task.h index 9a211a2..30b6ff2 100644 --- a/libs/hwui/thread/Task.h +++ b/libs/hwui/thread/Task.h @@ -17,8 +17,6 @@ #ifndef ANDROID_HWUI_TASK_H #define ANDROID_HWUI_TASK_H -#define ATRACE_TAG ATRACE_TAG_VIEW - #include <utils/RefBase.h> #include <utils/Trace.h> @@ -40,7 +38,7 @@ public: virtual ~Task() { } T getResult() const { - ATRACE_NAME("waitForTask"); + ScopedTrace tracer(ATRACE_TAG_VIEW, "waitForTask"); return mFuture->get(); } diff --git a/libs/hwui/utils/Blur.cpp b/libs/hwui/utils/Blur.cpp index c020b40..877a422 100644 --- a/libs/hwui/utils/Blur.cpp +++ b/libs/hwui/utils/Blur.cpp @@ -19,6 +19,7 @@ #include <math.h> #include "Blur.h" +#include "MathUtils.h" namespace android { namespace uirenderer { @@ -35,6 +36,17 @@ float Blur::convertSigmaToRadius(float sigma) { return sigma > 0.5f ? (sigma - 0.5f) / BLUR_SIGMA_SCALE : 0.0f; } +// if the original radius was on an integer boundary and the resulting radius +// is within the conversion error tolerance then we attempt to snap to the +// original integer boundary. +uint32_t Blur::convertRadiusToInt(float radius) { + const float radiusCeil = ceilf(radius); + if (MathUtils::areEqual(radiusCeil, radius)) { + return radiusCeil; + } + return radius; +} + /** * HWUI has used a slightly different equation than Skia to generate the value * for sigma and to preserve compatibility we have kept that logic. diff --git a/libs/hwui/utils/Blur.h b/libs/hwui/utils/Blur.h index 79aff65..b145333 100644 --- a/libs/hwui/utils/Blur.h +++ b/libs/hwui/utils/Blur.h @@ -27,8 +27,12 @@ class Blur { public: // If radius > 0, return the corresponding sigma, else return 0 ANDROID_API static float convertRadiusToSigma(float radius); - // If sigma > 0.6, return the corresponding radius, else return 0 + // If sigma > 0.5, return the corresponding radius, else return 0 ANDROID_API static float convertSigmaToRadius(float sigma); + // If the original radius was on an integer boundary then after the sigma to + // radius conversion a small rounding error may be introduced. This function + // accounts for that error and snaps to the appropriate integer boundary. + static uint32_t convertRadiusToInt(float radius); static void generateGaussianWeights(float* weights, int32_t radius); static void horizontal(float* weights, int32_t radius, const uint8_t* source, diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp index 3f6ccc9..063383b 100644 --- a/libs/input/SpriteController.cpp +++ b/libs/input/SpriteController.cpp @@ -206,9 +206,8 @@ void SpriteController::doUpdateSprites() { } else { SkBitmap surfaceBitmap; ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format); - surfaceBitmap.setConfig(SkBitmap::kARGB_8888_Config, - outBuffer.width, outBuffer.height, bpr); - surfaceBitmap.setPixels(outBuffer.bits); + surfaceBitmap.installPixels(SkImageInfo::MakeN32Premul(outBuffer.width, outBuffer.height), + outBuffer.bits, bpr); SkCanvas surfaceCanvas(surfaceBitmap); |