From 80094e39f90801c44cd80ab0f98df505828ea1f3 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Mon, 16 Nov 2020 23:08:18 +0000 Subject: Revert^2 "libandroidfw hardening for IncFs" 55ef6167a2c235bd88c7216238b2001b46795b79 Change-Id: I02d4890d181655dfd0a14c188468db512559d27b Merged-In: I02d4890d181655dfd0a14c188468db512559d27b --- libs/androidfw/Android.bp | 10 +- libs/androidfw/ApkAssets.cpp | 82 ++- libs/androidfw/Asset.cpp | 174 +++--- libs/androidfw/AssetManager.cpp | 16 +- libs/androidfw/AssetManager2.cpp | 665 +++++++++++---------- libs/androidfw/AttributeResolution.cpp | 447 ++++++-------- libs/androidfw/ChunkIterator.cpp | 27 +- libs/androidfw/Idmap.cpp | 12 +- libs/androidfw/LoadedArsc.cpp | 339 ++++++----- libs/androidfw/ResourceTypes.cpp | 488 +++++++++------ libs/androidfw/ResourceUtils.cpp | 77 ++- libs/androidfw/StreamingZipInflater.cpp | 6 +- libs/androidfw/ZipFileRO.cpp | 23 + libs/androidfw/ZipUtils.cpp | 21 +- .../resourcefile_fuzzer/resourcefile_fuzzer.cpp | 5 +- libs/androidfw/include/androidfw/Asset.h | 108 ++-- libs/androidfw/include/androidfw/AssetManager2.h | 256 ++++---- .../include/androidfw/AttributeResolution.h | 24 +- libs/androidfw/include/androidfw/Chunk.h | 23 +- libs/androidfw/include/androidfw/Errors.h | 47 ++ libs/androidfw/include/androidfw/Idmap.h | 4 +- libs/androidfw/include/androidfw/LoadedArsc.h | 37 +- libs/androidfw/include/androidfw/ResourceTypes.h | 70 ++- libs/androidfw/include/androidfw/ResourceUtils.h | 9 +- .../include/androidfw/StreamingZipInflater.h | 6 +- libs/androidfw/include/androidfw/Util.h | 8 +- libs/androidfw/include/androidfw/ZipFileRO.h | 27 +- libs/androidfw/include/androidfw/ZipUtils.h | 6 +- libs/androidfw/tests/AssetManager2_bench.cpp | 10 +- libs/androidfw/tests/AssetManager2_test.cpp | 469 +++++++-------- libs/androidfw/tests/AttributeResolution_bench.cpp | 19 +- libs/androidfw/tests/AttributeResolution_test.cpp | 18 +- libs/androidfw/tests/BenchmarkHelpers.cpp | 10 +- libs/androidfw/tests/CommonHelpers.cpp | 5 +- libs/androidfw/tests/Idmap_test.cpp | 153 ++--- libs/androidfw/tests/LoadedArsc_test.cpp | 63 +- libs/androidfw/tests/ResTable_test.cpp | 26 +- libs/androidfw/tests/TestHelpers.cpp | 12 +- libs/androidfw/tests/Theme_bench.cpp | 5 +- libs/androidfw/tests/Theme_test.cpp | 278 ++++----- 40 files changed, 2134 insertions(+), 1951 deletions(-) create mode 100644 libs/androidfw/include/androidfw/Errors.h (limited to 'libs') diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index aa34edf487fe..6a7df94331f3 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -61,6 +61,9 @@ cc_library { ], export_include_dirs: ["include"], export_shared_lib_headers: ["libz"], + static_libs: ["libincfs-utils"], + whole_static_libs: ["libincfs-utils"], + export_static_lib_headers: ["libincfs-utils"], target: { android: { srcs: [ @@ -69,13 +72,14 @@ cc_library { "CursorWindow.cpp", ], shared_libs: [ - "libziparchive", "libbase", "libbinder", "liblog", "libcutils", + "libincfs", "libutils", "libz", + "libziparchive", ], static: { enabled: false, @@ -86,11 +90,11 @@ cc_library { enabled: false, }, static_libs: [ - "libziparchive", "libbase", - "liblog", "libcutils", + "liblog", "libutils", + "libziparchive", ], shared_libs: [ "libz", diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index e15b42d46f53..cb56a5172a45 100755 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -25,13 +25,11 @@ #include "android-base/unique_fd.h" #include "android-base/utf8.h" #include "utils/Compat.h" -#include "utils/FileMap.h" #include "ziparchive/zip_archive.h" #include "androidfw/Asset.h" #include "androidfw/Idmap.h" #include "androidfw/misc.h" -#include "androidfw/ResourceTypes.h" #include "androidfw/Util.h" namespace android { @@ -161,50 +159,46 @@ class ZipAssetsProvider : public AssetsProvider { } const int fd = ::GetFileDescriptor(zip_handle_.get()); - const off64_t fd_offset = ::GetFileDescriptorOffset(zip_handle_.get()); + const off64_t fd_offset = ::GetFileDescriptorOffset(zip_handle_.get()); + incfs::IncFsFileMap asset_map; if (entry.method == kCompressDeflated) { - std::unique_ptr map = util::make_unique(); - if (!map->create(GetPath(), fd, entry.offset + fd_offset, entry.compressed_length, - true /*readOnly*/)) { + if (!asset_map.Create(fd, entry.offset + fd_offset, entry.compressed_length, GetPath())) { LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'"; return {}; } std::unique_ptr asset = - Asset::createFromCompressedMap(std::move(map), entry.uncompressed_length, mode); + Asset::createFromCompressedMap(std::move(asset_map), entry.uncompressed_length, mode); if (asset == nullptr) { LOG(ERROR) << "Failed to decompress '" << path << "' in APK '" << friendly_name_ << "'"; return {}; } return asset; - } else { - std::unique_ptr map = util::make_unique(); - if (!map->create(GetPath(), fd, entry.offset + fd_offset, entry.uncompressed_length, - true /*readOnly*/)) { - LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'"; - return {}; - } + } - unique_fd ufd; - if (!GetPath()) { - // If the `path` is not set, create a new `fd` for the new Asset to own in order to create - // new file descriptors using Asset::openFileDescriptor. If the path is set, it will be used - // to create new file descriptors. - ufd = unique_fd(dup(fd)); - if (!ufd.ok()) { - LOG(ERROR) << "Unable to dup fd '" << path << "' in APK '" << friendly_name_ << "'"; - return {}; - } - } + if (!asset_map.Create(fd, entry.offset + fd_offset, entry.uncompressed_length, GetPath())) { + LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'"; + return {}; + } - std::unique_ptr asset = Asset::createFromUncompressedMap(std::move(map), - std::move(ufd), mode); - if (asset == nullptr) { - LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'"; + unique_fd ufd; + if (!GetPath()) { + // If the `path` is not set, create a new `fd` for the new Asset to own in order to create + // new file descriptors using Asset::openFileDescriptor. If the path is set, it will be used + // to create new file descriptors. + ufd = unique_fd(dup(fd)); + if (!ufd.ok()) { + LOG(ERROR) << "Unable to dup fd '" << path << "' in APK '" << friendly_name_ << "'"; return {}; } - return asset; } + + auto asset = Asset::createFromUncompressedMap(std::move(asset_map), mode, std::move(ufd)); + if (asset == nullptr) { + LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'"; + return {}; + } + return asset; } private: @@ -446,8 +440,8 @@ std::unique_ptr ApkAssets::CreateAssetFromFd(base::unique_fd fd, } } - std::unique_ptr file_map = util::make_unique(); - if (!file_map->create(path, fd, offset, static_cast(length), true /*readOnly*/)) { + incfs::IncFsFileMap file_map; + if (!file_map.Create(fd, offset, static_cast(length), path)) { LOG(ERROR) << "Failed to mmap file '" << ((path) ? path : "anon") << "': " << SystemErrorCodeToString(errno); return {}; @@ -456,8 +450,8 @@ std::unique_ptr ApkAssets::CreateAssetFromFd(base::unique_fd fd, // If `path` is set, do not pass ownership of the `fd` to the new Asset since // Asset::openFileDescriptor can use `path` to create new file descriptors. return Asset::createFromUncompressedMap(std::move(file_map), - (path) ? base::unique_fd(-1) : std::move(fd), - Asset::AccessMode::ACCESS_RANDOM); + Asset::AccessMode::ACCESS_RANDOM, + (path) ? base::unique_fd(-1) : std::move(fd)); } std::unique_ptr ApkAssets::LoadImpl( @@ -493,15 +487,14 @@ std::unique_ptr ApkAssets::LoadImpl( loaded_apk->idmap_asset_ = std::move(idmap_asset); loaded_apk->loaded_idmap_ = std::move(idmap); - const StringPiece data( - reinterpret_cast(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)), - loaded_apk->resources_asset_->getLength()); - if (data.data() == nullptr || data.empty()) { + const auto data = loaded_apk->resources_asset_->getIncFsBuffer(true /* aligned */); + const size_t length = loaded_apk->resources_asset_->getLength(); + if (!data || length == 0) { LOG(ERROR) << "Failed to read '" << kResourcesArsc << "' data in APK '" << path << "'."; return {}; } - loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, loaded_apk->loaded_idmap_.get(), + loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, length, loaded_apk->loaded_idmap_.get(), property_flags); if (!loaded_apk->loaded_arsc_) { LOG(ERROR) << "Failed to load '" << kResourcesArsc << "' in APK '" << path << "'."; @@ -525,15 +518,15 @@ std::unique_ptr ApkAssets::LoadTableImpl( new ApkAssets(std::move(assets), path, last_mod_time, property_flags)); loaded_apk->resources_asset_ = std::move(resources_asset); - const StringPiece data( - reinterpret_cast(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)), - loaded_apk->resources_asset_->getLength()); - if (data.data() == nullptr || data.empty()) { + const auto data = loaded_apk->resources_asset_->getIncFsBuffer(true /* aligned */); + const size_t length = loaded_apk->resources_asset_->getLength(); + if (!data || length == 0) { LOG(ERROR) << "Failed to read resources table data in '" << path << "'."; return {}; } - loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, nullptr, property_flags); + loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, length, nullptr /* loaded_idmap */, + property_flags); if (loaded_apk->loaded_arsc_ == nullptr) { LOG(ERROR) << "Failed to read resources table in '" << path << "'."; return {}; @@ -550,7 +543,6 @@ bool ApkAssets::IsUpToDate() const { } return (!loaded_idmap_ || loaded_idmap_->IsUpToDate()) && last_mod_time_ == getFileModDate(path_.c_str()); - } } // namespace android diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp index cd30c184d5a4..4fbe4a3efbdd 100644 --- a/libs/androidfw/Asset.cpp +++ b/libs/androidfw/Asset.cpp @@ -298,34 +298,18 @@ Asset::Asset(void) /* * Create a new Asset from a memory mapping. */ -/*static*/ Asset* Asset::createFromUncompressedMap(FileMap* dataMap, AccessMode mode) +/*static*/ std::unique_ptr Asset::createFromUncompressedMap(incfs::IncFsFileMap&& dataMap, + AccessMode mode, + base::unique_fd fd) { - _FileAsset* pAsset; - status_t result; - - pAsset = new _FileAsset; - result = pAsset->openChunk(dataMap, base::unique_fd(-1)); - if (result != NO_ERROR) { - delete pAsset; - return NULL; - } - - pAsset->mAccessMode = mode; - return pAsset; -} + auto pAsset = util::make_unique<_FileAsset>(); -/*static*/ std::unique_ptr Asset::createFromUncompressedMap(std::unique_ptr dataMap, - base::unique_fd fd, AccessMode mode) -{ - std::unique_ptr<_FileAsset> pAsset = util::make_unique<_FileAsset>(); - - status_t result = pAsset->openChunk(dataMap.get(), std::move(fd)); + status_t result = pAsset->openChunk(std::move(dataMap), std::move(fd)); if (result != NO_ERROR) { return NULL; } // We succeeded, so relinquish control of dataMap - (void) dataMap.release(); pAsset->mAccessMode = mode; return std::move(pAsset); } @@ -333,35 +317,18 @@ Asset::Asset(void) /* * Create a new Asset from compressed data in a memory mapping. */ -/*static*/ Asset* Asset::createFromCompressedMap(FileMap* dataMap, - size_t uncompressedLen, AccessMode mode) +/*static*/ std::unique_ptr Asset::createFromCompressedMap(incfs::IncFsFileMap&& dataMap, + size_t uncompressedLen, + AccessMode mode) { - _CompressedAsset* pAsset; - status_t result; - - pAsset = new _CompressedAsset; - result = pAsset->openChunk(dataMap, uncompressedLen); - if (result != NO_ERROR) { - delete pAsset; - return NULL; - } + auto pAsset = util::make_unique<_CompressedAsset>(); - pAsset->mAccessMode = mode; - return pAsset; -} - -/*static*/ std::unique_ptr Asset::createFromCompressedMap(std::unique_ptr dataMap, - size_t uncompressedLen, AccessMode mode) -{ - std::unique_ptr<_CompressedAsset> pAsset = util::make_unique<_CompressedAsset>(); - - status_t result = pAsset->openChunk(dataMap.get(), uncompressedLen); + status_t result = pAsset->openChunk(std::move(dataMap), uncompressedLen); if (result != NO_ERROR) { return NULL; } // We succeeded, so relinquish control of dataMap - (void) dataMap.release(); pAsset->mAccessMode = mode; return std::move(pAsset); } @@ -414,7 +381,7 @@ off64_t Asset::handleSeek(off64_t offset, int whence, off64_t curPosn, off64_t m * Constructor. */ _FileAsset::_FileAsset(void) - : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mFd(-1), mMap(NULL), mBuf(NULL) + : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mFd(-1), mBuf(NULL) { // Register the Asset with the global list here after it is fully constructed and its // vtable pointer points to this concrete type. b/31113965 @@ -441,7 +408,7 @@ _FileAsset::~_FileAsset(void) status_t _FileAsset::openChunk(const char* fileName, int fd, off64_t offset, size_t length) { assert(mFp == NULL); // no reopen - assert(mMap == NULL); + assert(!mMap.has_value()); assert(fd >= 0); assert(offset >= 0); @@ -484,15 +451,15 @@ status_t _FileAsset::openChunk(const char* fileName, int fd, off64_t offset, siz /* * Create the chunk from the map. */ -status_t _FileAsset::openChunk(FileMap* dataMap, base::unique_fd fd) +status_t _FileAsset::openChunk(incfs::IncFsFileMap&& dataMap, base::unique_fd fd) { assert(mFp == NULL); // no reopen - assert(mMap == NULL); + assert(!mMap.has_value()); assert(dataMap != NULL); - mMap = dataMap; + mMap = std::move(dataMap); mStart = -1; // not used - mLength = dataMap->getDataLength(); + mLength = mMap->length(); mFd = std::move(fd); assert(mOffset == 0); @@ -528,10 +495,15 @@ ssize_t _FileAsset::read(void* buf, size_t count) if (!count) return 0; - if (mMap != NULL) { + if (mMap.has_value()) { /* copy from mapped area */ //printf("map read\n"); - memcpy(buf, (char*)mMap->getDataPtr() + mOffset, count); + const auto readPos = mMap->data().offset(mOffset).convert(); + if (!readPos.verify(count)) { + return -1; + } + + memcpy(buf, readPos.unsafe_ptr(), count); actual = count; } else if (mBuf != NULL) { /* copy from buffer */ @@ -594,10 +566,6 @@ off64_t _FileAsset::seek(off64_t offset, int whence) */ void _FileAsset::close(void) { - if (mMap != NULL) { - delete mMap; - mMap = NULL; - } if (mBuf != NULL) { delete[] mBuf; mBuf = NULL; @@ -624,16 +592,21 @@ void _FileAsset::close(void) * level and we'd be using a different object, but we didn't, so we * deal with it here. */ -const void* _FileAsset::getBuffer(bool wordAligned) +const void* _FileAsset::getBuffer(bool aligned) +{ + return getIncFsBuffer(aligned).unsafe_ptr(); +} + +incfs::map_ptr _FileAsset::getIncFsBuffer(bool aligned) { /* subsequent requests just use what we did previously */ if (mBuf != NULL) return mBuf; - if (mMap != NULL) { - if (!wordAligned) { - return mMap->getDataPtr(); + if (mMap.has_value()) { + if (!aligned) { + return mMap->data(); } - return ensureAlignment(mMap); + return ensureAlignment(*mMap); } assert(mFp != NULL); @@ -671,47 +644,44 @@ const void* _FileAsset::getBuffer(bool wordAligned) mBuf = buf; return mBuf; } else { - FileMap* map; - - map = new FileMap; - if (!map->create(NULL, fileno(mFp), mStart, mLength, true)) { - delete map; + incfs::IncFsFileMap map; + if (!map.Create(fileno(mFp), mStart, mLength, NULL /* file_name */ )) { return NULL; } ALOGV(" getBuffer: mapped\n"); - mMap = map; - if (!wordAligned) { - return mMap->getDataPtr(); + mMap = std::move(map); + if (!aligned) { + return mMap->data(); } - return ensureAlignment(mMap); + return ensureAlignment(*mMap); } } int _FileAsset::openFileDescriptor(off64_t* outStart, off64_t* outLength) const { - if (mMap != NULL) { + if (mMap.has_value()) { if (mFd.ok()) { - *outStart = mMap->getDataOffset(); - *outLength = mMap->getDataLength(); - const int fd = dup(mFd); - if (fd < 0) { - ALOGE("Unable to dup fd (%d).", mFd.get()); - return -1; - } - lseek64(fd, 0, SEEK_SET); - return fd; + *outStart = mMap->offset(); + *outLength = mMap->length(); + const int fd = dup(mFd); + if (fd < 0) { + ALOGE("Unable to dup fd (%d).", mFd.get()); + return -1; + } + lseek64(fd, 0, SEEK_SET); + return fd; } - const char* fname = mMap->getFileName(); + const char* fname = mMap->file_name(); if (fname == NULL) { fname = mFileName; } if (fname == NULL) { return -1; } - *outStart = mMap->getDataOffset(); - *outLength = mMap->getDataLength(); + *outStart = mMap->offset(); + *outLength = mMap->length(); return open(fname, O_RDONLY | O_BINARY); } if (mFileName == NULL) { @@ -722,16 +692,21 @@ int _FileAsset::openFileDescriptor(off64_t* outStart, off64_t* outLength) const return open(mFileName, O_RDONLY | O_BINARY); } -const void* _FileAsset::ensureAlignment(FileMap* map) +incfs::map_ptr _FileAsset::ensureAlignment(const incfs::IncFsFileMap& map) { - void* data = map->getDataPtr(); - if ((((size_t)data)&0x3) == 0) { + const auto data = map.data(); + if (util::IsFourByteAligned(data)) { // We can return this directly if it is aligned on a word // boundary. ALOGV("Returning aligned FileAsset %p (%s).", this, getAssetSource()); return data; } + + if (!data.convert().verify(mLength)) { + return NULL; + } + // If not aligned on a word boundary, then we need to copy it into // our own buffer. ALOGV("Copying FileAsset %p (%s) to buffer size %d to make it aligned.", this, @@ -741,7 +716,8 @@ const void* _FileAsset::ensureAlignment(FileMap* map) ALOGE("alloc of %ld bytes failed\n", (long) mLength); return NULL; } - memcpy(buf, data, mLength); + + memcpy(buf, data.unsafe_ptr(), mLength); mBuf = buf; return buf; } @@ -757,7 +733,7 @@ const void* _FileAsset::ensureAlignment(FileMap* map) */ _CompressedAsset::_CompressedAsset(void) : mStart(0), mCompressedLen(0), mUncompressedLen(0), mOffset(0), - mMap(NULL), mFd(-1), mZipInflater(NULL), mBuf(NULL) + mFd(-1), mZipInflater(NULL), mBuf(NULL) { // Register the Asset with the global list here after it is fully constructed and its // vtable pointer points to this concrete type. b/31113965 @@ -786,7 +762,7 @@ status_t _CompressedAsset::openChunk(int fd, off64_t offset, int compressionMethod, size_t uncompressedLen, size_t compressedLen) { assert(mFd < 0); // no re-open - assert(mMap == NULL); + assert(!mMap.has_value()); assert(fd >= 0); assert(offset >= 0); assert(compressedLen > 0); @@ -815,20 +791,20 @@ status_t _CompressedAsset::openChunk(int fd, off64_t offset, * * Nothing is expanded until the first read call. */ -status_t _CompressedAsset::openChunk(FileMap* dataMap, size_t uncompressedLen) +status_t _CompressedAsset::openChunk(incfs::IncFsFileMap&& dataMap, size_t uncompressedLen) { assert(mFd < 0); // no re-open - assert(mMap == NULL); + assert(!mMap.has_value()); assert(dataMap != NULL); - mMap = dataMap; + mMap = std::move(dataMap); mStart = -1; // not used - mCompressedLen = dataMap->getDataLength(); + mCompressedLen = mMap->length(); mUncompressedLen = uncompressedLen; assert(mOffset == 0); if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) { - mZipInflater = new StreamingZipInflater(dataMap, uncompressedLen); + mZipInflater = new StreamingZipInflater(&(*mMap), uncompressedLen); } return NO_ERROR; } @@ -901,11 +877,6 @@ off64_t _CompressedAsset::seek(off64_t offset, int whence) */ void _CompressedAsset::close(void) { - if (mMap != NULL) { - delete mMap; - mMap = NULL; - } - delete[] mBuf; mBuf = NULL; @@ -940,8 +911,8 @@ const void* _CompressedAsset::getBuffer(bool) goto bail; } - if (mMap != NULL) { - if (!ZipUtils::inflateToBuffer(mMap->getDataPtr(), buf, + if (mMap.has_value()) { + if (!ZipUtils::inflateToBuffer(mMap->data(), buf, mUncompressedLen, mCompressedLen)) goto bail; } else { @@ -976,3 +947,6 @@ bail: return mBuf; } +incfs::map_ptr _CompressedAsset::getIncFsBuffer(bool aligned) { + return incfs::map_ptr(getBuffer(aligned)); +} diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp index f7c83371f79c..fb2b57193b83 100644 --- a/libs/androidfw/AssetManager.cpp +++ b/libs/androidfw/AssetManager.cpp @@ -917,7 +917,7 @@ Asset* AssetManager::openAssetFromFileLocked(const String8& pathName, Asset* AssetManager::openAssetFromZipLocked(const ZipFileRO* pZipFile, const ZipEntryRO entry, AccessMode mode, const String8& entryName) { - Asset* pAsset = NULL; + std::unique_ptr pAsset; // TODO: look for previously-created shared memory slice? uint16_t method; @@ -932,28 +932,28 @@ Asset* AssetManager::openAssetFromZipLocked(const ZipFileRO* pZipFile, return NULL; } - FileMap* dataMap = pZipFile->createEntryFileMap(entry); - if (dataMap == NULL) { + std::optional dataMap = pZipFile->createEntryIncFsFileMap(entry); + if (!dataMap.has_value()) { ALOGW("create map from entry failed\n"); return NULL; } if (method == ZipFileRO::kCompressStored) { - pAsset = Asset::createFromUncompressedMap(dataMap, mode); + pAsset = Asset::createFromUncompressedMap(std::move(*dataMap), mode); ALOGV("Opened uncompressed entry %s in zip %s mode %d: %p", entryName.string(), - dataMap->getFileName(), mode, pAsset); + dataMap->file_name(), mode, pAsset.get()); } else { - pAsset = Asset::createFromCompressedMap(dataMap, + pAsset = Asset::createFromCompressedMap(std::move(*dataMap), static_cast(uncompressedLen), mode); ALOGV("Opened compressed entry %s in zip %s mode %d: %p", entryName.string(), - dataMap->getFileName(), mode, pAsset); + dataMap->file_name(), mode, pAsset.get()); } if (pAsset == NULL) { /* unexpected */ ALOGW("create from segment failed\n"); } - return pAsset; + return pAsset.release(); } /* diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 99dd3134ff8a..3e54dc67db76 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -38,9 +38,43 @@ namespace android { +namespace { + +using EntryValue = std::variant>; + +base::expected GetEntryValue( + incfs::verified_map_ptr table_entry) { + const uint16_t entry_size = dtohs(table_entry->size); + + // Check if the entry represents a bag value. + if (entry_size >= sizeof(ResTable_map_entry) && + (dtohs(table_entry->flags) & ResTable_entry::FLAG_COMPLEX)) { + const auto map_entry = table_entry.convert(); + if (!map_entry) { + return base::unexpected(IOError::PAGES_MISSING); + } + return map_entry.verified(); + } + + // The entry represents a non-bag value. + const auto entry_value = table_entry.offset(entry_size).convert(); + if (!entry_value) { + return base::unexpected(IOError::PAGES_MISSING); + } + Res_value value; + value.copyFrom_dtoh(entry_value.value()); + return value; +} + +} // namespace + struct FindEntryResult { - // A pointer to the value of the resource table entry. - std::variant entry; + // The cookie representing the ApkAssets in which the value resides. + ApkAssetsCookie cookie; + + // The value of the resource table entry. Either an android::Res_value for non-bag types or an + // incfs::verified_map_ptr for bag types. + EntryValue entry; // The configuration for which the resulting entry was defined. This is already swapped to host // endianness. @@ -265,7 +299,7 @@ const std::unordered_map* } const PackageGroup& package_group = package_groups_[idx]; - if (package_group.packages_.size() == 0) { + if (package_group.packages_.empty()) { return nullptr; } @@ -310,14 +344,14 @@ bool AssetManager2::GetOverlayablesToString(const android::StringPiece& package_ for (auto it = loaded_package->begin(); it != loaded_package->end(); it++) { const OverlayableInfo* info = loaded_package->GetOverlayableInfo(*it); if (info != nullptr) { - ResourceName res_name; - if (!GetResourceName(*it, &res_name)) { + auto res_name = GetResourceName(*it); + if (!res_name.has_value()) { ANDROID_LOG(ERROR) << base::StringPrintf( "Unable to retrieve name of overlayable resource 0x%08x", *it); return false; } - const std::string name = ToFormattedResourceString(&res_name); + const std::string name = ToFormattedResourceString(*res_name); output.append(base::StringPrintf( "resource='%s' overlayable='%s' actor='%s' policy='0x%08x'\n", name.c_str(), info->name.c_str(), info->actor.c_str(), info->policy_flags)); @@ -365,8 +399,8 @@ std::set AssetManager2::GetNonSystemOverlayPaths() const { return non_system_overlays; } -std::set AssetManager2::GetResourceConfigurations(bool exclude_system, - bool exclude_mipmap) const { +base::expected, IOError> AssetManager2::GetResourceConfigurations( + bool exclude_system, bool exclude_mipmap) const { ATRACE_NAME("AssetManager::GetResourceConfigurations"); const auto non_system_overlays = (exclude_system) ? GetNonSystemOverlayPaths() : std::set(); @@ -386,7 +420,10 @@ std::set AssetManager2::GetResourceConfigurations(bool exclude_ continue; } - package.loaded_package_->CollectConfigurations(exclude_mipmap, &configurations); + auto result = package.loaded_package_->CollectConfigurations(exclude_mipmap, &configurations); + if (UNLIKELY(!result.has_value())) { + return base::unexpected(result.error()); + } } } return configurations; @@ -501,11 +538,11 @@ std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, return apk_assets_[cookie]->GetAssetsProvider()->Open(filename, mode); } -ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override, - bool /*stop_at_first_match*/, - bool ignore_configuration, - FindEntryResult* out_entry) const { - if (resource_resolution_logging_enabled_) { +base::expected AssetManager2::FindEntry( + uint32_t resid, uint16_t density_override, bool stop_at_first_match, + bool ignore_configuration) const { + const bool logging_enabled = resource_resolution_logging_enabled_; + if (UNLIKELY(logging_enabled)) { // Clear the last logged resource resolution. ResetResourceResolution(); last_resolution_.resid = resid; @@ -523,94 +560,96 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri } // Retrieve the package group from the package id of the resource id. - if (!is_valid_resid(resid)) { + if (UNLIKELY(!is_valid_resid(resid))) { LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid); - return kInvalidCookie; + return base::unexpected(std::nullopt); } const uint32_t package_id = get_package_id(resid); const uint8_t type_idx = get_type_id(resid) - 1; const uint16_t entry_idx = get_entry_id(resid); uint8_t package_idx = package_ids_[package_id]; - if (package_idx == 0xff) { + if (UNLIKELY(package_idx == 0xff)) { ANDROID_LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.", package_id, resid); - return kInvalidCookie; + return base::unexpected(std::nullopt); } const PackageGroup& package_group = package_groups_[package_idx]; - ApkAssetsCookie cookie = FindEntryInternal(package_group, type_idx, entry_idx, *desired_config, - false /* stop_at_first_match */, - ignore_configuration, out_entry); - if (UNLIKELY(cookie == kInvalidCookie)) { - return kInvalidCookie; + auto result = FindEntryInternal(package_group, type_idx, entry_idx, *desired_config, + stop_at_first_match, ignore_configuration); + if (UNLIKELY(!result.has_value())) { + return base::unexpected(result.error()); } - if (!apk_assets_[cookie]->IsLoader()) { + if (!stop_at_first_match && !ignore_configuration && !apk_assets_[result->cookie]->IsLoader()) { for (const auto& id_map : package_group.overlays_) { auto overlay_entry = id_map.overlay_res_maps_.Lookup(resid); if (!overlay_entry) { // No id map entry exists for this target resource. continue; - } else if (overlay_entry.IsInlineValue()) { + } + if (overlay_entry.IsInlineValue()) { // The target resource is overlaid by an inline value not represented by a resource. - out_entry->entry = overlay_entry.GetInlineValue(); - out_entry->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable(); - cookie = id_map.cookie; + result->entry = overlay_entry.GetInlineValue(); + result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable(); + result->cookie = id_map.cookie; continue; } - FindEntryResult overlay_result; - ApkAssetsCookie overlay_cookie = FindEntry(overlay_entry.GetResourceId(), density_override, - false /* stop_at_first_match */, - ignore_configuration, &overlay_result); - if (UNLIKELY(overlay_cookie == kInvalidCookie)) { + auto overlay_result = FindEntry(overlay_entry.GetResourceId(), density_override, + false /* stop_at_first_match */, + false /* ignore_configuration */); + if (UNLIKELY(IsIOError(overlay_result))) { + return base::unexpected(overlay_result.error()); + } + if (!overlay_result.has_value()) { continue; } - if (!overlay_result.config.isBetterThan(out_entry->config, desired_config) - && overlay_result.config.compare(out_entry->config) != 0) { + if (!overlay_result->config.isBetterThan(result->config, desired_config) + && overlay_result->config.compare(result->config) != 0) { // The configuration of the entry for the overlay must be equal to or better than the target // configuration to be chosen as the better value. continue; } - cookie = overlay_cookie; - out_entry->entry = overlay_result.entry; - out_entry->config = overlay_result.config; - out_entry->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable(); - if (resource_resolution_logging_enabled_) { + result->cookie = overlay_result->cookie; + result->entry = overlay_result->entry; + result->config = overlay_result->config; + result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable(); + + if (UNLIKELY(logging_enabled)) { last_resolution_.steps.push_back( - Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result.config.toString(), - overlay_result.package_name}); + Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result->config.toString(), + overlay_result->package_name}); } } } - if (resource_resolution_logging_enabled_) { - last_resolution_.cookie = cookie; - last_resolution_.type_string_ref = out_entry->type_string_ref; - last_resolution_.entry_string_ref = out_entry->entry_string_ref; + if (UNLIKELY(logging_enabled)) { + last_resolution_.cookie = result->cookie; + last_resolution_.type_string_ref = result->type_string_ref; + last_resolution_.entry_string_ref = result->entry_string_ref; } - return cookie; + return result; } -ApkAssetsCookie AssetManager2::FindEntryInternal(const PackageGroup& package_group, - uint8_t type_idx, uint16_t entry_idx, - const ResTable_config& desired_config, - bool /*stop_at_first_match*/, - bool ignore_configuration, - FindEntryResult* out_entry) const { +base::expected AssetManager2::FindEntryInternal( + const PackageGroup& package_group, uint8_t type_idx, uint16_t entry_idx, + const ResTable_config& desired_config, bool stop_at_first_match, + bool ignore_configuration) const { + const bool logging_enabled = resource_resolution_logging_enabled_; ApkAssetsCookie best_cookie = kInvalidCookie; const LoadedPackage* best_package = nullptr; - const ResTable_type* best_type = nullptr; + incfs::verified_map_ptr best_type; const ResTable_config* best_config = nullptr; ResTable_config best_config_copy; - uint32_t best_offset = 0u; - uint32_t type_flags = 0u; + uint32_t best_offset = 0U; + uint32_t type_flags = 0U; - Resolution::Step::Type resolution_type = Resolution::Step::Type::NO_ENTRY; + auto resolution_type = Resolution::Step::Type::NO_ENTRY; std::vector resolution_steps; // If desired_config is the same as the set configuration, then we can use our filtered list @@ -630,17 +669,20 @@ ApkAssetsCookie AssetManager2::FindEntryInternal(const PackageGroup& package_gro continue; } + auto entry_flags = type_spec->GetFlagsForEntryIndex(entry_idx); + if (UNLIKELY(!entry_flags)) { + return base::unexpected(entry_flags.error()); + } + type_flags |= entry_flags.value(); + // If the package is an overlay or custom loader, // then even configurations that are the same MUST be chosen. const bool package_is_loader = loaded_package->IsCustomLoader(); - type_flags |= type_spec->GetFlagsForEntryIndex(entry_idx); if (use_fast_path) { const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx]; - const std::vector& candidate_configs = filtered_group.configurations; - const size_t type_count = candidate_configs.size(); - for (uint32_t i = 0; i < type_count; i++) { - const ResTable_config& this_config = candidate_configs[i]; + for (const auto& type_config : filtered_group.type_configs) { + const ResTable_config& this_config = type_config.config; // We can skip calling ResTable_config::match() because we know that all candidate // configurations that do NOT match have been filtered-out. @@ -652,7 +694,7 @@ ApkAssetsCookie AssetManager2::FindEntryInternal(const PackageGroup& package_gro } else if (package_is_loader && this_config.compare(*best_config) == 0) { resolution_type = Resolution::Step::Type::OVERLAID_LOADER; } else { - if (resource_resolution_logging_enabled_) { + if (UNLIKELY(logging_enabled)) { resolution_type = (package_is_loader) ? Resolution::Step::Type::SKIPPED_LOADER : Resolution::Step::Type::SKIPPED; resolution_steps.push_back(Resolution::Step{resolution_type, @@ -664,10 +706,13 @@ ApkAssetsCookie AssetManager2::FindEntryInternal(const PackageGroup& package_gro // The configuration matches and is better than the previous selection. // Find the entry value if it exists for this configuration. - const ResTable_type* type = filtered_group.types[i]; - const uint32_t offset = LoadedPackage::GetEntryOffset(type, entry_idx); - if (offset == ResTable_type::NO_ENTRY) { - if (resource_resolution_logging_enabled_) { + const auto& type = type_config.type; + const auto offset = LoadedPackage::GetEntryOffset(type, entry_idx); + if (UNLIKELY(IsIOError(offset))) { + return base::unexpected(offset.error()); + } + if (!offset.has_value()) { + if (UNLIKELY(logging_enabled)) { if (package_is_loader) { resolution_type = Resolution::Step::Type::NO_ENTRY_LOADER; } else { @@ -684,9 +729,9 @@ ApkAssetsCookie AssetManager2::FindEntryInternal(const PackageGroup& package_gro best_package = loaded_package; best_type = type; best_config = &this_config; - best_offset = offset; + best_offset = offset.value(); - if (resource_resolution_logging_enabled_) { + if (UNLIKELY(logging_enabled)) { last_resolution_.steps.push_back(Resolution::Step{resolution_type, this_config.toString(), &loaded_package->GetPackageName()}); @@ -700,10 +745,11 @@ ApkAssetsCookie AssetManager2::FindEntryInternal(const PackageGroup& package_gro // ResTable_config, we must copy it. const auto iter_end = type_spec->types + type_spec->type_count; for (auto iter = type_spec->types; iter != iter_end; ++iter) { - ResTable_config this_config{}; + const incfs::verified_map_ptr& type = *iter; + ResTable_config this_config{}; if (!ignore_configuration) { - this_config.copyFromDtoH((*iter)->config); + this_config.copyFromDtoH(type->config); if (!this_config.match(desired_config)) { continue; } @@ -722,24 +768,27 @@ ApkAssetsCookie AssetManager2::FindEntryInternal(const PackageGroup& package_gro // The configuration matches and is better than the previous selection. // Find the entry value if it exists for this configuration. - const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, entry_idx); - if (offset == ResTable_type::NO_ENTRY) { + const auto offset = LoadedPackage::GetEntryOffset(type, entry_idx); + if (UNLIKELY(IsIOError(offset))) { + return base::unexpected(offset.error()); + } + if (!offset.has_value()) { continue; } best_cookie = cookie; best_package = loaded_package; - best_type = *iter; + best_type = type; best_config_copy = this_config; best_config = &best_config_copy; - best_offset = offset; + best_offset = offset.value(); - if (ignore_configuration) { + if (stop_at_first_match) { // Any configuration will suffice, so break. break; } - if (resource_resolution_logging_enabled_) { + if (UNLIKELY(logging_enabled)) { last_resolution_.steps.push_back(Resolution::Step{resolution_type, this_config.toString(), &loaded_package->GetPackageName()}); @@ -749,36 +798,35 @@ ApkAssetsCookie AssetManager2::FindEntryInternal(const PackageGroup& package_gro } if (UNLIKELY(best_cookie == kInvalidCookie)) { - return kInvalidCookie; + return base::unexpected(std::nullopt); } - const ResTable_entry* best_entry = LoadedPackage::GetEntryFromOffset(best_type, best_offset); - if (UNLIKELY(best_entry == nullptr)) { - return kInvalidCookie; + auto best_entry_result = LoadedPackage::GetEntryFromOffset(best_type, best_offset); + if (!best_entry_result.has_value()) { + return base::unexpected(best_entry_result.error()); } - const uint16_t entry_size = dtohs(best_entry->size); - if (entry_size >= sizeof(ResTable_map_entry) && - (dtohs(best_entry->flags) & ResTable_entry::FLAG_COMPLEX)) { - // The entry represents a bag/map. - out_entry->entry = reinterpret_cast(best_entry); - } else { - // The entry represents a value. - Res_value value; - value.copyFrom_dtoh(*reinterpret_cast( - reinterpret_cast(best_entry) + entry_size)); - out_entry->entry = value; - } - - out_entry->config = *best_config; - out_entry->type_flags = type_flags; - out_entry->package_name = &best_package->GetPackageName(); - out_entry->type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1); - out_entry->entry_string_ref = - StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index); - out_entry->dynamic_ref_table = package_group.dynamic_ref_table.get(); - - return best_cookie; + const incfs::map_ptr best_entry = *best_entry_result; + if (!best_entry) { + return base::unexpected(IOError::PAGES_MISSING); + } + + const auto entry = GetEntryValue(best_entry.verified()); + if (!entry.has_value()) { + return base::unexpected(entry.error()); + } + + return FindEntryResult{ + .cookie = best_cookie, + .entry = *entry, + .config = *best_config, + .type_flags = type_flags, + .package_name = &best_package->GetPackageName(), + .type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1), + .entry_string_ref = StringPoolRef(best_package->GetKeyStringPool(), + best_entry->key.index), + .dynamic_ref_table = package_group.dynamic_ref_table.get(), + }; } void AssetManager2::ResetResourceResolution() const { @@ -799,30 +847,28 @@ void AssetManager2::SetResourceResolutionLoggingEnabled(bool enabled) { std::string AssetManager2::GetLastResourceResolution() const { if (!resource_resolution_logging_enabled_) { LOG(ERROR) << "Must enable resource resolution logging before getting path."; - return std::string(); + return {}; } auto cookie = last_resolution_.cookie; if (cookie == kInvalidCookie) { LOG(ERROR) << "AssetManager hasn't resolved a resource to read resolution path."; - return std::string(); + return {}; } uint32_t resid = last_resolution_.resid; std::vector& steps = last_resolution_.steps; - - ResourceName resource_name; std::string resource_name_string; const LoadedPackage* package = apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid)); if (package != nullptr) { - ToResourceName(last_resolution_.type_string_ref, - last_resolution_.entry_string_ref, - package->GetPackageName(), - &resource_name); - resource_name_string = ToFormattedResourceString(&resource_name); + auto resource_name = ToResourceName(last_resolution_.type_string_ref, + last_resolution_.entry_string_ref, + package->GetPackageName()); + resource_name_string = resource_name.has_value() ? + ToFormattedResourceString(resource_name.value()) : ""; } std::stringstream log_stream; @@ -875,200 +921,186 @@ std::string AssetManager2::GetLastResourceResolution() const { return log_stream.str(); } -bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) const { - FindEntryResult entry; - ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, - true /* stop_at_first_match */, - true /* ignore_configuration */, &entry); - if (cookie == kInvalidCookie) { - return false; +base::expected AssetManager2::GetResourceName( + uint32_t resid) const { + auto result = FindEntry(resid, 0u /* density_override */, true /* stop_at_first_match */, + true /* ignore_configuration */); + if (!result.has_value()) { + return base::unexpected(result.error()); } - return ToResourceName(entry.type_string_ref, - entry.entry_string_ref, - *entry.package_name, - out_name); + return ToResourceName(result->type_string_ref, + result->entry_string_ref, + *result->package_name); } -bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) const { - FindEntryResult entry; - ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, - false /* stop_at_first_match */, - true /* ignore_configuration */, &entry); - if (cookie != kInvalidCookie) { - *out_flags = entry.type_flags; - return true; - } - return false; -} - -ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, - uint16_t density_override, Res_value* out_value, - ResTable_config* out_selected_config, - uint32_t* out_flags) const { - FindEntryResult entry; - ApkAssetsCookie cookie = FindEntry(resid, density_override, false /* stop_at_first_match */, - false /* ignore_configuration */, &entry); - if (cookie == kInvalidCookie) { - return kInvalidCookie; +base::expected AssetManager2::GetResource( + uint32_t resid, bool may_be_bag, uint16_t density_override) const { + auto result = FindEntry(resid, density_override, false /* stop_at_first_match */, + false /* ignore_configuration */); + if (!result.has_value()) { + return base::unexpected(result.error()); } - auto result_map_entry = std::get_if(&entry.entry); + auto result_map_entry = std::get_if>(&result->entry); if (result_map_entry != nullptr) { if (!may_be_bag) { LOG(ERROR) << base::StringPrintf("Resource %08x is a complex map type.", resid); - return kInvalidCookie; + return base::unexpected(std::nullopt); } // Create a reference since we can't represent this complex type as a Res_value. - out_value->dataType = Res_value::TYPE_REFERENCE; - out_value->data = resid; - *out_selected_config = entry.config; - *out_flags = entry.type_flags; - return cookie; + return SelectedValue(Res_value::TYPE_REFERENCE, resid, result->cookie, result->type_flags, + resid, result->config); } // Convert the package ID to the runtime assigned package ID. - *out_value = std::get(entry.entry); - entry.dynamic_ref_table->lookupResourceValue(out_value); + Res_value value = std::get(result->entry); + result->dynamic_ref_table->lookupResourceValue(&value); - *out_selected_config = entry.config; - *out_flags = entry.type_flags; - return cookie; + return SelectedValue(value.dataType, value.data, result->cookie, result->type_flags, + resid, result->config); } -ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value, - ResTable_config* in_out_selected_config, - uint32_t* in_out_flags, - uint32_t* out_last_reference) const { - constexpr const int kMaxIterations = 20; - - for (size_t iteration = 0u; in_out_value->dataType == Res_value::TYPE_REFERENCE && - in_out_value->data != 0u && iteration < kMaxIterations; - iteration++) { - *out_last_reference = in_out_value->data; - uint32_t new_flags = 0u; - cookie = GetResource(in_out_value->data, true /*may_be_bag*/, 0u /*density_override*/, - in_out_value, in_out_selected_config, &new_flags); - if (cookie == kInvalidCookie) { - return kInvalidCookie; - } - if (in_out_flags != nullptr) { - *in_out_flags |= new_flags; +base::expected AssetManager2::ResolveReference( + AssetManager2::SelectedValue& value) const { + if (value.type != Res_value::TYPE_REFERENCE || value.data == 0U) { + // Not a reference. Nothing to do. + return {}; + } + + uint32_t combined_flags = value.flags; + uint32_t resolve_resid = value.data; + constexpr const uint32_t kMaxIterations = 20; + for (uint32_t i = 0U;; i++) { + auto result = GetResource(resolve_resid, true /*may_be_bag*/); + if (!result.has_value()) { + return base::unexpected(result.error()); } - if (*out_last_reference == in_out_value->data) { + + if (result->type != Res_value::TYPE_REFERENCE || + result->data == Res_value::DATA_NULL_UNDEFINED || + result->data == resolve_resid || i == kMaxIterations) { // This reference can't be resolved, so exit now and let the caller deal with it. - return cookie; + value = *result; + value.flags |= combined_flags; + return {}; } + + combined_flags |= result->flags; + resolve_resid = result->data; } - return cookie; } -const std::vector AssetManager2::GetBagResIdStack(uint32_t resid) { +const std::vector AssetManager2::GetBagResIdStack(uint32_t resid) const { auto cached_iter = cached_bag_resid_stacks_.find(resid); if (cached_iter != cached_bag_resid_stacks_.end()) { return cached_iter->second; - } else { - auto found_resids = std::vector(); - GetBag(resid, found_resids); - // Cache style stacks if they are not already cached. - cached_bag_resid_stacks_[resid] = found_resids; - return found_resids; } + + std::vector found_resids; + GetBag(resid, found_resids); + cached_bag_resid_stacks_.emplace(resid, found_resids); + return found_resids; } -const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { - auto found_resids = std::vector(); - auto bag = GetBag(resid, found_resids); +base::expected AssetManager2::ResolveBag( + AssetManager2::SelectedValue& value) const { + if (UNLIKELY(value.type != Res_value::TYPE_REFERENCE)) { + return base::unexpected(std::nullopt); + } - // Cache style stacks if they are not already cached. - auto cached_iter = cached_bag_resid_stacks_.find(resid); - if (cached_iter == cached_bag_resid_stacks_.end()) { - cached_bag_resid_stacks_[resid] = found_resids; + auto bag = GetBag(value.data); + if (bag.has_value()) { + value.flags |= (*bag)->type_spec_flags; } return bag; } -static bool compare_bag_entries(const ResolvedBag::Entry& entry1, - const ResolvedBag::Entry& entry2) { - return entry1.key < entry2.key; +base::expected AssetManager2::GetBag(uint32_t resid) const { + std::vector found_resids; + const auto bag = GetBag(resid, found_resids); + cached_bag_resid_stacks_.emplace(resid, found_resids); + return bag; } -const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector& child_resids) { - auto cached_iter = cached_bags_.find(resid); - if (cached_iter != cached_bags_.end()) { +base::expected AssetManager2::GetBag( + uint32_t resid, std::vector& child_resids) const { + if (auto cached_iter = cached_bags_.find(resid); cached_iter != cached_bags_.end()) { return cached_iter->second.get(); } - FindEntryResult entry; - ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, - false /* stop_at_first_match */, - false /* ignore_configuration */, - &entry); - if (cookie == kInvalidCookie) { - return nullptr; + auto entry = FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */, + false /* ignore_configuration */); + if (!entry.has_value()) { + return base::unexpected(entry.error()); } - auto result_map_entry = std::get_if(&entry.entry); - if (result_map_entry == nullptr) { + auto entry_map = std::get_if>(&entry->entry); + if (entry_map == nullptr) { // Not a bag, nothing to do. - return nullptr; + return base::unexpected(std::nullopt); } - auto map = reinterpret_cast(*result_map_entry); - auto map_entry = reinterpret_cast( - reinterpret_cast(map) + map->size); - const ResTable_map* const map_entry_end = map_entry + dtohl(map->count); + auto map = *entry_map; + auto map_entry = map.offset(dtohs(map->size)).convert(); + const auto map_entry_end = map_entry + dtohl(map->count); // Keep track of ids that have already been seen to prevent infinite loops caused by circular - // dependencies between bags + // dependencies between bags. child_resids.push_back(resid); uint32_t parent_resid = dtohl(map->parent.ident); - if (parent_resid == 0U || std::find(child_resids.begin(), child_resids.end(), parent_resid) - != child_resids.end()) { - // There is no parent or a circular dependency exist, meaning there is nothing to inherit and - // we can do a simple copy of the entries in the map. + if (parent_resid == 0U || + std::find(child_resids.begin(), child_resids.end(), parent_resid) != child_resids.end()) { + // There is no parent or a circular parental dependency exist, meaning there is nothing to + // inherit and we can do a simple copy of the entries in the map. const size_t entry_count = map_entry_end - map_entry; util::unique_cptr new_bag{reinterpret_cast( malloc(sizeof(ResolvedBag) + (entry_count * sizeof(ResolvedBag::Entry))))}; bool sort_entries = false; - ResolvedBag::Entry* new_entry = new_bag->entries; - for (; map_entry != map_entry_end; ++map_entry) { + for (auto new_entry = new_bag->entries; map_entry != map_entry_end; ++map_entry) { + if (UNLIKELY(!map_entry)) { + return base::unexpected(IOError::PAGES_MISSING); + } + uint32_t new_key = dtohl(map_entry->name.ident); if (!is_internal_resid(new_key)) { // Attributes, arrays, etc don't have a resource id as the name. They specify // other data, which would be wrong to change via a lookup. - if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) { + if (UNLIKELY(entry->dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR)) { LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid); - return nullptr; + return base::unexpected(std::nullopt); } } - new_entry->cookie = cookie; + + new_entry->cookie = entry->cookie; new_entry->key = new_key; new_entry->key_pool = nullptr; new_entry->type_pool = nullptr; new_entry->style = resid; new_entry->value.copyFrom_dtoh(map_entry->value); - status_t err = entry.dynamic_ref_table->lookupResourceValue(&new_entry->value); - if (err != NO_ERROR) { + status_t err = entry->dynamic_ref_table->lookupResourceValue(&new_entry->value); + if (UNLIKELY(err != NO_ERROR)) { LOG(ERROR) << base::StringPrintf( "Failed to resolve value t=0x%02x d=0x%08x for key 0x%08x.", new_entry->value.dataType, new_entry->value.data, new_key); - return nullptr; + return base::unexpected(std::nullopt); } + sort_entries = sort_entries || (new_entry != new_bag->entries && (new_entry->key < (new_entry - 1U)->key)); ++new_entry; } if (sort_entries) { - std::sort(new_bag->entries, new_bag->entries + entry_count, compare_bag_entries); + std::sort(new_bag->entries, new_bag->entries + entry_count, + [](auto&& lhs, auto&& rhs) { return lhs.key < rhs.key; }); } - new_bag->type_spec_flags = entry.type_flags; + new_bag->type_spec_flags = entry->type_flags; new_bag->entry_count = static_cast(entry_count); ResolvedBag* result = new_bag.get(); cached_bags_[resid] = std::move(new_bag); @@ -1076,54 +1108,58 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector& } // In case the parent is a dynamic reference, resolve it. - entry.dynamic_ref_table->lookupResourceId(&parent_resid); + entry->dynamic_ref_table->lookupResourceId(&parent_resid); // Get the parent and do a merge of the keys. - const ResolvedBag* parent_bag = GetBag(parent_resid, child_resids); - if (parent_bag == nullptr) { + const auto parent_bag = GetBag(parent_resid, child_resids); + if (UNLIKELY(!parent_bag.has_value())) { // Failed to get the parent that should exist. LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid, resid); - return nullptr; + return base::unexpected(parent_bag.error()); } // Create the max possible entries we can make. Once we construct the bag, // we will realloc to fit to size. - const size_t max_count = parent_bag->entry_count + dtohl(map->count); + const size_t max_count = (*parent_bag)->entry_count + dtohl(map->count); util::unique_cptr new_bag{reinterpret_cast( malloc(sizeof(ResolvedBag) + (max_count * sizeof(ResolvedBag::Entry))))}; ResolvedBag::Entry* new_entry = new_bag->entries; - const ResolvedBag::Entry* parent_entry = parent_bag->entries; - const ResolvedBag::Entry* const parent_entry_end = parent_entry + parent_bag->entry_count; + const ResolvedBag::Entry* parent_entry = (*parent_bag)->entries; + const ResolvedBag::Entry* const parent_entry_end = parent_entry + (*parent_bag)->entry_count; // The keys are expected to be in sorted order. Merge the two bags. bool sort_entries = false; while (map_entry != map_entry_end && parent_entry != parent_entry_end) { + if (UNLIKELY(!map_entry)) { + return base::unexpected(IOError::PAGES_MISSING); + } + uint32_t child_key = dtohl(map_entry->name.ident); if (!is_internal_resid(child_key)) { - if (entry.dynamic_ref_table->lookupResourceId(&child_key) != NO_ERROR) { + if (UNLIKELY(entry->dynamic_ref_table->lookupResourceId(&child_key) != NO_ERROR)) { LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key, resid); - return nullptr; + return base::unexpected(std::nullopt); } } if (child_key <= parent_entry->key) { // Use the child key if it comes before the parent // or is equal to the parent (overrides). - new_entry->cookie = cookie; + new_entry->cookie = entry->cookie; new_entry->key = child_key; new_entry->key_pool = nullptr; new_entry->type_pool = nullptr; new_entry->value.copyFrom_dtoh(map_entry->value); new_entry->style = resid; - status_t err = entry.dynamic_ref_table->lookupResourceValue(&new_entry->value); - if (err != NO_ERROR) { + status_t err = entry->dynamic_ref_table->lookupResourceValue(&new_entry->value); + if (UNLIKELY(err != NO_ERROR)) { LOG(ERROR) << base::StringPrintf( "Failed to resolve value t=0x%02x d=0x%08x for key 0x%08x.", new_entry->value.dataType, new_entry->value.data, child_key); - return nullptr; + return base::unexpected(std::nullopt); } ++map_entry; } else { @@ -1143,25 +1179,29 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector& // Finish the child entries if they exist. while (map_entry != map_entry_end) { + if (UNLIKELY(!map_entry)) { + return base::unexpected(IOError::PAGES_MISSING); + } + uint32_t new_key = dtohl(map_entry->name.ident); if (!is_internal_resid(new_key)) { - if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) { + if (UNLIKELY(entry->dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR)) { LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid); - return nullptr; + return base::unexpected(std::nullopt); } } - new_entry->cookie = cookie; + new_entry->cookie = entry->cookie; new_entry->key = new_key; new_entry->key_pool = nullptr; new_entry->type_pool = nullptr; new_entry->value.copyFrom_dtoh(map_entry->value); new_entry->style = resid; - status_t err = entry.dynamic_ref_table->lookupResourceValue(&new_entry->value); - if (err != NO_ERROR) { + status_t err = entry->dynamic_ref_table->lookupResourceValue(&new_entry->value); + if (UNLIKELY(err != NO_ERROR)) { LOG(ERROR) << base::StringPrintf("Failed to resolve value t=0x%02x d=0x%08x for key 0x%08x.", new_entry->value.dataType, new_entry->value.data, new_key); - return nullptr; + return base::unexpected(std::nullopt); } sort_entries = sort_entries || (new_entry != new_bag->entries && (new_entry->key < (new_entry - 1U)->key)); @@ -1185,11 +1225,12 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector& } if (sort_entries) { - std::sort(new_bag->entries, new_bag->entries + actual_count, compare_bag_entries); + std::sort(new_bag->entries, new_bag->entries + actual_count, + [](auto&& lhs, auto&& rhs) { return lhs.key < rhs.key; }); } // Combine flags from the parent and our own bag. - new_bag->type_spec_flags = entry.type_flags | parent_bag->type_spec_flags; + new_bag->type_spec_flags = entry->type_flags | (*parent_bag)->type_spec_flags; new_bag->entry_count = static_cast(actual_count); ResolvedBag* result = new_bag.get(); cached_bags_[resid] = std::move(new_bag); @@ -1208,16 +1249,16 @@ static bool Utf8ToUtf16(const StringPiece& str, std::u16string* out) { return true; } -uint32_t AssetManager2::GetResourceId(const std::string& resource_name, - const std::string& fallback_type, - const std::string& fallback_package) const { +base::expected AssetManager2::GetResourceId( + const std::string& resource_name, const std::string& fallback_type, + const std::string& fallback_package) const { StringPiece package_name, type, entry; if (!ExtractResourceName(resource_name, &package_name, &type, &entry)) { - return 0u; + return base::unexpected(std::nullopt); } if (entry.empty()) { - return 0u; + return base::unexpected(std::nullopt); } if (package_name.empty()) { @@ -1230,12 +1271,12 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name, std::u16string type16; if (!Utf8ToUtf16(type, &type16)) { - return 0u; + return base::unexpected(std::nullopt); } std::u16string entry16; if (!Utf8ToUtf16(entry, &entry16)) { - return 0u; + return base::unexpected(std::nullopt); } const StringPiece16 kAttr16 = u"attr"; @@ -1249,20 +1290,24 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name, break; } - uint32_t resid = package->FindEntryByName(type16, entry16); - if (resid == 0u && kAttr16 == type16) { + base::expected resid = package->FindEntryByName(type16, entry16); + if (UNLIKELY(IsIOError(resid))) { + return base::unexpected(resid.error()); + } + + if (!resid.has_value() && kAttr16 == type16) { // Private attributes in libraries (such as the framework) are sometimes encoded // under the type '^attr-private' in order to leave the ID space of public 'attr' // free for future additions. Check '^attr-private' for the same name. resid = package->FindEntryByName(kAttrPrivate16, entry16); } - if (resid != 0u) { - return fix_package_id(resid, package_group.dynamic_ref_table->mAssignedPackageId); + if (resid.has_value()) { + return fix_package_id(*resid, package_group.dynamic_ref_table->mAssignedPackageId); } } } - return 0u; + return base::unexpected(std::nullopt); } void AssetManager2::RebuildFilterList(bool filter_incompatible_configs) { @@ -1282,8 +1327,7 @@ void AssetManager2::RebuildFilterList(bool filter_incompatible_configs) { ResTable_config this_config; this_config.copyFromDtoH((*iter)->config); if (!filter_incompatible_configs || this_config.match(configuration_)) { - group.configurations.push_back(this_config); - group.types.push_back(*iter); + group.type_configs.push_back(TypeConfig{*iter, this_config}); } } }); @@ -1354,16 +1398,16 @@ struct Theme::Package { std::array, kTypeCount> types; }; -bool Theme::ApplyStyle(uint32_t resid, bool force) { +base::expected Theme::ApplyStyle(uint32_t resid, bool force) { ATRACE_NAME("Theme::ApplyStyle"); - const ResolvedBag* bag = asset_manager_->GetBag(resid); - if (bag == nullptr) { - return false; + auto bag = asset_manager_->GetBag(resid); + if (!bag.has_value()) { + return base::unexpected(bag.error()); } // Merge the flags from this style. - type_spec_flags_ |= bag->type_spec_flags; + type_spec_flags_ |= (*bag)->type_spec_flags; int last_type_idx = -1; int last_package_idx = -1; @@ -1373,14 +1417,14 @@ bool Theme::ApplyStyle(uint32_t resid, bool force) { // Iterate backwards, because each bag is sorted in ascending key ID order, meaning we will only // need to perform one resize per type. using reverse_bag_iterator = std::reverse_iterator; - const auto bag_iter_end = reverse_bag_iterator(begin(bag)); - for (auto bag_iter = reverse_bag_iterator(end(bag)); bag_iter != bag_iter_end; ++bag_iter) { - const uint32_t attr_resid = bag_iter->key; + const auto rbegin = reverse_bag_iterator(begin(*bag)); + for (auto it = reverse_bag_iterator(end(*bag)); it != rbegin; ++it) { + const uint32_t attr_resid = it->key; // If the resource ID passed in is not a style, the key can be some other identifier that is not // a resource ID. We should fail fast instead of operating with strange resource IDs. if (!is_valid_resid(attr_resid)) { - return false; + return base::unexpected(std::nullopt); } // We don't use the 0-based index for the type so that we can avoid doing ID validation @@ -1428,20 +1472,18 @@ bool Theme::ApplyStyle(uint32_t resid, bool force) { ThemeEntry& entry = last_type->entries[entry_idx]; if (force || (entry.value.dataType == Res_value::TYPE_NULL && entry.value.data != Res_value::DATA_NULL_EMPTY)) { - entry.cookie = bag_iter->cookie; - entry.type_spec_flags |= bag->type_spec_flags; - entry.value = bag_iter->value; + entry.cookie = it->cookie; + entry.type_spec_flags |= (*bag)->type_spec_flags; + entry.value = it->value; } } - return true; + return {}; } -ApkAssetsCookie Theme::GetAttribute(uint32_t resid, Res_value* out_value, - uint32_t* out_flags) const { - int cnt = 20; +std::optional Theme::GetAttribute(uint32_t resid) const { + int cnt = 20; uint32_t type_spec_flags = 0u; - do { const int package_idx = get_package_id(resid); const Package* package = packages_[package_idx].get(); @@ -1461,43 +1503,42 @@ ApkAssetsCookie Theme::GetAttribute(uint32_t resid, Res_value* out_value, resid = entry.value.data; continue; } - return kInvalidCookie; + return std::nullopt; } // @null is different than @empty. if (entry.value.dataType == Res_value::TYPE_NULL && entry.value.data != Res_value::DATA_NULL_EMPTY) { - return kInvalidCookie; + return std::nullopt; } - *out_value = entry.value; - *out_flags = type_spec_flags; - return entry.cookie; + return AssetManager2::SelectedValue(entry.value.dataType, entry.value.data, entry.cookie, + type_spec_flags, 0U /* resid */, {} /* config */); } } } break; } while (true); - return kInvalidCookie; + return std::nullopt; } -ApkAssetsCookie Theme::ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value, - ResTable_config* in_out_selected_config, - uint32_t* in_out_type_spec_flags, - uint32_t* out_last_ref) const { - if (in_out_value->dataType == Res_value::TYPE_ATTRIBUTE) { - uint32_t new_flags; - cookie = GetAttribute(in_out_value->data, in_out_value, &new_flags); - if (cookie == kInvalidCookie) { - return kInvalidCookie; - } +base::expected Theme::ResolveAttributeReference( + AssetManager2::SelectedValue& value) const { + if (value.type != Res_value::TYPE_ATTRIBUTE) { + return asset_manager_->ResolveReference(value); + } - if (in_out_type_spec_flags != nullptr) { - *in_out_type_spec_flags |= new_flags; - } + std::optional result = GetAttribute(value.data); + if (!result.has_value()) { + return base::unexpected(std::nullopt); } - return asset_manager_->ResolveReference(cookie, in_out_value, in_out_selected_config, - in_out_type_spec_flags, out_last_ref); + + auto resolve_result = asset_manager_->ResolveReference(*result); + if (resolve_result.has_value()) { + result->flags |= value.flags; + value = *result; + } + return resolve_result; } void Theme::Clear() { @@ -1507,9 +1548,9 @@ void Theme::Clear() { } } -void Theme::SetTo(const Theme& o) { +base::expected Theme::SetTo(const Theme& o) { if (this == &o) { - return; + return {}; } type_spec_flags_ = o.type_spec_flags_; @@ -1560,10 +1601,8 @@ void Theme::SetTo(const Theme& o) { // Map the runtime package of the source apk asset to the destination apk asset. if (src_asset->GetPath() == dest_asset->GetPath()) { - const std::vector>& src_packages = - src_asset->GetLoadedArsc()->GetPackages(); - const std::vector>& dest_packages = - dest_asset->GetLoadedArsc()->GetPackages(); + const auto& src_packages = src_asset->GetLoadedArsc()->GetPackages(); + const auto& dest_packages = dest_asset->GetLoadedArsc()->GetPackages(); SourceToDestinationRuntimePackageMap package_map; @@ -1660,15 +1699,20 @@ void Theme::SetTo(const Theme& o) { int attribute_dest_package_id = p; if (attribute_dest_package_id != 0x01) { // Find the cookie of the attribute resource id in the source AssetManager - FindEntryResult attribute_entry_result; - ApkAssetsCookie attribute_cookie = + base::expected attribute_entry_result = o.asset_manager_->FindEntry(make_resid(p, t, e), 0 /* density_override */ , true /* stop_at_first_match */, - true /* ignore_configuration */, - &attribute_entry_result); + true /* ignore_configuration */); + if (UNLIKELY(IsIOError(attribute_entry_result))) { + return base::unexpected(GetIOError(attribute_entry_result.error())); + } + if (!attribute_entry_result.has_value()) { + continue; + } // Determine the package id of the attribute in the destination AssetManager. - auto attribute_package_map = src_asset_cookie_id_map.find(attribute_cookie); + auto attribute_package_map = src_asset_cookie_id_map.find( + attribute_entry_result->cookie); if (attribute_package_map == src_asset_cookie_id_map.end()) { continue; } @@ -1712,6 +1756,7 @@ void Theme::SetTo(const Theme& o) { } } } + return {}; } void Theme::Dump() const { diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp index e62fb614e195..71919fdbb736 100644 --- a/libs/androidfw/AttributeResolution.cpp +++ b/libs/androidfw/AttributeResolution.cpp @@ -24,9 +24,12 @@ #include "androidfw/AttributeFinder.h" constexpr bool kDebugStyles = false; +#define DEBUG_LOG(...) do { if (kDebugStyles) { ALOGI(__VA_ARGS__); } } while(0) namespace android { +namespace { + // Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0. static uint32_t ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) { return cookie != kInvalidCookie ? static_cast(cookie + 1) : static_cast(-1); @@ -61,136 +64,149 @@ class BagAttributeFinder } }; -bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, - uint32_t* src_values, size_t src_values_length, uint32_t* attrs, - size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { - if (kDebugStyles) { - ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x", theme, - def_style_attr, def_style_res); +base::expected GetStyleBag(Theme* theme, + uint32_t theme_attribute_resid, + uint32_t fallback_resid, + uint32_t* out_theme_flags) { + // Load the style from the attribute if specified. + if (theme_attribute_resid != 0U) { + std::optional value = theme->GetAttribute(theme_attribute_resid); + if (value.has_value()) { + *out_theme_flags |= value->flags; + auto result = theme->GetAssetManager()->ResolveBag(*value); + if (result.has_value() || IsIOError(result)) { + return result; + } + } } - AssetManager2* assetmanager = theme->GetAssetManager(); - ResTable_config config; - Res_value value; + // Fallback to loading the style from the resource id if specified. + if (fallback_resid != 0U) { + return theme->GetAssetManager()->GetBag(fallback_resid); + } - int indices_idx = 0; + return base::unexpected(std::nullopt); +} - // Load default style from attribute, if specified... - uint32_t def_style_flags = 0u; - if (def_style_attr != 0) { - Res_value value; - if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) { - if (value.dataType == Res_value::TYPE_REFERENCE) { - def_style_res = value.data; - } - } +base::expected GetXmlStyleBag(Theme* theme, + ResXMLParser* xml_parser, + uint32_t* out_theme_flags) { + if (xml_parser == nullptr) { + return base::unexpected(std::nullopt); } - // Retrieve the default style bag, if requested. - const ResolvedBag* default_style_bag = nullptr; - if (def_style_res != 0) { - default_style_bag = assetmanager->GetBag(def_style_res); - if (default_style_bag != nullptr) { - def_style_flags |= default_style_bag->type_spec_flags; + // Retrieve the style resource ID associated with the current XML tag's style attribute. + Res_value value; + const ssize_t idx = xml_parser->indexOfStyle(); + if (idx < 0 || xml_parser->getAttributeValue(idx, &value) < 0) { + return base::unexpected(std::nullopt); + } + + if (value.dataType == Res_value::TYPE_ATTRIBUTE) { + // Resolve the attribute with out theme. + if (std::optional result = theme->GetAttribute(value.data)) { + *out_theme_flags |= result->flags; + return theme->GetAssetManager()->ResolveBag(*result); } } - BagAttributeFinder def_style_attr_finder(default_style_bag); + if (value.dataType == Res_value::TYPE_REFERENCE) { + return theme->GetAssetManager()->GetBag(value.data); + } + + return base::unexpected(std::nullopt); +} + +} // namespace + +base::expected ResolveAttrs(Theme* theme, uint32_t def_style_attr, + uint32_t def_style_res, uint32_t* src_values, + size_t src_values_length, uint32_t* attrs, + size_t attrs_length, uint32_t* out_values, + uint32_t* out_indices) { + DEBUG_LOG("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x", theme, def_style_attr, + def_style_res); + + int indices_idx = 0; + const AssetManager2* assetmanager = theme->GetAssetManager(); + + // Load default style from attribute or resource id, if specified... + uint32_t def_style_theme_flags = 0U; + const auto default_style_bag = GetStyleBag(theme, def_style_attr, def_style_res, + &def_style_theme_flags); + if (UNLIKELY(IsIOError(default_style_bag))) { + return base::unexpected(GetIOError(default_style_bag.error())); + } + + BagAttributeFinder def_style_attr_finder(default_style_bag.value_or(nullptr)); // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. for (size_t ii = 0; ii < attrs_length; ii++) { const uint32_t cur_ident = attrs[ii]; - - if (kDebugStyles) { - ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident); - } - - ApkAssetsCookie cookie = kInvalidCookie; - uint32_t type_set_flags = 0; - - value.dataType = Res_value::TYPE_NULL; - value.data = Res_value::DATA_NULL_UNDEFINED; - config.density = 0; + DEBUG_LOG("RETRIEVING ATTR 0x%08x...", cur_ident); // Try to find a value for this attribute... we prioritize values // coming from, first XML attributes, then XML style, then default // style, and finally the theme. // Retrieve the current input value if available. + AssetManager2::SelectedValue value{}; if (src_values_length > 0 && src_values[ii] != 0) { - value.dataType = Res_value::TYPE_ATTRIBUTE; + value.type = Res_value::TYPE_ATTRIBUTE; value.data = src_values[ii]; - if (kDebugStyles) { - ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, value.data); - } + DEBUG_LOG("-> From values: type=0x%x, data=0x%08x", value.type, value.data); } else { const ResolvedBag::Entry* const entry = def_style_attr_finder.Find(cur_ident); if (entry != def_style_attr_finder.end()) { - cookie = entry->cookie; - type_set_flags = def_style_flags; - value = entry->value; - if (kDebugStyles) { - ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data); - } + value = AssetManager2::SelectedValue(*default_style_bag, *entry); + value.flags |= def_style_theme_flags; + DEBUG_LOG("-> From def style: type=0x%x, data=0x%08x", value.type, value.data); } } - uint32_t resid = 0; - if (value.dataType != Res_value::TYPE_NULL) { + if (value.type != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - ApkAssetsCookie new_cookie = - theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid); - if (new_cookie != kInvalidCookie) { - cookie = new_cookie; - } - if (kDebugStyles) { - ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data); + const auto result = theme->ResolveAttributeReference(value); + if (UNLIKELY(IsIOError(result))) { + return base::unexpected(GetIOError(result.error())); } + DEBUG_LOG("-> Resolved attr: type=0x%x, data=0x%08x", value.type, value.data); } else if (value.data != Res_value::DATA_NULL_EMPTY) { // If we still don't have a value for this attribute, try to find it in the theme! - ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags); - if (new_cookie != kInvalidCookie) { - if (kDebugStyles) { - ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data); - } - new_cookie = - assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid); - if (new_cookie != kInvalidCookie) { - cookie = new_cookie; - } - if (kDebugStyles) { - ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data); + if (auto attr_value = theme->GetAttribute(cur_ident)) { + value = *attr_value; + DEBUG_LOG("-> From theme: type=0x%x, data=0x%08x", value.type, value.data); + + const auto result = assetmanager->ResolveReference(value); + if (UNLIKELY(IsIOError(result))) { + return base::unexpected(GetIOError(result.error())); } + DEBUG_LOG("-> Resolved theme: type=0x%x, data=0x%08x", value.type, value.data); } } // Deal with the special @null value -- it turns back to TYPE_NULL. - if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { - if (kDebugStyles) { - ALOGI("-> Setting to @null!"); - } - value.dataType = Res_value::TYPE_NULL; + if (value.type == Res_value::TYPE_REFERENCE && value.data == 0) { + DEBUG_LOG("-> Setting to @null!"); + value.type = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - cookie = kInvalidCookie; + value.cookie = kInvalidCookie; } - if (kDebugStyles) { - ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.dataType, value.data); - } + DEBUG_LOG("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.type, value.data); // Write the final value back to Java. - out_values[STYLE_TYPE] = value.dataType; + out_values[STYLE_TYPE] = value.type; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); - out_values[STYLE_RESOURCE_ID] = resid; - out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; - out_values[STYLE_DENSITY] = config.density; + out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(value.cookie); + out_values[STYLE_RESOURCE_ID] = value.resid; + out_values[STYLE_CHANGING_CONFIGURATIONS] = value.flags; + out_values[STYLE_DENSITY] = value.config.density; if (out_indices != nullptr && - (value.dataType != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY)) { - indices_idx++; - out_indices[indices_idx] = ii; + (value.type != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY)) { + out_indices[++indices_idx] = ii; } out_values += STYLE_NUM_ENTRIES; @@ -199,93 +215,46 @@ bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, if (out_indices != nullptr) { out_indices[0] = indices_idx; } - return true; + return {}; } -void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, - uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length, - uint32_t* out_values, uint32_t* out_indices) { - if (kDebugStyles) { - ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", theme, - def_style_attr, def_style_resid, xml_parser); - } - - AssetManager2* assetmanager = theme->GetAssetManager(); - ResTable_config config; - Res_value value; +base::expected ApplyStyle(Theme* theme, ResXMLParser* xml_parser, + uint32_t def_style_attr, + uint32_t def_style_resid, + const uint32_t* attrs, size_t attrs_length, + uint32_t* out_values, uint32_t* out_indices) { + DEBUG_LOG("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", theme, + def_style_attr, def_style_resid, xml_parser); int indices_idx = 0; + const AssetManager2* assetmanager = theme->GetAssetManager(); // Load default style from attribute, if specified... - uint32_t def_style_flags = 0u; - if (def_style_attr != 0) { - Res_value value; - if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) { - if (value.dataType == Res_value::TYPE_REFERENCE) { - def_style_resid = value.data; - } - } + uint32_t def_style_theme_flags = 0U; + const auto default_style_bag = GetStyleBag(theme, def_style_attr, def_style_resid, + &def_style_theme_flags); + if (IsIOError(default_style_bag)) { + return base::unexpected(GetIOError(default_style_bag.error())); } // Retrieve the style resource ID associated with the current XML tag's style attribute. - uint32_t style_resid = 0u; - uint32_t style_flags = 0u; - if (xml_parser != nullptr) { - ssize_t idx = xml_parser->indexOfStyle(); - if (idx >= 0 && xml_parser->getAttributeValue(idx, &value) >= 0) { - if (value.dataType == value.TYPE_ATTRIBUTE) { - // Resolve the attribute with out theme. - if (theme->GetAttribute(value.data, &value, &style_flags) == kInvalidCookie) { - value.dataType = Res_value::TYPE_NULL; - } - } - - if (value.dataType == value.TYPE_REFERENCE) { - style_resid = value.data; - } - } - } - - // Retrieve the default style bag, if requested. - const ResolvedBag* default_style_bag = nullptr; - if (def_style_resid != 0) { - default_style_bag = assetmanager->GetBag(def_style_resid); - if (default_style_bag != nullptr) { - def_style_flags |= default_style_bag->type_spec_flags; - } + uint32_t xml_style_theme_flags = 0U; + const auto xml_style_bag = GetXmlStyleBag(theme, xml_parser, &def_style_theme_flags); + if (IsIOError(xml_style_bag)) { + return base::unexpected(GetIOError(xml_style_bag.error())); } - BagAttributeFinder def_style_attr_finder(default_style_bag); - - // Retrieve the style class bag, if requested. - const ResolvedBag* xml_style_bag = nullptr; - if (style_resid != 0) { - xml_style_bag = assetmanager->GetBag(style_resid); - if (xml_style_bag != nullptr) { - style_flags |= xml_style_bag->type_spec_flags; - } - } - - BagAttributeFinder xml_style_attr_finder(xml_style_bag); - - // Retrieve the XML attributes, if requested. + BagAttributeFinder def_style_attr_finder(default_style_bag.value_or(nullptr)); + BagAttributeFinder xml_style_attr_finder(xml_style_bag.value_or(nullptr)); XmlAttributeFinder xml_attr_finder(xml_parser); // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. for (size_t ii = 0; ii < attrs_length; ii++) { const uint32_t cur_ident = attrs[ii]; + DEBUG_LOG("RETRIEVING ATTR 0x%08x...", cur_ident); - if (kDebugStyles) { - ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident); - } - - ApkAssetsCookie cookie = kInvalidCookie; - uint32_t type_set_flags = 0u; - - value.dataType = Res_value::TYPE_NULL; - value.data = Res_value::DATA_NULL_UNDEFINED; - config.density = 0; + AssetManager2::SelectedValue value{}; uint32_t value_source_resid = 0; // Try to find a value for this attribute... we prioritize values @@ -296,178 +265,152 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, const size_t xml_attr_idx = xml_attr_finder.Find(cur_ident); if (xml_attr_idx != xml_attr_finder.end()) { // We found the attribute we were looking for. - xml_parser->getAttributeValue(xml_attr_idx, &value); - if (kDebugStyles) { - ALOGI("-> From XML: type=0x%x, data=0x%08x", value.dataType, value.data); - } + Res_value attribute_value; + xml_parser->getAttributeValue(xml_attr_idx, &attribute_value); + value.type = attribute_value.dataType; + value.data = attribute_value.data; value_source_resid = xml_parser->getSourceResourceId(); + DEBUG_LOG("-> From XML: type=0x%x, data=0x%08x", value.type, value.data); } - if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { + if (value.type == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { // Walk through the style class values looking for the requested attribute. const ResolvedBag::Entry* entry = xml_style_attr_finder.Find(cur_ident); if (entry != xml_style_attr_finder.end()) { - // We found the attribute we were looking for. - cookie = entry->cookie; - type_set_flags = style_flags; - value = entry->value; + value = AssetManager2::SelectedValue(*xml_style_bag, *entry); + value.flags |= xml_style_theme_flags; value_source_resid = entry->style; - if (kDebugStyles) { - ALOGI("-> From style: type=0x%x, data=0x%08x, style=0x%08x", value.dataType, value.data, - entry->style); - } + DEBUG_LOG("-> From style: type=0x%x, data=0x%08x, style=0x%08x", value.type, value.data, + value_source_resid); } } - if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { + if (value.type == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { // Walk through the default style values looking for the requested attribute. const ResolvedBag::Entry* entry = def_style_attr_finder.Find(cur_ident); if (entry != def_style_attr_finder.end()) { - // We found the attribute we were looking for. - cookie = entry->cookie; - type_set_flags = def_style_flags; - value = entry->value; - if (kDebugStyles) { - ALOGI("-> From def style: type=0x%x, data=0x%08x, style=0x%08x", value.dataType, value.data, - entry->style); - } + value = AssetManager2::SelectedValue(*default_style_bag, *entry); + value.flags |= def_style_theme_flags; value_source_resid = entry->style; + DEBUG_LOG("-> From def style: type=0x%x, data=0x%08x, style=0x%08x", value.type, value.data, + entry->style); } } - uint32_t resid = 0u; - if (value.dataType != Res_value::TYPE_NULL) { + if (value.type != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - ApkAssetsCookie new_cookie = - theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid); - if (new_cookie != kInvalidCookie) { - cookie = new_cookie; - } - - if (kDebugStyles) { - ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data); + auto result = theme->ResolveAttributeReference(value); + if (UNLIKELY(IsIOError(result))) { + return base::unexpected(GetIOError(result.error())); } + DEBUG_LOG("-> Resolved attr: type=0x%x, data=0x%08x", value.type, value.data); } else if (value.data != Res_value::DATA_NULL_EMPTY) { // If we still don't have a value for this attribute, try to find it in the theme! - ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags); - // TODO: set value_source_resid for the style in the theme that was used. - if (new_cookie != kInvalidCookie) { - if (kDebugStyles) { - ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data); - } - new_cookie = - assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid); - if (new_cookie != kInvalidCookie) { - cookie = new_cookie; - } + if (auto attr_value = theme->GetAttribute(cur_ident)) { + value = *attr_value; + DEBUG_LOG("-> From theme: type=0x%x, data=0x%08x", value.type, value.data); - if (kDebugStyles) { - ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data); + auto result = assetmanager->ResolveReference(value); + if (UNLIKELY(IsIOError(result))) { + return base::unexpected(GetIOError(result.error())); } + DEBUG_LOG("-> Resolved theme: type=0x%x, data=0x%08x", value.type, value.data); + // TODO: set value_source_resid for the style in the theme that was used. } } // Deal with the special @null value -- it turns back to TYPE_NULL. - if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { - if (kDebugStyles) { - ALOGI("-> Setting to @null!"); - } - value.dataType = Res_value::TYPE_NULL; + if (value.type == Res_value::TYPE_REFERENCE && value.data == 0U) { + DEBUG_LOG("-> Setting to @null!"); + value.type = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - cookie = kInvalidCookie; + value.cookie = kInvalidCookie; } - if (kDebugStyles) { - ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.dataType, value.data); - } + DEBUG_LOG("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.type, value.data); // Write the final value back to Java. - out_values[STYLE_TYPE] = value.dataType; + out_values[STYLE_TYPE] = value.type; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); - out_values[STYLE_RESOURCE_ID] = resid; - out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; - out_values[STYLE_DENSITY] = config.density; + out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(value.cookie); + out_values[STYLE_RESOURCE_ID] = value.resid; + out_values[STYLE_CHANGING_CONFIGURATIONS] = value.flags; + out_values[STYLE_DENSITY] = value.config.density; out_values[STYLE_SOURCE_RESOURCE_ID] = value_source_resid; - if (value.dataType != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY) { - indices_idx++; - + if (value.type != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY) { // out_indices must NOT be nullptr. - out_indices[indices_idx] = ii; + out_indices[++indices_idx] = ii; } out_values += STYLE_NUM_ENTRIES; } // out_indices must NOT be nullptr. out_indices[0] = indices_idx; + return {}; } -bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs, - size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { - ResTable_config config; - Res_value value; - +base::expected RetrieveAttributes(AssetManager2* assetmanager, + ResXMLParser* xml_parser, + uint32_t* attrs, + size_t attrs_length, + uint32_t* out_values, + uint32_t* out_indices) { int indices_idx = 0; // Retrieve the XML attributes, if requested. - const size_t xml_attr_count = xml_parser->getAttributeCount(); size_t ix = 0; + const size_t xml_attr_count = xml_parser->getAttributeCount(); uint32_t cur_xml_attr = xml_parser->getAttributeNameResID(ix); // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. for (size_t ii = 0; ii < attrs_length; ii++) { const uint32_t cur_ident = attrs[ii]; - ApkAssetsCookie cookie = kInvalidCookie; - uint32_t type_set_flags = 0u; - - value.dataType = Res_value::TYPE_NULL; - value.data = Res_value::DATA_NULL_UNDEFINED; - config.density = 0; + AssetManager2::SelectedValue value{}; // Try to find a value for this attribute... // Skip through XML attributes until the end or the next possible match. while (ix < xml_attr_count && cur_ident > cur_xml_attr) { - ix++; - cur_xml_attr = xml_parser->getAttributeNameResID(ix); + cur_xml_attr = xml_parser->getAttributeNameResID(++ix); } + // Retrieve the current XML attribute if it matches, and step to next. if (ix < xml_attr_count && cur_ident == cur_xml_attr) { - xml_parser->getAttributeValue(ix, &value); - ix++; - cur_xml_attr = xml_parser->getAttributeNameResID(ix); + Res_value attribute_value; + xml_parser->getAttributeValue(ix, &attribute_value); + value.type = attribute_value.dataType; + value.data = attribute_value.data; + cur_xml_attr = xml_parser->getAttributeNameResID(++ix); } - uint32_t resid = 0u; - if (value.dataType != Res_value::TYPE_NULL) { + if (value.type != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - ApkAssetsCookie new_cookie = - assetmanager->ResolveReference(cookie, &value, &config, &type_set_flags, &resid); - if (new_cookie != kInvalidCookie) { - cookie = new_cookie; + auto result = assetmanager->ResolveReference(value); + if (UNLIKELY(IsIOError(result))) { + return base::unexpected(GetIOError(result.error())); } } // Deal with the special @null value -- it turns back to TYPE_NULL. - if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { - value.dataType = Res_value::TYPE_NULL; + if (value.type == Res_value::TYPE_REFERENCE && value.data == 0U) { + value.type = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - cookie = kInvalidCookie; + value.cookie = kInvalidCookie; } // Write the final value back to Java. - out_values[STYLE_TYPE] = value.dataType; + out_values[STYLE_TYPE] = value.type; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); - out_values[STYLE_RESOURCE_ID] = resid; - out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; - out_values[STYLE_DENSITY] = config.density; + out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(value.cookie); + out_values[STYLE_RESOURCE_ID] = value.resid; + out_values[STYLE_CHANGING_CONFIGURATIONS] = value.flags; + out_values[STYLE_DENSITY] = value.config.density; if (out_indices != nullptr && - (value.dataType != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY)) { - indices_idx++; - out_indices[indices_idx] = ii; + (value.type != Res_value::TYPE_NULL || + value.data == Res_value::DATA_NULL_EMPTY)) { + out_indices[++indices_idx] = ii; } out_values += STYLE_NUM_ENTRIES; @@ -476,7 +419,7 @@ bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, u if (out_indices != nullptr) { out_indices[0] = indices_idx; } - return true; + return {}; } } // namespace android diff --git a/libs/androidfw/ChunkIterator.cpp b/libs/androidfw/ChunkIterator.cpp index 8fc321968055..25c8aa64e492 100644 --- a/libs/androidfw/ChunkIterator.cpp +++ b/libs/androidfw/ChunkIterator.cpp @@ -15,6 +15,7 @@ */ #include "androidfw/Chunk.h" +#include "androidfw/Util.h" #include "android-base/logging.h" @@ -23,11 +24,11 @@ namespace android { Chunk ChunkIterator::Next() { CHECK(len_ != 0) << "called Next() after last chunk"; - const ResChunk_header* this_chunk = next_chunk_; + const incfs::map_ptr this_chunk = next_chunk_; + CHECK((bool) this_chunk) << "Next() called without verifying next chunk"; // We've already checked the values of this_chunk, so safely increment. - next_chunk_ = reinterpret_cast( - reinterpret_cast(this_chunk) + dtohl(this_chunk->size)); + next_chunk_ = this_chunk.offset(dtohl(this_chunk->size)).convert(); len_ -= dtohl(this_chunk->size); if (len_ != 0) { @@ -36,7 +37,7 @@ Chunk ChunkIterator::Next() { VerifyNextChunk(); } } - return Chunk(this_chunk); + return Chunk(this_chunk.verified()); } // TODO(b/111401637) remove this and have full resource file verification @@ -47,6 +48,13 @@ bool ChunkIterator::VerifyNextChunkNonFatal() { last_error_was_fatal_ = false; return false; } + + if (!next_chunk_) { + last_error_ = "failed to read chunk from data"; + last_error_was_fatal_ = false; + return false; + } + const size_t size = dtohl(next_chunk_->size); if (size > len_) { last_error_ = "chunk size is bigger than given data"; @@ -58,12 +66,10 @@ bool ChunkIterator::VerifyNextChunkNonFatal() { // Returns false if there was an error. bool ChunkIterator::VerifyNextChunk() { - const uintptr_t header_start = reinterpret_cast(next_chunk_); - // This data must be 4-byte aligned, since we directly // access 32-bit words, which must be aligned on // certain architectures. - if (header_start & 0x03) { + if (!util::IsFourByteAligned(next_chunk_)) { last_error_ = "header not aligned on 4-byte boundary"; return false; } @@ -73,6 +79,11 @@ bool ChunkIterator::VerifyNextChunk() { return false; } + if (!next_chunk_) { + last_error_ = "failed to read chunk from data"; + return false; + } + const size_t header_size = dtohs(next_chunk_->headerSize); const size_t size = dtohl(next_chunk_->size); if (header_size < sizeof(ResChunk_header)) { @@ -90,7 +101,7 @@ bool ChunkIterator::VerifyNextChunk() { return false; } - if ((size | header_size) & 0x03) { + if ((size | header_size) & 0x03U) { last_error_ = "header sizes are not aligned on 4-byte boundary"; return false; } diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp index 4e03ce5d9584..a61309514143 100644 --- a/libs/androidfw/Idmap.cpp +++ b/libs/androidfw/Idmap.cpp @@ -52,22 +52,22 @@ OverlayStringPool::~OverlayStringPool() { uninit(); } -const char16_t* OverlayStringPool::stringAt(size_t idx, size_t* outLen) const { +base::expected OverlayStringPool::stringAt(size_t idx) const { const size_t offset = dtohl(data_header_->string_pool_index_offset); if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) { - return idmap_string_pool_->stringAt(idx - offset, outLen); + return idmap_string_pool_->stringAt(idx - offset); } - return ResStringPool::stringAt(idx, outLen); + return ResStringPool::stringAt(idx); } -const char* OverlayStringPool::string8At(size_t idx, size_t* outLen) const { +base::expected OverlayStringPool::string8At(size_t idx) const { const size_t offset = dtohl(data_header_->string_pool_index_offset); if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) { - return idmap_string_pool_->string8At(idx - offset, outLen); + return idmap_string_pool_->string8At(idx - offset); } - return ResStringPool::string8At(idx, outLen); + return ResStringPool::string8At(idx); } size_t OverlayStringPool::size() const { diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index 70bb441f94cb..2fc3b05011c2 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -38,7 +38,7 @@ #include "androidfw/ResourceUtils.h" #include "androidfw/Util.h" -using ::android::base::StringPrintf; +using android::base::StringPrintf; namespace android { @@ -51,17 +51,17 @@ namespace { // the Type structs. class TypeSpecPtrBuilder { public: - explicit TypeSpecPtrBuilder(const ResTable_typeSpec* header) + explicit TypeSpecPtrBuilder(incfs::verified_map_ptr header) : header_(header) { } - void AddType(const ResTable_type* type) { + void AddType(incfs::verified_map_ptr type) { types_.push_back(type); } TypeSpecPtr Build() { // Check for overflow. - using ElementType = const ResTable_type*; + using ElementType = incfs::verified_map_ptr; if ((std::numeric_limits::max() - sizeof(TypeSpec)) / sizeof(ElementType) < types_.size()) { return {}; @@ -77,8 +77,8 @@ class TypeSpecPtrBuilder { private: DISALLOW_COPY_AND_ASSIGN(TypeSpecPtrBuilder); - const ResTable_typeSpec* header_; - std::vector types_; + incfs::verified_map_ptr header_; + std::vector> types_; }; } // namespace @@ -88,7 +88,7 @@ LoadedPackage::~LoadedPackage() = default; // Precondition: The header passed in has already been verified, so reading any fields and trusting // the ResChunk_header is safe. -static bool VerifyResTableType(const ResTable_type* header) { +static bool VerifyResTableType(incfs::map_ptr header) { if (header->id == 0) { LOG(ERROR) << "RES_TABLE_TYPE_TYPE has invalid ID 0."; return false; @@ -115,89 +115,99 @@ static bool VerifyResTableType(const ResTable_type* header) { return false; } - if (entries_offset & 0x03) { + if (entries_offset & 0x03U) { LOG(ERROR) << "RES_TABLE_TYPE_TYPE entries start at unaligned address."; return false; } return true; } -static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset) { +static base::expected VerifyResTableEntry( + incfs::verified_map_ptr type, uint32_t entry_offset) { // Check that the offset is aligned. - if (entry_offset & 0x03) { + if (UNLIKELY(entry_offset & 0x03U)) { LOG(ERROR) << "Entry at offset " << entry_offset << " is not 4-byte aligned."; - return false; + return base::unexpected(std::nullopt); } // Check that the offset doesn't overflow. - if (entry_offset > std::numeric_limits::max() - dtohl(type->entriesStart)) { + if (UNLIKELY(entry_offset > std::numeric_limits::max() - dtohl(type->entriesStart))) { // Overflow in offset. LOG(ERROR) << "Entry at offset " << entry_offset << " is too large."; - return false; + return base::unexpected(std::nullopt); } const size_t chunk_size = dtohl(type->header.size); entry_offset += dtohl(type->entriesStart); - if (entry_offset > chunk_size - sizeof(ResTable_entry)) { + if (UNLIKELY(entry_offset > chunk_size - sizeof(ResTable_entry))) { LOG(ERROR) << "Entry at offset " << entry_offset << " is too large. No room for ResTable_entry."; - return false; + return base::unexpected(std::nullopt); } - const ResTable_entry* entry = reinterpret_cast( - reinterpret_cast(type) + entry_offset); + auto entry = type.offset(entry_offset).convert(); + if (UNLIKELY(!entry)) { + return base::unexpected(IOError::PAGES_MISSING); + } const size_t entry_size = dtohs(entry->size); - if (entry_size < sizeof(*entry)) { + if (UNLIKELY(entry_size < sizeof(entry.value()))) { LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset << " is too small."; - return false; + return base::unexpected(std::nullopt); } - if (entry_size > chunk_size || entry_offset > chunk_size - entry_size) { + if (UNLIKELY(entry_size > chunk_size || entry_offset > chunk_size - entry_size)) { LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset << " is too large."; - return false; + return base::unexpected(std::nullopt); } if (entry_size < sizeof(ResTable_map_entry)) { // There needs to be room for one Res_value struct. - if (entry_offset + entry_size > chunk_size - sizeof(Res_value)) { + if (UNLIKELY(entry_offset + entry_size > chunk_size - sizeof(Res_value))) { LOG(ERROR) << "No room for Res_value after ResTable_entry at offset " << entry_offset << " for type " << (int)type->id << "."; - return false; + return base::unexpected(std::nullopt); + } + + auto value = entry.offset(entry_size).convert(); + if (UNLIKELY(!value)) { + return base::unexpected(IOError::PAGES_MISSING); } - const Res_value* value = - reinterpret_cast(reinterpret_cast(entry) + entry_size); const size_t value_size = dtohs(value->size); - if (value_size < sizeof(Res_value)) { + if (UNLIKELY(value_size < sizeof(Res_value))) { LOG(ERROR) << "Res_value at offset " << entry_offset << " is too small."; - return false; + return base::unexpected(std::nullopt); } - if (value_size > chunk_size || entry_offset + entry_size > chunk_size - value_size) { + if (UNLIKELY(value_size > chunk_size || entry_offset + entry_size > chunk_size - value_size)) { LOG(ERROR) << "Res_value size " << value_size << " at offset " << entry_offset << " is too large."; - return false; + return base::unexpected(std::nullopt); } } else { - const ResTable_map_entry* map = reinterpret_cast(entry); + auto map = entry.convert(); + if (UNLIKELY(!map)) { + return base::unexpected(IOError::PAGES_MISSING); + } + const size_t map_entry_count = dtohl(map->count); size_t map_entries_start = entry_offset + entry_size; - if (map_entries_start & 0x03) { + if (UNLIKELY(map_entries_start & 0x03U)) { LOG(ERROR) << "Map entries at offset " << entry_offset << " start at unaligned offset."; - return false; + return base::unexpected(std::nullopt); } // Each entry is sizeof(ResTable_map) big. - if (map_entry_count > ((chunk_size - map_entries_start) / sizeof(ResTable_map))) { + if (UNLIKELY(map_entry_count > ((chunk_size - map_entries_start) / sizeof(ResTable_map)))) { LOG(ERROR) << "Too many map entries in ResTable_map_entry at offset " << entry_offset << "."; - return false; + return base::unexpected(std::nullopt); } } - return true; + return {}; } LoadedPackage::iterator::iterator(const LoadedPackage* lp, size_t ti, size_t ei) @@ -233,99 +243,125 @@ uint32_t LoadedPackage::iterator::operator*() const { entryIndex_); } -const ResTable_entry* LoadedPackage::GetEntry(const ResTable_type* type_chunk, - uint16_t entry_index) { - uint32_t entry_offset = GetEntryOffset(type_chunk, entry_index); - if (entry_offset == ResTable_type::NO_ENTRY) { - return nullptr; +base::expected, NullOrIOError> LoadedPackage::GetEntry( + incfs::verified_map_ptr type_chunk, uint16_t entry_index) { + base::expected entry_offset = GetEntryOffset(type_chunk, entry_index); + if (UNLIKELY(!entry_offset.has_value())) { + return base::unexpected(entry_offset.error()); } - return GetEntryFromOffset(type_chunk, entry_offset); + return GetEntryFromOffset(type_chunk, entry_offset.value()); } -uint32_t LoadedPackage::GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index) { +base::expected LoadedPackage::GetEntryOffset( + incfs::verified_map_ptr type_chunk, uint16_t entry_index) { // The configuration matches and is better than the previous selection. // Find the entry value if it exists for this configuration. const size_t entry_count = dtohl(type_chunk->entryCount); const size_t offsets_offset = dtohs(type_chunk->header.headerSize); // Check if there is the desired entry in this type. - if (type_chunk->flags & ResTable_type::FLAG_SPARSE) { // This is encoded as a sparse map, so perform a binary search. - const ResTable_sparseTypeEntry* sparse_indices = - reinterpret_cast( - reinterpret_cast(type_chunk) + offsets_offset); - const ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count; - const ResTable_sparseTypeEntry* result = - std::lower_bound(sparse_indices, sparse_indices_end, entry_index, - [](const ResTable_sparseTypeEntry& entry, uint16_t entry_idx) { - return dtohs(entry.idx) < entry_idx; - }); - - if (result == sparse_indices_end || dtohs(result->idx) != entry_index) { + bool error = false; + auto sparse_indices = type_chunk.offset(offsets_offset) + .convert().iterator(); + auto sparse_indices_end = sparse_indices + entry_count; + auto result = std::lower_bound(sparse_indices, sparse_indices_end, entry_index, + [&error](const incfs::map_ptr& entry, + uint16_t entry_idx) { + if (UNLIKELY(!entry)) { + return error = true; + } + return dtohs(entry->idx) < entry_idx; + }); + + if (result == sparse_indices_end) { // No entry found. - return ResTable_type::NO_ENTRY; + return base::unexpected(std::nullopt); + } + + const incfs::verified_map_ptr entry = (*result).verified(); + if (dtohs(entry->idx) != entry_index) { + if (error) { + return base::unexpected(IOError::PAGES_MISSING); + } + return base::unexpected(std::nullopt); } // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as // the real offset divided by 4. - return uint32_t{dtohs(result->offset)} * 4u; + return uint32_t{dtohs(entry->offset)} * 4u; } // This type is encoded as a dense array. if (entry_index >= entry_count) { // This entry cannot be here. - return ResTable_type::NO_ENTRY; + return base::unexpected(std::nullopt); } - const uint32_t* entry_offsets = reinterpret_cast( - reinterpret_cast(type_chunk) + offsets_offset); - return dtohl(entry_offsets[entry_index]); + const auto entry_offset_ptr = type_chunk.offset(offsets_offset).convert() + entry_index; + if (UNLIKELY(!entry_offset_ptr)) { + return base::unexpected(IOError::PAGES_MISSING); + } + + const uint32_t value = dtohl(entry_offset_ptr.value()); + if (value == ResTable_type::NO_ENTRY) { + return base::unexpected(std::nullopt); + } + + return value; } -const ResTable_entry* LoadedPackage::GetEntryFromOffset(const ResTable_type* type_chunk, - uint32_t offset) { - if (UNLIKELY(!VerifyResTableEntry(type_chunk, offset))) { - return nullptr; +base::expected, NullOrIOError> LoadedPackage::GetEntryFromOffset( + incfs::verified_map_ptr type_chunk, uint32_t offset) { + auto valid = VerifyResTableEntry(type_chunk, offset); + if (UNLIKELY(!valid.has_value())) { + return base::unexpected(valid.error()); } - return reinterpret_cast(reinterpret_cast(type_chunk) + - offset + dtohl(type_chunk->entriesStart)); + return type_chunk.offset(offset + dtohl(type_chunk->entriesStart)).convert(); } -void LoadedPackage::CollectConfigurations(bool exclude_mipmap, - std::set* out_configs) const { - const static std::u16string kMipMap = u"mipmap"; +base::expected LoadedPackage::CollectConfigurations( + bool exclude_mipmap, std::set* out_configs) const { const size_t type_count = type_specs_.size(); for (size_t i = 0; i < type_count; i++) { const TypeSpecPtr& type_spec = type_specs_[i]; - if (type_spec != nullptr) { - if (exclude_mipmap) { - const int type_idx = type_spec->type_spec->id - 1; - size_t type_name_len; - const char16_t* type_name16 = type_string_pool_.stringAt(type_idx, &type_name_len); - if (type_name16 != nullptr) { - if (kMipMap.compare(0, std::u16string::npos, type_name16, type_name_len) == 0) { - // This is a mipmap type, skip collection. - continue; - } - } - const char* type_name = type_string_pool_.string8At(type_idx, &type_name_len); - if (type_name != nullptr) { - if (strncmp(type_name, "mipmap", type_name_len) == 0) { - // This is a mipmap type, skip collection. - continue; - } + if (type_spec == nullptr) { + continue; + } + if (exclude_mipmap) { + const int type_idx = type_spec->type_spec->id - 1; + const auto type_name16 = type_string_pool_.stringAt(type_idx); + if (UNLIKELY(IsIOError(type_name16))) { + return base::unexpected(GetIOError(type_name16.error())); + } + if (type_name16.has_value()) { + if (strncmp16(type_name16->data(), u"mipmap", type_name16->size()) == 0) { + // This is a mipmap type, skip collection. + continue; } } - const auto iter_end = type_spec->types + type_spec->type_count; - for (auto iter = type_spec->types; iter != iter_end; ++iter) { - ResTable_config config; - config.copyFromDtoH((*iter)->config); - out_configs->insert(config); + const auto type_name = type_string_pool_.string8At(type_idx); + if (UNLIKELY(IsIOError(type_name))) { + return base::unexpected(GetIOError(type_name.error())); } + if (type_name.has_value()) { + if (strncmp(type_name->data(), "mipmap", type_name->size()) == 0) { + // This is a mipmap type, skip collection. + continue; + } + } + } + + const auto iter_end = type_spec->types + type_spec->type_count; + for (auto iter = type_spec->types; iter != iter_end; ++iter) { + ResTable_config config; + config.copyFromDtoH((*iter)->config); + out_configs->insert(config); } } + return {}; } void LoadedPackage::CollectLocales(bool canonicalize, std::set* out_locales) const { @@ -348,43 +384,53 @@ void LoadedPackage::CollectLocales(bool canonicalize, std::set* out } } -uint32_t LoadedPackage::FindEntryByName(const std::u16string& type_name, - const std::u16string& entry_name) const { - ssize_t type_idx = type_string_pool_.indexOfString(type_name.data(), type_name.size()); - if (type_idx < 0) { - return 0u; +base::expected LoadedPackage::FindEntryByName( + const std::u16string& type_name, const std::u16string& entry_name) const { + const base::expected type_idx = type_string_pool_.indexOfString( + type_name.data(), type_name.size()); + if (!type_idx.has_value()) { + return base::unexpected(type_idx.error()); } - ssize_t key_idx = key_string_pool_.indexOfString(entry_name.data(), entry_name.size()); - if (key_idx < 0) { - return 0u; + const base::expected key_idx = key_string_pool_.indexOfString( + entry_name.data(), entry_name.size()); + if (!key_idx.has_value()) { + return base::unexpected(key_idx.error()); } - const TypeSpec* type_spec = type_specs_[type_idx].get(); + const TypeSpec* type_spec = type_specs_[*type_idx].get(); if (type_spec == nullptr) { - return 0u; + return base::unexpected(std::nullopt); } const auto iter_end = type_spec->types + type_spec->type_count; for (auto iter = type_spec->types; iter != iter_end; ++iter) { - const ResTable_type* type = *iter; + const incfs::verified_map_ptr& type = *iter; + size_t entry_count = dtohl(type->entryCount); for (size_t entry_idx = 0; entry_idx < entry_count; entry_idx++) { - const uint32_t* entry_offsets = reinterpret_cast( - reinterpret_cast(type) + dtohs(type->header.headerSize)); - const uint32_t offset = dtohl(entry_offsets[entry_idx]); + auto entry_offset_ptr = type.offset(dtohs(type->header.headerSize)).convert() + + entry_idx; + if (!entry_offset_ptr) { + return base::unexpected(IOError::PAGES_MISSING); + } + + auto offset = dtohl(entry_offset_ptr.value()); if (offset != ResTable_type::NO_ENTRY) { - const ResTable_entry* entry = reinterpret_cast( - reinterpret_cast(type) + dtohl(type->entriesStart) + offset); - if (dtohl(entry->key.index) == static_cast(key_idx)) { + auto entry = type.offset(dtohl(type->entriesStart) + offset).convert(); + if (!entry) { + return base::unexpected(IOError::PAGES_MISSING); + } + + if (dtohl(entry->key.index) == static_cast(*key_idx)) { // The package ID will be overridden by the caller (due to runtime assignment of package // IDs for shared libraries). - return make_resid(0x00, type_idx + type_id_offset_ + 1, entry_idx); + return make_resid(0x00, *type_idx + type_id_offset_ + 1, entry_idx); } } } } - return 0u; + return base::unexpected(std::nullopt); } const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const { @@ -405,8 +451,8 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, // was added. constexpr size_t kMinPackageSize = sizeof(ResTable_package) - sizeof(ResTable_package::typeIdOffset); - const ResTable_package* header = chunk.header(); - if (header == nullptr) { + const incfs::map_ptr header = chunk.header(); + if (!header) { LOG(ERROR) << "RES_TABLE_PACKAGE_TYPE too small."; return {}; } @@ -453,10 +499,13 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, const Chunk child_chunk = iter.Next(); switch (child_chunk.type()) { case RES_STRING_POOL_TYPE: { - const uintptr_t pool_address = - reinterpret_cast(child_chunk.header()); - const uintptr_t header_address = reinterpret_cast(header); - if (pool_address == header_address + dtohl(header->typeStrings)) { + const auto pool_address = child_chunk.header(); + if (!pool_address) { + LOG(ERROR) << "RES_STRING_POOL_TYPE is incomplete due to incremental installation."; + return {}; + } + + if (pool_address == header.offset(dtohl(header->typeStrings)).convert()) { // This string pool is the type string pool. status_t err = loaded_package->type_string_pool_.setTo( child_chunk.header(), child_chunk.size()); @@ -464,7 +513,8 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, LOG(ERROR) << "RES_STRING_POOL_TYPE for types corrupt."; return {}; } - } else if (pool_address == header_address + dtohl(header->keyStrings)) { + } else if (pool_address == header.offset(dtohl(header->keyStrings)) + .convert()) { // This string pool is the key string pool. status_t err = loaded_package->key_string_pool_.setTo( child_chunk.header(), child_chunk.size()); @@ -478,8 +528,8 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, } break; case RES_TABLE_TYPE_SPEC_TYPE: { - const ResTable_typeSpec* type_spec = child_chunk.header(); - if (type_spec == nullptr) { + const auto type_spec = child_chunk.header(); + if (!type_spec) { LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE too small."; return {}; } @@ -514,7 +564,7 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, std::unique_ptr& builder_ptr = type_builder_map[type_spec->id - 1]; if (builder_ptr == nullptr) { - builder_ptr = util::make_unique(type_spec); + builder_ptr = util::make_unique(type_spec.verified()); loaded_package->resource_ids_.set(type_spec->id, entry_count); } else { LOG(WARNING) << StringPrintf("RES_TABLE_TYPE_SPEC_TYPE already defined for ID %02x", @@ -523,8 +573,8 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, } break; case RES_TABLE_TYPE_TYPE: { - const ResTable_type* type = child_chunk.header(); - if (type == nullptr) { + const auto type = child_chunk.header(); + if (!type) { LOG(ERROR) << "RES_TABLE_TYPE_TYPE too small."; return {}; } @@ -536,7 +586,7 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, // Type chunks must be preceded by their TypeSpec chunks. std::unique_ptr& builder_ptr = type_builder_map[type->id - 1]; if (builder_ptr != nullptr) { - builder_ptr->AddType(type); + builder_ptr->AddType(type.verified()); } else { LOG(ERROR) << StringPrintf( "RES_TABLE_TYPE_TYPE with ID %02x found without preceding RES_TABLE_TYPE_SPEC_TYPE.", @@ -546,8 +596,8 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, } break; case RES_TABLE_LIBRARY_TYPE: { - const ResTable_lib_header* lib = child_chunk.header(); - if (lib == nullptr) { + const auto lib = child_chunk.header(); + if (!lib) { LOG(ERROR) << "RES_TABLE_LIBRARY_TYPE too small."; return {}; } @@ -559,10 +609,13 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, loaded_package->dynamic_package_map_.reserve(dtohl(lib->count)); - const ResTable_lib_entry* const entry_begin = - reinterpret_cast(child_chunk.data_ptr()); - const ResTable_lib_entry* const entry_end = entry_begin + dtohl(lib->count); + const auto entry_begin = child_chunk.data_ptr().convert(); + const auto entry_end = entry_begin + dtohl(lib->count); for (auto entry_iter = entry_begin; entry_iter != entry_end; ++entry_iter) { + if (!entry_iter) { + return {}; + } + std::string package_name; util::ReadUtf16StringFromDevice(entry_iter->packageName, arraysize(entry_iter->packageName), &package_name); @@ -580,17 +633,16 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, } break; case RES_TABLE_OVERLAYABLE_TYPE: { - const ResTable_overlayable_header* header = - child_chunk.header(); - if (header == nullptr) { + const auto overlayable = child_chunk.header(); + if (!overlayable) { LOG(ERROR) << "RES_TABLE_OVERLAYABLE_TYPE too small."; return {}; } std::string name; - util::ReadUtf16StringFromDevice(header->name, arraysize(header->name), &name); + util::ReadUtf16StringFromDevice(overlayable->name, arraysize(overlayable->name), &name); std::string actor; - util::ReadUtf16StringFromDevice(header->actor, arraysize(header->actor), &actor); + util::ReadUtf16StringFromDevice(overlayable->actor, arraysize(overlayable->actor), &actor); if (loaded_package->overlayable_map_.find(name) != loaded_package->overlayable_map_.end()) { @@ -606,9 +658,9 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, switch (overlayable_child_chunk.type()) { case RES_TABLE_OVERLAYABLE_POLICY_TYPE: { - const ResTable_overlayable_policy_header* policy_header = + const auto policy_header = overlayable_child_chunk.header(); - if (policy_header == nullptr) { + if (!policy_header) { LOG(ERROR) << "RES_TABLE_OVERLAYABLE_POLICY_TYPE too small."; return {}; } @@ -621,10 +673,12 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, // Retrieve all the resource ids belonging to this policy chunk std::unordered_set ids; - const auto ids_begin = - reinterpret_cast(overlayable_child_chunk.data_ptr()); + const auto ids_begin = overlayable_child_chunk.data_ptr().convert(); const auto ids_end = ids_begin + dtohl(policy_header->entry_count); for (auto id_iter = ids_begin; id_iter != ids_end; ++id_iter) { + if (!id_iter) { + return {}; + } ids.insert(dtohl(id_iter->ident)); } @@ -633,7 +687,7 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, overlayable_info.name = name; overlayable_info.actor = actor; overlayable_info.policy_flags = policy_header->policy_flags; - loaded_package->overlayable_infos_.push_back(std::make_pair(overlayable_info, ids)); + loaded_package->overlayable_infos_.emplace_back(overlayable_info, ids); loaded_package->defines_overlayable_ = true; break; } @@ -683,8 +737,8 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, package_property_t property_flags) { - const ResTable_header* header = chunk.header(); - if (header == nullptr) { + incfs::map_ptr header = chunk.header(); + if (!header) { LOG(ERROR) << "RES_TABLE_TYPE too small."; return false; } @@ -747,7 +801,8 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, return true; } -std::unique_ptr LoadedArsc::Load(const StringPiece& data, +std::unique_ptr LoadedArsc::Load(incfs::map_ptr data, + const size_t length, const LoadedIdmap* loaded_idmap, const package_property_t property_flags) { ATRACE_NAME("LoadedArsc::Load"); @@ -755,7 +810,7 @@ std::unique_ptr LoadedArsc::Load(const StringPiece& data, // Not using make_unique because the constructor is private. std::unique_ptr loaded_arsc(new LoadedArsc()); - ChunkIterator iter(data.data(), data.size()); + ChunkIterator iter(data, length); while (iter.HasNext()) { const Chunk chunk = iter.Next(); switch (chunk.type()) { diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index dfb4009b07e2..4010e4e10317 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -104,22 +104,26 @@ static void strcpy16_dtoh(char16_t* dst, const uint16_t* src, size_t avail) *dst = 0; } -static status_t validate_chunk(const ResChunk_header* chunk, +static status_t validate_chunk(const incfs::map_ptr& chunk, size_t minSize, - const uint8_t* dataEnd, + const incfs::map_ptr dataEnd, const char* name) { + if (!chunk) { + return BAD_TYPE; + } + const uint16_t headerSize = dtohs(chunk->headerSize); const uint32_t size = dtohl(chunk->size); if (headerSize >= minSize) { if (headerSize <= size) { if (((headerSize|size)&0x3) == 0) { - if ((size_t)size <= (size_t)(dataEnd-((const uint8_t*)chunk))) { + if ((size_t)size <= (size_t)(dataEnd-chunk.convert())) { return NO_ERROR; } ALOGW("%s data size 0x%x extends beyond resource end %p.", - name, size, (void*)(dataEnd-((const uint8_t*)chunk))); + name, size, (void*)(dataEnd-chunk.convert())); return BAD_TYPE; } ALOGW("%s size 0x%x or headerSize 0x%x is not on an integer boundary.", @@ -450,7 +454,7 @@ void ResStringPool::setToEmpty() mHeader = (const ResStringPool_header*) header; } -status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) +status_t ResStringPool::setTo(incfs::map_ptr data, size_t size, bool copyData) { if (!data || !size) { return (mError=BAD_TYPE); @@ -467,8 +471,8 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) // The data is at least as big as a ResChunk_header, so we can safely validate the other // header fields. // `data + size` is safe because the source of `size` comes from the kernel/filesystem. - if (validate_chunk(reinterpret_cast(data), sizeof(ResStringPool_header), - reinterpret_cast(data) + size, + const auto chunk_header = data.convert(); + if (validate_chunk(chunk_header, sizeof(ResStringPool_header), data.convert() + size, "ResStringPool_header") != NO_ERROR) { ALOGW("Bad string block: malformed block dimensions"); return (mError=BAD_TYPE); @@ -481,16 +485,25 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) if (mOwnedData == NULL) { return (mError=NO_MEMORY); } - memcpy(mOwnedData, data, size); + + if (!data.convert().verify(size)) { + return (mError=NO_MEMORY); + } + + memcpy(mOwnedData, data.unsafe_ptr(), size); data = mOwnedData; } // The size has been checked, so it is safe to read the data in the ResStringPool_header // data structure. - mHeader = (const ResStringPool_header*)data; + const auto header = data.convert(); + if (!header) { + return (mError=BAD_TYPE); + } + mHeader = header.verified(); if (notDeviceEndian) { - ResStringPool_header* h = const_cast(mHeader); + ResStringPool_header* h = const_cast(mHeader.unsafe_ptr()); h->header.headerSize = dtohs(mHeader->header.headerSize); h->header.type = dtohs(mHeader->header.type); h->header.size = dtohl(mHeader->header.size); @@ -508,8 +521,7 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) return (mError=BAD_TYPE); } mSize = mHeader->header.size; - mEntries = (const uint32_t*) - (((const uint8_t*)data)+mHeader->header.headerSize); + mEntries = data.offset(mHeader->header.headerSize).convert(); if (mHeader->stringCount > 0) { if ((mHeader->stringCount*sizeof(uint32_t) < mHeader->stringCount) // uint32 overflow? @@ -536,9 +548,7 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) return (mError=BAD_TYPE); } - mStrings = (const void*) - (((const uint8_t*)data) + mHeader->stringsStart); - + mStrings = data.offset(mHeader->stringsStart).convert(); if (mHeader->styleCount == 0) { mStringPoolSize = (mSize - mHeader->stringsStart) / charSize; } else { @@ -560,31 +570,37 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) // check invariant: stringCount > 0 requires a string pool to exist if (mStringPoolSize == 0) { - ALOGW("Bad string block: stringCount is %d but pool size is 0\n", (int)mHeader->stringCount); + ALOGW("Bad string block: stringCount is %d but pool size is 0\n", + (int)mHeader->stringCount); return (mError=BAD_TYPE); } if (notDeviceEndian) { size_t i; - uint32_t* e = const_cast(mEntries); + auto e = const_cast(mEntries.unsafe_ptr()); for (i=0; istringCount; i++) { - e[i] = dtohl(mEntries[i]); + e[i] = dtohl(e[i]); } if (!(mHeader->flags&ResStringPool_header::UTF8_FLAG)) { - const uint16_t* strings = (const uint16_t*)mStrings; - uint16_t* s = const_cast(strings); + uint16_t* s = const_cast(mStrings.convert().unsafe_ptr()); for (i=0; iflags&ResStringPool_header::UTF8_FLAG && - ((uint8_t*)mStrings)[mStringPoolSize-1] != 0) || - (!(mHeader->flags&ResStringPool_header::UTF8_FLAG) && - ((uint16_t*)mStrings)[mStringPoolSize-1] != 0)) { - ALOGW("Bad string block: last string is not 0-terminated\n"); - return (mError=BAD_TYPE); + if (mHeader->flags&ResStringPool_header::UTF8_FLAG) { + auto end = mStrings.convert() + (mStringPoolSize-1); + if (!end || end.value() != 0) { + ALOGW("Bad string block: last string is not 0-terminated\n"); + return (mError=BAD_TYPE); + } + } else { + auto end = mStrings.convert() + (mStringPoolSize-1); + if (!end || end.value() != 0) { + ALOGW("Bad string block: last string is not 0-terminated\n"); + return (mError=BAD_TYPE); + } } } else { mStrings = NULL; @@ -599,14 +615,13 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) return (mError=BAD_TYPE); } - if (((const uint8_t*)mEntryStyles-(const uint8_t*)mHeader) > (int)size) { + if ((mEntryStyles.convert() - mHeader.convert()) > (int)size) { ALOGW("Bad string block: entry of %d styles extends past data size %d\n", - (int)((const uint8_t*)mEntryStyles-(const uint8_t*)mHeader), + (int)(mEntryStyles.convert()-mHeader.convert()), (int)size); return (mError=BAD_TYPE); } - mStyles = (const uint32_t*) - (((const uint8_t*)data)+mHeader->stylesStart); + mStyles = data.offset(mHeader->stylesStart).convert(); if (mHeader->stylesStart >= mHeader->header.size) { ALOGW("Bad string block: style pool starts %d, after total size %d\n", (int)mHeader->stylesStart, (int)mHeader->header.size); @@ -617,13 +632,13 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) if (notDeviceEndian) { size_t i; - uint32_t* e = const_cast(mEntryStyles); + uint32_t* e = const_cast(mEntryStyles.unsafe_ptr()); for (i=0; istyleCount; i++) { - e[i] = dtohl(mEntryStyles[i]); + e[i] = dtohl(e[i]); } - uint32_t* s = const_cast(mStyles); + uint32_t* s = const_cast(mStyles.unsafe_ptr()); for (i=0; istringCount; x++) { if (mCache[x] != NULL) { free(mCache[x]); @@ -679,15 +695,21 @@ void ResStringPool::uninit() * data encoded. In that case, drop the high bit of the first character and * add it together with the next character. */ -static inline size_t -decodeLength(const uint16_t** str) +static inline base::expected decodeLength(incfs::map_ptr* str) { - size_t len = **str; - if ((len & 0x8000) != 0) { - (*str)++; - len = ((len & 0x7FFF) << 16) | **str; + if (UNLIKELY(!*str)) { + return base::unexpected(IOError::PAGES_MISSING); + } + + size_t len = str->value(); + if ((len & 0x8000U) != 0) { + ++(*str); + if (UNLIKELY(!*str)) { + return base::unexpected(IOError::PAGES_MISSING); + } + len = ((len & 0x7FFFU) << 16U) | str->value(); } - (*str)++; + ++(*str); return len; } @@ -701,82 +723,119 @@ decodeLength(const uint16_t** str) * data encoded. In that case, drop the high bit of the first character and * add it together with the next character. */ -static inline size_t -decodeLength(const uint8_t** str) +static inline base::expected decodeLength(incfs::map_ptr* str) { - size_t len = **str; - if ((len & 0x80) != 0) { - (*str)++; - len = ((len & 0x7F) << 8) | **str; + if (UNLIKELY(!*str)) { + return base::unexpected(IOError::PAGES_MISSING); } - (*str)++; + + size_t len = str->value(); + if ((len & 0x80U) != 0) { + ++(*str); + if (UNLIKELY(!*str)) { + return base::unexpected(IOError::PAGES_MISSING); + } + len = ((len & 0x7FU) << 8U) | str->value(); + } + ++(*str); return len; } -const char16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const +base::expected ResStringPool::stringAt(size_t idx) const { if (mError == NO_ERROR && idx < mHeader->stringCount) { const bool isUTF8 = (mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0; - const uint32_t off = mEntries[idx]/(isUTF8?sizeof(uint8_t):sizeof(uint16_t)); + auto offPtr = mEntries + idx; + if (UNLIKELY(!offPtr)) { + return base::unexpected(IOError::PAGES_MISSING); + } + + const uint32_t off = (offPtr.value())/(isUTF8?sizeof(uint8_t):sizeof(uint16_t)); if (off < (mStringPoolSize-1)) { if (!isUTF8) { - const uint16_t* strings = (uint16_t*)mStrings; - const uint16_t* str = strings+off; + auto strings = mStrings.convert(); + auto str = strings+off; + + const base::expected u16len = decodeLength(&str); + if (UNLIKELY(!u16len.has_value())) { + return base::unexpected(u16len.error()); + } - *u16len = decodeLength(&str); if ((uint32_t)(str+*u16len-strings) < mStringPoolSize) { // Reject malformed (non null-terminated) strings - if (str[*u16len] != 0x0000) { - ALOGW("Bad string block: string #%d is not null-terminated", - (int)idx); - return NULL; + const auto nullAddress = str + (*u16len); + if (UNLIKELY(!nullAddress)) { + return base::unexpected(IOError::PAGES_MISSING); + } + + if (nullAddress.value() != 0x0000) { + ALOGW("Bad string block: string #%d is not null-terminated", (int)idx); + return base::unexpected(std::nullopt); + } + + if (UNLIKELY(!str.verify(*u16len + 1U))) { + return base::unexpected(IOError::PAGES_MISSING); } - return reinterpret_cast(str); + + return StringPiece16(reinterpret_cast(str.unsafe_ptr()), + *u16len); } else { ALOGW("Bad string block: string #%d extends to %d, past end at %d\n", (int)idx, (int)(str+*u16len-strings), (int)mStringPoolSize); } } else { - const uint8_t* strings = (uint8_t*)mStrings; - const uint8_t* u8str = strings+off; + auto strings = mStrings.convert(); + auto u8str = strings+off; - *u16len = decodeLength(&u8str); - size_t u8len = decodeLength(&u8str); + base::expected u16len = decodeLength(&u8str); + if (UNLIKELY(!u16len.has_value())) { + return base::unexpected(u16len.error()); + } + + const base::expected u8len = decodeLength(&u8str); + if (UNLIKELY(!u8len.has_value())) { + return base::unexpected(u8len.error()); + } // encLen must be less than 0x7FFF due to encoding. - if ((uint32_t)(u8str+u8len-strings) < mStringPoolSize) { + if ((uint32_t)(u8str+*u8len-strings) < mStringPoolSize) { AutoMutex lock(mDecodeLock); if (mCache != NULL && mCache[idx] != NULL) { - return mCache[idx]; + return StringPiece16(mCache[idx], *u16len); } // Retrieve the actual length of the utf8 string if the // encoded length was truncated - if (stringDecodeAt(idx, u8str, u8len, &u8len) == NULL) { - return NULL; + auto decodedString = stringDecodeAt(idx, u8str, *u8len); + if (!decodedString.has_value()) { + return base::unexpected(decodedString.error()); } // Since AAPT truncated lengths longer than 0x7FFF, check // that the bits that remain after truncation at least match // the bits of the actual length - ssize_t actualLen = utf8_to_utf16_length(u8str, u8len); - if (actualLen < 0 || ((size_t)actualLen & 0x7FFF) != *u16len) { + ssize_t actualLen = utf8_to_utf16_length( + reinterpret_cast(decodedString->data()), + decodedString->size()); + + if (actualLen < 0 || ((size_t)actualLen & 0x7FFFU) != *u16len) { ALOGW("Bad string block: string #%lld decoded length is not correct " "%lld vs %llu\n", (long long)idx, (long long)actualLen, (long long)*u16len); - return NULL; + return base::unexpected(std::nullopt); } - *u16len = (size_t) actualLen; - char16_t *u16str = (char16_t *)calloc(*u16len+1, sizeof(char16_t)); + u16len = (size_t) actualLen; + auto u16str = (char16_t *)calloc(*u16len+1, sizeof(char16_t)); if (!u16str) { ALOGW("No memory when trying to allocate decode cache for string #%d\n", (int)idx); - return NULL; + return base::unexpected(std::nullopt); } - utf8_to_utf16(u8str, u8len, u16str, *u16len + 1); + utf8_to_utf16(reinterpret_cast(decodedString->data()), + decodedString->size(), u16str, *u16len + 1); if (mCache == NULL) { #ifndef __ANDROID__ @@ -793,19 +852,19 @@ const char16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const if (mCache == NULL) { ALOGW("No memory trying to allocate decode cache table of %d bytes\n", (int)(mHeader->stringCount*sizeof(char16_t**))); - return NULL; + return base::unexpected(std::nullopt); } } if (kDebugStringPoolNoisy) { - ALOGI("Caching UTF8 string: %s", u8str); + ALOGI("Caching UTF8 string: %s", u8str.unsafe_ptr()); } mCache[idx] = u16str; - return u16str; + return StringPiece16(u16str, *u16len); } else { ALOGW("Bad string block: string #%lld extends to %lld, past end at %lld\n", - (long long)idx, (long long)(u8str+u8len-strings), + (long long)idx, (long long)(u8str+*u8len-strings), (long long)mStringPoolSize); } } @@ -815,33 +874,43 @@ const char16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const (int)(mStringPoolSize*sizeof(uint16_t))); } } - return NULL; + return base::unexpected(std::nullopt); } -const char* ResStringPool::string8At(size_t idx, size_t* outLen) const +base::expected ResStringPool::string8At(size_t idx) const { if (mError == NO_ERROR && idx < mHeader->stringCount) { if ((mHeader->flags&ResStringPool_header::UTF8_FLAG) == 0) { - return NULL; + return base::unexpected(std::nullopt); } - const uint32_t off = mEntries[idx]/sizeof(char); + + auto offPtr = mEntries + idx; + if (UNLIKELY(!offPtr)) { + return base::unexpected(IOError::PAGES_MISSING); + } + + const uint32_t off = (offPtr.value())/sizeof(char); if (off < (mStringPoolSize-1)) { - const uint8_t* strings = (uint8_t*)mStrings; - const uint8_t* str = strings+off; + auto strings = mStrings.convert(); + auto str = strings+off; // Decode the UTF-16 length. This is not used if we're not // converting to UTF-16 from UTF-8. - decodeLength(&str); - - const size_t encLen = decodeLength(&str); - *outLen = encLen; + const base::expected u16len = decodeLength(&str); + if (UNLIKELY(!u16len)) { + return base::unexpected(u16len.error()); + } - if ((uint32_t)(str+encLen-strings) < mStringPoolSize) { - return stringDecodeAt(idx, str, encLen, outLen); + const base::expected u8len = decodeLength(&str); + if (UNLIKELY(!u8len)) { + return base::unexpected(u8len.error()); + } + if ((uint32_t)(str+*u8len-strings) < mStringPoolSize) { + return stringDecodeAt(idx, str, *u8len); } else { ALOGW("Bad string block: string #%d extends to %d, past end at %d\n", - (int)idx, (int)(str+encLen-strings), (int)mStringPoolSize); + (int)idx, (int)(str+*u8len-strings), (int)mStringPoolSize); } } else { ALOGW("Bad string block: string #%d entry is at %d, past end at %d\n", @@ -849,7 +918,7 @@ const char* ResStringPool::string8At(size_t idx, size_t* outLen) const (int)(mStringPoolSize*sizeof(uint16_t))); } } - return NULL; + return base::unexpected(std::nullopt); } /** @@ -859,74 +928,93 @@ const char* ResStringPool::string8At(size_t idx, size_t* outLen) const * bits. Strings that exceed the maximum encode length are not placed into * StringPools in AAPT2. **/ -const char* ResStringPool::stringDecodeAt(size_t idx, const uint8_t* str, - const size_t encLen, size_t* outLen) const { - const uint8_t* strings = (uint8_t*)mStrings; - +base::expected ResStringPool::stringDecodeAt( + size_t idx, incfs::map_ptr str, size_t encLen) const +{ + const auto strings = mStrings.convert(); size_t i = 0, end = encLen; while ((uint32_t)(str+end-strings) < mStringPoolSize) { - if (str[end] == 0x00) { + const auto nullAddress = str + end; + if (UNLIKELY(!nullAddress)) { + return base::unexpected(IOError::PAGES_MISSING); + } + + if (nullAddress.value() == 0x00) { if (i != 0) { ALOGW("Bad string block: string #%d is truncated (actual length is %d)", (int)idx, (int)end); } - *outLen = end; - return (const char*)str; + if (UNLIKELY(!str.verify(end + 1U))) { + return base::unexpected(IOError::PAGES_MISSING); + } + + return StringPiece((const char*) str.unsafe_ptr(), end); } end = (++i << (sizeof(uint8_t) * 8 * 2 - 1)) | encLen; } // Reject malformed (non null-terminated) strings - ALOGW("Bad string block: string #%d is not null-terminated", - (int)idx); - return NULL; + ALOGW("Bad string block: string #%d is not null-terminated", (int)idx); + return base::unexpected(std::nullopt); } -const String8 ResStringPool::string8ObjectAt(size_t idx) const +base::expected ResStringPool::string8ObjectAt(size_t idx) const { - size_t len; - const char *str = string8At(idx, &len); - if (str != NULL) { - return String8(str, len); + const base::expected str = string8At(idx); + if (UNLIKELY(IsIOError(str))) { + return base::unexpected(GetIOError(str.error())); + } + if (str.has_value()) { + return String8(str->data(), str->size()); } - const char16_t *str16 = stringAt(idx, &len); - if (str16 != NULL) { - return String8(str16, len); + const base::expected str16 = stringAt(idx); + if (UNLIKELY(IsIOError(str16))) { + return base::unexpected(GetIOError(str16.error())); } + if (str16.has_value()) { + return String8(str16->data(), str16->size()); + } + return String8(); } -const ResStringPool_span* ResStringPool::styleAt(const ResStringPool_ref& ref) const +base::expected, NullOrIOError> ResStringPool::styleAt( + const ResStringPool_ref& ref) const { return styleAt(ref.index); } -const ResStringPool_span* ResStringPool::styleAt(size_t idx) const +base::expected, NullOrIOError> ResStringPool::styleAt( + size_t idx) const { if (mError == NO_ERROR && idx < mHeader->styleCount) { - const uint32_t off = (mEntryStyles[idx]/sizeof(uint32_t)); + auto offPtr = mEntryStyles + idx; + if (UNLIKELY(!offPtr)) { + return base::unexpected(IOError::PAGES_MISSING); + } + + const uint32_t off = ((offPtr.value())/sizeof(uint32_t)); if (off < mStylePoolSize) { - return (const ResStringPool_span*)(mStyles+off); + return (mStyles+off).convert(); } else { ALOGW("Bad string block: style #%d entry is at %d, past end at %d\n", (int)idx, (int)(off*sizeof(uint32_t)), (int)(mStylePoolSize*sizeof(uint32_t))); } } - return NULL; + return base::unexpected(std::nullopt); } -ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const +base::expected ResStringPool::indexOfString(const char16_t* str, + size_t strLen) const { if (mError != NO_ERROR) { - return mError; + return base::unexpected(std::nullopt); } - size_t len; - if ((mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0) { if (kDebugStringPoolNoisy) { ALOGI("indexOfString UTF-8: %s", String8(str, strLen).string()); @@ -948,17 +1036,19 @@ ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const ssize_t mid; while (l <= h) { mid = l + (h - l)/2; - const uint8_t* s = (const uint8_t*)string8At(mid, &len); - int c; - if (s != NULL) { - char16_t* end = utf8_to_utf16(s, len, convBuffer, convBufferLen); + int c = -1; + const base::expected s = string8At(mid); + if (UNLIKELY(IsIOError(s))) { + return base::unexpected(s.error()); + } + if (s.has_value()) { + char16_t* end = utf8_to_utf16(reinterpret_cast(s->data()), + s->size(), convBuffer, convBufferLen); c = strzcmp16(convBuffer, end-convBuffer, str, strLen); - } else { - c = -1; } if (kDebugStringPoolNoisy) { ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n", - (const char*)s, c, (int)l, (int)mid, (int)h); + s->data(), c, (int)l, (int)mid, (int)h); } if (c == 0) { if (kDebugStringPoolNoisy) { @@ -981,15 +1071,21 @@ ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const String8 str8(str, strLen); const size_t str8Len = str8.size(); for (int i=mHeader->stringCount-1; i>=0; i--) { - const char* s = string8At(i, &len); - if (kDebugStringPoolNoisy) { - ALOGI("Looking at %s, i=%d\n", String8(s).string(), i); + const base::expected s = string8At(i); + if (UNLIKELY(IsIOError(s))) { + return base::unexpected(s.error()); } - if (s && str8Len == len && memcmp(s, str8.string(), str8Len) == 0) { + if (s.has_value()) { if (kDebugStringPoolNoisy) { - ALOGI("MATCH!"); + ALOGI("Looking at %s, i=%d\n", s->data(), i); + } + if (str8Len == s->size() + && memcmp(s->data(), str8.string(), str8Len) == 0) { + if (kDebugStringPoolNoisy) { + ALOGI("MATCH!"); + } + return i; } - return i; } } } @@ -1007,11 +1103,14 @@ ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const ssize_t mid; while (l <= h) { mid = l + (h - l)/2; - const char16_t* s = stringAt(mid, &len); - int c = s ? strzcmp16(s, len, str, strLen) : -1; + const base::expected s = stringAt(mid); + if (UNLIKELY(IsIOError(s))) { + return base::unexpected(s.error()); + } + int c = s.has_value() ? strzcmp16(s->data(), s->size(), str, strLen) : -1; if (kDebugStringPoolNoisy) { ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n", - String8(s).string(), c, (int)l, (int)mid, (int)h); + String8(s->data(), s->size()).string(), c, (int)l, (int)mid, (int)h); } if (c == 0) { if (kDebugStringPoolNoisy) { @@ -1030,11 +1129,15 @@ ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const // span tags; since those always appear at the end of the string // block, start searching at the back. for (int i=mHeader->stringCount-1; i>=0; i--) { - const char16_t* s = stringAt(i, &len); + const base::expected s = stringAt(i); + if (UNLIKELY(IsIOError(s))) { + return base::unexpected(s.error()); + } if (kDebugStringPoolNoisy) { - ALOGI("Looking at %s, i=%d\n", String8(s).string(), i); + ALOGI("Looking at %s, i=%d\n", String8(s->data(), s->size()).string(), i); } - if (s && strLen == len && strzcmp16(s, len, str, strLen) == 0) { + if (s.has_value() && strLen == s->size() && + strzcmp16(s->data(), s->size(), str, strLen) == 0) { if (kDebugStringPoolNoisy) { ALOGI("MATCH!"); } @@ -1043,8 +1146,7 @@ ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const } } } - - return NAME_NOT_FOUND; + return base::unexpected(std::nullopt); } size_t ResStringPool::size() const @@ -1062,9 +1164,10 @@ size_t ResStringPool::bytes() const return (mError == NO_ERROR) ? mHeader->header.size : 0; } -const void* ResStringPool::data() const +incfs::map_ptr ResStringPool::data() const { - return mHeader; + + return mHeader.unsafe_ptr(); } bool ResStringPool::isSorted() const @@ -1121,7 +1224,7 @@ int32_t ResXMLParser::getCommentID() const const char16_t* ResXMLParser::getComment(size_t* outLen) const { int32_t id = getCommentID(); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; + return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL; } uint32_t ResXMLParser::getLineNumber() const @@ -1140,7 +1243,7 @@ int32_t ResXMLParser::getTextID() const const char16_t* ResXMLParser::getText(size_t* outLen) const { int32_t id = getTextID(); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; + return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL; } ssize_t ResXMLParser::getTextValue(Res_value* outValue) const @@ -1164,7 +1267,7 @@ const char16_t* ResXMLParser::getNamespacePrefix(size_t* outLen) const { int32_t id = getNamespacePrefixID(); //printf("prefix=%d event=%p\n", id, mEventCode); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; + return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL; } int32_t ResXMLParser::getNamespaceUriID() const @@ -1179,7 +1282,7 @@ const char16_t* ResXMLParser::getNamespaceUri(size_t* outLen) const { int32_t id = getNamespaceUriID(); //printf("uri=%d event=%p\n", id, mEventCode); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; + return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL; } int32_t ResXMLParser::getElementNamespaceID() const @@ -1196,7 +1299,7 @@ int32_t ResXMLParser::getElementNamespaceID() const const char16_t* ResXMLParser::getElementNamespace(size_t* outLen) const { int32_t id = getElementNamespaceID(); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; + return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL; } int32_t ResXMLParser::getElementNameID() const @@ -1213,7 +1316,7 @@ int32_t ResXMLParser::getElementNameID() const const char16_t* ResXMLParser::getElementName(size_t* outLen) const { int32_t id = getElementNameID(); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; + return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL; } size_t ResXMLParser::getAttributeCount() const @@ -1246,7 +1349,7 @@ const char16_t* ResXMLParser::getAttributeNamespace(size_t idx, size_t* outLen) if (kDebugXMLNoisy) { printf("getAttributeNamespace 0x%zx=0x%x\n", idx, id); } - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; + return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL; } const char* ResXMLParser::getAttributeNamespace8(size_t idx, size_t* outLen) const @@ -1256,7 +1359,7 @@ const char* ResXMLParser::getAttributeNamespace8(size_t idx, size_t* outLen) con if (kDebugXMLNoisy) { printf("getAttributeNamespace 0x%zx=0x%x\n", idx, id); } - return id >= 0 ? mTree.mStrings.string8At(id, outLen) : NULL; + return id >= 0 ? UnpackOptionalString(mTree.mStrings.string8At(id), outLen) : NULL; } int32_t ResXMLParser::getAttributeNameID(size_t idx) const @@ -1281,7 +1384,7 @@ const char16_t* ResXMLParser::getAttributeName(size_t idx, size_t* outLen) const if (kDebugXMLNoisy) { printf("getAttributeName 0x%zx=0x%x\n", idx, id); } - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; + return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL; } const char* ResXMLParser::getAttributeName8(size_t idx, size_t* outLen) const @@ -1291,7 +1394,7 @@ const char* ResXMLParser::getAttributeName8(size_t idx, size_t* outLen) const if (kDebugXMLNoisy) { printf("getAttributeName 0x%zx=0x%x\n", idx, id); } - return id >= 0 ? mTree.mStrings.string8At(id, outLen) : NULL; + return id >= 0 ? UnpackOptionalString(mTree.mStrings.string8At(id), outLen) : NULL; } uint32_t ResXMLParser::getAttributeNameResID(size_t idx) const @@ -1328,7 +1431,7 @@ const char16_t* ResXMLParser::getAttributeStringValue(size_t idx, size_t* outLen if (kDebugXMLNoisy) { printf("getAttributeValue 0x%zx=0x%x\n", idx, id); } - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; + return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL; } int32_t ResXMLParser::getAttributeDataType(size_t idx) const @@ -3596,9 +3699,10 @@ struct ResTable::PackageGroup ssize_t findType16(const char16_t* type, size_t len) const { const size_t N = packages.size(); for (size_t i = 0; i < N; i++) { - ssize_t index = packages[i]->typeStrings.indexOfString(type, len); - if (index >= 0) { - return index + packages[i]->typeIdOffset; + const base::expected index = + packages[i]->typeStrings.indexOfString(type, len); + if (index.has_value()) { + return *index + packages[i]->typeIdOffset; } } return -1; @@ -4304,21 +4408,21 @@ bool ResTable::getResourceName(uint32_t resID, bool allowUtf8, resource_name* ou 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); + outName->type8 = UnpackOptionalString(entry.typeStr.string8(), &outName->typeLen); + outName->name8 = UnpackOptionalString(entry.keyStr.string8(), &outName->nameLen); } else { outName->type8 = NULL; outName->name8 = NULL; } if (outName->type8 == NULL) { - outName->type = entry.typeStr.string16(&outName->typeLen); + outName->type = UnpackOptionalString(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 = entry.keyStr.string16(&outName->nameLen); + outName->name = UnpackOptionalString(entry.keyStr.string16(), &outName->nameLen); // If we have a bad index for some reason, we should abort. if (outName->name == NULL) { return false; @@ -4406,7 +4510,8 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag entry.package->header->index, outValue->dataType, outValue->dataType == Res_value::TYPE_STRING ? - String8(entry.package->header->values.stringAt(outValue->data, &len)).string() : + String8(UnpackOptionalString( + entry.package->header->values.stringAt(outValue->data), &len)).string() : "", outValue->data); } @@ -4462,7 +4567,8 @@ const char16_t* ResTable::valueToString( return NULL; } if (value->dataType == value->TYPE_STRING) { - return getTableStringBlock(stringBlock)->stringAt(value->data, outLen); + return UnpackOptionalString(getTableStringBlock(stringBlock)->stringAt(value->data), + outLen); } // XXX do int to string conversions. return NULL; @@ -4978,15 +5084,13 @@ nope: size_t targetTypeLen = typeLen; do { - ssize_t ti = group->packages[pi]->typeStrings.indexOfString( - targetType, targetTypeLen); - if (ti < 0) { + auto ti = group->packages[pi]->typeStrings.indexOfString(targetType, targetTypeLen); + if (!ti.has_value()) { continue; } - ti += group->packages[pi]->typeIdOffset; - - const uint32_t identifier = findEntry(group, ti, name, nameLen, + *ti += group->packages[pi]->typeIdOffset; + const uint32_t identifier = findEntry(group, *ti, name, nameLen, outTypeSpecFlags); if (identifier != 0) { if (fakePublic && outTypeSpecFlags) { @@ -5009,8 +5113,9 @@ uint32_t ResTable::findEntry(const PackageGroup* group, ssize_t typeIndex, const const size_t typeCount = typeList.size(); for (size_t i = 0; i < typeCount; i++) { const Type* t = typeList[i]; - const ssize_t ei = t->package->keyStrings.indexOfString(name, nameLen); - if (ei < 0) { + const base::expected ei = + t->package->keyStrings.indexOfString(name, nameLen); + if (!ei.has_value()) { continue; } @@ -5025,7 +5130,7 @@ uint32_t ResTable::findEntry(const PackageGroup* group, ssize_t typeIndex, const continue; } - if (dtohl(entry->key.index) == (size_t) ei) { + if (dtohl(entry->key.index) == (size_t) *ei) { uint32_t resId = Res_MAKEID(group->id - 1, typeIndex, iter.index()); if (outTypeSpecFlags) { Entry result; @@ -6187,8 +6292,9 @@ void ResTable::forEachConfiguration(bool ignoreMipmap, bool ignoreAndroidPackage for (size_t k = 0; k < numTypes; k++) { const Type* type = typeList[k]; const ResStringPool& typeStrings = type->package->typeStrings; - if (ignoreMipmap && typeStrings.string8ObjectAt( - type->typeSpec->id - 1) == "mipmap") { + const base::expected typeStr = typeStrings.string8ObjectAt( + type->typeSpec->id - 1); + if (ignoreMipmap && typeStr.has_value() && *typeStr == "mipmap") { continue; } @@ -6244,24 +6350,18 @@ void ResTable::getLocales(Vector* locales, bool includeSystemLocales, StringPoolRef::StringPoolRef(const ResStringPool* pool, uint32_t index) : mPool(pool), mIndex(index) {} -const char* StringPoolRef::string8(size_t* outLen) const { - if (mPool != NULL) { - return mPool->string8At(mIndex, outLen); +base::expected StringPoolRef::string8() const { + if (LIKELY(mPool != NULL)) { + return mPool->string8At(mIndex); } - if (outLen != NULL) { - *outLen = 0; - } - return NULL; + return base::unexpected(std::nullopt); } -const char16_t* StringPoolRef::string16(size_t* outLen) const { - if (mPool != NULL) { - return mPool->stringAt(mIndex, outLen); +base::expected StringPoolRef::string16() const { + if (LIKELY(mPool != NULL)) { + return mPool->stringAt(mIndex); } - if (outLen != NULL) { - *outLen = 0; - } - return NULL; + return base::unexpected(std::nullopt); } bool ResTable::getResourceFlags(uint32_t resID, uint32_t* outFlags) const { @@ -7380,13 +7480,13 @@ void ResTable::print_value(const Package* pkg, const Res_value& value) const printf("(dynamic attribute) 0x%08x\n", value.data); } else if (value.dataType == Res_value::TYPE_STRING) { size_t len; - const char* str8 = pkg->header->values.string8At( - value.data, &len); + const char* str8 = UnpackOptionalString(pkg->header->values.string8At( + value.data), &len); if (str8 != NULL) { printf("(string8) \"%s\"\n", normalizeForOutput(str8).string()); } else { - const char16_t* str16 = pkg->header->values.stringAt( - value.data, &len); + const char16_t* str16 = UnpackOptionalString(pkg->header->values.stringAt( + value.data), &len); if (str16 != NULL) { printf("(string16) \"%s\"\n", normalizeForOutput(String8(str16, len).string()).string()); diff --git a/libs/androidfw/ResourceUtils.cpp b/libs/androidfw/ResourceUtils.cpp index c63dff8f9104..a34aa7239250 100644 --- a/libs/androidfw/ResourceUtils.cpp +++ b/libs/androidfw/ResourceUtils.cpp @@ -48,61 +48,76 @@ bool ExtractResourceName(const StringPiece& str, StringPiece* out_package, Strin !(has_type_separator && out_type->empty()); } -bool ToResourceName(const StringPoolRef& type_string_ref, - const StringPoolRef& entry_string_ref, - const StringPiece& package_name, - AssetManager2::ResourceName* out_name) { - out_name->package = package_name.data(); - out_name->package_len = package_name.size(); - - out_name->type = type_string_ref.string8(&out_name->type_len); - out_name->type16 = nullptr; - if (out_name->type == nullptr) { - out_name->type16 = type_string_ref.string16(&out_name->type_len); - if (out_name->type16 == nullptr) { - return false; +base::expected ToResourceName( + const StringPoolRef& type_string_ref, const StringPoolRef& entry_string_ref, + const StringPiece& package_name) { + AssetManager2::ResourceName name{ + .package = package_name.data(), + .package_len = package_name.size(), + }; + + if (base::expected type_str = type_string_ref.string8()) { + name.type = type_str->data(); + name.type_len = type_str->size(); + } else if (UNLIKELY(IsIOError(type_str))) { + return base::unexpected(type_str.error()); + } + + if (name.type == nullptr) { + if (base::expected type16_str = type_string_ref.string16()) { + name.type16 = type16_str->data(); + name.type_len = type16_str->size(); + } else if (!type16_str.has_value()) { + return base::unexpected(type16_str.error()); } } - out_name->entry = entry_string_ref.string8(&out_name->entry_len); - out_name->entry16 = nullptr; - if (out_name->entry == nullptr) { - out_name->entry16 = entry_string_ref.string16(&out_name->entry_len); - if (out_name->entry16 == nullptr) { - return false; + if (base::expected entry_str = entry_string_ref.string8()) { + name.entry = entry_str->data(); + name.entry_len = entry_str->size(); + } else if (UNLIKELY(IsIOError(entry_str))) { + return base::unexpected(entry_str.error()); + } + + if (name.entry == nullptr) { + if (base::expected entry16_str = entry_string_ref.string16()) { + name.entry16 = entry16_str->data(); + name.entry_len = entry16_str->size(); + } else if (!entry16_str.has_value()) { + return base::unexpected(entry16_str.error()); } } - return true; + return name; } -std::string ToFormattedResourceString(AssetManager2::ResourceName* resource_name) { +std::string ToFormattedResourceString(const AssetManager2::ResourceName& resource_name) { std::string result; - if (resource_name->package != nullptr) { - result.append(resource_name->package, resource_name->package_len); + if (resource_name.package != nullptr) { + result.append(resource_name.package, resource_name.package_len); } - if (resource_name->type != nullptr || resource_name->type16 != nullptr) { + if (resource_name.type != nullptr || resource_name.type16 != nullptr) { if (!result.empty()) { result += ":"; } - if (resource_name->type != nullptr) { - result.append(resource_name->type, resource_name->type_len); + if (resource_name.type != nullptr) { + result.append(resource_name.type, resource_name.type_len); } else { - result += util::Utf16ToUtf8(StringPiece16(resource_name->type16, resource_name->type_len)); + result += util::Utf16ToUtf8(StringPiece16(resource_name.type16, resource_name.type_len)); } } - if (resource_name->entry != nullptr || resource_name->entry16 != nullptr) { + if (resource_name.entry != nullptr || resource_name.entry16 != nullptr) { if (!result.empty()) { result += "/"; } - if (resource_name->entry != nullptr) { - result.append(resource_name->entry, resource_name->entry_len); + if (resource_name.entry != nullptr) { + result.append(resource_name.entry, resource_name.entry_len); } else { - result += util::Utf16ToUtf8(StringPiece16(resource_name->entry16, resource_name->entry_len)); + result += util::Utf16ToUtf8(StringPiece16(resource_name.entry16, resource_name.entry_len)); } } diff --git a/libs/androidfw/StreamingZipInflater.cpp b/libs/androidfw/StreamingZipInflater.cpp index b39b5f0b8b36..1c5e5d44c845 100644 --- a/libs/androidfw/StreamingZipInflater.cpp +++ b/libs/androidfw/StreamingZipInflater.cpp @@ -70,13 +70,13 @@ StreamingZipInflater::StreamingZipInflater(int fd, off64_t compDataStart, /* * Streaming access to compressed data held in an mmapped region of memory */ -StreamingZipInflater::StreamingZipInflater(FileMap* dataMap, size_t uncompSize) { +StreamingZipInflater::StreamingZipInflater(const incfs::IncFsFileMap* dataMap, size_t uncompSize) { mFd = -1; mDataMap = dataMap; mOutTotalSize = uncompSize; - mInTotalSize = dataMap->getDataLength(); + mInTotalSize = dataMap->length(); - mInBuf = (uint8_t*) dataMap->getDataPtr(); + mInBuf = (uint8_t*) dataMap->unsafe_data(); // IncFs safety handled in zlib. mInBufSize = mInTotalSize; mOutBufSize = StreamingZipInflater::OUTPUT_CHUNK_SIZE; diff --git a/libs/androidfw/ZipFileRO.cpp b/libs/androidfw/ZipFileRO.cpp index e77ac3df474c..52e7a70521a1 100644 --- a/libs/androidfw/ZipFileRO.cpp +++ b/libs/androidfw/ZipFileRO.cpp @@ -232,6 +232,29 @@ FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const return newMap; } +/* + * Create a new incfs::IncFsFileMap object that spans the data in "entry". + */ +std::optional ZipFileRO::createEntryIncFsFileMap(ZipEntryRO entry) const +{ + const _ZipEntryRO *zipEntry = reinterpret_cast<_ZipEntryRO*>(entry); + const ZipEntry& ze = zipEntry->entry; + int fd = GetFileDescriptor(mHandle); + size_t actualLen = 0; + + if (ze.method == kCompressStored) { + actualLen = ze.uncompressed_length; + } else { + actualLen = ze.compressed_length; + } + + incfs::IncFsFileMap newMap; + if (!newMap.Create(fd, ze.offset, actualLen, mFileName)) { + return std::nullopt; + } + return std::move(newMap); +} + /* * Uncompress an entry, in its entirety, into the provided output buffer. * diff --git a/libs/androidfw/ZipUtils.cpp b/libs/androidfw/ZipUtils.cpp index 568e3b63d67f..58fc5bbbab5e 100644 --- a/libs/androidfw/ZipUtils.cpp +++ b/libs/androidfw/ZipUtils.cpp @@ -40,7 +40,7 @@ class FileReader : public zip_archive::Reader { explicit FileReader(FILE* fp) : Reader(), mFp(fp), mCurrentOffset(0) { } - bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const { + bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const override { // Data is usually requested sequentially, so this helps avoid pointless // fseeks every time we perform a read. There's an impedence mismatch // here because the original API was designed around pread and pwrite. @@ -71,7 +71,7 @@ class FdReader : public zip_archive::Reader { explicit FdReader(int fd) : mFd(fd) { } - bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const { + bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const override { return android::base::ReadFullyAtOffset(mFd, buf, len, offset); } @@ -81,22 +81,27 @@ class FdReader : public zip_archive::Reader { class BufferReader : public zip_archive::Reader { public: - BufferReader(const void* input, size_t inputSize) : Reader(), - mInput(reinterpret_cast(input)), + BufferReader(incfs::map_ptr input, size_t inputSize) : Reader(), + mInput(input.convert()), mInputSize(inputSize) { } - bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const { + bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const override { if (mInputSize < len || offset > mInputSize - len) { return false; } - memcpy(buf, mInput + offset, len); + const incfs::map_ptr pos = mInput.offset(offset); + if (!pos.verify(len)) { + return false; + } + + memcpy(buf, pos.unsafe_ptr(), len); return true; } private: - const uint8_t* mInput; + const incfs::map_ptr mInput; const size_t mInputSize; }; @@ -138,7 +143,7 @@ class BufferWriter : public zip_archive::Writer { return (zip_archive::Inflate(reader, compressedLen, uncompressedLen, &writer, nullptr) == 0); } -/*static*/ bool ZipUtils::inflateToBuffer(const void* in, void* buf, +/*static*/ bool ZipUtils::inflateToBuffer(incfs::map_ptr in, void* buf, long uncompressedLen, long compressedLen) { BufferReader reader(in, compressedLen); diff --git a/libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp b/libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp index 96d44ab8e45c..5309ab2b6e20 100644 --- a/libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp +++ b/libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp @@ -31,9 +31,6 @@ using android::LoadedArsc; using android::StringPiece; extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - - std::unique_ptr loaded_arsc = - LoadedArsc::Load(StringPiece(reinterpret_cast(data), size)); - + std::unique_ptr loaded_arsc = LoadedArsc::Load(data, size); return 0; } \ No newline at end of file diff --git a/libs/androidfw/include/androidfw/Asset.h b/libs/androidfw/include/androidfw/Asset.h index 298509eb37a1..80bae20f3419 100644 --- a/libs/androidfw/include/androidfw/Asset.h +++ b/libs/androidfw/include/androidfw/Asset.h @@ -23,18 +23,18 @@ #include #include - #include +#include #include +#include + #include #include #include namespace android { -class FileMap; - /* * Instances of this class provide read-only operations on a byte stream. * @@ -49,6 +49,8 @@ class FileMap; class Asset { public: virtual ~Asset(void) = default; + Asset(const Asset& src) = delete; + Asset& operator=(const Asset& src) = delete; static int32_t getGlobalCount(); static String8 getAssetAllocations(); @@ -87,8 +89,19 @@ public: /* * Get a pointer to a buffer with the entire contents of the file. + * If `aligned` is true, the buffer data will be aligned to a 4-byte boundary. + * + * Use this function if the asset can never reside on IncFs. */ - virtual const void* getBuffer(bool wordAligned) = 0; + virtual const void* getBuffer(bool aligned) = 0; + + /* + * Get a incfs::map_ptr to a buffer with the entire contents of the file. + * If `aligned` is true, the buffer data will be aligned to a 4-byte boundary. + * + * Use this function if the asset can potentially reside on IncFs. + */ + virtual incfs::map_ptr getIncFsBuffer(bool aligned) = 0; /* * Get the total amount of data that can be read. @@ -152,10 +165,6 @@ protected: AccessMode getAccessMode(void) const { return mAccessMode; } private: - /* these operations are not implemented */ - Asset(const Asset& src); - Asset& operator=(const Asset& src); - /* AssetManager needs access to our "create" functions */ friend class AssetManager; friend class ApkAssets; @@ -169,8 +178,7 @@ private: /* * Create the asset from a named, compressed file on disk (e.g. ".gz"). */ - static Asset* createFromCompressedFile(const char* fileName, - AccessMode mode); + static Asset* createFromCompressedFile(const char* fileName, AccessMode mode); #if 0 /* @@ -200,31 +208,21 @@ private: /* * Create the asset from a memory-mapped file segment. * - * The asset takes ownership of the FileMap. - */ - static Asset* createFromUncompressedMap(FileMap* dataMap, AccessMode mode); - - /* - * Create the asset from a memory-mapped file segment. - * - * The asset takes ownership of the FileMap and the file descriptor "fd". The file descriptor is - * used to request new file descriptors using "openFileDescriptor". + * The asset takes ownership of the incfs::IncFsFileMap and the file descriptor "fd". The + * file descriptor is used to request new file descriptors using "openFileDescriptor". */ - static std::unique_ptr createFromUncompressedMap(std::unique_ptr dataMap, - base::unique_fd fd, AccessMode mode); + static std::unique_ptr createFromUncompressedMap(incfs::IncFsFileMap&& dataMap, + AccessMode mode, + base::unique_fd fd = {}); /* * Create the asset from a memory-mapped file segment with compressed * data. * - * The asset takes ownership of the FileMap. + * The asset takes ownership of the incfs::IncFsFileMap. */ - static Asset* createFromCompressedMap(FileMap* dataMap, - size_t uncompressedLen, AccessMode mode); - - static std::unique_ptr createFromCompressedMap(std::unique_ptr dataMap, - size_t uncompressedLen, AccessMode mode); - + static std::unique_ptr createFromCompressedMap(incfs::IncFsFileMap&& dataMap, + size_t uncompressedLen, AccessMode mode); /* * Create from a reference-counted chunk of shared memory. @@ -252,7 +250,7 @@ private: class _FileAsset : public Asset { public: _FileAsset(void); - virtual ~_FileAsset(void); + ~_FileAsset(void) override; /* * Use a piece of an already-open file. @@ -266,21 +264,24 @@ public: * * On success, the object takes ownership of "dataMap" and "fd". */ - status_t openChunk(FileMap* dataMap, base::unique_fd fd); + status_t openChunk(incfs::IncFsFileMap&& dataMap, base::unique_fd fd); /* * Standard Asset interfaces. */ - virtual ssize_t read(void* buf, size_t count); - virtual off64_t seek(off64_t offset, int whence); - virtual void close(void); - virtual const void* getBuffer(bool wordAligned); - virtual off64_t getLength(void) const { return mLength; } - virtual off64_t getRemainingLength(void) const { return mLength-mOffset; } - virtual int openFileDescriptor(off64_t* outStart, off64_t* outLength) const; - virtual bool isAllocated(void) const { return mBuf != NULL; } + ssize_t read(void* buf, size_t count) override; + off64_t seek(off64_t offset, int whence) override; + void close(void) override; + const void* getBuffer(bool aligned) override; + incfs::map_ptr getIncFsBuffer(bool aligned) override; + off64_t getLength(void) const override { return mLength; } + off64_t getRemainingLength(void) const override { return mLength-mOffset; } + int openFileDescriptor(off64_t* outStart, off64_t* outLength) const override; + bool isAllocated(void) const override { return mBuf != NULL; } private: + incfs::map_ptr ensureAlignment(const incfs::IncFsFileMap& map); + off64_t mStart; // absolute file offset of start of chunk off64_t mLength; // length of the chunk off64_t mOffset; // current local offset, 0 == mStart @@ -295,10 +296,8 @@ private: */ enum { kReadVsMapThreshold = 4096 }; - FileMap* mMap; // for memory map - unsigned char* mBuf; // for read - - const void* ensureAlignment(FileMap* map); + unsigned char* mBuf; // for read + std::optional mMap; // for memory map }; @@ -323,7 +322,7 @@ public: * * On success, the object takes ownership of "fd". */ - status_t openChunk(FileMap* dataMap, size_t uncompressedLen); + status_t openChunk(incfs::IncFsFileMap&& dataMap, size_t uncompressedLen); /* * Standard Asset interfaces. @@ -331,24 +330,23 @@ public: virtual ssize_t read(void* buf, size_t count); virtual off64_t seek(off64_t offset, int whence); virtual void close(void); - virtual const void* getBuffer(bool wordAligned); + virtual const void* getBuffer(bool aligned); + virtual incfs::map_ptr getIncFsBuffer(bool aligned); virtual off64_t getLength(void) const { return mUncompressedLen; } virtual off64_t getRemainingLength(void) const { return mUncompressedLen-mOffset; } virtual int openFileDescriptor(off64_t* /* outStart */, off64_t* /* outLength */) const { return -1; } virtual bool isAllocated(void) const { return mBuf != NULL; } private: - off64_t mStart; // offset to start of compressed data - off64_t mCompressedLen; // length of the compressed data - off64_t mUncompressedLen; // length of the uncompressed data - off64_t mOffset; // current offset, 0 == start of uncomp data - - FileMap* mMap; // for memory-mapped input - int mFd; // for file input - - class StreamingZipInflater* mZipInflater; // for streaming large compressed assets - - unsigned char* mBuf; // for getBuffer() + off64_t mStart; // offset to start of compressed data + off64_t mCompressedLen; // length of the compressed data + off64_t mUncompressedLen; // length of the uncompressed data + off64_t mOffset; // current offset, 0 == start of uncomp data + int mFd; // for file input + + class StreamingZipInflater* mZipInflater; // for streaming large compressed assets + unsigned char* mBuf; // for getBuffer() + std::optional mMap; // for memory-mapped input }; // need: shared mmap version? diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index 30ef25c6a516..4e993b0838a2 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -131,8 +131,8 @@ class AssetManager2 { bool GetOverlayablesToString(const android::StringPiece& package_name, std::string* out) const; - const std::unordered_map* - GetOverlayableMapForPackage(uint32_t package_id) const; + const std::unordered_map* GetOverlayableMapForPackage( + uint32_t package_id) const; // Returns whether the resources.arsc of any loaded apk assets is allocated in RAM (not mmapped). bool ContainsAllocatedTable() const; @@ -145,14 +145,16 @@ class AssetManager2 { return configuration_; } - // Returns all configurations for which there are resources defined. This includes resource - // configurations in all the ApkAssets set for this AssetManager. + // Returns all configurations for which there are resources defined, or an I/O error if reading + // resource data failed. + // + // This includes resource configurations in all the ApkAssets set for this AssetManager. // If `exclude_system` is set to true, resource configurations from system APKs // ('android' package, other libraries) will be excluded from the list. // If `exclude_mipmap` is set to true, resource configurations defined for resource type 'mipmap' // will be excluded from the list. - std::set GetResourceConfigurations(bool exclude_system = false, - bool exclude_mipmap = false) const; + base::expected, IOError> GetResourceConfigurations( + bool exclude_system = false, bool exclude_mipmap = false) const; // Returns all the locales for which there are resources defined. This includes resource // locales in all the ApkAssets set for this AssetManager. @@ -194,77 +196,116 @@ class AssetManager2 { std::unique_ptr OpenNonAsset(const std::string& filename, ApkAssetsCookie cookie, Asset::AccessMode mode) const; - // Populates the `out_name` parameter with resource name information. - // Utf8 strings are preferred, and only if they are unavailable are - // the Utf16 variants populated. - // Returns false if the resource was not found or the name was missing/corrupt. - bool GetResourceName(uint32_t resid, ResourceName* out_name) const; - - // Populates `out_flags` with the bitmask of configuration axis that this resource varies with. - // See ResTable_config for the list of configuration axis. - // Returns false if the resource was not found. - bool GetResourceFlags(uint32_t resid, uint32_t* out_flags) const; + // Returns the resource name of the specified resource ID. + // + // Utf8 strings are preferred, and only if they are unavailable are the Utf16 variants populated. + // + // Returns a null error if the name is missing/corrupt, or an I/O error if reading resource data + // failed. + base::expected GetResourceName(uint32_t resid) const; // Finds the resource ID assigned to `resource_name`. + // // `resource_name` must be of the form '[package:][type/]entry'. // If no package is specified in `resource_name`, then `fallback_package` is used as the package. // If no type is specified in `resource_name`, then `fallback_type` is used as the type. - // Returns 0x0 if no resource by that name was found. - uint32_t GetResourceId(const std::string& resource_name, const std::string& fallback_type = {}, - const std::string& fallback_package = {}) const; - - // Retrieves the best matching resource with ID `resid`. The resource value is filled into - // `out_value` and the configuration for the selected value is populated in `out_selected_config`. - // `out_flags` holds the same flags as retrieved with GetResourceFlags(). - // If `density_override` is non-zero, the configuration to match against is overridden with that - // density. // - // Returns a valid cookie if the resource was found. If the resource was not found, or if the - // resource was a map/bag type, then kInvalidCookie is returned. If `may_be_bag` is false, - // this function logs if the resource was a map/bag type before returning kInvalidCookie. - ApkAssetsCookie GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override, - Res_value* out_value, ResTable_config* out_selected_config, - uint32_t* out_flags) const; - - // Resolves the resource reference in `in_out_value` if the data type is - // Res_value::TYPE_REFERENCE. - // `cookie` is the ApkAssetsCookie of the reference in `in_out_value`. - // `in_out_value` is the reference to resolve. The result is placed back into this object. - // `in_out_flags` is the type spec flags returned from calls to GetResource() or - // GetResourceFlags(). Configuration flags of the values pointed to by the reference - // are OR'd together with `in_out_flags`. - // `in_out_config` is populated with the configuration for which the resolved value was defined. - // `out_last_reference` is populated with the last reference ID before resolving to an actual - // value. This is only initialized if the passed in `in_out_value` is a reference. - // Returns the cookie of the APK the resolved resource was defined in, or kInvalidCookie if - // it was not found. - ApkAssetsCookie ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value, - ResTable_config* in_out_selected_config, uint32_t* in_out_flags, - uint32_t* out_last_reference) const; + // Returns a null error if no resource by that name was found, or an I/O error if reading resource + // data failed. + base::expected GetResourceId( + const std::string& resource_name, const std::string& fallback_type = {}, + const std::string& fallback_package = {}) const; + + struct SelectedValue { + friend AssetManager2; + friend Theme; + SelectedValue() = default; + SelectedValue(const ResolvedBag* bag, const ResolvedBag::Entry& entry) : + cookie(entry.cookie), data(entry.value.data), type(entry.value.dataType), + flags(bag->type_spec_flags), resid(0U), config({}) {}; + + // The cookie representing the ApkAssets in which the value resides. + ApkAssetsCookie cookie = kInvalidCookie; - // Resets the resource resolution structures in preparation for the next resource retrieval. - void ResetResourceResolution() const; + // The data for this value, as interpreted according to `type`. + Res_value::data_type data; - // Enables or disables resource resolution logging. Clears stored steps when disabled. - void SetResourceResolutionLoggingEnabled(bool enabled); + // Type of the data value. + uint8_t type; - // Returns formatted log of last resource resolution path, or empty if no resource has been - // resolved yet. - std::string GetLastResourceResolution() const; + // The bitmask of configuration axis that this resource varies with. + // See ResTable_config::CONFIG_*. + uint32_t flags; + + // The resource ID from which this value was resolved. + uint32_t resid; - const std::vector GetBagResIdStack(uint32_t resid); + // The configuration for which the resolved value was defined. + ResTable_config config; + + private: + SelectedValue(uint8_t value_type, Res_value::data_type value_data, ApkAssetsCookie cookie, + uint32_t type_flags, uint32_t resid, const ResTable_config& config) : + cookie(cookie), data(value_data), type(value_type), flags(type_flags), + resid(resid), config(config) {}; + }; + + // Retrieves the best matching resource value with ID `resid`. + // + // If `may_be_bag` is false, this function logs if the resource was a map/bag type and returns a + // null result. If `density_override` is non-zero, the configuration to match against is + // overridden with that density. + // + // Returns a null error if a best match could not be found, or an I/O error if reading resource + // data failed. + base::expected GetResource(uint32_t resid, bool may_be_bag = false, + uint16_t density_override = 0U) const; + + // Resolves the resource referenced in `value` if the type is Res_value::TYPE_REFERENCE. + // + // If the data type is not Res_value::TYPE_REFERENCE, no work is done. Configuration flags of the + // values pointed to by the reference are OR'd into `value.flags`. + // + // Returns a null error if the resource could not be resolved, or an I/O error if reading + // resource data failed. + base::expected ResolveReference(SelectedValue& value) const; // Retrieves the best matching bag/map resource with ID `resid`. + // // This method will resolve all parent references for this bag and merge keys with the child. // To iterate over the keys, use the following idiom: // - // const AssetManager2::ResolvedBag* bag = asset_manager->GetBag(id); - // if (bag != nullptr) { - // for (auto iter = begin(bag); iter != end(bag); ++iter) { + // base::expected bag = asset_manager->GetBag(id); + // if (bag.has_value()) { + // for (auto iter = begin(*bag); iter != end(*bag); ++iter) { // ... // } // } - const ResolvedBag* GetBag(uint32_t resid); + // + // Returns a null error if a best match could not be found, or an I/O error if reading resource + // data failed. + base::expected GetBag(uint32_t resid) const; + + // Retrieves the best matching bag/map resource of the resource referenced in `value`. + // + // If `value.type` is not Res_value::TYPE_REFERENCE, a null result is returned. + // Configuration flags of the bag pointed to by the reference are OR'd into `value.flags`. + // + // Returns a null error if a best match could not be found, or an I/O error if reading resource + // data failed. + base::expected ResolveBag(SelectedValue& value) const; + + const std::vector GetBagResIdStack(uint32_t resid) const; + + // Resets the resource resolution structures in preparation for the next resource retrieval. + void ResetResourceResolution() const; + + // Enables or disables resource resolution logging. Clears stored steps when disabled. + void SetResourceResolutionLoggingEnabled(bool enabled); + + // Returns formatted log of last resource resolution path, or empty if no resource has been + // resolved yet. + std::string GetLastResourceResolution() const; // Creates a new Theme from this AssetManager. std::unique_ptr NewTheme(); @@ -286,11 +327,15 @@ class AssetManager2 { private: DISALLOW_COPY_AND_ASSIGN(AssetManager2); + struct TypeConfig { + incfs::verified_map_ptr type; + ResTable_config config; + }; + // A collection of configurations and their associated ResTable_type that match the current // AssetManager configuration. struct FilteredConfigGroup { - std::vector configurations; - std::vector types; + std::vector type_configs; }; // Represents an single package. @@ -331,9 +376,7 @@ class AssetManager2 { }; // Finds the best entry for `resid` from the set of ApkAssets. The entry can be a simple - // Res_value, or a complex map/bag type. If successful, it is available in `out_entry`. - // Returns kInvalidCookie on failure. Otherwise, the return value is the cookie associated with - // the ApkAssets in which the entry was found. + // Res_value, or a complex map/bag type. Returns a null result if a best entry cannot be found. // // `density_override` overrides the density of the current configuration when doing a search. // @@ -347,13 +390,15 @@ class AssetManager2 { // // NOTE: FindEntry takes care of ensuring that structs within FindEntryResult have been properly // bounds-checked. Callers of FindEntry are free to trust the data if this method succeeds. - ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match, - bool ignore_configuration, FindEntryResult* out_entry) const; + base::expected FindEntry(uint32_t resid, + uint16_t density_override, + bool stop_at_first_match, + bool ignore_configuration) const; - ApkAssetsCookie FindEntryInternal(const PackageGroup& package_group, uint8_t type_idx, - uint16_t entry_idx, const ResTable_config& desired_config, - bool /*stop_at_first_match*/, - bool ignore_configuration, FindEntryResult* out_entry) const; + base::expected FindEntryInternal( + const PackageGroup& package_group, uint8_t type_idx, uint16_t entry_idx, + const ResTable_config& desired_config, bool stop_at_first_match, + bool ignore_configuration) const; // Assigns package IDs to all shared library ApkAssets. // Should be called whenever the ApkAssets are changed. @@ -372,7 +417,8 @@ class AssetManager2 { // AssetManager2::GetBag(resid) wraps this function to track which resource ids have already // been seen while traversing bag parents. - const ResolvedBag* GetBag(uint32_t resid, std::vector& child_resids); + base::expected GetBag( + uint32_t resid, std::vector& child_resids) const; // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must // have a longer lifetime. @@ -394,11 +440,11 @@ class AssetManager2 { // Cached set of bags. These are cached because they can inherit keys from parent bags, // which involves some calculation. - std::unordered_map> cached_bags_; + mutable std::unordered_map> cached_bags_; // Cached set of bag resid stacks for each bag. These are cached because they might be requested // a number of times for each view during View inspection. - std::unordered_map> cached_bag_resid_stacks_; + mutable std::unordered_map> cached_bag_resid_stacks_; // Whether or not to save resource resolution steps bool resource_resolution_logging_enabled_ = false; @@ -455,55 +501,53 @@ class Theme { public: ~Theme(); - // Applies the style identified by `resid` to this theme. This can be called - // multiple times with different styles. By default, any theme attributes that - // are already defined before this call are not overridden. If `force` is set - // to true, this behavior is changed and all theme attributes from the style at - // `resid` are applied. - // Returns false if the style failed to apply. - bool ApplyStyle(uint32_t resid, bool force = false); + // Applies the style identified by `resid` to this theme. + // + // This can be called multiple times with different styles. By default, any theme attributes that + // are already defined before this call are not overridden. If `force` is set to true, this + // behavior is changed and all theme attributes from the style at `resid` are applied. + // + // Returns a null error if the style could not be applied, or an I/O error if reading resource + // data failed. + base::expected ApplyStyle(uint32_t resid, bool force = false); - // Sets this Theme to be a copy of `o` if `o` has the same AssetManager as this Theme. - // If `o` does not have the same AssetManager as this theme, only attributes from ApkAssets loaded - // into both AssetManagers will be copied to this theme. - void SetTo(const Theme& o); + // Sets this Theme to be a copy of `other` if `other` has the same AssetManager as this Theme. + // + // If `other` does not have the same AssetManager as this theme, only attributes from ApkAssets + // loaded into both AssetManagers will be copied to this theme. + // + // Returns an I/O error if reading resource data failed. + base::expected SetTo(const Theme& other); void Clear(); - void Dump() const; + // Retrieves the value of attribute ID `resid` in the theme. + // + // NOTE: This function does not do reference traversal. If you want to follow references to other + // resources to get the "real" value to use, you need to call ResolveReference() after this + // function. + std::optional GetAttribute(uint32_t resid) const; - inline const AssetManager2* GetAssetManager() const { + // This is like AssetManager2::ResolveReference(), but also takes care of resolving attribute + // references to the theme. + base::expected ResolveAttributeReference( + AssetManager2::SelectedValue& value) const; + + AssetManager2* GetAssetManager() { return asset_manager_; } - inline AssetManager2* GetAssetManager() { + const AssetManager2* GetAssetManager() const { return asset_manager_; } // Returns a bit mask of configuration changes that will impact this // theme (and thus require completely reloading it). - inline uint32_t GetChangingConfigurations() const { + uint32_t GetChangingConfigurations() const { return type_spec_flags_; } - // Retrieve a value in the theme. If the theme defines this value, returns an asset cookie - // indicating which ApkAssets it came from and populates `out_value` with the value. - // `out_flags` is populated with a bitmask of the configuration axis with which the resource - // varies. - // - // If the attribute is not found, returns kInvalidCookie. - // - // NOTE: This function does not do reference traversal. If you want to follow references to other - // resources to get the "real" value to use, you need to call ResolveReference() after this - // function. - ApkAssetsCookie GetAttribute(uint32_t resid, Res_value* out_value, uint32_t* out_flags) const; - - // This is like AssetManager2::ResolveReference(), but also takes - // care of resolving attribute references to the theme. - ApkAssetsCookie ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value, - ResTable_config* in_out_selected_config = nullptr, - uint32_t* in_out_type_spec_flags = nullptr, - uint32_t* out_last_ref = nullptr) const; + void Dump() const; private: DISALLOW_COPY_AND_ASSIGN(Theme); diff --git a/libs/androidfw/include/androidfw/AttributeResolution.h b/libs/androidfw/include/androidfw/AttributeResolution.h index d71aad29d917..1a69a309d365 100644 --- a/libs/androidfw/include/androidfw/AttributeResolution.h +++ b/libs/androidfw/include/androidfw/AttributeResolution.h @@ -45,20 +45,28 @@ enum { // `out_values` must NOT be nullptr. // `out_indices` may be nullptr. -bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_resid, - uint32_t* src_values, size_t src_values_length, uint32_t* attrs, - size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); +base::expected ResolveAttrs(Theme* theme, uint32_t def_style_attr, + uint32_t def_style_resid, uint32_t* src_values, + size_t src_values_length, uint32_t* attrs, + size_t attrs_length, uint32_t* out_values, + uint32_t* out_indices); // `out_values` must NOT be nullptr. // `out_indices` is NOT optional and must NOT be nullptr. -void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, - uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length, - uint32_t* out_values, uint32_t* out_indices); +base::expected ApplyStyle(Theme* theme, ResXMLParser* xml_parser, + uint32_t def_style_attr, + uint32_t def_style_resid, + const uint32_t* attrs, size_t attrs_length, + uint32_t* out_values, uint32_t* out_indices); // `out_values` must NOT be nullptr. // `out_indices` may be nullptr. -bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs, - size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); +base::expected RetrieveAttributes(AssetManager2* assetmanager, + ResXMLParser* xml_parser, + uint32_t* attrs, + size_t attrs_length, + uint32_t* out_values, + uint32_t* out_indices); } // namespace android diff --git a/libs/androidfw/include/androidfw/Chunk.h b/libs/androidfw/include/androidfw/Chunk.h index a0f23433c676..f1c43b298e53 100644 --- a/libs/androidfw/include/androidfw/Chunk.h +++ b/libs/androidfw/include/androidfw/Chunk.h @@ -36,7 +36,7 @@ namespace android { // of the chunk. class Chunk { public: - explicit Chunk(const ResChunk_header* chunk) : device_chunk_(chunk) {} + explicit Chunk(incfs::verified_map_ptr chunk) : device_chunk_(chunk) {} // Returns the type of the chunk. Caller need not worry about endianness. inline int type() const { return dtohs(device_chunk_->type); } @@ -49,21 +49,18 @@ class Chunk { inline size_t header_size() const { return dtohs(device_chunk_->headerSize); } template - inline const T* header() const { - if (header_size() >= MinSize) { - return reinterpret_cast(device_chunk_); - } - return nullptr; + inline incfs::map_ptr header() const { + return (header_size() >= MinSize) ? device_chunk_.convert() : nullptr; } - inline const void* data_ptr() const { - return reinterpret_cast(device_chunk_) + header_size(); + inline incfs::map_ptr data_ptr() const { + return device_chunk_.offset(header_size()); } inline size_t data_size() const { return size() - header_size(); } private: - const ResChunk_header* device_chunk_; + const incfs::verified_map_ptr device_chunk_; }; // Provides a Java style iterator over an array of ResChunk_header's. @@ -84,11 +81,11 @@ class Chunk { // class ChunkIterator { public: - ChunkIterator(const void* data, size_t len) - : next_chunk_(reinterpret_cast(data)), + ChunkIterator(incfs::map_ptr data, size_t len) + : next_chunk_(data.convert()), len_(len), last_error_(nullptr) { - CHECK(next_chunk_ != nullptr) << "data can't be nullptr"; + CHECK((bool) next_chunk_) << "data can't be null"; if (len_ != 0) { VerifyNextChunk(); } @@ -113,7 +110,7 @@ class ChunkIterator { // Returns false if there was an error. For legacy purposes. bool VerifyNextChunkNonFatal(); - const ResChunk_header* next_chunk_; + incfs::map_ptr next_chunk_; size_t len_; const char* last_error_; bool last_error_was_fatal_ = true; diff --git a/libs/androidfw/include/androidfw/Errors.h b/libs/androidfw/include/androidfw/Errors.h new file mode 100644 index 000000000000..948162d10480 --- /dev/null +++ b/libs/androidfw/include/androidfw/Errors.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2020 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 ANDROIDFW_ERRORS_H_ +#define ANDROIDFW_ERRORS_H_ + +#include +#include + +#include + +namespace android { + +enum class IOError { + // Used when reading a file residing on an IncFs file-system times out. + PAGES_MISSING = -1, +}; + +// Represents an absent result or an I/O error. +using NullOrIOError = std::variant; + +// Checks whether the result holds an unexpected I/O error. +template +static inline bool IsIOError(const base::expected result) { + return !result.has_value() && std::holds_alternative(result.error()); +} + +static inline IOError GetIOError(const NullOrIOError& error) { + return std::get(error); +} + +} // namespace android + +#endif //ANDROIDFW_ERRORS_H_ diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h index ab0f47f025d2..fdab03ba2de4 100644 --- a/libs/androidfw/include/androidfw/Idmap.h +++ b/libs/androidfw/include/androidfw/Idmap.h @@ -40,8 +40,8 @@ class IdmapResMap; class OverlayStringPool : public ResStringPool { public: virtual ~OverlayStringPool(); - const char16_t* stringAt(size_t idx, size_t* outLen) const override; - const char* string8At(size_t idx, size_t* outLen) const override; + base::expected stringAt(size_t idx) const override; + base::expected string8At(size_t idx) const override; size_t size() const override; explicit OverlayStringPool(const LoadedIdmap* loaded_idmap); diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 89ff9f52125d..17d97a2a2e73 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -23,7 +23,8 @@ #include #include -#include "android-base/macros.h" +#include +#include #include "androidfw/ByteBucketArray.h" #include "androidfw/Chunk.h" @@ -49,7 +50,7 @@ struct TypeSpec { // Pointer to the mmapped data where flags are kept. // Flags denote whether the resource entry is public // and under which configurations it varies. - const ResTable_typeSpec* type_spec; + incfs::verified_map_ptr type_spec; // The number of types that follow this struct. // There is a type for each configuration that entries are defined for. @@ -57,15 +58,17 @@ struct TypeSpec { // Trick to easily access a variable number of Type structs // proceeding this struct, and to ensure their alignment. - const ResTable_type* types[0]; + incfs::verified_map_ptr types[0]; - inline uint32_t GetFlagsForEntryIndex(uint16_t entry_index) const { + base::expected GetFlagsForEntryIndex(uint16_t entry_index) const { if (entry_index >= dtohl(type_spec->entryCount)) { - return 0u; + return 0U; } - - const uint32_t* flags = reinterpret_cast(type_spec + 1); - return flags[entry_index]; + const auto entry_flags_ptr = ((type_spec + 1).convert() + entry_index); + if (!entry_flags_ptr) { + return base::unexpected(IOError::PAGES_MISSING); + } + return entry_flags_ptr.value(); } }; @@ -161,13 +164,17 @@ class LoadedPackage { // the default policy in AAPT2 is to build UTF-8 string pools, this needs to change. // Returns a partial resource ID, with the package ID left as 0x00. The caller is responsible // for patching the correct package ID to the resource ID. - uint32_t FindEntryByName(const std::u16string& type_name, const std::u16string& entry_name) const; + base::expected FindEntryByName(const std::u16string& type_name, + const std::u16string& entry_name) const; - static const ResTable_entry* GetEntry(const ResTable_type* type_chunk, uint16_t entry_index); + static base::expected, NullOrIOError> GetEntry( + incfs::verified_map_ptr type_chunk, uint16_t entry_index); - static uint32_t GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index); + static base::expected GetEntryOffset( + incfs::verified_map_ptr type_chunk, uint16_t entry_index); - static const ResTable_entry* GetEntryFromOffset(const ResTable_type* type_chunk, uint32_t offset); + static base::expected, NullOrIOError> GetEntryFromOffset( + incfs::verified_map_ptr type_chunk, uint32_t offset); // Returns the string pool where type names are stored. inline const ResStringPool* GetTypeStringPool() const { @@ -220,7 +227,8 @@ class LoadedPackage { // Populates a set of ResTable_config structs, possibly excluding configurations defined for // the mipmap type. - void CollectConfigurations(bool exclude_mipmap, std::set* out_configs) const; + base::expected CollectConfigurations( + bool exclude_mipmap, std::set* out_configs) const; // Populates a set of strings representing locales. // If `canonicalize` is set to true, each locale is transformed into its canonical format @@ -300,7 +308,8 @@ class LoadedArsc { // If `load_as_shared_library` is set to true, the application package (0x7f) is treated // as a shared library (0x00). When loaded into an AssetManager, the package will be assigned an // ID. - static std::unique_ptr Load(const StringPiece& data, + static std::unique_ptr Load(incfs::map_ptr data, + size_t length, const LoadedIdmap* loaded_idmap = nullptr, package_property_t property_flags = 0U); diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index 04ba78b6705d..fb5f86473189 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -20,7 +20,10 @@ #ifndef _LIBS_UTILS_RESOURCE_TYPES_H #define _LIBS_UTILS_RESOURCE_TYPES_H +#include + #include +#include #include #include #include @@ -497,7 +500,7 @@ public: virtual ~ResStringPool(); void setToEmpty(); - status_t setTo(const void* data, size_t size, bool copyData=false); + status_t setTo(incfs::map_ptr data, size_t size, bool copyData=false); status_t getError() const; @@ -505,48 +508,49 @@ public: // Return string entry as UTF16; if the pool is UTF8, the string will // be converted before returning. - inline const char16_t* stringAt(const ResStringPool_ref& ref, size_t* outLen) const { - return stringAt(ref.index, outLen); + inline base::expected stringAt( + const ResStringPool_ref& ref) const { + return stringAt(ref.index); } - virtual const char16_t* stringAt(size_t idx, size_t* outLen) const; + virtual base::expected stringAt(size_t idx) const; // Note: returns null if the string pool is not UTF8. - virtual const char* string8At(size_t idx, size_t* outLen) const; + virtual base::expected string8At(size_t idx) const; // Return string whether the pool is UTF8 or UTF16. Does not allow you // to distinguish null. - const String8 string8ObjectAt(size_t idx) const; + base::expected string8ObjectAt(size_t idx) const; - const ResStringPool_span* styleAt(const ResStringPool_ref& ref) const; - const ResStringPool_span* styleAt(size_t idx) const; + base::expected, NullOrIOError> styleAt( + const ResStringPool_ref& ref) const; + base::expected, NullOrIOError> styleAt(size_t idx) const; - ssize_t indexOfString(const char16_t* str, size_t strLen) const; + base::expected indexOfString(const char16_t* str, size_t strLen) const; virtual size_t size() const; size_t styleCount() const; size_t bytes() const; - const void* data() const; - + incfs::map_ptr data() const; bool isSorted() const; bool isUTF8() const; private: - status_t mError; - void* mOwnedData; - const ResStringPool_header* mHeader; - size_t mSize; - mutable Mutex mDecodeLock; - const uint32_t* mEntries; - const uint32_t* mEntryStyles; - const void* mStrings; - char16_t mutable** mCache; - uint32_t mStringPoolSize; // number of uint16_t - const uint32_t* mStyles; - uint32_t mStylePoolSize; // number of uint32_t - - const char* stringDecodeAt(size_t idx, const uint8_t* str, const size_t encLen, - size_t* outLen) const; + status_t mError; + void* mOwnedData; + incfs::verified_map_ptr mHeader; + size_t mSize; + mutable Mutex mDecodeLock; + incfs::map_ptr mEntries; + incfs::map_ptr mEntryStyles; + incfs::map_ptr mStrings; + char16_t mutable** mCache; + uint32_t mStringPoolSize; // number of uint16_t + incfs::map_ptr mStyles; + uint32_t mStylePoolSize; // number of uint32_t + + base::expected stringDecodeAt( + size_t idx, incfs::map_ptr str, size_t encLen) const; }; /** @@ -558,8 +562,8 @@ public: StringPoolRef() = default; StringPoolRef(const ResStringPool* pool, uint32_t index); - const char* string8(size_t* outLen) const; - const char16_t* string16(size_t* outLen) const; + base::expected string8() const; + base::expected string16() const; private: const ResStringPool* mPool = nullptr; @@ -1797,6 +1801,16 @@ private: bool U16StringToInt(const char16_t* s, size_t len, Res_value* outValue); +template +static const TChar* UnpackOptionalString(base::expected, E>&& result, + size_t* outLen) { + if (result.has_value()) { + *outLen = result->size(); + return result->data(); + } + return NULL; +} + /** * Convenience class for accessing data in a ResTable resource. */ diff --git a/libs/androidfw/include/androidfw/ResourceUtils.h b/libs/androidfw/include/androidfw/ResourceUtils.h index e649940cdde1..bd1c44033b88 100644 --- a/libs/androidfw/include/androidfw/ResourceUtils.h +++ b/libs/androidfw/include/androidfw/ResourceUtils.h @@ -30,13 +30,12 @@ bool ExtractResourceName(const StringPiece& str, StringPiece* out_package, Strin // Convert a type_string_ref, entry_string_ref, and package to AssetManager2::ResourceName. // Useful for getting resource name without re-running AssetManager2::FindEntry searches. -bool ToResourceName(const StringPoolRef& type_string_ref, - const StringPoolRef& entry_string_ref, - const StringPiece& package_name, - AssetManager2::ResourceName* out_name); +base::expected ToResourceName( + const StringPoolRef& type_string_ref, const StringPoolRef& entry_string_ref, + const StringPiece& package_name); // Formats a ResourceName to "package:type/entry_name". -std::string ToFormattedResourceString(AssetManager2::ResourceName* resource_name); +std::string ToFormattedResourceString(const AssetManager2::ResourceName& resource_name); inline uint32_t fix_package_id(uint32_t resid, uint8_t package_id) { return (resid & 0x00ffffffu) | (static_cast(package_id) << 24); diff --git a/libs/androidfw/include/androidfw/StreamingZipInflater.h b/libs/androidfw/include/androidfw/StreamingZipInflater.h index 3ace5d5a83cf..472b794b911c 100644 --- a/libs/androidfw/include/androidfw/StreamingZipInflater.h +++ b/libs/androidfw/include/androidfw/StreamingZipInflater.h @@ -19,6 +19,8 @@ #include #include + +#include #include #include @@ -34,7 +36,7 @@ public: StreamingZipInflater(int fd, off64_t compDataStart, size_t uncompSize, size_t compSize); // Flavor that gets the compressed data from an in-memory buffer - StreamingZipInflater(class FileMap* dataMap, size_t uncompSize); + StreamingZipInflater(const incfs::IncFsFileMap* dataMap, size_t uncompSize); ~StreamingZipInflater(); @@ -54,7 +56,7 @@ private: // where to find the uncompressed data int mFd; off64_t mInFileStart; // where the compressed data lives in the file - class FileMap* mDataMap; + const incfs::IncFsFileMap* mDataMap; z_stream mInflateState; bool mStreamNeedsInit; diff --git a/libs/androidfw/include/androidfw/Util.h b/libs/androidfw/include/androidfw/Util.h index 9a3646b49db8..aceeeccccb61 100644 --- a/libs/androidfw/include/androidfw/Util.h +++ b/libs/androidfw/include/androidfw/Util.h @@ -22,7 +22,8 @@ #include #include -#include "android-base/macros.h" +#include +#include #include "androidfw/StringPiece.h" @@ -126,6 +127,11 @@ std::string Utf16ToUtf8(const StringPiece16& utf16); std::vector SplitAndLowercase(const android::StringPiece& str, char sep); +template +bool IsFourByteAligned(const incfs::map_ptr& data) { + return ((size_t)data.unsafe_ptr() & 0x3U) == 0; +} + } // namespace util } // namespace android diff --git a/libs/androidfw/include/androidfw/ZipFileRO.h b/libs/androidfw/include/androidfw/ZipFileRO.h index c221e3b7aeae..10f6d0655bf4 100644 --- a/libs/androidfw/include/androidfw/ZipFileRO.h +++ b/libs/androidfw/include/androidfw/ZipFileRO.h @@ -30,17 +30,20 @@ #ifndef __LIBS_ZIPFILERO_H #define __LIBS_ZIPFILERO_H -#include -#include -#include -#include - +#include #include #include #include #include #include +#include + +#include +#include +#include +#include + struct ZipArchive; typedef ZipArchive* ZipArchiveHandle; @@ -136,13 +139,25 @@ public: uint32_t* pCrc32) const; /* - * Create a new FileMap object that maps a subset of the archive. For + * Create a new FileMap object that maps a subset of the archive. For * an uncompressed entry this effectively provides a pointer to the * actual data, for a compressed entry this provides the input buffer * for inflate(). + * + * Use this function if the archive can never reside on IncFs. */ FileMap* createEntryFileMap(ZipEntryRO entry) const; + /* + * Create a new incfs::IncFsFileMap object that maps a subset of the archive. For + * an uncompressed entry this effectively provides a pointer to the + * actual data, for a compressed entry this provides the input buffer + * for inflate(). + * + * Use this function if the archive can potentially reside on IncFs. + */ + std::optional createEntryIncFsFileMap(ZipEntryRO entry) const; + /* * Uncompress the data into a buffer. Depending on the compression * format, this is either an "inflate" operation or a memcpy. diff --git a/libs/androidfw/include/androidfw/ZipUtils.h b/libs/androidfw/include/androidfw/ZipUtils.h index 4d35e992cc89..dbfec34fda89 100644 --- a/libs/androidfw/include/androidfw/ZipUtils.h +++ b/libs/androidfw/include/androidfw/ZipUtils.h @@ -25,6 +25,8 @@ #include #include +#include "util/map_ptr.h" + namespace android { /* @@ -40,8 +42,8 @@ public: long compressedLen); static bool inflateToBuffer(int fd, void* buf, long uncompressedLen, long compressedLen); - static bool inflateToBuffer(const void *in, void* buf, long uncompressedLen, - long compressedLen); + static bool inflateToBuffer(incfs::map_ptr in, void* buf, + long uncompressedLen, long compressedLen); /* * Someday we might want to make this generic and handle bzip2 ".bz2" diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp index 437e14772964..c7ae618991b9 100644 --- a/libs/androidfw/tests/AssetManager2_bench.cpp +++ b/libs/androidfw/tests/AssetManager2_bench.cpp @@ -139,9 +139,13 @@ static void BM_AssetManagerGetBag(benchmark::State& state) { assets.SetApkAssets({apk.get()}); while (state.KeepRunning()) { - const ResolvedBag* bag = assets.GetBag(app::R::style::StyleTwo); - const auto bag_end = end(bag); - for (auto iter = begin(bag); iter != bag_end; ++iter) { + auto bag = assets.GetBag(app::R::style::StyleTwo); + if (!bag.has_value()) { + state.SkipWithError("Failed to load get bag"); + return; + } + const auto bag_end = end(*bag); + for (auto iter = begin(*bag); iter != bag_end; ++iter) { uint32_t key = iter->key; Res_value value = iter->value; benchmark::DoNotOptimize(key); diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index 8c255d16fe1f..3638ce1f92d7 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -108,24 +108,18 @@ TEST_F(AssetManager2Test, FindsResourceFromSingleApkAssets) { assetmanager.SetConfiguration(desired_config); assetmanager.SetApkAssets({basic_assets_.get()}); - Res_value value; - ResTable_config selected_config; - uint32_t flags; - - ApkAssetsCookie cookie = - assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/, - 0 /*density_override*/, &value, &selected_config, &flags); - ASSERT_NE(kInvalidCookie, cookie); + auto value = assetmanager.GetResource(basic::R::string::test1); + ASSERT_TRUE(value.has_value()); // Came from our ApkAssets. - EXPECT_EQ(0, cookie); + EXPECT_EQ(0, value->cookie); // It is the default config. - EXPECT_EQ(0, selected_config.language[0]); - EXPECT_EQ(0, selected_config.language[1]); + EXPECT_EQ(0, value->config.language[0]); + EXPECT_EQ(0, value->config.language[1]); // It is a string. - EXPECT_EQ(Res_value::TYPE_STRING, value.dataType); + EXPECT_EQ(Res_value::TYPE_STRING, value->type); } TEST_F(AssetManager2Test, FindsResourceFromMultipleApkAssets) { @@ -138,24 +132,18 @@ TEST_F(AssetManager2Test, FindsResourceFromMultipleApkAssets) { assetmanager.SetConfiguration(desired_config); assetmanager.SetApkAssets({basic_assets_.get(), basic_de_fr_assets_.get()}); - Res_value value; - ResTable_config selected_config; - uint32_t flags; - - ApkAssetsCookie cookie = - assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/, - 0 /*density_override*/, &value, &selected_config, &flags); - ASSERT_NE(kInvalidCookie, cookie); + auto value = assetmanager.GetResource(basic::R::string::test1); + ASSERT_TRUE(value.has_value()); // Came from our de_fr ApkAssets. - EXPECT_EQ(1, cookie); + EXPECT_EQ(1, value->cookie); // The configuration is German. - EXPECT_EQ('d', selected_config.language[0]); - EXPECT_EQ('e', selected_config.language[1]); + EXPECT_EQ('d', value->config.language[0]); + EXPECT_EQ('e', value->config.language[1]); // It is a string. - EXPECT_EQ(Res_value::TYPE_STRING, value.dataType); + EXPECT_EQ(Res_value::TYPE_STRING, value->type); } TEST_F(AssetManager2Test, FindsResourceFromSharedLibrary) { @@ -166,44 +154,35 @@ TEST_F(AssetManager2Test, FindsResourceFromSharedLibrary) { assetmanager.SetApkAssets( {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()}); - Res_value value; - ResTable_config selected_config; - uint32_t flags; - - ApkAssetsCookie cookie = - assetmanager.GetResource(libclient::R::string::foo_one, false /*may_be_bag*/, - 0 /*density_override*/, &value, &selected_config, &flags); - ASSERT_NE(kInvalidCookie, cookie); + auto value = assetmanager.GetResource(libclient::R::string::foo_one); + ASSERT_TRUE(value.has_value()); // Reference comes from libclient. - EXPECT_EQ(2, cookie); - EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType); + EXPECT_EQ(2, value->cookie); + EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type); // Lookup the reference. - cookie = assetmanager.GetResource(value.data, false /* may_be_bag */, 0 /* density_override*/, - &value, &selected_config, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(1, cookie); - EXPECT_EQ(Res_value::TYPE_STRING, value.dataType); + value = assetmanager.GetResource(value->data); + ASSERT_TRUE(value.has_value()); + EXPECT_EQ(1, value->cookie); + EXPECT_EQ(Res_value::TYPE_STRING, value->type); EXPECT_EQ(std::string("Foo from lib_one"), - GetStringFromPool(assetmanager.GetStringPoolForCookie(cookie), value.data)); + GetStringFromPool(assetmanager.GetStringPoolForCookie(value->cookie), value->data)); - cookie = assetmanager.GetResource(libclient::R::string::foo_two, false /*may_be_bag*/, - 0 /*density_override*/, &value, &selected_config, &flags); - ASSERT_NE(kInvalidCookie, cookie); + value = assetmanager.GetResource(libclient::R::string::foo_two); + ASSERT_TRUE(value.has_value()); // Reference comes from libclient. - EXPECT_EQ(2, cookie); - EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType); + EXPECT_EQ(2, value->cookie); + EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type); // Lookup the reference. - cookie = assetmanager.GetResource(value.data, false /* may_be_bag */, 0 /* density_override*/, - &value, &selected_config, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(0, cookie); - EXPECT_EQ(Res_value::TYPE_STRING, value.dataType); + value = assetmanager.GetResource(value->data); + ASSERT_TRUE(value.has_value()); + EXPECT_EQ(0, value->cookie); + EXPECT_EQ(Res_value::TYPE_STRING, value->type); EXPECT_EQ(std::string("Foo from lib_two"), - GetStringFromPool(assetmanager.GetStringPoolForCookie(cookie), value.data)); + GetStringFromPool(assetmanager.GetStringPoolForCookie(value->cookie), value->data)); } TEST_F(AssetManager2Test, FindsResourceFromAppLoadedAsSharedLibrary) { @@ -211,16 +190,10 @@ TEST_F(AssetManager2Test, FindsResourceFromAppLoadedAsSharedLibrary) { assetmanager.SetApkAssets({appaslib_assets_.get()}); // The appaslib package will have been assigned the package ID 0x02. - - Res_value value; - ResTable_config selected_config; - uint32_t flags; - ApkAssetsCookie cookie = assetmanager.GetResource( - fix_package_id(appaslib::R::integer::number1, 0x02), false /*may_be_bag*/, - 0u /*density_override*/, &value, &selected_config, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType); - EXPECT_EQ(fix_package_id(appaslib::R::array::integerArray1, 0x02), value.data); + auto value = assetmanager.GetResource(fix_package_id(appaslib::R::integer::number1, 0x02)); + ASSERT_TRUE(value.has_value()); + EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type); + EXPECT_EQ(fix_package_id(appaslib::R::array::integerArray1, 0x02), value->data); } TEST_F(AssetManager2Test, AssignsOverlayPackageIdLast) { @@ -238,40 +211,40 @@ TEST_F(AssetManager2Test, AssignsOverlayPackageIdLast) { return assetmanager.GetAssignedPackageId(apkAssets->GetLoadedArsc()->GetPackages()[0].get()); }; - ASSERT_EQ(get_first_package_id(overlayable_assets_.get()), 0x7f); - ASSERT_EQ(get_first_package_id(overlay_assets_.get()), 0x03); - ASSERT_EQ(get_first_package_id(lib_one_assets_.get()), 0x02); + ASSERT_EQ(0x7f, get_first_package_id(overlayable_assets_.get())); + ASSERT_EQ(0x03, get_first_package_id(overlay_assets_.get())); + ASSERT_EQ(0x02, get_first_package_id(lib_one_assets_.get())); } TEST_F(AssetManager2Test, GetSharedLibraryResourceName) { AssetManager2 assetmanager; assetmanager.SetApkAssets({lib_one_assets_.get()}); - AssetManager2::ResourceName name; - ASSERT_TRUE(assetmanager.GetResourceName(lib_one::R::string::foo, &name)); - std::string formatted_name = ToFormattedResourceString(&name); - ASSERT_EQ(formatted_name, "com.android.lib_one:string/foo"); + auto name = assetmanager.GetResourceName(lib_one::R::string::foo); + ASSERT_TRUE(name.has_value()); + ASSERT_EQ("com.android.lib_one:string/foo", ToFormattedResourceString(*name)); } TEST_F(AssetManager2Test, FindsBagResourceFromSingleApkAssets) { AssetManager2 assetmanager; assetmanager.SetApkAssets({basic_assets_.get()}); - const ResolvedBag* bag = assetmanager.GetBag(basic::R::array::integerArray1); - ASSERT_NE(nullptr, bag); - ASSERT_EQ(3u, bag->entry_count); + auto bag = assetmanager.GetBag(basic::R::array::integerArray1); + ASSERT_TRUE(bag.has_value()); + + ASSERT_EQ(3u, (*bag)->entry_count); - EXPECT_EQ(static_cast(Res_value::TYPE_INT_DEC), bag->entries[0].value.dataType); - EXPECT_EQ(1u, bag->entries[0].value.data); - EXPECT_EQ(0, bag->entries[0].cookie); + EXPECT_EQ(static_cast(Res_value::TYPE_INT_DEC), (*bag)->entries[0].value.dataType); + EXPECT_EQ(1u, (*bag)->entries[0].value.data); + EXPECT_EQ(0, (*bag)->entries[0].cookie); - EXPECT_EQ(static_cast(Res_value::TYPE_INT_DEC), bag->entries[1].value.dataType); - EXPECT_EQ(2u, bag->entries[1].value.data); - EXPECT_EQ(0, bag->entries[1].cookie); + EXPECT_EQ(static_cast(Res_value::TYPE_INT_DEC), (*bag)->entries[1].value.dataType); + EXPECT_EQ(2u, (*bag)->entries[1].value.data); + EXPECT_EQ(0, (*bag)->entries[1].cookie); - EXPECT_EQ(static_cast(Res_value::TYPE_INT_DEC), bag->entries[2].value.dataType); - EXPECT_EQ(3u, bag->entries[2].value.data); - EXPECT_EQ(0, bag->entries[2].cookie); + EXPECT_EQ(static_cast(Res_value::TYPE_INT_DEC), (*bag)->entries[2].value.dataType); + EXPECT_EQ(3u, (*bag)->entries[2].value.data); + EXPECT_EQ(0, (*bag)->entries[2].cookie); } TEST_F(AssetManager2Test, FindsBagResourceFromMultipleApkAssets) {} @@ -284,15 +257,16 @@ TEST_F(AssetManager2Test, FindsBagResourceFromSharedLibrary) { assetmanager.SetApkAssets( {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()}); - const ResolvedBag* bag = assetmanager.GetBag(fix_package_id(lib_one::R::style::Theme, 0x03)); - ASSERT_NE(nullptr, bag); - ASSERT_GE(bag->entry_count, 2u); + auto bag = assetmanager.GetBag(fix_package_id(lib_one::R::style::Theme, 0x03)); + ASSERT_TRUE(bag.has_value()); + + ASSERT_GE((*bag)->entry_count, 2u); // First two attributes come from lib_one. - EXPECT_EQ(1, bag->entries[0].cookie); - EXPECT_EQ(0x03, get_package_id(bag->entries[0].key)); - EXPECT_EQ(1, bag->entries[1].cookie); - EXPECT_EQ(0x03, get_package_id(bag->entries[1].key)); + EXPECT_EQ(1, (*bag)->entries[0].cookie); + EXPECT_EQ(0x03, get_package_id((*bag)->entries[0].key)); + EXPECT_EQ(1, (*bag)->entries[1].cookie); + EXPECT_EQ(0x03, get_package_id((*bag)->entries[1].key)); } TEST_F(AssetManager2Test, FindsBagResourceFromMultipleSharedLibraries) { @@ -303,17 +277,17 @@ TEST_F(AssetManager2Test, FindsBagResourceFromMultipleSharedLibraries) { assetmanager.SetApkAssets( {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()}); - const ResolvedBag* bag = assetmanager.GetBag(libclient::R::style::ThemeMultiLib); - ASSERT_NE(nullptr, bag); - ASSERT_EQ(bag->entry_count, 2u); + auto bag = assetmanager.GetBag(libclient::R::style::ThemeMultiLib); + ASSERT_TRUE(bag.has_value()); + ASSERT_EQ((*bag)->entry_count, 2u); // First attribute comes from lib_two. - EXPECT_EQ(2, bag->entries[0].cookie); - EXPECT_EQ(0x02, get_package_id(bag->entries[0].key)); + EXPECT_EQ(2, (*bag)->entries[0].cookie); + EXPECT_EQ(0x02, get_package_id((*bag)->entries[0].key)); // The next two attributes come from lib_one. - EXPECT_EQ(2, bag->entries[1].cookie); - EXPECT_EQ(0x03, get_package_id(bag->entries[1].key)); + EXPECT_EQ(2, (*bag)->entries[1].cookie); + EXPECT_EQ(0x03, get_package_id((*bag)->entries[1].key)); } TEST_F(AssetManager2Test, FindsStyleResourceWithParentFromSharedLibrary) { @@ -324,79 +298,79 @@ TEST_F(AssetManager2Test, FindsStyleResourceWithParentFromSharedLibrary) { assetmanager.SetApkAssets( {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()}); - const ResolvedBag* bag = assetmanager.GetBag(libclient::R::style::Theme); - ASSERT_NE(nullptr, bag); - ASSERT_GE(bag->entry_count, 2u); + auto bag = assetmanager.GetBag(libclient::R::style::Theme); + ASSERT_TRUE(bag.has_value()); + ASSERT_GE((*bag)->entry_count, 2u); // First two attributes come from lib_one. - EXPECT_EQ(1, bag->entries[0].cookie); - EXPECT_EQ(0x03, get_package_id(bag->entries[0].key)); - EXPECT_EQ(1, bag->entries[1].cookie); - EXPECT_EQ(0x03, get_package_id(bag->entries[1].key)); + EXPECT_EQ(1, (*bag)->entries[0].cookie); + EXPECT_EQ(0x03, get_package_id((*bag)->entries[0].key)); + EXPECT_EQ(1, (*bag)->entries[1].cookie); + EXPECT_EQ(0x03, get_package_id((*bag)->entries[1].key)); } TEST_F(AssetManager2Test, MergesStylesWithParentFromSingleApkAssets) { AssetManager2 assetmanager; assetmanager.SetApkAssets({style_assets_.get()}); - const ResolvedBag* bag_one = assetmanager.GetBag(app::R::style::StyleOne); - ASSERT_NE(nullptr, bag_one); - ASSERT_EQ(2u, bag_one->entry_count); + auto bag_one = assetmanager.GetBag(app::R::style::StyleOne); + ASSERT_TRUE(bag_one.has_value()); + ASSERT_EQ(2u, (*bag_one)->entry_count); - EXPECT_EQ(app::R::attr::attr_one, bag_one->entries[0].key); - EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_one->entries[0].value.dataType); - EXPECT_EQ(1u, bag_one->entries[0].value.data); - EXPECT_EQ(0, bag_one->entries[0].cookie); + EXPECT_EQ(app::R::attr::attr_one, (*bag_one)->entries[0].key); + EXPECT_EQ(Res_value::TYPE_INT_DEC, (*bag_one)->entries[0].value.dataType); + EXPECT_EQ(1u, (*bag_one)->entries[0].value.data); + EXPECT_EQ(0, (*bag_one)->entries[0].cookie); - EXPECT_EQ(app::R::attr::attr_two, bag_one->entries[1].key); - EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_one->entries[1].value.dataType); - EXPECT_EQ(2u, bag_one->entries[1].value.data); - EXPECT_EQ(0, bag_one->entries[1].cookie); + EXPECT_EQ(app::R::attr::attr_two, (*bag_one)->entries[1].key); + EXPECT_EQ(Res_value::TYPE_INT_DEC, (*bag_one)->entries[1].value.dataType); + EXPECT_EQ(2u, (*bag_one)->entries[1].value.data); + EXPECT_EQ(0, (*bag_one)->entries[1].cookie); - const ResolvedBag* bag_two = assetmanager.GetBag(app::R::style::StyleTwo); - ASSERT_NE(nullptr, bag_two); - ASSERT_EQ(6u, bag_two->entry_count); + auto bag_two = assetmanager.GetBag(app::R::style::StyleTwo); + ASSERT_TRUE(bag_two.has_value()); + ASSERT_EQ(6u, (*bag_two)->entry_count); // attr_one is inherited from StyleOne. - EXPECT_EQ(app::R::attr::attr_one, bag_two->entries[0].key); - EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_two->entries[0].value.dataType); - EXPECT_EQ(1u, bag_two->entries[0].value.data); - EXPECT_EQ(0, bag_two->entries[0].cookie); - EXPECT_EQ(app::R::style::StyleOne, bag_two->entries[0].style); + EXPECT_EQ(app::R::attr::attr_one, (*bag_two)->entries[0].key); + EXPECT_EQ(Res_value::TYPE_INT_DEC, (*bag_two)->entries[0].value.dataType); + EXPECT_EQ(1u, (*bag_two)->entries[0].value.data); + EXPECT_EQ(0, (*bag_two)->entries[0].cookie); + EXPECT_EQ(app::R::style::StyleOne, (*bag_two)->entries[0].style); // attr_two should be overridden from StyleOne by StyleTwo. - EXPECT_EQ(app::R::attr::attr_two, bag_two->entries[1].key); - EXPECT_EQ(Res_value::TYPE_STRING, bag_two->entries[1].value.dataType); - EXPECT_EQ(0, bag_two->entries[1].cookie); - EXPECT_EQ(app::R::style::StyleTwo, bag_two->entries[1].style); + EXPECT_EQ(app::R::attr::attr_two, (*bag_two)->entries[1].key); + EXPECT_EQ(Res_value::TYPE_STRING, (*bag_two)->entries[1].value.dataType); + EXPECT_EQ(0, (*bag_two)->entries[1].cookie); + EXPECT_EQ(app::R::style::StyleTwo, (*bag_two)->entries[1].style); EXPECT_EQ(std::string("string"), GetStringFromPool(assetmanager.GetStringPoolForCookie(0), - bag_two->entries[1].value.data)); + (*bag_two)->entries[1].value.data)); // The rest are new attributes. - EXPECT_EQ(app::R::attr::attr_three, bag_two->entries[2].key); - EXPECT_EQ(Res_value::TYPE_ATTRIBUTE, bag_two->entries[2].value.dataType); - EXPECT_EQ(app::R::attr::attr_indirect, bag_two->entries[2].value.data); - EXPECT_EQ(0, bag_two->entries[2].cookie); - EXPECT_EQ(app::R::style::StyleTwo, bag_two->entries[2].style); - - EXPECT_EQ(app::R::attr::attr_five, bag_two->entries[3].key); - EXPECT_EQ(Res_value::TYPE_REFERENCE, bag_two->entries[3].value.dataType); - EXPECT_EQ(app::R::string::string_one, bag_two->entries[3].value.data); - EXPECT_EQ(0, bag_two->entries[3].cookie); - EXPECT_EQ(app::R::style::StyleTwo, bag_two->entries[3].style); - - EXPECT_EQ(app::R::attr::attr_indirect, bag_two->entries[4].key); - EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_two->entries[4].value.dataType); - EXPECT_EQ(3u, bag_two->entries[4].value.data); - EXPECT_EQ(0, bag_two->entries[4].cookie); - EXPECT_EQ(app::R::style::StyleTwo, bag_two->entries[4].style); - - EXPECT_EQ(app::R::attr::attr_empty, bag_two->entries[5].key); - EXPECT_EQ(Res_value::TYPE_NULL, bag_two->entries[5].value.dataType); - EXPECT_EQ(Res_value::DATA_NULL_EMPTY, bag_two->entries[5].value.data); - EXPECT_EQ(0, bag_two->entries[5].cookie); - EXPECT_EQ(app::R::style::StyleTwo, bag_two->entries[5].style); + EXPECT_EQ(app::R::attr::attr_three, (*bag_two)->entries[2].key); + EXPECT_EQ(Res_value::TYPE_ATTRIBUTE, (*bag_two)->entries[2].value.dataType); + EXPECT_EQ(app::R::attr::attr_indirect, (*bag_two)->entries[2].value.data); + EXPECT_EQ(0, (*bag_two)->entries[2].cookie); + EXPECT_EQ(app::R::style::StyleTwo, (*bag_two)->entries[2].style); + + EXPECT_EQ(app::R::attr::attr_five, (*bag_two)->entries[3].key); + EXPECT_EQ(Res_value::TYPE_REFERENCE, (*bag_two)->entries[3].value.dataType); + EXPECT_EQ(app::R::string::string_one, (*bag_two)->entries[3].value.data); + EXPECT_EQ(0, (*bag_two)->entries[3].cookie); + EXPECT_EQ(app::R::style::StyleTwo, (*bag_two)->entries[3].style); + + EXPECT_EQ(app::R::attr::attr_indirect, (*bag_two)->entries[4].key); + EXPECT_EQ(Res_value::TYPE_INT_DEC, (*bag_two)->entries[4].value.dataType); + EXPECT_EQ(3u, (*bag_two)->entries[4].value.data); + EXPECT_EQ(0, (*bag_two)->entries[4].cookie); + EXPECT_EQ(app::R::style::StyleTwo, (*bag_two)->entries[4].style); + + EXPECT_EQ(app::R::attr::attr_empty, (*bag_two)->entries[5].key); + EXPECT_EQ(Res_value::TYPE_NULL, (*bag_two)->entries[5].value.dataType); + EXPECT_EQ(Res_value::DATA_NULL_EMPTY, (*bag_two)->entries[5].value.data); + EXPECT_EQ(0, (*bag_two)->entries[5].cookie); + EXPECT_EQ(app::R::style::StyleTwo, (*bag_two)->entries[5].style); } TEST_F(AssetManager2Test, MergeStylesCircularDependency) { @@ -405,55 +379,41 @@ TEST_F(AssetManager2Test, MergeStylesCircularDependency) { // GetBag should stop traversing the parents of styles when a circular // dependency is detected - const ResolvedBag* bag_one = assetmanager.GetBag(app::R::style::StyleFour); - ASSERT_NE(nullptr, bag_one); - ASSERT_EQ(3u, bag_one->entry_count); + auto bag = assetmanager.GetBag(app::R::style::StyleFour); + ASSERT_TRUE(bag.has_value()); + ASSERT_EQ(3u, (*bag)->entry_count); } TEST_F(AssetManager2Test, ResolveReferenceToResource) { AssetManager2 assetmanager; assetmanager.SetApkAssets({basic_assets_.get()}); - Res_value value; - ResTable_config selected_config; - uint32_t flags; - ApkAssetsCookie cookie = - assetmanager.GetResource(basic::R::integer::ref1, false /*may_be_bag*/, - 0u /*density_override*/, &value, &selected_config, &flags); - ASSERT_NE(kInvalidCookie, cookie); - - EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType); - EXPECT_EQ(basic::R::integer::ref2, value.data); - - uint32_t last_ref = 0u; - cookie = assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_ref); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); - EXPECT_EQ(12000u, value.data); - EXPECT_EQ(basic::R::integer::ref2, last_ref); + auto value = assetmanager.GetResource(basic::R::integer::ref1); + ASSERT_TRUE(value.has_value()); + EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type); + EXPECT_EQ(basic::R::integer::ref2, value->data); + + auto result = assetmanager.ResolveReference(*value); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); + EXPECT_EQ(12000u, value->data); + EXPECT_EQ(basic::R::integer::ref2, value->resid); } TEST_F(AssetManager2Test, ResolveReferenceToBag) { AssetManager2 assetmanager; assetmanager.SetApkAssets({basic_assets_.get()}); - Res_value value; - ResTable_config selected_config; - uint32_t flags; - ApkAssetsCookie cookie = - assetmanager.GetResource(basic::R::integer::number2, true /*may_be_bag*/, - 0u /*density_override*/, &value, &selected_config, &flags); - ASSERT_NE(kInvalidCookie, cookie); - - EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType); - EXPECT_EQ(basic::R::array::integerArray1, value.data); - - uint32_t last_ref = 0u; - cookie = assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_ref); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType); - EXPECT_EQ(basic::R::array::integerArray1, value.data); - EXPECT_EQ(basic::R::array::integerArray1, last_ref); + auto value = assetmanager.GetResource(basic::R::integer::number2, true /*may_be_bag*/); + ASSERT_TRUE(value.has_value()); + EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type); + EXPECT_EQ(basic::R::array::integerArray1, value->data); + + auto result = assetmanager.ResolveReference(*value); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type); + EXPECT_EQ(basic::R::array::integerArray1, value->data); + EXPECT_EQ(basic::R::array::integerArray1, value->resid); } TEST_F(AssetManager2Test, ResolveDeepIdReference) { @@ -461,31 +421,25 @@ TEST_F(AssetManager2Test, ResolveDeepIdReference) { assetmanager.SetApkAssets({basic_assets_.get()}); // Set up the resource ids - const uint32_t high_ref = assetmanager - .GetResourceId("@id/high_ref", "values", "com.android.basic"); - ASSERT_NE(high_ref, 0u); - const uint32_t middle_ref = assetmanager - .GetResourceId("@id/middle_ref", "values", "com.android.basic"); - ASSERT_NE(middle_ref, 0u); - const uint32_t low_ref = assetmanager - .GetResourceId("@id/low_ref", "values", "com.android.basic"); - ASSERT_NE(low_ref, 0u); + auto high_ref = assetmanager.GetResourceId("@id/high_ref", "values", "com.android.basic"); + ASSERT_TRUE(high_ref.has_value()); + + auto middle_ref = assetmanager.GetResourceId("@id/middle_ref", "values", "com.android.basic"); + ASSERT_TRUE(middle_ref.has_value()); + + auto low_ref = assetmanager.GetResourceId("@id/low_ref", "values", "com.android.basic"); + ASSERT_TRUE(low_ref.has_value()); // Retrieve the most shallow resource - Res_value value; - ResTable_config config; - uint32_t flags; - ApkAssetsCookie cookie = assetmanager.GetResource(high_ref, false /*may_be_bag*/, - 0 /*density_override*/, - &value, &config, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType); - EXPECT_EQ(middle_ref, value.data); + auto value = assetmanager.GetResource(*high_ref); + ASSERT_TRUE(value.has_value()); + EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type); + EXPECT_EQ(*middle_ref, value->data);; // Check that resolving the reference resolves to the deepest id - uint32_t last_ref = high_ref; - assetmanager.ResolveReference(cookie, &value, &config, &flags, &last_ref); - EXPECT_EQ(last_ref, low_ref); + auto result = assetmanager.ResolveReference(*value); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(*low_ref, value->resid); } TEST_F(AssetManager2Test, KeepLastReferenceIdUnmodifiedIfNoReferenceIsResolved) { @@ -495,16 +449,16 @@ TEST_F(AssetManager2Test, KeepLastReferenceIdUnmodifiedIfNoReferenceIsResolved) ResTable_config selected_config; memset(&selected_config, 0, sizeof(selected_config)); - uint32_t flags = 0u; + // Create some kind of value that is NOT a reference. + AssetManager2::SelectedValue value{}; + value.cookie = 1; + value.type = Res_value::TYPE_STRING; + value.resid = basic::R::string::test1; - // Create some kind of Res_value that is NOT a reference. - Res_value value; - value.dataType = Res_value::TYPE_STRING; - value.data = 0; - - uint32_t last_ref = basic::R::string::test1; - EXPECT_EQ(1, assetmanager.ResolveReference(1, &value, &selected_config, &flags, &last_ref)); - EXPECT_EQ(basic::R::string::test1, last_ref); + auto result = assetmanager.ResolveReference(value); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(1, value.cookie); + EXPECT_EQ(basic::R::string::test1, value.resid); } static bool IsConfigurationPresent(const std::set& configurations, @@ -516,43 +470,45 @@ TEST_F(AssetManager2Test, GetResourceConfigurations) { AssetManager2 assetmanager; assetmanager.SetApkAssets({system_assets_.get(), basic_de_fr_assets_.get()}); - std::set configurations = assetmanager.GetResourceConfigurations(); + auto configurations = assetmanager.GetResourceConfigurations(); + ASSERT_TRUE(configurations.has_value()); // We expect the locale sv from the system assets, and de and fr from basic_de_fr assets. // And one extra for the default configuration. - EXPECT_EQ(4u, configurations.size()); + EXPECT_EQ(4u, configurations->size()); ResTable_config expected_config; memset(&expected_config, 0, sizeof(expected_config)); expected_config.language[0] = 's'; expected_config.language[1] = 'v'; - EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config)); + EXPECT_TRUE(IsConfigurationPresent(*configurations, expected_config)); expected_config.language[0] = 'd'; expected_config.language[1] = 'e'; - EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config)); + EXPECT_TRUE(IsConfigurationPresent(*configurations, expected_config)); expected_config.language[0] = 'f'; expected_config.language[1] = 'r'; - EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config)); + EXPECT_TRUE(IsConfigurationPresent(*configurations, expected_config)); // Take out the system assets. configurations = assetmanager.GetResourceConfigurations(true /* exclude_system */); + ASSERT_TRUE(configurations.has_value()); // We expect de and fr from basic_de_fr assets. - EXPECT_EQ(2u, configurations.size()); + EXPECT_EQ(2u, configurations->size()); expected_config.language[0] = 's'; expected_config.language[1] = 'v'; - EXPECT_FALSE(IsConfigurationPresent(configurations, expected_config)); + EXPECT_FALSE(IsConfigurationPresent(*configurations, expected_config)); expected_config.language[0] = 'd'; expected_config.language[1] = 'e'; - EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config)); + EXPECT_TRUE(IsConfigurationPresent(*configurations, expected_config)); expected_config.language[0] = 'f'; expected_config.language[1] = 'r'; - EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config)); + EXPECT_TRUE(IsConfigurationPresent(*configurations, expected_config)); } TEST_F(AssetManager2Test, GetResourceLocales) { @@ -578,12 +534,17 @@ TEST_F(AssetManager2Test, GetResourceId) { AssetManager2 assetmanager; assetmanager.SetApkAssets({basic_assets_.get()}); - EXPECT_EQ(basic::R::layout::main, - assetmanager.GetResourceId("com.android.basic:layout/main", "", "")); - EXPECT_EQ(basic::R::layout::main, - assetmanager.GetResourceId("layout/main", "", "com.android.basic")); - EXPECT_EQ(basic::R::layout::main, - assetmanager.GetResourceId("main", "layout", "com.android.basic")); + auto resid = assetmanager.GetResourceId("com.android.basic:layout/main", "", ""); + ASSERT_TRUE(resid.has_value()); + EXPECT_EQ(basic::R::layout::main, *resid); + + resid = assetmanager.GetResourceId("layout/main", "", "com.android.basic"); + ASSERT_TRUE(resid.has_value()); + EXPECT_EQ(basic::R::layout::main, *resid); + + resid = assetmanager.GetResourceId("main", "layout", "com.android.basic"); + ASSERT_TRUE(resid.has_value()); + EXPECT_EQ(basic::R::layout::main, *resid); } TEST_F(AssetManager2Test, OpensFileFromSingleApkAssets) { @@ -658,14 +619,8 @@ TEST_F(AssetManager2Test, GetLastPathWithoutEnablingReturnsEmpty) { assetmanager.SetApkAssets({basic_assets_.get()}); assetmanager.SetResourceResolutionLoggingEnabled(false); - Res_value value; - ResTable_config selected_config; - uint32_t flags; - - ApkAssetsCookie cookie = - assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/, - 0 /*density_override*/, &value, &selected_config, &flags); - ASSERT_NE(kInvalidCookie, cookie); + auto value = assetmanager.GetResource(basic::R::string::test1); + ASSERT_TRUE(value.has_value()); auto result = assetmanager.GetLastResourceResolution(); EXPECT_EQ("", result); @@ -693,17 +648,12 @@ TEST_F(AssetManager2Test, GetLastPathWithSingleApkAssets) { assetmanager.SetConfiguration(desired_config); assetmanager.SetApkAssets({basic_assets_.get()}); - Res_value value; - ResTable_config selected_config; - uint32_t flags; - - ApkAssetsCookie cookie = - assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/, - 0 /*density_override*/, &value, &selected_config, &flags); - ASSERT_NE(kInvalidCookie, cookie); + auto value = assetmanager.GetResource(basic::R::string::test1); + ASSERT_TRUE(value.has_value()); auto result = assetmanager.GetLastResourceResolution(); - EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n\tFor config -de\n\tFound initial: com.android.basic", result); + EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n" + "\tFor config -de\n\tFound initial: com.android.basic", result); } TEST_F(AssetManager2Test, GetLastPathWithMultipleApkAssets) { @@ -717,17 +667,14 @@ TEST_F(AssetManager2Test, GetLastPathWithMultipleApkAssets) { assetmanager.SetConfiguration(desired_config); assetmanager.SetApkAssets({basic_assets_.get(), basic_de_fr_assets_.get()}); - Res_value value = Res_value(); - ResTable_config selected_config; - uint32_t flags; - - ApkAssetsCookie cookie = - assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/, - 0 /*density_override*/, &value, &selected_config, &flags); - ASSERT_NE(kInvalidCookie, cookie); + auto value = assetmanager.GetResource(basic::R::string::test1); + ASSERT_TRUE(value.has_value()); auto result = assetmanager.GetLastResourceResolution(); - EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n\tFor config -de\n\tFound initial: com.android.basic\n\tFound better: com.android.basic -de", result); + EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n" + "\tFor config -de\n" + "\tFound initial: com.android.basic\n" + "\tFound better: com.android.basic -de", result); } TEST_F(AssetManager2Test, GetLastPathAfterDisablingReturnsEmpty) { @@ -739,14 +686,8 @@ TEST_F(AssetManager2Test, GetLastPathAfterDisablingReturnsEmpty) { assetmanager.SetConfiguration(desired_config); assetmanager.SetApkAssets({basic_assets_.get()}); - Res_value value = Res_value(); - ResTable_config selected_config; - uint32_t flags; - - ApkAssetsCookie cookie = - assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/, - 0 /*density_override*/, &value, &selected_config, &flags); - ASSERT_NE(kInvalidCookie, cookie); + auto value = assetmanager.GetResource(basic::R::string::test1); + ASSERT_TRUE(value.has_value()); auto resultEnabled = assetmanager.GetLastResourceResolution(); ASSERT_NE("", resultEnabled); diff --git a/libs/androidfw/tests/AttributeResolution_bench.cpp b/libs/androidfw/tests/AttributeResolution_bench.cpp index fa300c50218a..ddd8ab820cb1 100644 --- a/libs/androidfw/tests/AttributeResolution_bench.cpp +++ b/libs/androidfw/tests/AttributeResolution_bench.cpp @@ -108,27 +108,20 @@ static void BM_ApplyStyleFramework(benchmark::State& state) { device_config.screenHeightDp = 1024; device_config.sdkVersion = 27; - Res_value value; - ResTable_config config; - uint32_t flags = 0u; - ApkAssetsCookie cookie = - assetmanager.GetResource(basic::R::layout::layoutt, false /*may_be_bag*/, - 0u /*density_override*/, &value, &config, &flags); - if (cookie == kInvalidCookie) { + auto value = assetmanager.GetResource(basic::R::layout::layoutt); + if (!value.has_value()) { state.SkipWithError("failed to find R.layout.layout"); return; } - size_t len = 0u; - const char* layout_path = - assetmanager.GetStringPoolForCookie(cookie)->string8At(value.data, &len); - if (layout_path == nullptr || len == 0u) { + auto layout_path = assetmanager.GetStringPoolForCookie(value->cookie)->string8At(value->data); + if (!layout_path.has_value()) { state.SkipWithError("failed to lookup layout path"); return; } - std::unique_ptr asset = assetmanager.OpenNonAsset( - StringPiece(layout_path, len).to_string(), cookie, Asset::ACCESS_BUFFER); + std::unique_ptr asset = assetmanager.OpenNonAsset(layout_path->to_string(), value->cookie, + Asset::ACCESS_BUFFER); if (asset == nullptr) { state.SkipWithError("failed to load layout"); return; diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp index 24361b5817f4..bb9129ad01c8 100644 --- a/libs/androidfw/tests/AttributeResolution_test.cpp +++ b/libs/androidfw/tests/AttributeResolution_test.cpp @@ -77,9 +77,9 @@ TEST(AttributeResolutionLibraryTest, ApplyStyleWithDefaultStyleResId) { {fix_package_id(R::attr::attr_one, 0x02), fix_package_id(R::attr::attr_two, 0x02)}}; std::array values; std::array indices; - ApplyStyle(theme.get(), nullptr /*xml_parser*/, 0u /*def_style_attr*/, - fix_package_id(R::style::StyleOne, 0x02), attrs.data(), attrs.size(), values.data(), - indices.data()); + ASSERT_TRUE(ApplyStyle(theme.get(), nullptr /*xml_parser*/, 0u /*def_style_attr*/, + fix_package_id(R::style::StyleOne, 0x02), attrs.data(), attrs.size(), + values.data(), indices.data()).has_value()); const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC; @@ -102,7 +102,7 @@ TEST(AttributeResolutionLibraryTest, ApplyStyleWithDefaultStyleResId) { TEST_F(AttributeResolutionTest, Theme) { std::unique_ptr theme = assetmanager_.NewTheme(); - ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo)); + ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo).has_value()); std::array attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four, R::attr::attr_empty}}; @@ -110,7 +110,7 @@ TEST_F(AttributeResolutionTest, Theme) { ASSERT_TRUE(ResolveAttrs(theme.get(), 0u /*def_style_attr*/, 0u /*def_style_res*/, nullptr /*src_values*/, 0 /*src_values_length*/, attrs.data(), - attrs.size(), values.data(), nullptr /*out_indices*/)); + attrs.size(), values.data(), nullptr /*out_indices*/).has_value()); const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC; @@ -162,7 +162,7 @@ TEST_F(AttributeResolutionXmlTest, XmlParser) { std::array values; ASSERT_TRUE(RetrieveAttributes(&assetmanager_, &xml_parser_, attrs.data(), attrs.size(), - values.data(), nullptr /*out_indices*/)); + values.data(), nullptr /*out_indices*/).has_value()); uint32_t* values_cursor = values.data(); EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]); @@ -207,15 +207,15 @@ TEST_F(AttributeResolutionXmlTest, XmlParser) { TEST_F(AttributeResolutionXmlTest, ThemeAndXmlParser) { std::unique_ptr theme = assetmanager_.NewTheme(); - ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo)); + ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo).has_value()); std::array attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four, R::attr::attr_five, R::attr::attr_empty}}; std::array values; std::array indices; - ApplyStyle(theme.get(), &xml_parser_, 0u /*def_style_attr*/, 0u /*def_style_res*/, attrs.data(), - attrs.size(), values.data(), indices.data()); + ASSERT_TRUE(ApplyStyle(theme.get(), &xml_parser_, 0u /*def_style_attr*/, 0u /*def_style_res*/, + attrs.data(), attrs.size(), values.data(), indices.data()).has_value()); const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC; diff --git a/libs/androidfw/tests/BenchmarkHelpers.cpp b/libs/androidfw/tests/BenchmarkHelpers.cpp index faddfe599af4..0fa0573bcbb8 100644 --- a/libs/androidfw/tests/BenchmarkHelpers.cpp +++ b/libs/androidfw/tests/BenchmarkHelpers.cpp @@ -71,15 +71,9 @@ void GetResourceBenchmark(const std::vector& paths, const ResTable_ assetmanager.SetConfiguration(*config); } - Res_value value; - ResTable_config selected_config; - uint32_t flags; - uint32_t last_id = 0u; - while (state.KeepRunning()) { - ApkAssetsCookie cookie = assetmanager.GetResource( - resid, false /* may_be_bag */, 0u /* density_override */, &value, &selected_config, &flags); - assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_id); + auto value = assetmanager.GetResource(resid); + assetmanager.ResolveReference(*value); } } diff --git a/libs/androidfw/tests/CommonHelpers.cpp b/libs/androidfw/tests/CommonHelpers.cpp index faa5350f9ecc..3396729536a4 100644 --- a/libs/androidfw/tests/CommonHelpers.cpp +++ b/libs/androidfw/tests/CommonHelpers.cpp @@ -58,8 +58,9 @@ const std::string& GetTestDataPath() { } std::string GetStringFromPool(const ResStringPool* pool, uint32_t idx) { - String8 str = pool->string8ObjectAt(idx); - return std::string(str.string(), str.length()); + auto str = pool->string8ObjectAt(idx); + CHECK(str.has_value()) << "failed to find string entry"; + return std::string(str->string(), str->length()); } } // namespace android diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp index 7aa0dbbafab3..3f0c7cbc8ffc 100644 --- a/libs/androidfw/tests/Idmap_test.cpp +++ b/libs/androidfw/tests/Idmap_test.cpp @@ -62,10 +62,10 @@ class IdmapTest : public ::testing::Test { std::unique_ptr overlayable_assets_; }; -std::string GetStringFromApkAssets(const AssetManager2& asset_manager, const Res_value& value, - ApkAssetsCookie cookie) { +std::string GetStringFromApkAssets(const AssetManager2& asset_manager, + const AssetManager2::SelectedValue& value) { auto assets = asset_manager.GetApkAssets(); - const ResStringPool* string_pool = assets[cookie]->GetLoadedArsc()->GetStringPool(); + const ResStringPool* string_pool = assets[value.cookie]->GetLoadedArsc()->GetStringPool(); return GetStringFromPool(string_pool, value.data); } @@ -75,117 +75,88 @@ TEST_F(IdmapTest, OverlayOverridesResourceValue) { AssetManager2 asset_manager; asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(), overlay_assets_.get()}); - Res_value val; - ResTable_config config; - uint32_t flags; - ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable5, - false /* may_be_bag */, - 0 /* density_override */, &val, &config, - &flags); - ASSERT_EQ(cookie, 2U); - ASSERT_EQ(val.dataType, Res_value::TYPE_STRING); - ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "Overlay One"); + + auto value = asset_manager.GetResource(overlayable::R::string::overlayable5); + ASSERT_TRUE(value.has_value()); + ASSERT_EQ(value->cookie, 2U); + ASSERT_EQ(value->type, Res_value::TYPE_STRING); + ASSERT_EQ("Overlay One", GetStringFromApkAssets(asset_manager, *value)); } TEST_F(IdmapTest, OverlayOverridesResourceValueUsingDifferentPackage) { AssetManager2 asset_manager; asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(), overlay_assets_.get()}); - Res_value val; - ResTable_config config; - uint32_t flags; - ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable10, - false /* may_be_bag */, - 0 /* density_override */, &val, &config, - &flags); - ASSERT_EQ(cookie, 0U); - ASSERT_EQ(val.dataType, Res_value::TYPE_STRING); - ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "yes"); + + auto value = asset_manager.GetResource(overlayable::R::string::overlayable10); + ASSERT_TRUE(value.has_value()); + ASSERT_EQ(value->cookie, 0U); + ASSERT_EQ(value->type, Res_value::TYPE_STRING); + ASSERT_EQ("yes", GetStringFromApkAssets(asset_manager, *value)); } TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInternalResource) { AssetManager2 asset_manager; asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(), overlay_assets_.get()}); - Res_value val; - ResTable_config config; - uint32_t flags; - ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable8, - false /* may_be_bag */, - 0 /* density_override */, &val, &config, - &flags); - ASSERT_EQ(cookie, 2U); - ASSERT_EQ(val.dataType, Res_value::TYPE_REFERENCE); - ASSERT_EQ(val.data, (overlay::R::string::internal & 0x00ffffff) | (0x02 << 24)); + + auto value = asset_manager.GetResource(overlayable::R::string::overlayable8); + ASSERT_TRUE(value.has_value()); + ASSERT_EQ(value->cookie, 2U); + ASSERT_EQ(value->type, Res_value::TYPE_REFERENCE); + ASSERT_EQ(value->data, (overlay::R::string::internal & 0x00ffffffU) | (0x02U << 24)); } TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInlineInteger) { AssetManager2 asset_manager; asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(), overlay_assets_.get()}); - Res_value val; - ResTable_config config; - uint32_t flags; - ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::integer::config_integer, - false /* may_be_bag */, - 0 /* density_override */, &val, &config, - &flags); - ASSERT_EQ(cookie, 2U); - ASSERT_EQ(val.dataType, Res_value::TYPE_INT_DEC); - ASSERT_EQ(val.data, 42); + + auto value = asset_manager.GetResource(overlayable::R::integer::config_integer); + ASSERT_TRUE(value.has_value()); + ASSERT_EQ(value->cookie, 2U); + ASSERT_EQ(value->type, Res_value::TYPE_INT_DEC); + ASSERT_EQ(value->data, 42); } TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInlineString) { AssetManager2 asset_manager; asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(), overlay_assets_.get()}); - Res_value val; - ResTable_config config; - uint32_t flags; - - ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable11, - false /* may_be_bag */, - 0 /* density_override */, &val, &config, - &flags); - ASSERT_EQ(cookie, 2U); - ASSERT_EQ(val.dataType, Res_value::TYPE_STRING); - ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "Hardcoded string"); + + auto value = asset_manager.GetResource(overlayable::R::string::overlayable11); + ASSERT_TRUE(value.has_value()); + ASSERT_EQ(value->cookie, 2U); + ASSERT_EQ(value->type, Res_value::TYPE_STRING); + ASSERT_EQ("Hardcoded string", GetStringFromApkAssets(asset_manager, *value)); } TEST_F(IdmapTest, OverlayOverridesResourceValueUsingOverlayingResource) { AssetManager2 asset_manager; asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(), overlay_assets_.get()}); - Res_value val; - ResTable_config config; - uint32_t flags; - ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable9, - false /* may_be_bag */, - 0 /* density_override */, &val, &config, - &flags); - ASSERT_EQ(cookie, 2U); - ASSERT_EQ(val.dataType, Res_value::TYPE_REFERENCE); - ASSERT_EQ(val.data, overlayable::R::string::overlayable7); + + auto value = asset_manager.GetResource(overlayable::R::string::overlayable9); + ASSERT_TRUE(value.has_value()); + ASSERT_EQ(value->cookie, 2U); + ASSERT_EQ(value->type, Res_value::TYPE_REFERENCE); + ASSERT_EQ(value->data, overlayable::R::string::overlayable7); } TEST_F(IdmapTest, OverlayOverridesXmlParser) { AssetManager2 asset_manager; asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(), overlay_assets_.get()}); - Res_value val; - ResTable_config config; - uint32_t flags; - ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::layout::hello_view, - false /* may_be_bag */, - 0 /* density_override */, &val, &config, - &flags); - ASSERT_EQ(cookie, 2U); - ASSERT_EQ(val.dataType, Res_value::TYPE_STRING); - ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "res/layout/hello_view.xml"); - - auto asset = asset_manager.OpenNonAsset("res/layout/hello_view.xml", cookie, + + auto value = asset_manager.GetResource(overlayable::R::layout::hello_view); + ASSERT_TRUE(value.has_value()); + ASSERT_EQ(value->cookie, 2U); + ASSERT_EQ(value->type, Res_value::TYPE_STRING); + ASSERT_EQ("res/layout/hello_view.xml", GetStringFromApkAssets(asset_manager, *value)); + + auto asset = asset_manager.OpenNonAsset("res/layout/hello_view.xml", value->cookie, Asset::ACCESS_RANDOM); - auto dynamic_ref_table = asset_manager.GetDynamicRefTableForCookie(cookie); + auto dynamic_ref_table = asset_manager.GetDynamicRefTableForCookie(value->cookie); auto xml_tree = util::make_unique(std::move(dynamic_ref_table)); status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), false); ASSERT_EQ(err, NO_ERROR); @@ -216,32 +187,24 @@ TEST_F(IdmapTest, OverlaidResourceHasSameName) { asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(), overlay_assets_.get()}); - AssetManager2::ResourceName name; - ASSERT_TRUE(asset_manager.GetResourceName(overlayable::R::string::overlayable9, &name)); - ASSERT_EQ(std::string(name.package), "com.android.overlayable"); - ASSERT_EQ(String16(name.type16), u"string"); - ASSERT_EQ(std::string(name.entry), "overlayable9"); + auto name = asset_manager.GetResourceName(overlayable::R::string::overlayable9); + ASSERT_TRUE(name.has_value()); + ASSERT_EQ("com.android.overlayable", std::string(name->package)); + ASSERT_EQ(std::u16string(u"string"), std::u16string(name->type16)); + ASSERT_EQ("overlayable9", std::string(name->entry)); } TEST_F(IdmapTest, OverlayLoaderInterop) { - std::string contents; auto loader_assets = ApkAssets::LoadTable("loader/resources.arsc", PROPERTY_LOADER); - AssetManager2 asset_manager; asset_manager.SetApkAssets({overlayable_assets_.get(), loader_assets.get(), overlay_assets_.get()}); - Res_value val; - ResTable_config config; - uint32_t flags; - ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable11, - false /* may_be_bag */, - 0 /* density_override */, &val, &config, - &flags); - std::cout << asset_manager.GetLastResourceResolution(); - ASSERT_EQ(cookie, 1U); - ASSERT_EQ(val.dataType, Res_value::TYPE_STRING); - ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "loader"); + auto value = asset_manager.GetResource(overlayable::R::string::overlayable11); + ASSERT_TRUE(value.has_value()); + ASSERT_EQ(1U, value->cookie); + ASSERT_EQ(Res_value::TYPE_STRING, value->type); + ASSERT_EQ("loader", GetStringFromApkAssets(asset_manager, *value)); } TEST_F(IdmapTest, OverlayAssetsIsUpToDate) { diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp index 2d69dfe4f429..63574110a817 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -50,7 +50,8 @@ TEST(LoadedArscTest, LoadSinglePackageArsc) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/styles/styles.apk", "resources.arsc", &contents)); - std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + auto loaded_arsc = LoadedArsc::Load(reinterpret_cast(contents.data()), + contents.length()); ASSERT_THAT(loaded_arsc, NotNull()); const LoadedPackage* package = @@ -66,9 +67,8 @@ TEST(LoadedArscTest, LoadSinglePackageArsc) { ASSERT_THAT(type_spec, NotNull()); ASSERT_THAT(type_spec->type_count, Ge(1u)); - const ResTable_type* type = type_spec->types[0]; - ASSERT_THAT(type, NotNull()); - ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull()); + auto type = type_spec->types[0]; + ASSERT_TRUE(LoadedPackage::GetEntry(type, entry_index).has_value()); } TEST(LoadedArscTest, LoadSparseEntryApp) { @@ -76,7 +76,8 @@ TEST(LoadedArscTest, LoadSparseEntryApp) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/sparse/sparse.apk", "resources.arsc", &contents)); - std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + std::unique_ptr loaded_arsc = LoadedArsc::Load(contents.data(), + contents.length()); ASSERT_THAT(loaded_arsc, NotNull()); const LoadedPackage* package = @@ -90,9 +91,8 @@ TEST(LoadedArscTest, LoadSparseEntryApp) { ASSERT_THAT(type_spec, NotNull()); ASSERT_THAT(type_spec->type_count, Ge(1u)); - const ResTable_type* type = type_spec->types[0]; - ASSERT_THAT(type, NotNull()); - ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull()); + auto type = type_spec->types[0]; + ASSERT_TRUE(LoadedPackage::GetEntry(type, entry_index).has_value()); } TEST(LoadedArscTest, LoadSharedLibrary) { @@ -100,7 +100,8 @@ TEST(LoadedArscTest, LoadSharedLibrary) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/lib_one/lib_one.apk", "resources.arsc", &contents)); - std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + std::unique_ptr loaded_arsc = LoadedArsc::Load(contents.data(), + contents.length()); ASSERT_THAT(loaded_arsc, NotNull()); const auto& packages = loaded_arsc->GetPackages(); @@ -120,7 +121,8 @@ TEST(LoadedArscTest, LoadAppLinkedAgainstSharedLibrary) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/libclient/libclient.apk", "resources.arsc", &contents)); - std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + std::unique_ptr loaded_arsc = LoadedArsc::Load(contents.data(), + contents.length()); ASSERT_THAT(loaded_arsc, NotNull()); const auto& packages = loaded_arsc->GetPackages(); @@ -145,8 +147,10 @@ TEST(LoadedArscTest, LoadAppAsSharedLibrary) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/appaslib/appaslib.apk", "resources.arsc", &contents)); - std::unique_ptr loaded_arsc = - LoadedArsc::Load(StringPiece(contents), nullptr /* loaded_idmap */, PROPERTY_DYNAMIC); + std::unique_ptr loaded_arsc = LoadedArsc::Load(contents.data(), + contents.length(), + nullptr /* loaded_idmap */, + PROPERTY_DYNAMIC); ASSERT_THAT(loaded_arsc, NotNull()); const auto& packages = loaded_arsc->GetPackages(); @@ -159,7 +163,8 @@ TEST(LoadedArscTest, LoadFeatureSplit) { std::string contents; ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/feature/feature.apk", "resources.arsc", &contents)); - std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + std::unique_ptr loaded_arsc = LoadedArsc::Load(contents.data(), + contents.length()); ASSERT_THAT(loaded_arsc, NotNull()); const LoadedPackage* package = @@ -172,15 +177,12 @@ TEST(LoadedArscTest, LoadFeatureSplit) { const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); ASSERT_THAT(type_spec, NotNull()); ASSERT_THAT(type_spec->type_count, Ge(1u)); - ASSERT_THAT(type_spec->types[0], NotNull()); - size_t len; - const char16_t* type_name16 = - package->GetTypeStringPool()->stringAt(type_spec->type_spec->id - 1, &len); - ASSERT_THAT(type_name16, NotNull()); - EXPECT_THAT(util::Utf16ToUtf8(StringPiece16(type_name16, len)), StrEq("string")); + auto type_name16 = package->GetTypeStringPool()->stringAt(type_spec->type_spec->id - 1); + ASSERT_TRUE(type_name16.has_value()); + EXPECT_THAT(util::Utf16ToUtf8(*type_name16), StrEq("string")); - ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], entry_index), NotNull()); + ASSERT_TRUE(LoadedPackage::GetEntry(type_spec->types[0], entry_index).has_value()); } // AAPT(2) generates resource tables with chunks in a certain order. The rule is that @@ -205,7 +207,8 @@ TEST(LoadedArscTest, LoadOutOfOrderTypeSpecs) { ReadFileFromZipToString(GetTestDataPath() + "/out_of_order_types/out_of_order_types.apk", "resources.arsc", &contents)); - std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + std::unique_ptr loaded_arsc = LoadedArsc::Load(contents.data(), + contents.length()); ASSERT_THAT(loaded_arsc, NotNull()); ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u)); @@ -215,12 +218,10 @@ TEST(LoadedArscTest, LoadOutOfOrderTypeSpecs) { const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(0); ASSERT_THAT(type_spec, NotNull()); ASSERT_THAT(type_spec->type_count, Ge(1u)); - ASSERT_THAT(type_spec->types[0], NotNull()); type_spec = package->GetTypeSpecByTypeIndex(1); ASSERT_THAT(type_spec, NotNull()); ASSERT_THAT(type_spec->type_count, Ge(1u)); - ASSERT_THAT(type_spec->types[0], NotNull()); } TEST(LoadedArscTest, LoadOverlayable) { @@ -228,7 +229,8 @@ TEST(LoadedArscTest, LoadOverlayable) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlayable/overlayable.apk", "resources.arsc", &contents)); - std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + std::unique_ptr loaded_arsc = LoadedArsc::Load(contents.data(), + contents.length()); ASSERT_THAT(loaded_arsc, NotNull()); const LoadedPackage* package = loaded_arsc->GetPackageById( @@ -272,7 +274,8 @@ TEST(LoadedArscTest, ResourceIdentifierIterator) { ASSERT_TRUE( ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents)); - std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + std::unique_ptr loaded_arsc = LoadedArsc::Load(contents.data(), + contents.length()); ASSERT_NE(nullptr, loaded_arsc); const std::vector>& packages = loaded_arsc->GetPackages(); @@ -320,7 +323,8 @@ TEST(LoadedArscTest, GetOverlayableMap) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlayable/overlayable.apk", "resources.arsc", &contents)); - std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + std::unique_ptr loaded_arsc = LoadedArsc::Load(contents.data(), + contents.length()); ASSERT_NE(nullptr, loaded_arsc); const std::vector>& packages = loaded_arsc->GetPackages(); @@ -345,7 +349,7 @@ TEST(LoadedArscTest, LoadCustomLoader) { asset->getLength()); std::unique_ptr loaded_arsc = - LoadedArsc::Load(data, nullptr, PROPERTY_LOADER); + LoadedArsc::Load(data.data(), data.length(), nullptr, PROPERTY_LOADER); ASSERT_THAT(loaded_arsc, NotNull()); const LoadedPackage* package = @@ -361,9 +365,8 @@ TEST(LoadedArscTest, LoadCustomLoader) { ASSERT_THAT(type_spec, NotNull()); ASSERT_THAT(type_spec->type_count, Ge(1u)); - const ResTable_type* type = type_spec->types[0]; - ASSERT_THAT(type, NotNull()); - ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull()); + auto type = type_spec->types[0]; + ASSERT_TRUE(LoadedPackage::GetEntry(type, entry_index).has_value()); } // structs with size fields (like Res_value, ResTable_entry) should be diff --git a/libs/androidfw/tests/ResTable_test.cpp b/libs/androidfw/tests/ResTable_test.cpp index 326474e16e5d..9aeb00c47e63 100644 --- a/libs/androidfw/tests/ResTable_test.cpp +++ b/libs/androidfw/tests/ResTable_test.cpp @@ -442,22 +442,22 @@ TEST(ResTableTest, TruncatedEncodeLength) { ASSERT_LT(val.data, pool->size()); // Make sure a string with a truncated length is read to its correct length - size_t str_len; - const char* target_str8 = pool->string8At(val.data, &str_len); - ASSERT_TRUE(target_str8 != NULL); - ASSERT_EQ(size_t(40076), String8(target_str8, str_len).size()); - ASSERT_EQ(target_str8[40075], ']'); + auto target_str8 = pool->string8At(val.data); + ASSERT_TRUE(target_str8.has_value()); + ASSERT_EQ(size_t(40076), String8(target_str8->data(), target_str8->size()).size()); + ASSERT_EQ(target_str8->data()[40075], ']'); - const char16_t* target_str16 = pool->stringAt(val.data, &str_len); - ASSERT_TRUE(target_str16 != NULL); - ASSERT_EQ(size_t(40076), String16(target_str16, str_len).size()); - ASSERT_EQ(target_str8[40075], (char16_t) ']'); + auto target_str16 = pool->stringAt(val.data); + ASSERT_TRUE(target_str16.has_value()); + ASSERT_EQ(size_t(40076), String16(target_str16->data(), target_str16->size()).size()); + ASSERT_EQ(target_str8->data()[40075], (char16_t) ']'); // Load an edited apk with the null terminator removed from the end of the // string std::string invalid_contents; - ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/length_decode/length_decode_invalid.apk", - "resources.arsc", &invalid_contents)); + ASSERT_TRUE(ReadFileFromZipToString( + GetTestDataPath() + "/length_decode/length_decode_invalid.apk", "resources.arsc", + &invalid_contents)); ResTable invalid_table; ASSERT_EQ(NO_ERROR, invalid_table.add(invalid_contents.data(), invalid_contents.size())); @@ -472,8 +472,8 @@ TEST(ResTableTest, TruncatedEncodeLength) { // Make sure a string with a truncated length that is not null terminated errors // and does not return the string - ASSERT_TRUE(invalid_pool->string8At(invalid_val.data, &str_len) == NULL); - ASSERT_TRUE(invalid_pool->stringAt(invalid_val.data, &str_len) == NULL); + ASSERT_FALSE(invalid_pool->string8At(invalid_val.data).has_value()); + ASSERT_FALSE(invalid_pool->stringAt(invalid_val.data).has_value()); } } // namespace android diff --git a/libs/androidfw/tests/TestHelpers.cpp b/libs/androidfw/tests/TestHelpers.cpp index a81bb6ffab06..10c0a4fc8316 100644 --- a/libs/androidfw/tests/TestHelpers.cpp +++ b/libs/androidfw/tests/TestHelpers.cpp @@ -73,11 +73,15 @@ AssertionResult IsStringEqual(const ResTable& table, uint32_t resource_id, return AssertionFailure() << "table has no string pool for block " << block; } - const String8 actual_str = pool->string8ObjectAt(val.data); - if (String8(expected_str) != actual_str) { - return AssertionFailure() << actual_str.string(); + auto actual_str = pool->string8ObjectAt(val.data); + if (!actual_str.has_value()) { + return AssertionFailure() << "could not find string entry"; } - return AssertionSuccess() << actual_str.string(); + + if (String8(expected_str) != *actual_str) { + return AssertionFailure() << actual_str->string(); + } + return AssertionSuccess() << actual_str->string(); } } // namespace android diff --git a/libs/androidfw/tests/Theme_bench.cpp b/libs/androidfw/tests/Theme_bench.cpp index 594c39eb682f..f3d60bbe4f15 100644 --- a/libs/androidfw/tests/Theme_bench.cpp +++ b/libs/androidfw/tests/Theme_bench.cpp @@ -70,11 +70,8 @@ static void BM_ThemeGetAttribute(benchmark::State& state) { auto theme = assets.NewTheme(); theme->ApplyStyle(kStyleId, false /* force */); - Res_value value; - uint32_t flags; - while (state.KeepRunning()) { - theme->GetAttribute(kAttrId, &value, &flags); + theme->GetAttribute(kAttrId); } } BENCHMARK(BM_ThemeGetAttribute); diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp index 16b9c75982fb..f658735da515 100644 --- a/libs/androidfw/tests/Theme_test.cpp +++ b/libs/androidfw/tests/Theme_test.cpp @@ -67,10 +67,7 @@ TEST_F(ThemeTest, EmptyTheme) { std::unique_ptr theme = assetmanager.NewTheme(); EXPECT_EQ(0u, theme->GetChangingConfigurations()); EXPECT_EQ(&assetmanager, theme->GetAssetManager()); - - Res_value value; - uint32_t flags; - EXPECT_EQ(kInvalidCookie, theme->GetAttribute(app::R::attr::attr_one, &value, &flags)); + EXPECT_FALSE(theme->GetAttribute(app::R::attr::attr_one).has_value()); } TEST_F(ThemeTest, SingleThemeNoParent) { @@ -78,23 +75,19 @@ TEST_F(ThemeTest, SingleThemeNoParent) { assetmanager.SetApkAssets({style_assets_.get()}); std::unique_ptr theme = assetmanager.NewTheme(); - ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleOne)); - - Res_value value; - uint32_t flags; - ApkAssetsCookie cookie; - - cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); - EXPECT_EQ(1u, value.data); - EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), flags); - - cookie = theme->GetAttribute(app::R::attr::attr_two, &value, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); - EXPECT_EQ(2u, value.data); - EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), flags); + ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleOne).has_value()); + + auto value = theme->GetAttribute(app::R::attr::attr_one); + ASSERT_TRUE(value); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); + EXPECT_EQ(1u, value->data); + EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), value->flags); + + value = theme->GetAttribute(app::R::attr::attr_two); + ASSERT_TRUE(value); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); + EXPECT_EQ(2u, value->data); + EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), value->flags); } TEST_F(ThemeTest, SingleThemeWithParent) { @@ -102,32 +95,28 @@ TEST_F(ThemeTest, SingleThemeWithParent) { assetmanager.SetApkAssets({style_assets_.get()}); std::unique_ptr theme = assetmanager.NewTheme(); - ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo)); - - Res_value value; - uint32_t flags; - ApkAssetsCookie cookie; - - cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); - EXPECT_EQ(1u, value.data); - EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), flags); - - cookie = theme->GetAttribute(app::R::attr::attr_two, &value, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_STRING, value.dataType); - EXPECT_EQ(0, cookie); + ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo).has_value()); + + auto value = theme->GetAttribute(app::R::attr::attr_one); + ASSERT_TRUE(value); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); + EXPECT_EQ(1u, value->data); + EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), value->flags); + + value = theme->GetAttribute(app::R::attr::attr_two); + ASSERT_TRUE(value); + EXPECT_EQ(Res_value::TYPE_STRING, value->type); + EXPECT_EQ(0, value->cookie); EXPECT_EQ(std::string("string"), - GetStringFromPool(assetmanager.GetStringPoolForCookie(0), value.data)); - EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), flags); + GetStringFromPool(assetmanager.GetStringPoolForCookie(0), value->data)); + EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), value->flags); // This attribute should point to an attr_indirect, so the result should be 3. - cookie = theme->GetAttribute(app::R::attr::attr_three, &value, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); - EXPECT_EQ(3u, value.data); - EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), flags); + value = theme->GetAttribute(app::R::attr::attr_three); + ASSERT_TRUE(value); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); + EXPECT_EQ(3u, value->data); + EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), value->flags); } TEST_F(ThemeTest, TryToUseBadResourceId) { @@ -135,11 +124,8 @@ TEST_F(ThemeTest, TryToUseBadResourceId) { assetmanager.SetApkAssets({style_assets_.get()}); std::unique_ptr theme = assetmanager.NewTheme(); - ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo)); - - Res_value value; - uint32_t flags; - ASSERT_EQ(kInvalidCookie, theme->GetAttribute(0x7f000001, &value, &flags)); + ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo).has_value()); + ASSERT_FALSE(theme->GetAttribute(0x7f000001)); } TEST_F(ThemeTest, MultipleThemesOverlaidNotForce) { @@ -147,33 +133,29 @@ TEST_F(ThemeTest, MultipleThemesOverlaidNotForce) { assetmanager.SetApkAssets({style_assets_.get()}); std::unique_ptr theme = assetmanager.NewTheme(); - ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo)); - ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleThree)); - - Res_value value; - uint32_t flags; - ApkAssetsCookie cookie; + ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo).has_value()); + ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleThree).has_value()); // attr_one is still here from the base. - cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); - EXPECT_EQ(1u, value.data); - EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), flags); + auto value = theme->GetAttribute(app::R::attr::attr_one); + ASSERT_TRUE(value); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); + EXPECT_EQ(1u, value->data); + EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), value->flags); // check for the new attr_six - cookie = theme->GetAttribute(app::R::attr::attr_six, &value, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); - EXPECT_EQ(6u, value.data); - EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), flags); + value = theme->GetAttribute(app::R::attr::attr_six); + ASSERT_TRUE(value); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); + EXPECT_EQ(6u, value->data); + EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), value->flags); // check for the old attr_five (force=true was not used). - cookie = theme->GetAttribute(app::R::attr::attr_five, &value, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType); - EXPECT_EQ(app::R::string::string_one, value.data); - EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), flags); + value = theme->GetAttribute(app::R::attr::attr_five); + ASSERT_TRUE(value); + EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type); + EXPECT_EQ(app::R::string::string_one, value->data); + EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), value->flags); } TEST_F(ThemeTest, MultipleThemesOverlaidForced) { @@ -181,33 +163,29 @@ TEST_F(ThemeTest, MultipleThemesOverlaidForced) { assetmanager.SetApkAssets({style_assets_.get()}); std::unique_ptr theme = assetmanager.NewTheme(); - ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo)); - ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleThree, true /* force */)); - - Res_value value; - uint32_t flags; - ApkAssetsCookie cookie; + ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo).has_value()); + ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleThree, true /* force */).has_value()); // attr_one is still here from the base. - cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); - EXPECT_EQ(1u, value.data); - EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), flags); + auto value = theme->GetAttribute(app::R::attr::attr_one); + ASSERT_TRUE(value); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); + EXPECT_EQ(1u, value->data); + EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), value->flags); // check for the new attr_six - cookie = theme->GetAttribute(app::R::attr::attr_six, &value, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); - EXPECT_EQ(6u, value.data); - EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), flags); + value = theme->GetAttribute(app::R::attr::attr_six); + ASSERT_TRUE(value); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); + EXPECT_EQ(6u, value->data); + EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), value->flags); // check for the new attr_five (force=true was used). - cookie = theme->GetAttribute(app::R::attr::attr_five, &value, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); - EXPECT_EQ(5u, value.data); - EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), flags); + value = theme->GetAttribute(app::R::attr::attr_five); + ASSERT_TRUE(value); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); + EXPECT_EQ(5u, value->data); + EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), value->flags); } TEST_F(ThemeTest, ResolveDynamicAttributesAndReferencesToSharedLibrary) { @@ -216,28 +194,24 @@ TEST_F(ThemeTest, ResolveDynamicAttributesAndReferencesToSharedLibrary) { {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()}); std::unique_ptr theme = assetmanager.NewTheme(); - ASSERT_TRUE(theme->ApplyStyle(libclient::R::style::Theme, false /*force*/)); - - Res_value value; - uint32_t flags; - ApkAssetsCookie cookie; + ASSERT_TRUE(theme->ApplyStyle(libclient::R::style::Theme, false /*force*/).has_value()); // The attribute should be resolved to the final value. - cookie = theme->GetAttribute(libclient::R::attr::foo, &value, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); - EXPECT_EQ(700u, value.data); - EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), flags); + auto value = theme->GetAttribute(libclient::R::attr::foo); + ASSERT_TRUE(value); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); + EXPECT_EQ(700u, value->data); + EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), value->flags); // The reference should be resolved to a TYPE_REFERENCE. - cookie = theme->GetAttribute(libclient::R::attr::bar, &value, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType); + value = theme->GetAttribute(libclient::R::attr::bar); + ASSERT_TRUE(value); + EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type); // lib_one is assigned package ID 0x03. - EXPECT_EQ(3u, get_package_id(value.data)); - EXPECT_EQ(get_type_id(lib_one::R::string::foo), get_type_id(value.data)); - EXPECT_EQ(get_entry_id(lib_one::R::string::foo), get_entry_id(value.data)); + EXPECT_EQ(3u, get_package_id(value->data)); + EXPECT_EQ(get_type_id(lib_one::R::string::foo), get_type_id(value->data)); + EXPECT_EQ(get_entry_id(lib_one::R::string::foo), get_entry_id(value->data)); } TEST_F(ThemeTest, CopyThemeSameAssetManager) { @@ -245,24 +219,20 @@ TEST_F(ThemeTest, CopyThemeSameAssetManager) { assetmanager.SetApkAssets({style_assets_.get()}); std::unique_ptr theme_one = assetmanager.NewTheme(); - ASSERT_TRUE(theme_one->ApplyStyle(app::R::style::StyleOne)); - - Res_value value; - uint32_t flags; - ApkAssetsCookie cookie; + ASSERT_TRUE(theme_one->ApplyStyle(app::R::style::StyleOne).has_value()); // attr_one is still here from the base. - cookie = theme_one->GetAttribute(app::R::attr::attr_one, &value, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); - EXPECT_EQ(1u, value.data); - EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), flags); + auto value = theme_one->GetAttribute(app::R::attr::attr_one); + ASSERT_TRUE(value); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); + EXPECT_EQ(1u, value->data); + EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), value->flags); // attr_six is not here. - EXPECT_EQ(kInvalidCookie, theme_one->GetAttribute(app::R::attr::attr_six, &value, &flags)); + ASSERT_FALSE(theme_one->GetAttribute(app::R::attr::attr_six).has_value()); std::unique_ptr theme_two = assetmanager.NewTheme(); - ASSERT_TRUE(theme_two->ApplyStyle(app::R::style::StyleThree)); + ASSERT_TRUE(theme_two->ApplyStyle(app::R::style::StyleThree).has_value()); // Copy the theme to theme_one. theme_one->SetTo(*theme_two); @@ -271,14 +241,14 @@ TEST_F(ThemeTest, CopyThemeSameAssetManager) { theme_two->Clear(); // attr_one is now not here. - EXPECT_EQ(kInvalidCookie, theme_one->GetAttribute(app::R::attr::attr_one, &value, &flags)); + ASSERT_FALSE(theme_one->GetAttribute(app::R::attr::attr_one).has_value()); // attr_six is now here because it was copied. - cookie = theme_one->GetAttribute(app::R::attr::attr_six, &value, &flags); - ASSERT_NE(kInvalidCookie, cookie); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); - EXPECT_EQ(6u, value.data); - EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), flags); + value = theme_one->GetAttribute(app::R::attr::attr_six); + ASSERT_TRUE(value); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); + EXPECT_EQ(6u, value->data); + EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), value->flags); } TEST_F(ThemeTest, OnlyCopySameAssetsThemeWhenAssetManagersDiffer) { @@ -291,39 +261,43 @@ TEST_F(ThemeTest, OnlyCopySameAssetsThemeWhenAssetManagersDiffer) { style_assets_.get()}); auto theme_dst = assetmanager_dst.NewTheme(); - ASSERT_TRUE(theme_dst->ApplyStyle(app::R::style::StyleOne)); + ASSERT_TRUE(theme_dst->ApplyStyle(app::R::style::StyleOne).has_value()); auto theme_src = assetmanager_src.NewTheme(); - ASSERT_TRUE(theme_src->ApplyStyle(R::style::Theme_One)); - ASSERT_TRUE(theme_src->ApplyStyle(app::R::style::StyleTwo)); + ASSERT_TRUE(theme_src->ApplyStyle(R::style::Theme_One).has_value()); + ASSERT_TRUE(theme_src->ApplyStyle(app::R::style::StyleTwo).has_value()); ASSERT_TRUE(theme_src->ApplyStyle(fix_package_id(lib_one::R::style::Theme, 0x03), - false /*force*/)); + false /*force*/).has_value()); ASSERT_TRUE(theme_src->ApplyStyle(fix_package_id(lib_two::R::style::Theme, 0x02), - false /*force*/)); + false /*force*/).has_value()); theme_dst->SetTo(*theme_src); - Res_value value; - uint32_t flags; - // System resources (present in destination asset manager). - EXPECT_EQ(0, theme_dst->GetAttribute(R::attr::foreground, &value, &flags)); + auto value = theme_dst->GetAttribute(R::attr::foreground); + ASSERT_TRUE(value.has_value()); + EXPECT_EQ(0, value->cookie); // The cookie of the style asset is 3 in the source and 2 in the destination. // Check that the cookie has been rewritten to the destination values. - EXPECT_EQ(2, theme_dst->GetAttribute(app::R::attr::attr_one, &value, &flags)); + value = theme_dst->GetAttribute(app::R::attr::attr_one); + ASSERT_TRUE(value.has_value()); + EXPECT_EQ(2, value->cookie); // The cookie of the lib_one asset is 2 in the source and 1 in the destination. // The package id of the lib_one package is 0x03 in the source and 0x02 in the destination // Check that the cookie and packages have been rewritten to the destination values. - EXPECT_EQ(1, theme_dst->GetAttribute(fix_package_id(lib_one::R::attr::attr1, 0x02), &value, - &flags)); - EXPECT_EQ(1, theme_dst->GetAttribute(fix_package_id(lib_one::R::attr::attr2, 0x02), &value, - &flags)); + value = theme_dst->GetAttribute(fix_package_id(lib_one::R::attr::attr1, 0x02)); + ASSERT_TRUE(value.has_value()); + EXPECT_EQ(1, value->cookie); + + value = theme_dst->GetAttribute(fix_package_id(lib_one::R::attr::attr2, 0x02)); + ASSERT_TRUE(value.has_value()); + EXPECT_EQ(1, value->cookie); // attr2 references an attribute in lib_one. Check that the resolution of the attribute value is // correct after the value of attr2 had its package id rewritten to the destination package id. - EXPECT_EQ(700, value.data); + EXPECT_EQ(700, value->data); } TEST_F(ThemeTest, CopyNonReferencesWhenPackagesDiffer) { @@ -335,28 +309,32 @@ TEST_F(ThemeTest, CopyNonReferencesWhenPackagesDiffer) { auto theme_dst = assetmanager_dst.NewTheme(); auto theme_src = assetmanager_src.NewTheme(); - ASSERT_TRUE(theme_src->ApplyStyle(app::R::style::StyleSeven)); + ASSERT_TRUE(theme_src->ApplyStyle(app::R::style::StyleSeven).has_value()); theme_dst->SetTo(*theme_src); - Res_value value; - uint32_t flags; - // Allow inline resource values to be copied even if the source apk asset is not present in the // destination. - EXPECT_EQ(0, theme_dst->GetAttribute(0x0101021b /* android:versionCode */, &value, &flags)); + auto value = theme_dst->GetAttribute(0x0101021b /* android:versionCode */); + ASSERT_TRUE(value.has_value()); + EXPECT_EQ(0, value->cookie); // Do not copy strings since the data is an index into the values string pool of the source apk // asset. - EXPECT_EQ(-1, theme_dst->GetAttribute(0x01010001 /* android:label */, &value, &flags)); + EXPECT_FALSE(theme_dst->GetAttribute(0x01010001 /* android:label */).has_value()); // Do not copy values that reference another resource if the resource is not present in the // destination. - EXPECT_EQ(-1, theme_dst->GetAttribute(0x01010002 /* android:icon */, &value, &flags)); - EXPECT_EQ(-1, theme_dst->GetAttribute(0x010100d1 /* android:tag */, &value, &flags)); + EXPECT_FALSE(theme_dst->GetAttribute(0x01010002 /* android:icon */).has_value()); + EXPECT_FALSE(theme_dst->GetAttribute(0x010100d1 /* android:tag */).has_value()); // Allow @empty to and @null to be copied. - EXPECT_EQ(0, theme_dst->GetAttribute(0x010100d0 /* android:id */, &value, &flags)); - EXPECT_EQ(0, theme_dst->GetAttribute(0x01010000 /* android:theme */, &value, &flags)); + value = theme_dst->GetAttribute(0x010100d0 /* android:id */); + ASSERT_TRUE(value.has_value()); + EXPECT_EQ(0, value->cookie); + + value = theme_dst->GetAttribute(0x01010000 /* android:theme */); + ASSERT_TRUE(value.has_value()); + EXPECT_EQ(0, value->cookie); } } // namespace android -- cgit v1.2.3 From e7ab62723ac8bc1c95405353e7f625956b1dfbe3 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Fri, 13 Nov 2020 18:06:15 -0800 Subject: Set resource id correctly when resolve fails If for some reason the resource id cannot be resolved to a value (there is no configuration that matches the AssetManager configuration or some error occurs), set the resource id of the SelectedValue to the resource id that could not be resolved. This was the behavior before the AssetManager IncFs hardening refactor. Bug: 173203252 Test: atest com.google.android.config.pts.PreinstalledAppsTestCase Test: Chrome icon appears on launcher Change-Id: Iad1760c0e246da1a4bf64d1c2ec60bb08da32d06 Merged-In: Iad1760c0e246da1a4bf64d1c2ec60bb08da32d06 --- libs/androidfw/AssetManager2.cpp | 10 +++++--- libs/androidfw/AttributeResolution.cpp | 4 +-- libs/androidfw/tests/AssetManager2_test.cpp | 38 ++++++++++++++++++++++++++--- 3 files changed, 44 insertions(+), 8 deletions(-) (limited to 'libs') diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 3e54dc67db76..8bab73cd15f0 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -975,19 +975,23 @@ base::expected AssetManager2::ResolveReference( for (uint32_t i = 0U;; i++) { auto result = GetResource(resolve_resid, true /*may_be_bag*/); if (!result.has_value()) { + value.resid = resolve_resid; return base::unexpected(result.error()); } + // If resource resolution fails, the value should be set to the last reference that was able to + // be resolved successfully. + value = *result; + value.flags |= combined_flags; + if (result->type != Res_value::TYPE_REFERENCE || result->data == Res_value::DATA_NULL_UNDEFINED || result->data == resolve_resid || i == kMaxIterations) { // This reference can't be resolved, so exit now and let the caller deal with it. - value = *result; - value.flags |= combined_flags; return {}; } - combined_flags |= result->flags; + combined_flags = result->flags; resolve_resid = result->data; } } diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp index 71919fdbb736..d07b452ad40e 100644 --- a/libs/androidfw/AttributeResolution.cpp +++ b/libs/androidfw/AttributeResolution.cpp @@ -265,7 +265,7 @@ base::expected ApplyStyle(Theme* theme, ResXMLParser* x const size_t xml_attr_idx = xml_attr_finder.Find(cur_ident); if (xml_attr_idx != xml_attr_finder.end()) { // We found the attribute we were looking for. - Res_value attribute_value; + Res_value attribute_value{}; xml_parser->getAttributeValue(xml_attr_idx, &attribute_value); value.type = attribute_value.dataType; value.data = attribute_value.data; @@ -377,7 +377,7 @@ base::expected RetrieveAttributes(AssetManager2* assetm // Retrieve the current XML attribute if it matches, and step to next. if (ix < xml_attr_count && cur_ident == cur_xml_attr) { - Res_value attribute_value; + Res_value attribute_value{}; xml_parser->getAttributeValue(ix, &attribute_value); value.type = attribute_value.dataType; value.data = attribute_value.data; diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index 3638ce1f92d7..0f5afd45546c 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -446,9 +446,6 @@ TEST_F(AssetManager2Test, KeepLastReferenceIdUnmodifiedIfNoReferenceIsResolved) AssetManager2 assetmanager; assetmanager.SetApkAssets({basic_assets_.get()}); - ResTable_config selected_config; - memset(&selected_config, 0, sizeof(selected_config)); - // Create some kind of value that is NOT a reference. AssetManager2::SelectedValue value{}; value.cookie = 1; @@ -461,6 +458,41 @@ TEST_F(AssetManager2Test, KeepLastReferenceIdUnmodifiedIfNoReferenceIsResolved) EXPECT_EQ(basic::R::string::test1, value.resid); } +TEST_F(AssetManager2Test, ResolveReferenceMissingResource) { + AssetManager2 assetmanager; + assetmanager.SetApkAssets({basic_assets_.get()}); + + const uint32_t kMissingResId = 0x8001ffff; + AssetManager2::SelectedValue value{}; + value.type = Res_value::TYPE_REFERENCE; + value.data = kMissingResId; + + auto result = assetmanager.ResolveReference(value); + ASSERT_FALSE(result.has_value()); + EXPECT_EQ(Res_value::TYPE_REFERENCE, value.type); + EXPECT_EQ(kMissingResId, value.data); + EXPECT_EQ(kMissingResId, value.resid); + EXPECT_EQ(-1, value.cookie); + EXPECT_EQ(0, value.flags); +} + +TEST_F(AssetManager2Test, ResolveReferenceMissingResourceLib) { + AssetManager2 assetmanager; + assetmanager.SetApkAssets({libclient_assets_.get()}); + + AssetManager2::SelectedValue value{}; + value.type = Res_value::TYPE_REFERENCE; + value.data = libclient::R::string::foo_one; + + auto result = assetmanager.ResolveReference(value); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(Res_value::TYPE_DYNAMIC_REFERENCE, value.type); + EXPECT_EQ(lib_one::R::string::foo, value.data); + EXPECT_EQ(libclient::R::string::foo_one, value.resid); + EXPECT_EQ(0, value.cookie); + EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), value.flags); +} + static bool IsConfigurationPresent(const std::set& configurations, const ResTable_config& configuration) { return configurations.count(configuration) > 0; -- cgit v1.2.3 From a45506e6f6619f59ce1ae94b20ad377b86966be0 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Mon, 16 Nov 2020 23:08:18 +0000 Subject: Revert^2 "Cache resolved theme values" 6ca48473e533a8b89abac6294a0bb8130b8c8c89 Change-Id: Icb295186b85e1edcdcebc1d746f7ff0d6ef66829 Merged-In: Icb295186b85e1edcdcebc1d746f7ff0d6ef66829 --- libs/androidfw/AssetManager2.cpp | 28 ++++++++++++++++++--- libs/androidfw/AttributeResolution.cpp | 7 +++--- libs/androidfw/include/androidfw/AssetManager2.h | 12 ++++++--- libs/androidfw/tests/AssetManager2_test.cpp | 31 ++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 12 deletions(-) (limited to 'libs') diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 8bab73cd15f0..a545b3d5e134 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -963,14 +963,25 @@ base::expected AssetManager2::GetRe } base::expected AssetManager2::ResolveReference( - AssetManager2::SelectedValue& value) const { + AssetManager2::SelectedValue& value, bool cache_value) const { if (value.type != Res_value::TYPE_REFERENCE || value.data == 0U) { // Not a reference. Nothing to do. return {}; } - uint32_t combined_flags = value.flags; - uint32_t resolve_resid = value.data; + const uint32_t original_flags = value.flags; + const uint32_t original_resid = value.data; + if (cache_value) { + auto cached_value = cached_resolved_values_.find(value.data); + if (cached_value != cached_resolved_values_.end()) { + value = cached_value->second; + value.flags |= original_flags; + return {}; + } + } + + uint32_t combined_flags = 0U; + uint32_t resolve_resid = original_resid; constexpr const uint32_t kMaxIterations = 20; for (uint32_t i = 0U;; i++) { auto result = GetResource(resolve_resid, true /*may_be_bag*/); @@ -988,6 +999,13 @@ base::expected AssetManager2::ResolveReference( result->data == Res_value::DATA_NULL_UNDEFINED || result->data == resolve_resid || i == kMaxIterations) { // This reference can't be resolved, so exit now and let the caller deal with it. + if (cache_value) { + cached_resolved_values_[original_resid] = value; + } + + // Above value is cached without original_flags to ensure they don't get included in future + // queries that hit the cache + value.flags |= original_flags; return {}; } @@ -1357,6 +1375,8 @@ void AssetManager2::InvalidateCaches(uint32_t diff) { ++iter; } } + + cached_resolved_values_.clear(); } uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) const { @@ -1537,7 +1557,7 @@ base::expected Theme::ResolveAttributeReference( return base::unexpected(std::nullopt); } - auto resolve_result = asset_manager_->ResolveReference(*result); + auto resolve_result = asset_manager_->ResolveReference(*result, true /* cache_value */); if (resolve_result.has_value()) { result->flags |= value.flags; value = *result; diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp index d07b452ad40e..c188712e5877 100644 --- a/libs/androidfw/AttributeResolution.cpp +++ b/libs/androidfw/AttributeResolution.cpp @@ -39,8 +39,7 @@ class XmlAttributeFinder : public BackTrackingAttributeFinder { public: explicit XmlAttributeFinder(const ResXMLParser* parser) - : BackTrackingAttributeFinder( - 0, parser != nullptr ? parser->getAttributeCount() : 0), + : BackTrackingAttributeFinder(0, parser != nullptr ? parser->getAttributeCount() : 0), parser_(parser) {} inline uint32_t GetAttribute(size_t index) const { @@ -178,7 +177,7 @@ base::expected ResolveAttrs(Theme* theme, uint32_t def_ value = *attr_value; DEBUG_LOG("-> From theme: type=0x%x, data=0x%08x", value.type, value.data); - const auto result = assetmanager->ResolveReference(value); + const auto result = assetmanager->ResolveReference(value, true /* cache_value */); if (UNLIKELY(IsIOError(result))) { return base::unexpected(GetIOError(result.error())); } @@ -310,7 +309,7 @@ base::expected ApplyStyle(Theme* theme, ResXMLParser* x value = *attr_value; DEBUG_LOG("-> From theme: type=0x%x, data=0x%08x", value.type, value.data); - auto result = assetmanager->ResolveReference(value); + auto result = assetmanager->ResolveReference(value, true /* cache_value */); if (UNLIKELY(IsIOError(result))) { return base::unexpected(GetIOError(result.error())); } diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index 4e993b0838a2..a92694c94b9f 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -264,11 +264,14 @@ class AssetManager2 { // Resolves the resource referenced in `value` if the type is Res_value::TYPE_REFERENCE. // // If the data type is not Res_value::TYPE_REFERENCE, no work is done. Configuration flags of the - // values pointed to by the reference are OR'd into `value.flags`. + // values pointed to by the reference are OR'd into `value.flags`. If `cache_value` is true, then + // the resolved value will be cached and used when attempting to resolve the resource id specified + // in `value`. // // Returns a null error if the resource could not be resolved, or an I/O error if reading // resource data failed. - base::expected ResolveReference(SelectedValue& value) const; + base::expected ResolveReference(SelectedValue& value, + bool cache_value = false) const; // Retrieves the best matching bag/map resource with ID `resid`. // @@ -446,13 +449,14 @@ class AssetManager2 { // a number of times for each view during View inspection. mutable std::unordered_map> cached_bag_resid_stacks_; + // Cached set of resolved resource values. + mutable std::unordered_map cached_resolved_values_; + // Whether or not to save resource resolution steps bool resource_resolution_logging_enabled_ = false; struct Resolution { - struct Step { - enum class Type { INITIAL, BETTER_MATCH, diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index 0f5afd45546c..471b0ee1e7e9 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -458,6 +458,37 @@ TEST_F(AssetManager2Test, KeepLastReferenceIdUnmodifiedIfNoReferenceIsResolved) EXPECT_EQ(basic::R::string::test1, value.resid); } +TEST_F(AssetManager2Test, ResolveReferenceMissingResourceDoNotCacheFlags) { + AssetManager2 assetmanager; + assetmanager.SetApkAssets({basic_assets_.get()}); + { + AssetManager2::SelectedValue value{}; + value.data = basic::R::string::test1; + value.type = Res_value::TYPE_REFERENCE; + value.flags = ResTable_config::CONFIG_KEYBOARD; + + auto result = assetmanager.ResolveReference(value); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(Res_value::TYPE_STRING, value.type); + EXPECT_EQ(0, value.cookie); + EXPECT_EQ(basic::R::string::test1, value.resid); + EXPECT_EQ(ResTable_typeSpec::SPEC_PUBLIC | ResTable_config::CONFIG_KEYBOARD, value.flags); + } + { + AssetManager2::SelectedValue value{}; + value.data = basic::R::string::test1; + value.type = Res_value::TYPE_REFERENCE; + value.flags = ResTable_config::CONFIG_COLOR_MODE; + + auto result = assetmanager.ResolveReference(value); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(Res_value::TYPE_STRING, value.type); + EXPECT_EQ(0, value.cookie); + EXPECT_EQ(basic::R::string::test1, value.resid); + EXPECT_EQ(ResTable_typeSpec::SPEC_PUBLIC | ResTable_config::CONFIG_COLOR_MODE, value.flags); + } +} + TEST_F(AssetManager2Test, ResolveReferenceMissingResource) { AssetManager2 assetmanager; assetmanager.SetApkAssets({basic_assets_.get()}); -- cgit v1.2.3