diff --git a/cmds/idmap/create.cpp b/cmds/idmap/create.cpp index ae35f7b0a8c1f..593a1973669a8 100644 --- a/cmds/idmap/create.cpp +++ b/cmds/idmap/create.cpp @@ -105,7 +105,7 @@ namespace { uint32_t cached_target_crc, cached_overlay_crc; String8 cached_target_path, cached_overlay_path; - if (!ResTable::getIdmapInfo(buf, N, &cached_target_crc, &cached_overlay_crc, + if (!ResTable::getIdmapInfo(buf, N, NULL, &cached_target_crc, &cached_overlay_crc, &cached_target_path, &cached_overlay_path)) { return true; } diff --git a/cmds/idmap/idmap.cpp b/cmds/idmap/idmap.cpp index 46c0edc035234..90cfa2c610f00 100644 --- a/cmds/idmap/idmap.cpp +++ b/cmds/idmap/idmap.cpp @@ -66,26 +66,32 @@ EXAMPLES \n\ Display an idmap file: \n\ \n\ $ adb shell idmap --inspect /data/resource-cache/vendor@overlay@overlay.apk@idmap \n\ - SECTION ENTRY VALUE OFFSET COMMENT \n\ - IDMAP HEADER magic 0x706d6469 0x0 \n\ - base crc 0x484aa77f 0x1 \n\ - overlay crc 0x03c66fa5 0x2 \n\ - base path .......... 0x03-0x42 /system/app/target.apk \n\ - overlay path .......... 0x43-0x82 /vendor/overlay/overlay.apk \n\ - DATA HEADER types count 0x00000003 0x83 \n\ - padding 0x00000000 0x84 \n\ - type offset 0x00000004 0x85 absolute offset 0x87, xml \n\ - type offset 0x00000007 0x86 absolute offset 0x8a, string \n\ - DATA BLOCK entry count 0x00000001 0x87 \n\ - entry offset 0x00000000 0x88 \n\ - entry 0x7f020000 0x89 xml/integer \n\ - DATA BLOCK entry count 0x00000002 0x8a \n\ - entry offset 0x00000000 0x8b \n\ - entry 0x7f030000 0x8c string/str \n\ - entry 0x7f030001 0x8d string/str2 \n\ + SECTION ENTRY VALUE COMMENT \n\ + IDMAP HEADER magic 0x706d6469 \n\ + base crc 0xb65a383f \n\ + overlay crc 0x7b9675e8 \n\ + base path .......... /path/to/target.apk \n\ + overlay path .......... /path/to/overlay.apk \n\ + DATA HEADER target pkg 0x0000007f \n\ + types count 0x00000003 \n\ + DATA BLOCK target type 0x00000002 \n\ + overlay type 0x00000002 \n\ + entry count 0x00000001 \n\ + entry offset 0x00000000 \n\ + entry 0x00000000 drawable/drawable \n\ + DATA BLOCK target type 0x00000003 \n\ + overlay type 0x00000003 \n\ + entry count 0x00000001 \n\ + entry offset 0x00000000 \n\ + entry 0x00000000 xml/integer \n\ + DATA BLOCK target type 0x00000004 \n\ + overlay type 0x00000004 \n\ + entry count 0x00000001 \n\ + entry offset 0x00000000 \n\ + entry 0x00000000 raw/lorem_ipsum \n\ \n\ In this example, the overlay package provides three alternative resource values:\n\ - xml/integer, string/str and string/str2.\n\ + drawable/drawable, xml/integer, and raw/lorem_ipsum \n\ \n\ NOTES \n\ This tool and its expected invocation from installd is modelled on dexopt."; diff --git a/cmds/idmap/inspect.cpp b/cmds/idmap/inspect.cpp index a59f5d31cab8d..b9ac8a59de02c 100644 --- a/cmds/idmap/inspect.cpp +++ b/cmds/idmap/inspect.cpp @@ -10,92 +10,108 @@ using namespace android; -#define NEXT(b, i, o) do { if (buf.next(&i, &o) < 0) { return -1; } } while (0) - namespace { - static const uint32_t IDMAP_MAGIC = 0x706d6469; + static const uint32_t IDMAP_MAGIC = 0x504D4449; static const size_t PATH_LENGTH = 256; - static const uint32_t IDMAP_HEADER_SIZE = (3 + 2 * (PATH_LENGTH / sizeof(uint32_t))); void printe(const char *fmt, ...); class IdmapBuffer { private: - char *buf_; + const char* buf_; size_t len_; - mutable size_t pos_; + size_t pos_; public: - IdmapBuffer() : buf_((char *)MAP_FAILED), len_(0), pos_(0) {} + IdmapBuffer() : buf_((const char *)MAP_FAILED), len_(0), pos_(0) {} ~IdmapBuffer() { if (buf_ != MAP_FAILED) { - munmap(buf_, len_); + munmap(const_cast(buf_), len_); } } - int init(const char *idmap_path) - { + status_t init(const char *idmap_path) { struct stat st; int fd; if (stat(idmap_path, &st) < 0) { printe("failed to stat idmap '%s': %s\n", idmap_path, strerror(errno)); - return -1; + return UNKNOWN_ERROR; } len_ = st.st_size; if ((fd = TEMP_FAILURE_RETRY(open(idmap_path, O_RDONLY))) < 0) { printe("failed to open idmap '%s': %s\n", idmap_path, strerror(errno)); - return -1; + return UNKNOWN_ERROR; } - if ((buf_ = (char*)mmap(NULL, len_, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) { + if ((buf_ = (const char*)mmap(NULL, len_, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) { close(fd); printe("failed to mmap idmap: %s\n", strerror(errno)); - return -1; + return UNKNOWN_ERROR; } close(fd); - return 0; + return NO_ERROR; } - int next(uint32_t *i, uint32_t *offset) const - { + status_t nextUint32(uint32_t* i) { if (!buf_) { printe("failed to read next uint32_t: buffer not initialized\n"); - return -1; + return UNKNOWN_ERROR; } - if (pos_ + 4 > len_) { + + if (pos_ + sizeof(uint32_t) > len_) { printe("failed to read next uint32_t: end of buffer reached at pos=0x%08x\n", pos_); - return -1; + return UNKNOWN_ERROR; + } + + if ((reinterpret_cast(buf_ + pos_) & 0x3) != 0) { + printe("failed to read next uint32_t: not aligned on 4-byte boundary\n"); + return UNKNOWN_ERROR; } - *offset = pos_ / sizeof(uint32_t); - char a = buf_[pos_++]; - char b = buf_[pos_++]; - char c = buf_[pos_++]; - char d = buf_[pos_++]; - *i = (d << 24) | (c << 16) | (b << 8) | a; - return 0; + + *i = dtohl(*reinterpret_cast(buf_ + pos_)); + pos_ += sizeof(uint32_t); + return NO_ERROR; } - int nextPath(char *b, uint32_t *offset_start, uint32_t *offset_end) const - { + status_t nextUint16(uint16_t* i) { + if (!buf_) { + printe("failed to read next uint16_t: buffer not initialized\n"); + return UNKNOWN_ERROR; + } + + if (pos_ + sizeof(uint16_t) > len_) { + printe("failed to read next uint16_t: end of buffer reached at pos=0x%08x\n", + pos_); + return UNKNOWN_ERROR; + } + + if ((reinterpret_cast(buf_ + pos_) & 0x1) != 0) { + printe("failed to read next uint32_t: not aligned on 2-byte boundary\n"); + return UNKNOWN_ERROR; + } + + *i = dtohs(*reinterpret_cast(buf_ + pos_)); + pos_ += sizeof(uint16_t); + return NO_ERROR; + } + + status_t nextPath(char *b) { if (!buf_) { printe("failed to read next path: buffer not initialized\n"); - return -1; + return UNKNOWN_ERROR; } if (pos_ + PATH_LENGTH > len_) { printe("failed to read next path: end of buffer reached at pos=0x%08x\n", pos_); - return -1; + return UNKNOWN_ERROR; } memcpy(b, buf_ + pos_, PATH_LENGTH); - *offset_start = pos_ / sizeof(uint32_t); pos_ += PATH_LENGTH; - *offset_end = pos_ / sizeof(uint32_t) - 1; - return 0; + return NO_ERROR; } }; - void printe(const char *fmt, ...) - { + void printe(const char *fmt, ...) { va_list ap; va_start(ap, fmt); @@ -104,44 +120,37 @@ namespace { va_end(ap); } - void print_header() - { - printf("SECTION ENTRY VALUE OFFSET COMMENT\n"); + void print_header() { + printf("SECTION ENTRY VALUE COMMENT\n"); } - void print(const char *section, const char *subsection, uint32_t value, uint32_t offset, - const char *fmt, ...) - { + void print(const char *section, const char *subsection, uint32_t value, const char *fmt, ...) { va_list ap; va_start(ap, fmt); - printf("%-12s %-12s 0x%08x 0x%-4x ", section, subsection, value, offset); + printf("%-12s %-12s 0x%08x ", section, subsection, value); vprintf(fmt, ap); printf("\n"); va_end(ap); } - void print_path(const char *section, const char *subsection, uint32_t offset_start, - uint32_t offset_end, const char *fmt, ...) - { + void print_path(const char *section, const char *subsection, const char *fmt, ...) { va_list ap; va_start(ap, fmt); - printf("%-12s %-12s .......... 0x%02x-0x%02x ", section, subsection, offset_start, - offset_end); + printf("%-12s %-12s .......... ", section, subsection); vprintf(fmt, ap); printf("\n"); va_end(ap); } - int resource_metadata(const AssetManager& am, uint32_t res_id, - String8 *package, String8 *type, String8 *name) - { + status_t resource_metadata(const AssetManager& am, uint32_t res_id, + String8 *package, String8 *type, String8 *name) { const ResTable& rt = am.getResources(); struct ResTable::resource_name data; if (!rt.getResourceName(res_id, false, &data)) { printe("failed to get resource name id=0x%08x\n", res_id); - return -1; + return UNKNOWN_ERROR; } if (package) { *package = String8(String16(data.package, data.packageLen)); @@ -152,140 +161,150 @@ namespace { if (name) { *name = String8(String16(data.name, data.nameLen)); } - return 0; - } - - int package_id(const AssetManager& am) - { - return (am.getResources().getBasePackageId(0)) << 24; + return NO_ERROR; } - int parse_idmap_header(const IdmapBuffer& buf, AssetManager& am) - { - uint32_t i, o, e; + status_t parse_idmap_header(IdmapBuffer& buf, AssetManager& am) { + uint32_t i; char path[PATH_LENGTH]; - NEXT(buf, i, o); + status_t err = buf.nextUint32(&i); + if (err != NO_ERROR) { + return err; + } + if (i != IDMAP_MAGIC) { printe("not an idmap file: actual magic constant 0x%08x does not match expected magic " "constant 0x%08x\n", i, IDMAP_MAGIC); - return -1; + return UNKNOWN_ERROR; } + print_header(); - print("IDMAP HEADER", "magic", i, o, ""); + print("IDMAP HEADER", "magic", i, ""); + + err = buf.nextUint32(&i); + if (err != NO_ERROR) { + return err; + } + print("", "version", i, ""); - NEXT(buf, i, o); - print("", "base crc", i, o, ""); + err = buf.nextUint32(&i); + if (err != NO_ERROR) { + return err; + } + print("", "base crc", i, ""); - NEXT(buf, i, o); - print("", "overlay crc", i, o, ""); + err = buf.nextUint32(&i); + if (err != NO_ERROR) { + return err; + } + print("", "overlay crc", i, ""); - if (buf.nextPath(path, &o, &e) < 0) { + err = buf.nextPath(path); + if (err != NO_ERROR) { // printe done from IdmapBuffer::nextPath - return -1; + return err; } - print_path("", "base path", o, e, "%s", path); + print_path("", "base path", "%s", path); + if (!am.addAssetPath(String8(path), NULL)) { printe("failed to add '%s' as asset path\n", path); - return -1; + return UNKNOWN_ERROR; } - if (buf.nextPath(path, &o, &e) < 0) { + err = buf.nextPath(path); + if (err != NO_ERROR) { // printe done from IdmapBuffer::nextPath - return -1; + return err; } - print_path("", "overlay path", o, e, "%s", path); + print_path("", "overlay path", "%s", path); - return 0; + return NO_ERROR; } - int parse_data_header(const IdmapBuffer& buf, const AssetManager& am, Vector& types) - { - uint32_t i, o; - const uint32_t numeric_package = package_id(am); + status_t parse_data(IdmapBuffer& buf, const AssetManager& am) { + const uint32_t packageId = am.getResources().getBasePackageId(0); - NEXT(buf, i, o); - print("DATA HEADER", "types count", i, o, ""); - const uint32_t N = i; + uint16_t data16; + status_t err = buf.nextUint16(&data16); + if (err != NO_ERROR) { + return err; + } + print("DATA HEADER", "target pkg", static_cast(data16), ""); - for (uint32_t j = 0; j < N; ++j) { - NEXT(buf, i, o); - if (i == 0) { - print("", "padding", i, o, ""); - } else { - String8 type; - const uint32_t numeric_type = (j + 1) << 16; - const uint32_t res_id = numeric_package | numeric_type; - if (resource_metadata(am, res_id, NULL, &type, NULL) < 0) { - // printe done from resource_metadata - return -1; - } - print("", "type offset", i, o, "absolute offset 0x%02x, %s", - i + IDMAP_HEADER_SIZE, type.string()); - types.add(numeric_type); - } + err = buf.nextUint16(&data16); + if (err != NO_ERROR) { + return err; } + print("", "types count", static_cast(data16), ""); - return 0; - } + uint32_t typeCount = static_cast(data16); + while (typeCount > 0) { + typeCount--; + + err = buf.nextUint16(&data16); + if (err != NO_ERROR) { + return err; + } + const uint32_t targetTypeId = static_cast(data16); + print("DATA BLOCK", "target type", targetTypeId, ""); - int parse_data_block(const IdmapBuffer& buf, const AssetManager& am, size_t numeric_type) - { - uint32_t i, o, n, id_offset; - const uint32_t numeric_package = package_id(am); - - NEXT(buf, i, o); - print("DATA BLOCK", "entry count", i, o, ""); - n = i; - - NEXT(buf, i, o); - print("", "entry offset", i, o, ""); - id_offset = i; - - for ( ; n > 0; --n) { - String8 type, name; - - NEXT(buf, i, o); - if (i == 0) { - print("", "padding", i, o, ""); - } else { - uint32_t res_id = numeric_package | numeric_type | id_offset; - if (resource_metadata(am, res_id, NULL, &type, &name) < 0) { - // printe done from resource_metadata - return -1; + err = buf.nextUint16(&data16); + if (err != NO_ERROR) { + return err; + } + print("", "overlay type", static_cast(data16), ""); + + err = buf.nextUint16(&data16); + if (err != NO_ERROR) { + return err; + } + const uint32_t entryCount = static_cast(data16); + print("", "entry count", entryCount, ""); + + err = buf.nextUint16(&data16); + if (err != NO_ERROR) { + return err; + } + const uint32_t entryOffset = static_cast(data16); + print("", "entry offset", entryOffset, ""); + + for (uint32_t i = 0; i < entryCount; i++) { + uint32_t data32; + err = buf.nextUint32(&data32); + if (err != NO_ERROR) { + return err; } - print("", "entry", i, o, "%s/%s", type.string(), name.string()); + + uint32_t resID = (packageId << 24) | (targetTypeId << 16) | (entryOffset + i); + String8 type; + String8 name; + err = resource_metadata(am, resID, NULL, &type, &name); + if (err != NO_ERROR) { + return err; + } + print("", "entry", data32, "%s/%s", type.string(), name.string()); } - ++id_offset; } - return 0; + return NO_ERROR; } } -int idmap_inspect(const char *idmap_path) -{ +int idmap_inspect(const char *idmap_path) { IdmapBuffer buf; if (buf.init(idmap_path) < 0) { // printe done from IdmapBuffer::init return EXIT_FAILURE; } AssetManager am; - if (parse_idmap_header(buf, am) < 0) { + if (parse_idmap_header(buf, am) != NO_ERROR) { // printe done from parse_idmap_header return EXIT_FAILURE; } - Vector types; - if (parse_data_header(buf, am, types) < 0) { + if (parse_data(buf, am) != NO_ERROR) { // printe done from parse_data_header return EXIT_FAILURE; } - const size_t N = types.size(); - for (size_t i = 0; i < N; ++i) { - if (parse_data_block(buf, am, types.itemAt(i)) < 0) { - // printe done from parse_data_block - return EXIT_FAILURE; - } - } return EXIT_SUCCESS; } diff --git a/include/androidfw/ByteBucketArray.h b/include/androidfw/ByteBucketArray.h new file mode 100644 index 0000000000000..87c6b128eca1f --- /dev/null +++ b/include/androidfw/ByteBucketArray.h @@ -0,0 +1,97 @@ +/* + * 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 __BYTE_BUCKET_ARRAY_H +#define __BYTE_BUCKET_ARRAY_H + +#include +#include +#include + +namespace android { + +/** + * Stores a sparsely populated array. Has a fixed size of 256 + * (number of entries that a byte can represent). + */ +template +class ByteBucketArray { +public: + ByteBucketArray() : mDefault() { + memset(mBuckets, 0, sizeof(mBuckets)); + } + + ~ByteBucketArray() { + for (size_t i = 0; i < NUM_BUCKETS; i++) { + if (mBuckets[i] != NULL) { + delete [] mBuckets[i]; + } + } + memset(mBuckets, 0, sizeof(mBuckets)); + } + + inline size_t size() const { + return NUM_BUCKETS * BUCKET_SIZE; + } + + inline const T& get(size_t index) const { + return (*this)[index]; + } + + const T& operator[](size_t index) const { + if (index >= size()) { + return mDefault; + } + + uint8_t bucketIndex = static_cast(index) >> 4; + T* bucket = mBuckets[bucketIndex]; + if (bucket == NULL) { + return mDefault; + } + return bucket[0x0f & static_cast(index)]; + } + + T& editItemAt(size_t index) { + ALOG_ASSERT(index < size(), "ByteBucketArray.getOrCreate(index=%u) with size=%u", + (uint32_t) index, (uint32_t) size()); + + uint8_t bucketIndex = static_cast(index) >> 4; + T* bucket = mBuckets[bucketIndex]; + if (bucket == NULL) { + bucket = mBuckets[bucketIndex] = new T[BUCKET_SIZE](); + } + return bucket[0x0f & static_cast(index)]; + } + + bool set(size_t index, const T& value) { + if (index >= size()) { + return false; + } + + editItemAt(index) = value; + return true; + } + +private: + enum { NUM_BUCKETS = 16, BUCKET_SIZE = 16 }; + + T* mBuckets[NUM_BUCKETS]; + T mDefault; +}; + +} // namespace android + +#endif // __BYTE_BUCKET_ARRAY_H diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h index 4d8e512d16515..e612c0a92a514 100644 --- a/include/androidfw/ResourceTypes.h +++ b/include/androidfw/ResourceTypes.h @@ -237,6 +237,7 @@ enum { #define Res_MAKEARRAY(entry) (0x02000000 | (entry&0xFFFF)) #define Res_MAXPACKAGE 255 +#define Res_MAXTYPE 255 /** * Representation of a value in a resource, supplying type @@ -510,6 +511,23 @@ class ResStringPool uint32_t mStylePoolSize; // number of uint32_t }; +/** + * Wrapper class that allows the caller to retrieve a string from + * a string pool without knowing which string pool to look. + */ +class StringPoolRef { +public: + StringPoolRef(); + StringPoolRef(const ResStringPool* pool, uint32_t index); + + const char* string8(size_t* outLen) const; + const char16_t* string16(size_t* outLen) const; + +private: + const ResStringPool* mPool; + uint32_t mIndex; +}; + /** ******************************************************************** * XML Tree * @@ -835,6 +853,8 @@ struct ResTable_package // Last index into keyStrings that is for public use by others. uint32_t lastPublicKey; + + uint32_t typeIdOffset; }; // The most specific locale can consist of: @@ -1469,9 +1489,13 @@ class ResTable bool copyData=false); ~ResTable(); - status_t add(Asset* asset, const int32_t cookie, bool copyData, - const void* idmap = NULL); - status_t add(const void *data, size_t size); + status_t add(const void* data, size_t size, const int32_t cookie=-1, bool copyData=false); + status_t add(const void* data, size_t size, const void* idmapData, size_t idmapDataSize, + const int32_t cookie=-1, bool copyData=false); + + status_t add(Asset* asset, const int32_t cookie=-1, bool copyData=false); + status_t add(Asset* asset, Asset* idmapAsset, const int32_t cookie=-1, bool copyData=false); + status_t add(ResTable* src); status_t addEmpty(const int32_t cookie); @@ -1610,13 +1634,14 @@ class ResTable uint32_t typeSpecFlags; Res_value value; }; + struct type_info { size_t numEntries; theme_entry* entries; }; + struct package_info { - size_t numTypes; - type_info types[]; + type_info types[Res_MAXTYPE + 1]; }; void free_package(package_info* pi); @@ -1711,6 +1736,7 @@ class ResTable size_t getBasePackageCount() const; const String16 getBasePackageName(size_t idx) const; uint32_t getBasePackageId(size_t idx) const; + uint32_t getLastTypeIdForPackage(size_t idx) const; // Return the number of resource tables that the object contains. size_t getTableCount() const; @@ -1740,13 +1766,15 @@ class ResTable void** outData, size_t* outSize) const; enum { - IDMAP_HEADER_SIZE_BYTES = 3 * sizeof(uint32_t) + 2 * 256, + IDMAP_HEADER_SIZE_BYTES = 4 * sizeof(uint32_t) + 2 * 256, }; + // Retrieve idmap meta-data. // // This function only requires the idmap header (the first // IDMAP_HEADER_SIZE_BYTES) bytes of an idmap file. static bool getIdmapInfo(const void* idmap, size_t size, + uint32_t* pVersion, uint32_t* pTargetCrc, uint32_t* pOverlayCrc, String8* pTargetPath, String8* pOverlayPath); @@ -1756,21 +1784,24 @@ class ResTable private: struct Header; struct Type; + struct Entry; struct Package; struct PackageGroup; struct bag_set; + typedef Vector TypeList; - status_t addInternal(const void* data, size_t size, const int32_t cookie, - bool copyData, const Asset* idmap); + status_t addInternal(const void* data, size_t size, const void* idmapData, size_t idmapDataSize, + const int32_t cookie, bool copyData); ssize_t getResourcePackageIndex(uint32_t resID) const; - ssize_t getEntry( - const Package* package, int typeIndex, int entryIndex, + + status_t getEntry( + const PackageGroup* packageGroup, int typeIndex, int entryIndex, const ResTable_config* config, - const ResTable_type** outType, const ResTable_entry** outEntry, - const Type** outTypeClass) const; + Entry* outEntry) const; + status_t parsePackage( - const ResTable_package* const pkg, const Header* const header, uint32_t idmap_id); + const ResTable_package* const pkg, const Header* const header); void print_value(const Package* pkg, const Res_value& value) const; diff --git a/include/androidfw/TypeWrappers.h b/include/androidfw/TypeWrappers.h new file mode 100644 index 0000000000000..7bdf8af0ad4c7 --- /dev/null +++ b/include/androidfw/TypeWrappers.h @@ -0,0 +1,81 @@ +/* + * 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 __TYPE_WRAPPERS_H +#define __TYPE_WRAPPERS_H + +#include + +namespace android { + +struct TypeVariant { + TypeVariant(const ResTable_type* data) + : data(data) {} + + class iterator { + public: + iterator& operator=(const iterator& rhs) { + mTypeVariant = rhs.mTypeVariant; + mIndex = rhs.mIndex; + } + + bool operator==(const iterator& rhs) const { + return mTypeVariant == rhs.mTypeVariant && mIndex == rhs.mIndex; + } + + bool operator!=(const iterator& rhs) const { + return mTypeVariant != rhs.mTypeVariant || mIndex != rhs.mIndex; + } + + iterator operator++(int) { + uint32_t prevIndex = mIndex; + operator++(); + return iterator(mTypeVariant, prevIndex); + } + + const ResTable_entry* operator->() const { + return operator*(); + } + + uint32_t index() const { + return mIndex; + } + + iterator& operator++(); + const ResTable_entry* operator*() const; + + private: + friend struct TypeVariant; + iterator(const TypeVariant* tv, uint32_t index) + : mTypeVariant(tv), mIndex(index) {} + const TypeVariant* mTypeVariant; + uint32_t mIndex; + }; + + iterator beginEntries() const { + return iterator(this, 0); + } + + iterator endEntries() const { + return iterator(this, dtohl(data->entryCount)); + } + + const ResTable_type* data; +}; + +} // namespace android + +#endif // __TYPE_WRAPPERS_H diff --git a/libs/androidfw/Android.mk b/libs/androidfw/Android.mk index d21197e3a6739..957809d82d0e2 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 1b3f1fd9c5736..03409286ea6c4 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(this)->mAssetPaths.add(oap); const_cast(this)->mZipSet.addOverlay(targetPackagePath, oap); } diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index a4b78a6054961..2e3abb53363cc 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -17,7 +17,9 @@ #define LOG_TAG "ResourceType" //#define LOG_NDEBUG 0 +#include #include +#include #include #include #include @@ -30,6 +32,7 @@ #include #include #include +#include #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 +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(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(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(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(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(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(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(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(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(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* 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( + reinterpret_cast(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(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 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 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(_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; isize(); 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; jtypeStrings.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 packages; - // This is for finding typeStrings and other common package stuff. - Package* basePackage; + ByteBucketArray 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* 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; jnumTypes; 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; jnumTypes; 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; jnumTypes; 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; kgetBuffer(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(idmap)); + + return addInternal(data, static_cast(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(idmapAsset->getLength()); + } + + return addInternal(data, static_cast(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; jpackages.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(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( + reinterpret_cast(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(); 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(entry.entry) - reinterpret_cast(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 @@ uint32_t ResTable::identifierForName(const char16_t* name, size_t nameLen, 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; tciconfigs[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; ipackage->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* configs) const { - const size_t I = mPackageGroups.size(); - for (size_t i=0; ipackages.size(); - for (size_t j=0; jpackages[j]; - const size_t K = package->types.size(); - for (size_t k=0; ktypes[k]; - if (type == NULL) continue; - const size_t L = type->configs.size(); - for (size_t l=0; lconfigs[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; msize(); + 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* 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; iconfigs[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(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(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(thisType) + + dtohl(thisType->header.size); + const uint32_t* const eindex = reinterpret_cast( + reinterpret_cast(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( + reinterpret_cast(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 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(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(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(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 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 > map; + KeyedVector 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& 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(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(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(data); + *typeData++ = htods(pg->id); + *typeData++ = htods(mapSize); for (size_t i = 0; i < mapSize; ++i) { - const Vector& 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& 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(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(map + 3)); + pTargetPath->setTo(reinterpret_cast(map + 4)); } if (pOverlayPath) { - pOverlayPath->setTo(reinterpret_cast(map + 3 + 256 / sizeof(uint32_t))); + pOverlayPath->setTo(reinterpret_cast(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; pkgIndexpackages[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; typeIndexgetType(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; entryIndexentryCount; 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; configIndexconfigs[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; entryIndexentryCount; 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; entryIndexheader.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; configIndexconfigs[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; entryIndexheader.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(""); + 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(""); - 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; iname.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; iname.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 0000000000000..8929b66bbdc41 --- /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 + +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(type) + + dtohl(type->header.size); + const uint32_t* const entryIndices = reinterpret_cast( + reinterpret_cast(type) + dtohs(type->header.headerSize)); + if (reinterpret_cast(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 %p", mIndex, (void*) entryOffset); + return NULL; + } + + const ResTable_entry* entry = reinterpret_cast( + reinterpret_cast(type) + dtohl(type->entriesStart) + entryOffset); + if (reinterpret_cast(entry) > containerEnd - sizeof(*entry)) { + ALOGE("Entry offset at index %u points outside the Type's boundaries", mIndex); + return NULL; + } else if (reinterpret_cast(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 9e9649cf3675c..4ff6eecf16858 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 0000000000000..376e79c6e7cbb --- /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 + +#include + +using android::ByteBucketArray; + +TEST(ByteBucketArrayTest, TestSparseInsertion) { + ByteBucketArray 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 0000000000000..d829b7603ad8c --- /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 + +#include +#include +#include "TestHelpers.h" + +#include + +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 0000000000000..54d42c39752bf --- /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 + +#include +#include +#include "TestHelpers.h" + +#include + +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 4888b4a2ea7b3..6041e08009e34 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 0000000000000..dbfdeae04d8e0 --- /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 + +#include +#include +#include "TestHelpers.h" + +#include + +/** + * 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(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 0000000000000..75a233acad264 --- /dev/null +++ b/libs/androidfw/tests/TestHelpers.h @@ -0,0 +1,17 @@ +#ifndef __TEST_HELPERS_H +#define __TEST_HELPERS_H + +#include + +#include +#include + +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 0000000000000..d69abe5d0f11c --- /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 +#include +#include + +#include + +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 0000000000000..c05cfb0430246 --- /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 0000000000000..a56ac18e900b2 --- /dev/null +++ b/libs/androidfw/tests/data/basic/AndroidManifest.xml @@ -0,0 +1,21 @@ + + + + + + + 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 0000000000000..6532076d3493f --- /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 0000000000000..237342c8dc40b --- /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 0000000000000..9d523071dd07b --- /dev/null +++ b/libs/androidfw/tests/data/basic/res/values-sv/values.xml @@ -0,0 +1,4 @@ + + + 400 + 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 0000000000000..662eda6a4ed58 --- /dev/null +++ b/libs/androidfw/tests/data/basic/res/values/values.xml @@ -0,0 +1,26 @@ + + + + + + test1 + test2 + + 200 + @array/integerArray1 + + + + + + + 1 + 2 + 3 + + diff --git a/libs/androidfw/tests/data/overlay/AndroidManifest.xml b/libs/androidfw/tests/data/overlay/AndroidManifest.xml new file mode 100644 index 0000000000000..a56ac18e900b2 --- /dev/null +++ b/libs/androidfw/tests/data/overlay/AndroidManifest.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/libs/androidfw/tests/data/overlay/build b/libs/androidfw/tests/data/overlay/build new file mode 100755 index 0000000000000..87cf6de4c933a --- /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 0000000000000..5bd98b28409d2 --- /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 0000000000000..227e88973cc74 --- /dev/null +++ b/libs/androidfw/tests/data/overlay/res/values/values.xml @@ -0,0 +1,8 @@ + + + test2-overlay + + 10 + 11 + + 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 0000000000000..e0321e9bf492c --- /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 0000000000000..6f6a41626e0fa --- /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/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp index f10904c871f44..3d93bbe62f678 100644 --- a/tools/aapt/Resource.cpp +++ b/tools/aapt/Resource.cpp @@ -589,11 +589,11 @@ static bool applyFileOverlay(Bundle *bundle, if (bundle->getVerbose()) { printf("trying overlaySet Key=%s\n",overlaySet->keyAt(overlayIndex).string()); } - size_t baseIndex = UNKNOWN_ERROR; + ssize_t baseIndex = -1; if (baseSet->get() != NULL) { baseIndex = (*baseSet)->indexOfKey(overlaySet->keyAt(overlayIndex)); } - if (baseIndex < UNKNOWN_ERROR) { + if (baseIndex >= 0) { // look for same flavor. For a given file (strings.xml, for example) // there may be a locale specific or other flavors - we want to match // the same flavor. @@ -619,10 +619,10 @@ static bool applyFileOverlay(Bundle *bundle, for (size_t overlayGroupIndex = 0; overlayGroupIndexgetFiles().indexOfKey(overlayFiles. keyAt(overlayGroupIndex)); - if (baseFileIndex < UNKNOWN_ERROR) { + if (baseFileIndex >= 0) { if (bundle->getVerbose()) { printf("found a match (" ZD ") for overlay file %s, for flavor %s\n", (ZD_TYPE) baseFileIndex, @@ -1363,7 +1363,11 @@ status_t buildResources(Bundle* bundle, const sp& assets, spisBase()) { resFile = flattenedTable; - finalResTable.add(flattenedTable->getData(), flattenedTable->getSize()); + err = finalResTable.add(flattenedTable->getData(), flattenedTable->getSize()); + if (err != NO_ERROR) { + fprintf(stderr, "Generated resource table is corrupt.\n"); + return err; + } } else { sp generatedManifest = new AaptFile(String8("AndroidManifest.xml"), AaptGroupEntry(), String8()); diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index efbba40f950db..1a9f1b95dada1 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -2292,8 +2292,14 @@ uint32_t ResourceTable::getCustomResourceWithCreation( if (resId != 0 || !createIfNotFound) { return resId; } - String16 value("false"); + if (mAssetsPackage != package) { + mCurrentXmlPos.warning("creating resource for external package %s: %s/%s.", + String8(package).string(), String8(type).string(), String8(name).string()); + mCurrentXmlPos.printf("This will be an error in a future version of AAPT."); + } + + String16 value("false"); status_t status = addEntry(mCurrentXmlPos, package, type, name, value, NULL, NULL, true); if (status == NO_ERROR) { resId = getResId(package, type, name); @@ -3062,8 +3068,9 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp& for (size_t i = 0; i < N; ++i) { if (!validResources[i]) { sp c = t->getOrderedConfigs().itemAt(i); - fprintf(stderr, "%s: no entries written for %s/%s\n", log_prefix, - String8(typeName).string(), String8(c->getName()).string()); + fprintf(stderr, "%s: no entries written for %s/%s (0x%08x)\n", log_prefix, + String8(typeName).string(), String8(c->getName()).string(), + Res_MAKEID(p->getAssignedId() - 1, ti, i)); missing_entry = true; } }